Version 2.17.0-241.0.dev
Merge commit 'ca99bde913e531e2f7717d1f491d1a5d58ba0649' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index f55d3ca..94f206f 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -40,6 +40,16 @@
50,
"Remove '{0}'",
);
+ static const REPLACE_WITH_STRICT_CASTS = FixKind(
+ 'analysisOptions.fix.replaceWithStrictCasts',
+ 50,
+ 'Replace with the strict-casts analysis mode',
+ );
+ static const REPLACE_WITH_STRICT_RAW_TYPES = FixKind(
+ 'analysisOptions.fix.replaceWithStrictRawTypes',
+ 50,
+ 'Replace with the strict-raw-types analysis mode',
+ );
}
/// The implementation of [DartFixContext].
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart b/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
index 8c148db..5e36f57 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
@@ -16,9 +16,12 @@
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/lint/options_rule_validator.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_yaml.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:collection/collection.dart';
import 'package:yaml/yaml.dart';
+import 'package:yaml_edit/yaml_edit.dart';
/// The generator used to generate fixes in analysis options files.
class AnalysisOptionsFixGenerator {
@@ -67,7 +70,26 @@
// AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED) {
// } else
- if (errorCode == DEPRECATED_LINT_HINT) {
+ if (errorCode ==
+ AnalysisOptionsWarningCode
+ .ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT) {
+ var analyzerMap = options['analyzer'];
+ if (analyzerMap is! YamlMap) {
+ return fixes;
+ }
+ var strongModeMap = analyzerMap['strong-mode'];
+
+ if (strongModeMap is! YamlMap) {
+ return fixes;
+ }
+ if (_isErrorAtMapKey(strongModeMap, 'implicit-casts')) {
+ await _addFix_replaceWithStrictCasts(
+ coveringNodePath, analyzerMap, strongModeMap);
+ } else if (_isErrorAtMapKey(strongModeMap, 'implicit-dynamic')) {
+ await _addFix_replaceWithStrictRawTypes(
+ coveringNodePath, analyzerMap, strongModeMap);
+ }
+ } else if (errorCode == DEPRECATED_LINT_HINT) {
await _addFix_removeLint(coveringNodePath);
} else if (errorCode ==
AnalysisOptionsHintCode.SUPER_MIXINS_SETTING_DEPRECATED) {
@@ -109,6 +131,52 @@
}
}
+ /// Replaces `analyzer: strong-mode: implicit-casts: false` with
+ /// `analyzer: language: strict-casts: true`.
+ Future<void> _addFix_replaceWithStrictCasts(List<YamlNode> coveringNodePath,
+ YamlMap analyzerMap, YamlMap strongModeMap) async {
+ var builder =
+ ChangeBuilder(workspace: _NonDartChangeWorkspace(resourceProvider));
+ await builder.addYamlFileEdit(file, (builder) {
+ _replaceStrongModeEntryWithLanguageEntry(
+ builder,
+ coveringNodePath,
+ analyzerMap,
+ strongModeMap,
+ strongModeKey: 'implicit-casts',
+ languageKey: 'strict-casts',
+ languageValue: true,
+ );
+ });
+ _addFixFromBuilder(
+ builder, AnalysisOptionsFixKind.REPLACE_WITH_STRICT_CASTS,
+ args: [coveringNodePath[0].toString()]);
+ }
+
+ /// Replaces `analyzer: strong-mode: implicit-dynamic: false` with
+ /// `analyzer: language: strict-raw-types: true`.
+ Future<void> _addFix_replaceWithStrictRawTypes(
+ List<YamlNode> coveringNodePath,
+ YamlMap analyzerMap,
+ YamlMap strongModeMap) async {
+ var builder =
+ ChangeBuilder(workspace: _NonDartChangeWorkspace(resourceProvider));
+ await builder.addYamlFileEdit(file, (builder) {
+ _replaceStrongModeEntryWithLanguageEntry(
+ builder,
+ coveringNodePath,
+ analyzerMap,
+ strongModeMap,
+ strongModeKey: 'implicit-dynamic',
+ languageKey: 'strict-raw-types',
+ languageValue: true,
+ );
+ });
+ _addFixFromBuilder(
+ builder, AnalysisOptionsFixKind.REPLACE_WITH_STRICT_RAW_TYPES,
+ args: [coveringNodePath[0].toString()]);
+ }
+
/// Add a fix whose edits were built by the [builder] that has the given
/// [kind]. If [args] are provided, they will be used to fill in the message
/// for the fix.
@@ -191,6 +259,20 @@
return offset;
}
+ /// Returns whether the error is located within [map], covering the
+ /// [YamlScalar] node for [key].
+ bool _isErrorAtMapKey(YamlMap map, String key) {
+ var keyNode = map.nodes.keys
+ .whereType<YamlScalar>()
+ .firstWhereOrNull((k) => k.value == key);
+ if (keyNode == null) {
+ return false;
+ }
+ var keyOffset = keyNode.span.start.offset;
+ var keyLength = keyNode.span.end.offset - keyOffset;
+ return keyOffset == errorOffset && keyLength == errorLength;
+ }
+
SourceRange _lines(int start, int end) {
var startLocation = lineInfo.getLocation(start);
var startOffset = lineInfo.getOffsetOfLine(startLocation.lineNumber - 1);
@@ -199,6 +281,53 @@
math.min(endLocation.lineNumber, lineInfo.lineCount - 1));
return SourceRange(startOffset, endOffset - startOffset);
}
+
+ /// Replaces a 'strong-mode' entry keyed to [strongModeKey] with a 'language'
+ /// entry with [languageKey] and [languageValue].
+ ///
+ /// 'strong-mode' and 'language' are each maps which can be found under the
+ /// top-level 'analyzer' map. 'strong-mode' (given as [strongModeMap]) must
+ /// already be present under the 'analyzer' map (given as [analyzerMap]).
+ void _replaceStrongModeEntryWithLanguageEntry(
+ YamlFileEditBuilder builder,
+ List<YamlNode> coveringNodePath,
+ YamlMap analyzerMap,
+ YamlMap strongModeMap, {
+ required String strongModeKey,
+ required String languageKey,
+ required Object? languageValue,
+ }) {
+ var yamlEditor = YamlEditor(content);
+ // If 'language' does not exist yet under 'analyzer', create it.
+ if (analyzerMap['language'] == null) {
+ yamlEditor.update(['analyzer', 'language'], {languageKey: languageValue});
+ } else {
+ yamlEditor.update(['analyzer', 'language', languageKey], languageValue);
+ }
+ var languageEdit = yamlEditor.edits.single;
+ builder.addSimpleReplacement(
+ SourceRange(languageEdit.offset, languageEdit.length),
+ languageEdit.replacement);
+
+ // If `strongModeKey` is the only entry under 'strong-mode', then remove
+ // the entire 'strong-mode' entry.
+ if (strongModeMap.length == 1) {
+ yamlEditor.remove(['analyzer', 'strong-mode']);
+ } else {
+ yamlEditor.remove(['analyzer', 'strong-mode', strongModeKey]);
+ }
+ var strongModeEdit = yamlEditor.edits[1];
+ int strongModeEditOffset;
+ if (strongModeEdit.offset > languageEdit.offset) {
+ strongModeEditOffset = strongModeEdit.offset -
+ (languageEdit.replacement.length - languageEdit.length);
+ } else {
+ strongModeEditOffset = strongModeEdit.offset;
+ }
+ builder.addSimpleReplacement(
+ SourceRange(strongModeEditOffset, strongModeEdit.length),
+ strongModeEdit.replacement);
+ }
}
class _NonDartChangeWorkspace implements ChangeWorkspace {
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index 5f62d61..98487cb 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -29,6 +29,7 @@
path: any
watcher: any
yaml: any
+ yaml_edit: any
dev_dependencies:
analyzer_utilities:
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index ab5be7e..3d99a77 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -66,6 +66,10 @@
Future<AnalysisSession> get session => sessionFor(testPackageRootPath);
+ /// The path for `analysis_options.yaml` in [testPackageRootPath].
+ String get testAnalysisOptionsPath =>
+ convertPath('$testPackageRootPath/analysis_options.yaml');
+
String? get testPackageLanguageVersion => latestLanguageVersion;
String get testPackageLibPath => '$testPackageRootPath/lib';
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 816c714..f4d93622 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -43,8 +43,6 @@
/// TODO(scheglov) this is duplicate
class AnalysisOptionsFileConfig {
final List<String> experiments;
- final bool implicitCasts;
- final bool implicitDynamic;
final List<String> lints;
final bool strictCasts;
final bool strictInference;
@@ -52,8 +50,6 @@
AnalysisOptionsFileConfig({
this.experiments = const [],
- this.implicitCasts = true,
- this.implicitDynamic = true,
this.lints = const [],
this.strictCasts = false,
this.strictInference = false,
@@ -72,9 +68,6 @@
buffer.writeln(' strict-casts: $strictCasts');
buffer.writeln(' strict-inference: $strictInference');
buffer.writeln(' strict-raw-types: $strictRawTypes');
- buffer.writeln(' strong-mode:');
- buffer.writeln(' implicit-casts: $implicitCasts');
- buffer.writeln(' implicit-dynamic: $implicitDynamic');
buffer.writeln('linter:');
buffer.writeln(' rules:');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_casts_test.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_casts_test.dart
new file mode 100644
index 0000000..c8be60d
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_casts_test.dart
@@ -0,0 +1,163 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ReplaceWithStrictCastsTest);
+ });
+}
+
+@reflectiveTest
+class ReplaceWithStrictCastsTest extends AnalysisOptionsFixTest {
+ Future<void> test_hasLanguage() async {
+ await assertHasFix('''
+analyzer:
+ language:
+ strict-inference: true
+ strong-mode:
+ implicit-casts: false
+''', '''
+analyzer:
+ language:
+ strict-casts: true
+ strict-inference: true
+''');
+ }
+
+ Future<void> test_hasLanguage_isAfter() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-casts: false
+ language:
+ strict-inference: true
+''', '''
+analyzer:
+ language:
+ strict-casts: true
+ strict-inference: true
+''');
+ }
+
+ Future<void> test_hasStrictCastsFalse() async {
+ await assertHasFix('''
+analyzer:
+ language:
+ strict-casts: false
+ strong-mode:
+ implicit-casts: false
+''', '''
+analyzer:
+ language:
+ strict-casts: true
+''');
+ }
+
+ Future<void> test_hasStrictCastsTrue() async {
+ await assertHasFix('''
+analyzer:
+ language:
+ strict-casts: true
+ strong-mode:
+ implicit-casts: false
+''', '''
+analyzer:
+ language:
+ strict-casts: true
+''');
+ }
+
+ Future<void> test_noLanguage() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-casts: false
+''', '''
+analyzer:
+ language:
+ strict-casts: true
+''');
+ }
+
+ Future<void> test_noLanguage_analyzerHasOtherEntries() async {
+ await assertHasFix('''
+analyzer:
+ errors:
+ unused_import: ignore
+ strong-mode:
+ implicit-casts: false
+''', '''
+analyzer:
+ errors:
+ unused_import: ignore
+ language:
+ strict-casts: true
+''');
+ }
+
+ Future<void> test_noLanguage_analyzerHasOtherEntriesAfter() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-casts: false
+ errors:
+ unused_import: ignore
+''', '''
+analyzer:
+ errors:
+ unused_import: ignore
+ language:
+ strict-casts: true
+''');
+ }
+
+ Future<void> test_noLanguage_hasOtherStrongModeEntry() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-casts: false
+ implicit-dynamic: false
+''', '''
+analyzer:
+ language:
+ strict-casts: true
+ strong-mode:
+ implicit-dynamic: false
+''', errorFilter: (error) => error.message.contains('implicit-casts'));
+ }
+
+ Future<void> test_noLanguage_implicitCastsHasComment() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ # No implicit casts
+ implicit-casts: false
+''', '''
+analyzer:
+ language:
+ strict-casts: true
+''');
+ }
+
+ Future<void> test_noLanguage_strongModeHasComment() async {
+ // TODO(srawlins): This is unfortunate; it would be better to remove the
+ // comment. But we leave this assertion as is to show at least the file is
+ // not corrupted.
+ await assertHasFix('''
+analyzer:
+ # Strong mode
+ strong-mode:
+ implicit-casts: false
+''', '''
+analyzer:
+ # Strong mode
+ language:
+ strict-casts: true
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_raw_types_test.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_raw_types_test.dart
new file mode 100644
index 0000000..eb8012d
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_raw_types_test.dart
@@ -0,0 +1,163 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ReplaceWithStrictRawTypesTest);
+ });
+}
+
+@reflectiveTest
+class ReplaceWithStrictRawTypesTest extends AnalysisOptionsFixTest {
+ Future<void> test_hasLanguage() async {
+ await assertHasFix('''
+analyzer:
+ language:
+ strict-inference: true
+ strong-mode:
+ implicit-dynamic: false
+''', '''
+analyzer:
+ language:
+ strict-inference: true
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_hasLanguage_isAfter() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-dynamic: false
+ language:
+ strict-inference: true
+''', '''
+analyzer:
+ language:
+ strict-inference: true
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_hasStrictRawTypesFalse() async {
+ await assertHasFix('''
+analyzer:
+ language:
+ strict-raw-types: false
+ strong-mode:
+ implicit-dynamic: false
+''', '''
+analyzer:
+ language:
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_hasStrictRawTypesTrue() async {
+ await assertHasFix('''
+analyzer:
+ language:
+ strict-raw-types: true
+ strong-mode:
+ implicit-dynamic: false
+''', '''
+analyzer:
+ language:
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_noLanguage() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-dynamic: false
+''', '''
+analyzer:
+ language:
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_noLanguage_analyzerHasOtherEntries() async {
+ await assertHasFix('''
+analyzer:
+ errors:
+ unused_import: ignore
+ strong-mode:
+ implicit-dynamic: false
+''', '''
+analyzer:
+ errors:
+ unused_import: ignore
+ language:
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_noLanguage_analyzerHasOtherEntriesAfter() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-dynamic: false
+ errors:
+ unused_import: ignore
+''', '''
+analyzer:
+ errors:
+ unused_import: ignore
+ language:
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_noLanguage_hasOtherStrongModeEntry() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ implicit-casts: false
+ implicit-dynamic: false
+''', '''
+analyzer:
+ language:
+ strict-raw-types: true
+ strong-mode:
+ implicit-casts: false
+''', errorFilter: (error) => error.message.contains('implicit-dynamic'));
+ }
+
+ Future<void> test_noLanguage_implicitDynamicHasComment() async {
+ await assertHasFix('''
+analyzer:
+ strong-mode:
+ # No implicit dynamic
+ implicit-dynamic: false
+''', '''
+analyzer:
+ language:
+ strict-raw-types: true
+''');
+ }
+
+ Future<void> test_noLanguage_strongModeHasComment() async {
+ // TODO(srawlins): This is unfortunate; it would be better to remove the
+ // comment. But we leave this assertion as is to show at least the file is
+ // not corrupted.
+ await assertHasFix('''
+analyzer:
+ # Strong mode
+ strong-mode:
+ implicit-dynamic: false
+''', '''
+analyzer:
+ # Strong mode
+ language:
+ strict-raw-types: true
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart
index 6d61c19..c4d940e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart
@@ -6,10 +6,15 @@
import 'remove_lint_test.dart' as remove_lint;
import 'remove_setting_test.dart' as remove_setting;
+import 'replace_with_strict_casts_test.dart' as replace_with_strict_casts;
+import 'replace_with_strict_raw_types_test.dart'
+ as replace_with_strict_raw_types;
void main() {
defineReflectiveSuite(() {
remove_lint.main();
remove_setting.main();
+ replace_with_strict_casts.main();
+ replace_with_strict_raw_types.main();
});
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart
index 058fc31..d5cc2c4 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart
@@ -3,12 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
-import 'package:analysis_server/src/protocol_server.dart' show SourceEdit;
import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
+import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart'
+ hide AnalysisError;
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';
@@ -16,8 +17,11 @@
/// errors in Dart files.
class AnalysisOptionsFixTest with ResourceProviderMixin {
Future<void> assertHasFix(
- String initialContent, String expectedContent) async {
- var fixes = await _getFixes(initialContent);
+ String initialContent,
+ String expectedContent, {
+ bool Function(AnalysisError)? errorFilter,
+ }) async {
+ var fixes = await _getFixes(initialContent, errorFilter: errorFilter);
expect(fixes, hasLength(1));
var fileEdits = fixes[0].change.edits;
expect(fileEdits, hasLength(1));
@@ -32,8 +36,11 @@
expect(fixes, hasLength(0));
}
- Future<List<Fix>> _getFixes(String content) {
- var optionsFile = getFile('/analysis_options.yaml');
+ Future<List<Fix>> _getFixes(
+ String content, {
+ bool Function(AnalysisError)? errorFilter,
+ }) {
+ var optionsFile = newFile2('/analysis_options.yaml', content);
var sourceFactory = SourceFactory([]);
var errors = analyzeAnalysisOptions(
optionsFile.createSource(),
@@ -41,6 +48,12 @@
sourceFactory,
'/',
);
+ if (errorFilter != null) {
+ if (errors.length == 1) {
+ fail('Unnecessary error filter');
+ }
+ errors = errors.where(errorFilter).toList();
+ }
expect(errors, hasLength(1));
var error = errors[0];
var options = _parseYaml(content);
diff --git a/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart b/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart
index cbe95e2..962d243 100644
--- a/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart
+++ b/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart
@@ -135,6 +135,17 @@
);
/**
+ * An error code indicating that the given option is deprecated.
+ */
+ static const AnalysisOptionsWarningCode
+ ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT = AnalysisOptionsWarningCode(
+ 'ANALYSIS_OPTION_DEPRECATED',
+ "The option '{0}' is no longer supported.",
+ correctionMessage: "Try using the new '{1}' option.",
+ uniqueName: 'ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT',
+ );
+
+ /**
* An error code indicating a specified include file has a warning.
*
* Parameters:
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index ea2f3f4..3c67e40 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -635,43 +635,48 @@
void validate(ErrorReporter reporter, YamlMap options) {
var analyzer = options.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
- var v = analyzer.valueAt(AnalyzerOptions.strong_mode);
- if (v is YamlScalar) {
- var value = toLowerCase(v.value);
- if (!AnalyzerOptions.trueOrFalse.contains(value)) {
+ var strongModeNode = analyzer.valueAt(AnalyzerOptions.strong_mode);
+ if (strongModeNode is YamlScalar) {
+ return _validateStrongModeAsScalar(reporter, strongModeNode);
+ } else if (strongModeNode is YamlMap) {
+ return _validateStrongModeAsMap(reporter, strongModeNode);
+ } else if (strongModeNode != null) {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+ strongModeNode.span,
+ [AnalyzerOptions.enableExperiment]);
+ }
+ }
+ }
+
+ void _validateStrongModeAsMap(
+ ErrorReporter reporter, YamlMap strongModeNode) {
+ strongModeNode.nodes.forEach((k, v) {
+ if (k is YamlScalar) {
+ var key = k.value?.toString();
+ if (!AnalyzerOptions.strongModeOptions.contains(key)) {
+ _builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
+ } else if (key == AnalyzerOptions.declarationCasts) {
reporter.reportErrorForSpan(
- AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, v.span, [
- AnalyzerOptions.strong_mode,
- v.value,
- AnalyzerOptions.trueOrFalseProposal
- ]);
- } else if (value == 'false') {
+ AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
+ k.span,
+ [AnalyzerOptions.declarationCasts]);
+ } else if (key == AnalyzerOptions.implicitCasts) {
reporter.reportErrorForSpan(
- AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, v.span);
- } else if (value == 'true') {
+ AnalysisOptionsWarningCode
+ .ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT,
+ k.span,
+ [AnalyzerOptions.implicitCasts, 'strict-casts']);
+ } else if (key == AnalyzerOptions.implicitDynamic) {
reporter.reportErrorForSpan(
- AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED, v.span);
- }
- } else if (v is YamlMap) {
- v.nodes.forEach((k, v) {
- String? key, value;
- bool validKey = false;
- if (k is YamlScalar) {
- key = k.value?.toString();
- if (!AnalyzerOptions.strongModeOptions.contains(key)) {
- _builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
- } else if (key == AnalyzerOptions.declarationCasts) {
- reporter.reportErrorForSpan(
- AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
- k.span,
- [AnalyzerOptions.declarationCasts]);
- } else {
- // If we have a valid key, go on and check the value.
- validKey = true;
- }
- }
- if (validKey && v is YamlScalar) {
- value = toLowerCase(v.value);
+ AnalysisOptionsWarningCode
+ .ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT,
+ k.span,
+ [AnalyzerOptions.implicitDynamic, 'strict-raw-types']);
+ } else {
+ // The key is valid.
+ if (v is YamlScalar) {
+ var value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
@@ -679,13 +684,28 @@
[key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
}
}
- });
- } else if (v != null) {
- reporter.reportErrorForSpan(
- AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
- v.span,
- [AnalyzerOptions.enableExperiment]);
+ }
}
+ });
+ }
+
+ void _validateStrongModeAsScalar(
+ ErrorReporter reporter, YamlScalar strongModeNode) {
+ var stringValue = toLowerCase(strongModeNode.value);
+ if (!AnalyzerOptions.trueOrFalse.contains(stringValue)) {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, strongModeNode.span, [
+ AnalyzerOptions.strong_mode,
+ strongModeNode.value,
+ AnalyzerOptions.trueOrFalseProposal
+ ]);
+ } else if (stringValue == 'false') {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, strongModeNode.span);
+ } else if (stringValue == 'true') {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED,
+ strongModeNode.span);
}
}
}
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 2401083..67a45e2 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -74,6 +74,11 @@
ANALYSIS_OPTION_DEPRECATED:
problemMessage: "The option '{0}' is no longer supported."
comment: An error code indicating that the given option is deprecated.
+ ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT:
+ sharedName: ANALYSIS_OPTION_DEPRECATED
+ problemMessage: "The option '{0}' is no longer supported."
+ correctionMessage: "Try using the new '{1}' option."
+ comment: An error code indicating that the given option is deprecated.
INCLUDED_FILE_WARNING:
problemMessage: "Warning in the included options file {0}({1}..{2}): {3}"
comment: |-
diff --git a/pkg/analyzer/test/src/task/options_test.dart b/pkg/analyzer/test/src/task/options_test.dart
index bf13737..0afa6ef 100644
--- a/pkg/analyzer/test/src/task/options_test.dart
+++ b/pkg/analyzer/test/src/task/options_test.dart
@@ -460,6 +460,22 @@
''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED]);
}
+ test_analyzer_strong_mode_deprecated_key_implicit_casts() {
+ validate('''
+analyzer:
+ strong-mode:
+ implicit-casts: false
+''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT]);
+ }
+
+ test_analyzer_strong_mode_deprecated_key_implicit_dynamic() {
+ validate('''
+analyzer:
+ strong-mode:
+ implicit-dynamic: false
+''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT]);
+ }
+
test_analyzer_strong_mode_error_code_supported() {
validate('''
analyzer:
@@ -491,14 +507,6 @@
''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES]);
}
- test_analyzer_strong_mode_unsupported_value() {
- validate('''
-analyzer:
- strong-mode:
- implicit-dynamic: foo
-''', [AnalysisOptionsWarningCode.UNSUPPORTED_VALUE]);
- }
-
test_analyzer_supported_exclude() {
validate('''
analyzer:
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index dca43e9..5ea1a4a 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -181,11 +181,7 @@
this,
path,
loadYamlDocument(
- workspace
- .getSession(path)!
- .resourceProvider
- .getFile(path)
- .readAsStringSync(),
+ workspace.resourceProvider.getFile(path).readAsStringSync(),
recover: true),
0);
_yamlFileEditBuilders[path] = builder;
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 06b64b7..c472ba5 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -26,7 +26,6 @@
import 'js/js.dart' as jsAst;
import 'js_model/js_strategy.dart';
import 'js_backend/field_analysis.dart';
-import 'universe/codegen_world_builder.dart';
import 'universe/world_impact.dart' show WorldImpact, WorldImpactVisitorImpl;
import 'util/sink_adapter.dart';
import 'world.dart' show JClosedWorld;
@@ -38,7 +37,6 @@
final DumpInfoTask dumpInfoTask;
JElementEnvironment get environment => closedWorld.elementEnvironment;
- CodegenWorldBuilder get codegenWorldBuilder => compiler.codegenWorldBuilder;
final AllInfo result = AllInfo();
final Map<Entity, Info> _entityToInfo = <Entity, Info>{};
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 3c01756..ce19532 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -256,31 +256,14 @@
}
}
-/// Interface for resources only used during code generation.
-abstract class CodegenInputs {
- CheckedModeHelpers get checkedModeHelpers;
- RuntimeTypesSubstitutions get rtiSubstitutions;
- RecipeEncoder get rtiRecipeEncoder;
- Tracer get tracer;
- FixedNames get fixedNames;
-}
-
-class CodegenInputsImpl implements CodegenInputs {
- @override
+/// Holds resources only used during code generation.
+class CodegenInputs {
final CheckedModeHelpers checkedModeHelpers = CheckedModeHelpers();
-
- @override
final RuntimeTypesSubstitutions rtiSubstitutions;
-
- @override
final RecipeEncoder rtiRecipeEncoder;
-
- @override
final Tracer tracer;
-
- @override
final FixedNames fixedNames;
- CodegenInputsImpl(this.rtiSubstitutions, this.rtiRecipeEncoder, this.tracer,
+ CodegenInputs(this.rtiSubstitutions, this.rtiRecipeEncoder, this.tracer,
this.fixedNames);
}
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 61dfd89..534f422 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -213,8 +213,8 @@
RecipeEncoder rtiRecipeEncoder = RecipeEncoderImpl(closedWorld,
rtiSubstitutions, closedWorld.nativeData, closedWorld.commonElements);
- CodegenInputs codegen = CodegenInputsImpl(
- rtiSubstitutions, rtiRecipeEncoder, tracer, fixedNames);
+ CodegenInputs codegen =
+ CodegenInputs(rtiSubstitutions, rtiRecipeEncoder, tracer, fixedNames);
functionCompiler.initialize(globalTypeInferenceResults, codegen);
return codegen;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 8a647cb..888041a 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -7167,6 +7167,23 @@
#undef EXPECT_NOT_SUBTYPE
#undef EXPECT_SUBTYPE
+static void ExpectTypesEquivalent(const Expect& expect,
+ const AbstractType& expected,
+ const AbstractType& got,
+ TypeEquality kind) {
+ if (got.IsEquivalent(expected, kind)) return;
+ TextBuffer buffer(128);
+ buffer.AddString("Expected type ");
+ expected.PrintName(Object::kScrubbedName, &buffer);
+ buffer.AddString(", got ");
+ got.PrintName(Object::kScrubbedName, &buffer);
+ expect.Fail("%s", buffer.buffer());
+}
+
+#define EXPECT_TYPES_EQUAL(expected, got) \
+ ExpectTypesEquivalent(Expect(__FILE__, __LINE__), expected, got, \
+ TypeEquality::kCanonical);
+
TEST_CASE(Class_GetInstantiationOf) {
const char* kScript = R"(
class B<T> {}
@@ -7188,17 +7205,6 @@
const auto& core_lib = Library::Handle(zone, Library::CoreLibrary());
const auto& class_list = Class::Handle(zone, GetClass(core_lib, "List"));
- auto expect_type_equal = [](const AbstractType& expected,
- const AbstractType& got) {
- if (got.Equals(expected)) return;
- TextBuffer buffer(128);
- buffer.AddString("Expected type ");
- expected.PrintName(Object::kScrubbedName, &buffer);
- buffer.AddString(", got ");
- got.PrintName(Object::kScrubbedName, &buffer);
- dart::Expect(__FILE__, __LINE__).Fail("%s", buffer.buffer());
- };
-
const auto& decl_type_b = Type::Handle(zone, class_b.DeclarationType());
const auto& decl_type_list = Type::Handle(zone, class_list.DeclarationType());
const auto& null_tav = Object::null_type_arguments();
@@ -7228,7 +7234,7 @@
const auto& inst_b_a1 =
Type::Handle(zone, class_a1.GetInstantiationOf(zone, class_b));
EXPECT(!inst_b_a1.IsNull());
- expect_type_equal(type_b_list_a1_y, inst_b_a1);
+ EXPECT_TYPES_EQUAL(type_b_list_a1_y, inst_b_a1);
}
// Test that A2.GetInstantiationOf(B) returns B<List<A2::X>>.
@@ -7256,8 +7262,342 @@
const auto& inst_b_a2 =
Type::Handle(zone, class_a2.GetInstantiationOf(zone, class_b));
EXPECT(!inst_b_a2.IsNull());
- expect_type_equal(type_b_list_a2_x, inst_b_a2);
+ EXPECT_TYPES_EQUAL(type_b_list_a2_x, inst_b_a2);
}
}
+#undef EXPECT_TYPES_EQUAL
+
+#define EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got) \
+ ExpectTypesEquivalent(Expect(__FILE__, __LINE__), expected, got, \
+ TypeEquality::kSyntactical);
+
+static TypePtr CreateFutureOrType(const AbstractType& param,
+ Nullability nullability) {
+ const auto& async_lib = Library::Handle(Library::AsyncLibrary());
+ const auto& future_or_class =
+ Class::Handle(async_lib.LookupClass(Symbols::FutureOr()));
+ const auto& tav = TypeArguments::Handle(TypeArguments::New(1));
+ tav.SetTypeAt(0, param);
+ const auto& type =
+ AbstractType::Handle(Type::New(future_or_class, tav, nullability));
+ return Type::RawCast(
+ ClassFinalizer::FinalizeType(type, ClassFinalizer::kFinalize));
+}
+
+static TypePtr CreateFutureType(const AbstractType& param,
+ Nullability nullability) {
+ ObjectStore* const object_store = IsolateGroup::Current()->object_store();
+ const auto& future_class = Class::Handle(object_store->future_class());
+ const auto& tav = TypeArguments::Handle(TypeArguments::New(1));
+ tav.SetTypeAt(0, param);
+ const auto& type = Type::Handle(Type::New(future_class, tav, nullability));
+ return Type::RawCast(
+ ClassFinalizer::FinalizeType(type, ClassFinalizer::kFinalize));
+}
+
+ISOLATE_UNIT_TEST_CASE(AbstractType_NormalizeFutureOrType) {
+ // This should be kept up to date with any changes in
+ // https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md
+
+ ObjectStore* const object_store = IsolateGroup::Current()->object_store();
+
+ auto normalized_future_or = [&](const AbstractType& param,
+ Nullability nullability) -> AbstractTypePtr {
+ const auto& type = Type::Handle(CreateFutureOrType(param, nullability));
+ return type.NormalizeFutureOrType(Heap::kNew);
+ };
+
+ // NORM(FutureOr<T>) =
+ // let S be NORM(T)
+ // if S is a top type then S
+ {
+ const auto& type = AbstractType::Handle(normalized_future_or(
+ Object::dynamic_type(), Nullability::kNonNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::dynamic_type(), type);
+ }
+
+ {
+ const auto& type = AbstractType::Handle(
+ normalized_future_or(Object::void_type(), Nullability::kNonNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::void_type(), type);
+ }
+
+ {
+ const auto& type_nullable_object =
+ Type::Handle(object_store->nullable_object_type());
+ const auto& type = AbstractType::Handle(
+ normalized_future_or(type_nullable_object, Nullability::kNonNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, type);
+ }
+
+ // if S is Object then S
+
+ {
+ const auto& type_non_nullable_object =
+ Type::Handle(object_store->non_nullable_object_type());
+ const auto& type = AbstractType::Handle(normalized_future_or(
+ type_non_nullable_object, Nullability::kNonNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_non_nullable_object, type);
+ }
+
+ // if S is Object* then S
+
+ {
+ const auto& type_legacy_object =
+ Type::Handle(object_store->legacy_object_type());
+ const auto& type = AbstractType::Handle(
+ normalized_future_or(type_legacy_object, Nullability::kNonNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_legacy_object, type);
+ }
+
+ // if S is Never then Future<Never>
+
+ {
+ const auto& type_never = Type::Handle(object_store->never_type());
+ const auto& expected =
+ Type::Handle(CreateFutureType(type_never, Nullability::kNonNullable));
+ const auto& got = AbstractType::Handle(
+ normalized_future_or(type_never, Nullability::kNonNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // if S is Null then Future<Null>?
+
+ {
+ const auto& type_null = Type::Handle(object_store->null_type());
+ const auto& expected =
+ Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+ const auto& got = AbstractType::Handle(
+ normalized_future_or(type_null, Nullability::kNonNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // else FutureOr<S>
+
+ // NORM(T?) =
+ // let S be NORM(T)
+ // ...
+ // if S is FutureOr<R> and R is nullable then S
+
+ {
+ const auto& type_nullable_int =
+ Type::Handle(object_store->nullable_int_type());
+ const auto& expected = Type::Handle(
+ CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+ const auto& got = AbstractType::Handle(
+ normalized_future_or(type_nullable_int, Nullability::kNullable));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+}
+
+TEST_CASE(AbstractType_InstantiatedFutureOrIsNormalized) {
+ const char* kScript = R"(
+import 'dart:async';
+
+FutureOr<T>? foo<T>() { return null; }
+FutureOr<T?> bar<T>() { return null; }
+)";
+
+ Dart_Handle api_lib = TestCase::LoadTestScript(kScript, nullptr);
+ EXPECT_VALID(api_lib);
+ TransitionNativeToVM transition(thread);
+ Zone* const zone = thread->zone();
+ ObjectStore* const object_store = IsolateGroup::Current()->object_store();
+
+ const auto& null_tav = Object::null_type_arguments();
+ auto instantiate_future_or =
+ [&](const AbstractType& generic,
+ const AbstractType& param) -> AbstractTypePtr {
+ const auto& tav = TypeArguments::Handle(TypeArguments::New(1));
+ tav.SetTypeAt(0, param);
+ return generic.InstantiateFrom(null_tav, tav, kCurrentAndEnclosingFree,
+ Heap::kNew);
+ };
+
+ const auto& root_lib =
+ Library::CheckedHandle(zone, Api::UnwrapHandle(api_lib));
+ EXPECT(!root_lib.IsNull());
+ const auto& foo = Function::Handle(zone, GetFunction(root_lib, "foo"));
+ const auto& bar = Function::Handle(zone, GetFunction(root_lib, "bar"));
+ const auto& foo_sig = FunctionType::Handle(zone, foo.signature());
+ const auto& bar_sig = FunctionType::Handle(zone, bar.signature());
+
+ const auto& nullable_future_or_T =
+ AbstractType::Handle(zone, foo_sig.result_type());
+ const auto& future_or_nullable_T =
+ AbstractType::Handle(zone, bar_sig.result_type());
+
+ const auto& type_nullable_object =
+ Type::Handle(object_store->nullable_object_type());
+ const auto& type_non_nullable_object =
+ Type::Handle(object_store->non_nullable_object_type());
+ const auto& type_legacy_object =
+ Type::Handle(object_store->legacy_object_type());
+
+ // Testing same cases as AbstractType_NormalizeFutureOrType.
+
+ // FutureOr<T>?[top type] = top type
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, Object::dynamic_type()));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::dynamic_type(), got);
+ }
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, Object::void_type()));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::void_type(), got);
+ }
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, type_nullable_object));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+ }
+
+ // FutureOr<T?>[top type] = top type
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, Object::dynamic_type()));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::dynamic_type(), got);
+ }
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, Object::void_type()));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::void_type(), got);
+ }
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, type_nullable_object));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+ }
+
+ // FutureOr<T?>[Object] = Object?
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, type_non_nullable_object));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+ }
+
+ // FutureOr<T?>[Object*] = Object?
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, type_legacy_object));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+ }
+
+ // FutureOr<T>?[Object] = Object?
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, type_non_nullable_object));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+ }
+
+ // FutureOr<T>?[Object*] = Object?
+
+ {
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, type_legacy_object));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+ }
+
+ const auto& type_never = Type::Handle(object_store->never_type());
+ const auto& type_null = Type::Handle(object_store->null_type());
+
+ // FutureOr<T?>[Never] = Future<Null>?
+
+ {
+ const auto& expected =
+ Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, type_never));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // FutureOr<T>?[Never] = Future<Never>?
+
+ {
+ const auto& expected =
+ Type::Handle(CreateFutureType(type_never, Nullability::kNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, type_never));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // FutureOr<T?>[Null] = Future<Null>?
+
+ {
+ const auto& expected =
+ Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, type_null));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // FutureOr<T>?[Null] = Future<Null>?
+
+ {
+ const auto& expected =
+ Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, type_null));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ const auto& type_nullable_int =
+ Type::Handle(object_store->nullable_int_type());
+ const auto& type_non_nullable_int =
+ Type::Handle(object_store->non_nullable_int_type());
+
+ // FutureOr<T?>[int] = FutureOr<int?>
+
+ {
+ const auto& expected = Type::Handle(
+ CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, type_non_nullable_int));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // FutureOr<T?>[int?] = FutureOr<int?>
+
+ {
+ const auto& expected = Type::Handle(
+ CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(future_or_nullable_T, type_nullable_int));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // FutureOr<T>?[int?] = FutureOr<int?>
+
+ {
+ const auto& expected = Type::Handle(
+ CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, type_nullable_int));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+
+ // FutureOr<T>?[int] = FutureOr<int>?
+
+ {
+ const auto& expected = Type::Handle(
+ CreateFutureOrType(type_non_nullable_int, Nullability::kNullable));
+ const auto& got = AbstractType::Handle(
+ instantiate_future_or(nullable_future_or_T, type_non_nullable_int));
+ EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+ }
+}
+
+#undef EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT
+
} // namespace dart
diff --git a/tools/VERSION b/tools/VERSION
index 4113a05..78b37fe 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 240
+PRERELEASE 241
PRERELEASE_PATCH 0
\ No newline at end of file