Skip to content

Commit b9a89b2

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Migrator: improve pubspec parsing
* Prevent cast exception crash when pubspec.yaml is not a map * Prevent cast exception crash when pubspec sdk is not a string * Test error cases for parsing pubspec. Change-Id: Id392815207dc52719cfec5038d6fa2d09f952f08 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149602 Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent fe6fc78 commit b9a89b2

File tree

2 files changed

+137
-33
lines changed

2 files changed

+137
-33
lines changed

pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class NonNullableFix {
122122

123123
/// Update the pubspec.yaml file to specify a minimum Dart SDK version which
124124
/// enables the Null Safety feature.
125-
Future<void> processPackage(Folder pkgFolder) async {
125+
void processPackage(Folder pkgFolder) {
126126
if (!_packageIsNNBD) {
127127
return;
128128
}
@@ -137,14 +137,17 @@ class NonNullableFix {
137137

138138
try {
139139
pubspecContent = pubspecFile.readAsStringSync();
140+
var pubspecYaml = loadYaml(pubspecContent);
141+
if (pubspecYaml is YamlMap) {
142+
pubspecMap = pubspecYaml;
143+
} else {
144+
throw FormatException('pubspec.yaml file is not a YAML map');
145+
}
140146
} on FileSystemException catch (e) {
141-
processYamlException('read', pubspecFile.path, e);
147+
_processPubspecException('read', pubspecFile.path, e);
142148
return;
143-
}
144-
try {
145-
pubspecMap = loadYaml(pubspecContent) as YamlNode;
146-
} on YamlException catch (e) {
147-
processYamlException('parse', pubspecFile.path, e);
149+
} on FormatException catch (e) {
150+
_processPubspecException('parse', pubspecFile.path, e);
148151
return;
149152
}
150153

@@ -202,17 +205,25 @@ environment:
202205
sdk: '$_intendedSdkVersionConstraint'""";
203206
insertAfterParent(environmentOptions.span, content);
204207
} else if (sdk is YamlScalar) {
205-
var currentConstraint = VersionConstraint.parse(sdk.value as String);
206-
var minimumVersion = Version.parse(_intendedMinimumSdkVersion);
207-
if (currentConstraint is VersionRange &&
208-
currentConstraint.min >= minimumVersion) {
209-
// The current SDK version constraint already enables Null Safety.
210-
return;
208+
VersionConstraint currentConstraint;
209+
if (sdk.value is String) {
210+
currentConstraint = VersionConstraint.parse(sdk.value as String);
211+
var minimumVersion = Version.parse(_intendedMinimumSdkVersion);
212+
if (currentConstraint is VersionRange &&
213+
currentConstraint.min >= minimumVersion) {
214+
// The current SDK version constraint already enables Null Safety.
215+
// Do not edit pubspec.yaml.
216+
return;
217+
} else {
218+
// TODO(srawlins): This overwrites the current maximum version. In
219+
// the uncommon situation that the maximum is not '<3.0.0', it
220+
// should not.
221+
replaceSpan(sdk.span, "'$_intendedSdkVersionConstraint'");
222+
}
211223
} else {
212-
// TODO(srawlins): This overwrites the current maximum version. In
213-
// the uncommon situation that the maximum is not '<3.0.0', it should
214-
// not.
215-
replaceSpan(sdk.span, "'$_intendedSdkVersionConstraint'");
224+
// Something is odd with the SDK constraint we've found in
225+
// pubspec.yaml; Best to leave it alone.
226+
return;
216227
}
217228
}
218229
}
@@ -237,20 +248,6 @@ environment:
237248
}
238249
}
239250

240-
void processYamlException(String action, String optionsFilePath, error) {
241-
listener.addRecommendation('''Failed to $action options file
242-
$optionsFilePath
243-
$error
244-
245-
Manually update this file to enable the Null Safety language feature by
246-
adding:
247-
248-
environment:
249-
sdk: '$_intendedSdkVersionConstraint';
250-
''');
251-
_packageIsNNBD = false;
252-
}
253-
254251
Future<MigrationState> rerun() async {
255252
reset();
256253
await rerunFunction();
@@ -275,6 +272,20 @@ environment:
275272
_server?.close();
276273
}
277274

275+
void _processPubspecException(String action, String pubspecPath, error) {
276+
listener.addRecommendation('''Failed to $action pubspec file
277+
$pubspecPath
278+
$error
279+
280+
Manually update this file to enable the Null Safety language feature by
281+
adding:
282+
283+
environment:
284+
sdk: '$_intendedSdkVersionConstraint';
285+
''');
286+
_packageIsNNBD = false;
287+
}
288+
278289
/// Allows unit tests to shut down any rogue servers that have been started,
279290
/// so that unit testing can complete.
280291
@visibleForTesting

pkg/nnbd_migration/test/migration_cli_test.dart

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,11 @@ mixin _MigrationCliTestMethods on _MigrationCliTestBase {
208208
resourceProvider.newFolder(resourceProvider.pathContext.current);
209209
}
210210

211-
Map<String, String> simpleProject({bool migrated: false, String sourceText}) {
211+
Map<String, String> simpleProject(
212+
{bool migrated: false, String sourceText, String pubspecText}) {
212213
return {
213-
'pubspec.yaml': '''
214+
'pubspec.yaml': pubspecText ??
215+
'''
214216
name: test
215217
environment:
216218
sdk: '${migrated ? '>=2.9.0 <2.10.0' : '>=2.6.0 <3.0.0'}'
@@ -1091,6 +1093,97 @@ int f() => null;
10911093
expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
10921094
}
10931095

1096+
test_pubspec_does_not_exist() async {
1097+
var projectContents = simpleProject()..remove('pubspec.yaml');
1098+
var projectDir = await createProjectDir(projectContents);
1099+
var cli = _createCli();
1100+
await cli
1101+
.run(_parseArgs(['--no-web-preview', '--apply-changes', projectDir]));
1102+
// The Dart source code should still be migrated.
1103+
assertProjectContents(
1104+
projectDir, simpleProject(migrated: true)..remove('pubspec.yaml'));
1105+
}
1106+
1107+
test_pubspec_environment_is_missing_sdk() async {
1108+
var projectContents = simpleProject(pubspecText: '''
1109+
name: test
1110+
environment:
1111+
foo: 1
1112+
''');
1113+
var projectDir = await createProjectDir(projectContents);
1114+
var cli = _createCli();
1115+
await cli
1116+
.run(_parseArgs(['--no-web-preview', '--apply-changes', projectDir]));
1117+
// The Dart source code should still be migrated.
1118+
assertProjectContents(
1119+
projectDir, simpleProject(migrated: true, pubspecText: '''
1120+
name: test
1121+
environment:
1122+
foo: 1
1123+
sdk: '>=2.9.0 <2.10.0'
1124+
'''));
1125+
}
1126+
1127+
test_pubspec_environment_is_not_a_map() async {
1128+
var pubspecText = '''
1129+
name: test
1130+
environment: 1
1131+
''';
1132+
var projectContents = simpleProject(pubspecText: pubspecText);
1133+
var projectDir = await createProjectDir(projectContents);
1134+
var cli = _createCli();
1135+
await cli
1136+
.run(_parseArgs(['--no-web-preview', '--apply-changes', projectDir]));
1137+
// The Dart source code should still be migrated.
1138+
assertProjectContents(
1139+
projectDir, simpleProject(migrated: true, pubspecText: pubspecText));
1140+
}
1141+
1142+
test_pubspec_environment_sdk_is_not_string() async {
1143+
var pubspecText = '''
1144+
name: test
1145+
environment:
1146+
sdk: 1
1147+
''';
1148+
var projectContents = simpleProject(pubspecText: pubspecText);
1149+
var projectDir = await createProjectDir(projectContents);
1150+
var cli = _createCli();
1151+
await cli
1152+
.run(_parseArgs(['--no-web-preview', '--apply-changes', projectDir]));
1153+
// The Dart source code should still be migrated.
1154+
assertProjectContents(
1155+
projectDir, simpleProject(migrated: true, pubspecText: pubspecText));
1156+
}
1157+
1158+
test_pubspec_is_missing_environment() async {
1159+
var projectContents = simpleProject(pubspecText: '''
1160+
name: test
1161+
''');
1162+
var projectDir = await createProjectDir(projectContents);
1163+
var cli = _createCli();
1164+
await cli
1165+
.run(_parseArgs(['--no-web-preview', '--apply-changes', projectDir]));
1166+
// The Dart source code should still be migrated.
1167+
assertProjectContents(projectDir, simpleProject(migrated: true, pubspecText:
1168+
// This is strange-looking, but valid.
1169+
'''
1170+
environment:
1171+
sdk: '>=2.9.0 <2.10.0'
1172+
1173+
name: test
1174+
'''));
1175+
}
1176+
1177+
test_pubspec_is_not_a_map() async {
1178+
var projectContents = simpleProject(pubspecText: 'not-a-map');
1179+
var projectDir = await createProjectDir(projectContents);
1180+
var cli = _createCli();
1181+
expect(
1182+
() async => await cli.run(
1183+
_parseArgs(['--no-web-preview', '--apply-changes', projectDir])),
1184+
throwsUnsupportedError);
1185+
}
1186+
10941187
test_uses_physical_resource_provider_by_default() {
10951188
var cli = MigrationCli(binaryName: 'nnbd_migration');
10961189
expect(cli.resourceProvider, same(PhysicalResourceProvider.INSTANCE));

0 commit comments

Comments
 (0)