Version 2.12.0-251.0.dev
Merge commit '6854871069eaa5fe56f24bada4325f9aef23888b' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index ff74a21..eb075b1 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -632,7 +632,7 @@
"name": "sync_http",
"rootUri": "../third_party/pkg/sync_http",
"packageUri": "lib/",
- "languageVersion": "2.0"
+ "languageVersion": "2.12"
},
{
"name": "telemetry",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 01e66a5..86c7101 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,11 +20,19 @@
### Core libraries
+#### `dart:async`
+
+* Adds extension method `onError` on `Future` to allow better typing
+ of error handling.
+
#### `dart:collection`
* Added `UnmodifiableSetView` class, which allows users to guarantee that
methods that could change underlying `Set` instance can not be invoked.
+* `LinkedList` made it explicit that elements are compared by identity,
+ and updated `contains` to take advantage of this.
+
#### `dart:core`
* Added `unmodifiable` constructor to class `Set`, which allows users to create
@@ -41,11 +49,6 @@
constructors, a name which can be associated with the port and displayed in
tooling.
-#### `dart:collection`
-
-* `LinkedList` made it explicit that elements are compared by identity,
- and updated `contains` to take advantage of this.
-
#### `dart:html`
* `EventStreamSubscription.cancel` has been updated to retain its synchronous
@@ -85,7 +88,7 @@
#### Linter
-Updated the Linter to `0.1.128`, which includes:
+Updated the Linter to `0.1.129`, which includes:
* New lint: `avoid_dynamic_calls`.
* (Internal): `avoid_type_to_string` updated to use `addArgumentList` registry API.
diff --git a/DEPS b/DEPS
index f591082..f867cb4 100644
--- a/DEPS
+++ b/DEPS
@@ -118,7 +118,7 @@
"intl_tag": "0.17.0-nullsafety",
"jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
"json_rpc_2_rev": "b8dfe403fd8528fd14399dee3a6527b55802dd4d",
- "linter_tag": "0.1.128",
+ "linter_tag": "0.1.129",
"logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2",
"markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
"markdown_rev": "6f89681d59541ddb1cf3a58efbdaa2304ffc3f51",
@@ -152,7 +152,7 @@
"stagehand_rev": "e64ac90cac508981011299c4ceb819149e71f1bd",
"stream_channel_tag": "d7251e61253ec389ee6e045ee1042311bced8f1d",
"string_scanner_rev": "1b63e6e5db5933d7be0a45da6e1129fe00262734",
- "sync_http_rev": "a85d7ec764ea485cbbc49f3f3e7f1b43f87a1c74",
+ "sync_http_rev": "b59c134f2e34d12acac110d4f17f83e5a7db4330",
"test_descriptor_tag": "1.1.1",
"test_process_tag": "1.0.3",
"term_glyph_rev": "6a0f9b6fb645ba75e7a00a4e20072678327a0347",
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/and.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/and.dart
new file mode 100644
index 0000000..f0fc09b
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/and.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.
+
+// This test checks that binary `&&` expressions are annotated with the
+// variables written on their right hand sides. This is needed for legacy type
+// promotion, to ensure that an assignment on the RHS defeats promotion.
+
+/*member: rhs:declared={a, b, c}, read={a, c}, assigned={b}*/
+rhs(bool a, bool b, bool c) {
+ a /*read={c}, assigned={b}*/ && (b = c);
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart
index 08d699d..ddd0931 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart
@@ -2,17 +2,17 @@
// 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.
-/*member: ordinary:declared={x, y}, assigned={x}*/
+/*member: ordinary:declared={x, y}, read={y}, assigned={x}*/
ordinary(int x, int y) {
x = y;
}
-/*member: nullAware:declared={x, y}, assigned={x}*/
+/*member: nullAware:declared={x, y}, read={x, y}, assigned={x}*/
nullAware(int? x, int y) {
x ??= y;
}
-/*member: compound:declared={x, y}, assigned={x}*/
+/*member: compound:declared={x, y}, read={x, y}, assigned={x}*/
compound(int x, int y) {
x += y;
}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/conditional.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/conditional.dart
new file mode 100644
index 0000000..4671eff
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/conditional.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2020, 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.
+
+// This test checks that conditional (`?:`) expressions are annotated with the
+// variables written in their "then" branches. This is needed for legacy type
+// promotion, to ensure that an assignment in the "then" branch defeats
+// promotion.
+
+/*member: then:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+then(bool a, bool b, bool c, bool d, bool e) {
+ a /*read={c}, assigned={b}*/ ? b = c : d = e;
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
index cb3d20b..11b6beb 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
@@ -7,9 +7,9 @@
[/*assigned={b, c, d}*/ for (a = 0; (b = 0) != 0; c = 0) (d = 0)];
}
-/*member: cStyle_unparenthesized:declared={a, b, c, d, e}, assigned={a, b, d, e}*/
+/*member: cStyle_unparenthesized:declared={a, b, c, d, e}, read={c}, assigned={a, b, d, e}*/
cStyle_unparenthesized(int a, bool b, bool c, int d, int e) {
- [/*assigned={b, d, e}*/ for (a = 0; b = c; d = 0) e = 0];
+ [/*read={c}, assigned={b, d, e}*/ for (a = 0; b = c; d = 0) e = 0];
}
/*member: cStyleWithDeclaration:declared={a, b, c, d, e}, assigned={a, b, c, d}*/
@@ -17,9 +17,9 @@
[/*assigned={b, c, d}*/ for (int e = (a = 0); (b = 0) != 0; c = 0) (d = 0)];
}
-/*member: cStyleWithDeclaration_unparenthesized:declared={a, b, c, d, e, f}, assigned={a, b, d, e}*/
+/*member: cStyleWithDeclaration_unparenthesized:declared={a, b, c, d, e, f}, read={c}, assigned={a, b, d, e}*/
cStyleWithDeclaration_unparenthesized(int a, bool b, bool c, int d, int e) {
- [/*assigned={b, d, e}*/ for (int f = a = 0; b = c; d = 0) e = 0];
+ [/*read={c}, assigned={b, d, e}*/ for (int f = a = 0; b = c; d = 0) e = 0];
}
/*member: forEach:declared={a, b, c}, assigned={a, b, c}*/
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/if.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/if.dart
new file mode 100644
index 0000000..7a426c9
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/if.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2020, 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.
+
+// This test checks that `if` statements and collection elements are annotated
+// with the variables written in their "then" branches. This is needed for
+// legacy type promotion, to ensure that an assignment in the "then" branch
+// defeats promotion.
+
+/*member: ifThenStatement:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenStatement(bool a, bool b, bool c) {
+ /*read={c}, assigned={b}*/ if (a) b = c;
+}
+
+/*member: ifThenElseStatement:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseStatement(bool a, bool b, bool c, bool d, bool e) {
+ /*read={c}, assigned={b}*/ if (a)
+ b = c;
+ else
+ d = e;
+}
+
+/*member: ifThenListElement:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenListElement(bool a, bool b, bool c) {
+ [/*read={c}, assigned={b}*/ if (a) b = c];
+}
+
+/*member: ifThenElseListElement:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseListElement(bool a, bool b, bool c, bool d, bool e) {
+ [/*read={c}, assigned={b}*/ if (a) b = c else d = e];
+}
+
+/*member: ifThenSetElement:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenSetElement(bool a, bool b, bool c) {
+ ({/*read={c}, assigned={b}*/ if (a) b = c});
+}
+
+/*member: ifThenElseSetElement:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseSetElement(bool a, bool b, bool c, bool d, bool e) {
+ ({/*read={c}, assigned={b}*/ if (a) b = c else d = e});
+}
+
+/*member: ifThenMapKey:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenMapKey(bool a, bool b, bool c) {
+ ({/*read={c}, assigned={b}*/ if (a) b = c: null});
+}
+
+/*member: ifThenElseMapKey:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseMapKey(bool a, bool b, bool c, bool d, bool e) {
+ ({/*read={c}, assigned={b}*/ if (a) b = c: null else d = e: null});
+}
+
+/*member: ifThenMapValue:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenMapValue(bool a, bool b, bool c) {
+ ({/*read={c}, assigned={b}*/ if (a) null: b = c});
+}
+
+/*member: ifThenElseMapValue:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseMapValue(bool a, bool b, bool c, bool d, bool e) {
+ ({/*read={c}, assigned={b}*/ if (a) null: b = c else null: d = e});
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart
index 8f91e74..df8d011 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart
@@ -2,17 +2,17 @@
// 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.
-/*member: increment:declared={x}, assigned={x}*/
+/*member: increment:declared={x}, read={x}, assigned={x}*/
increment(int x) {
x++;
}
-/*member: decrement:declared={x}, assigned={x}*/
+/*member: decrement:declared={x}, read={x}, assigned={x}*/
decrement(int x) {
x--;
}
-/*member: nonNullAssert:declared={x}*/
+/*member: nonNullAssert:declared={x}, read={x}*/
nonNullAssert(int? x) {
x!;
}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart
index 445015c..131b478 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart
@@ -2,27 +2,27 @@
// 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.
-/*member: increment:declared={x}, assigned={x}*/
+/*member: increment:declared={x}, read={x}, assigned={x}*/
increment(int x) {
++x;
}
-/*member: decrement:declared={x}, assigned={x}*/
+/*member: decrement:declared={x}, read={x}, assigned={x}*/
decrement(int x) {
--x;
}
-/*member: not:declared={b}*/
+/*member: not:declared={b}, read={b}*/
not(bool b) {
!b;
}
-/*member: binaryNot:declared={x}*/
+/*member: binaryNot:declared={x}, read={x}*/
binaryNot(int x) {
~x;
}
-/*member: unaryMinus:declared={x}*/
+/*member: unaryMinus:declared={x}, read={x}*/
unaryMinus(int x) {
-x;
}
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 6b15978..fe63631 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -105,8 +105,8 @@
}
var workspace = DartChangeWorkspace(server.currentSessions);
- var processor =
- BulkFixProcessor(server.instrumentationService, workspace);
+ var processor = BulkFixProcessor(server.instrumentationService, workspace,
+ useConfigFiles: params.inTestMode);
String sdkPath;
var sdk = server.findSdk();
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index ae7dbd0..a0dbd4c 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -67,12 +67,16 @@
import 'package:analysis_server/src/services/correction/dart/use_is_not_empty.dart';
import 'package:analysis_server/src/services/correction/dart/use_rethrow.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set_parser.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
import 'package:analysis_server/src/services/linter/lint_names.dart';
import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/error/error.dart';
+import 'package:analyzer/error/listener.dart';
import 'package:analyzer/exception/exception.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/src/error/codes.dart';
@@ -391,6 +395,10 @@
/// will be produced.
final DartChangeWorkspace workspace;
+ /// A flag indicating whether configuration files should be used to override
+ /// the transforms.
+ final bool useConfigFiles;
+
/// The change builder used to build the changes required to fix the
/// diagnostics.
ChangeBuilder builder;
@@ -400,8 +408,10 @@
/// Initialize a newly created processor to create fixes for diagnostics in
/// libraries in the [workspace].
- BulkFixProcessor(this.instrumentationService, this.workspace)
- : builder = ChangeBuilder(workspace: workspace);
+ BulkFixProcessor(this.instrumentationService, this.workspace,
+ {bool useConfigFiles})
+ : useConfigFiles = useConfigFiles ?? false,
+ builder = ChangeBuilder(workspace: workspace);
List<BulkFix> get fixDetails {
var details = <BulkFix>[];
@@ -455,11 +465,12 @@
(name) => [],
);
+ var overrideSet = _readOverrideSet(unit);
for (var error in errors) {
final processor = ErrorProcessor.getProcessor(analysisOptions, error);
// Only fix errors not filtered out in analysis options.
if (processor == null || processor.severity != null) {
- await _fixSingleError(fixContext, unit, error);
+ await _fixSingleError(fixContext, unit, error, overrideSet);
}
}
@@ -533,11 +544,12 @@
null,
(name) => [],
);
+ var overrideSet = _readOverrideSet(unitResult);
for (var error in unitResult.errors) {
var processor = ErrorProcessor.getProcessor(analysisOptions, error);
// Only fix errors not filtered out in analysis options.
if (processor == null || processor.severity != null) {
- await _fixSingleError(fixContext, unitResult, error);
+ await _fixSingleError(fixContext, unitResult, error, overrideSet);
}
}
}
@@ -546,12 +558,16 @@
/// Use the change [builder] and the [fixContext] to create a fix for the
/// given [diagnostic] in the compilation unit associated with the analysis
/// [result].
- Future<void> _fixSingleError(DartFixContext fixContext,
- ResolvedUnitResult result, AnalysisError diagnostic) async {
+ Future<void> _fixSingleError(
+ DartFixContext fixContext,
+ ResolvedUnitResult result,
+ AnalysisError diagnostic,
+ TransformOverrideSet overrideSet) async {
var context = CorrectionProducerContext(
applyingBulkFixes: true,
dartFixContext: fixContext,
diagnostic: diagnostic,
+ overrideSet: overrideSet,
resolvedResult: result,
selectionOffset: diagnostic.offset,
selectionLength: diagnostic.length,
@@ -626,6 +642,28 @@
s);
}
}
+
+ /// Return the override set corresponding to the given [result], or `null` if
+ /// there is no corresponding configuration file or the file content isn't a
+ /// valid override set.
+ TransformOverrideSet _readOverrideSet(ResolvedUnitResult result) {
+ if (useConfigFiles) {
+ var provider = result.session.resourceProvider;
+ var context = provider.pathContext;
+ var dartFileName = result.path;
+ var configFileName = '${context.withoutExtension(dartFileName)}.config';
+ var configFile = provider.getFile(configFileName);
+ try {
+ var content = configFile.readAsStringSync();
+ var parser = TransformOverrideSetParser(ErrorReporter(
+ AnalysisErrorListener.NULL_LISTENER, configFile.createSource()));
+ return parser.parse(content);
+ } on FileSystemException {
+ // Fall through to return null.
+ }
+ }
+ return null;
+ }
}
/// Maps changes to library paths.
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
index 93409b4..559d342 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
@@ -7,6 +7,7 @@
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analysis_server/src/utilities/flutter.dart';
import 'package:analyzer/dart/analysis/results.dart';
@@ -232,6 +233,8 @@
final Diagnostic diagnostic;
+ final TransformOverrideSet overrideSet;
+
AstNode _node;
CorrectionProducerContext({
@@ -240,6 +243,7 @@
this.applyingBulkFixes = false,
this.dartFixContext,
this.diagnostic,
+ this.overrideSet,
this.selectionOffset = -1,
this.selectionLength = 0,
}) : file = resolvedResult.path,
@@ -319,6 +323,7 @@
/// Initialize a newly created producer.
_AbstractCorrectionProducer();
+ /// Return `true` if the fixes are being built for the bulk-fix request.
bool get applyingBulkFixes => _context.applyingBulkFixes;
/// The most deeply nested node that completely covers the highlight region of
@@ -357,6 +362,10 @@
AstNode get node => _context.node;
+ /// Return the set of overrides to be applied to the transform set when
+ /// running tests, or `null` if there are no overrides to apply.
+ TransformOverrideSet get overrideSet => _context.overrideSet;
+
ResolvedUnitResult get resolvedResult => _context.resolvedResult;
/// Return the resource provider used to access the file system.
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
index 524cdaa..e4c9bd3 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
@@ -52,7 +52,13 @@
if (transformSetsForTests != null) {
return transformSetsForTests;
}
- return TransformSetManager.instance.forLibrary(library);
+ var transformSets = TransformSetManager.instance.forLibrary(library);
+ var overrideSet = this.overrideSet;
+ if (overrideSet != null) {
+ transformSets =
+ transformSets.map((set) => set.applyOverrides(overrideSet)).toList();
+ }
+ return transformSets;
}
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
index 20d7ff4..0a60dd7 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
@@ -5,6 +5,7 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/changes_selector.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override.dart';
import 'package:meta/meta.dart';
/// A description of a set of changes to a single element of the API.
@@ -44,4 +45,19 @@
}
return matcher.matches(element);
}
+
+ /// Return a new transform with the [override] applied, or this transform if
+ /// there are no overrides.
+ Transform applyOverride(TransformOverride override) {
+ var overriddenBulkApply = override.bulkApply;
+ if (overriddenBulkApply != null && overriddenBulkApply != bulkApply) {
+ return Transform(
+ title: title,
+ date: date,
+ bulkApply: overriddenBulkApply,
+ element: element,
+ changesSelector: changesSelector);
+ }
+ return this;
+ }
}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override.dart
new file mode 100644
index 0000000..abb7246
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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:meta/meta.dart';
+
+/// A description of a set of changes to a single transform.
+class TransformOverride {
+ /// The title of the transform being overridden.
+ final String title;
+
+ /// The overridden value of the `bulkApply` property of the transform, or
+ /// `null` if the property should not be overridden.
+ final bool bulkApply;
+
+ /// Initialize a newly created transform override to override the transform
+ /// with the given [title]. The remaining parameters correspond to properties
+ /// of the transform. They should have non-null values when the property is to
+ /// be overridden, and a value of `null` when the property should be
+ /// unchanged.
+ TransformOverride({@required this.title, this.bulkApply});
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set.dart
new file mode 100644
index 0000000..597efde
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2021, 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:analysis_server/src/services/correction/fix/data_driven/transform_override.dart';
+
+/// A description of a set of transform overrides.
+class TransformOverrideSet {
+ /// A map from transform titles to the override for that transform.
+ final Map<String, TransformOverride> overrideMap = {};
+
+ /// Initialize a newly created transform override set to include all of the
+ /// [overrides].
+ TransformOverrideSet(List<TransformOverride> overrides) {
+ for (var override in overrides) {
+ overrideMap[override.title] = override;
+ }
+ }
+
+ /// Return the overrides in this set.
+ List<TransformOverride> get overrides => overrideMap.values.toList();
+
+ /// Return the override for the transform with the given [title] or `null` if
+ /// there is no such override in this set.
+ TransformOverride overrideForTransform(String title) => overrideMap[title];
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
new file mode 100644
index 0000000..fea46f7
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
@@ -0,0 +1,217 @@
+// Copyright (c) 2021, 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:analysis_server/src/services/correction/fix/data_driven/transform_override.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_parser.dart';
+import 'package:analysis_server/src/utilities/extensions/yaml.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:yaml/src/yaml_exception.dart';
+import 'package:yaml/src/yaml_node.dart';
+import 'package:yaml/yaml.dart';
+
+/// A parser used to parse the content of a configuration file.
+class TransformOverrideSetParser {
+ // TODO(brianwilkerson) Create a class or mixin that would allow this class
+ // and `TransformSetParser` to share code.
+ static const String _bulkApplyKey = 'bulkApply';
+
+ /// The error reporter to which diagnostics will be reported.
+ final ErrorReporter errorReporter;
+
+ /// Initialize a newly created parser to report errors to the [errorReporter].
+ TransformOverrideSetParser(this.errorReporter);
+
+ /// Return the result of parsing the file [content] into a transform override,
+ /// or `null` if the content does not represent a valid transform override.
+ TransformOverrideSet parse(String content) {
+ assert(content != null);
+ var map = _parseYaml(content);
+ if (map == null) {
+ // The error has already been reported.
+ return null;
+ }
+ return _translateTransformOverrideSet(map);
+ }
+
+ /// Return a textual description of the type of value represented by the
+ /// [node].
+ String _nodeType(YamlNode node) {
+ if (node is YamlScalar) {
+ return node.value.runtimeType.toString();
+ } else if (node is YamlList) {
+ return 'List';
+ } else if (node is YamlMap) {
+ return 'Map';
+ }
+ // We shouldn't get here.
+ return node.runtimeType.toString();
+ }
+
+ /// Return the result of parsing the file [content] into a YAML node.
+ YamlNode _parseYaml(String content) {
+ try {
+ return loadYamlNode(content);
+ } on YamlException catch (e) {
+ var span = e.span;
+ errorReporter.reportErrorForOffset(TransformSetErrorCode.yamlSyntaxError,
+ span.start.offset, span.length, [e.message]);
+ }
+ return null;
+ }
+
+ /// Report a diagnostic with the given [code] associated with the given
+ /// [node]. A list of [arguments] should be provided if the diagnostic message
+ /// has parameters.
+ void _reportError(TransformSetErrorCode code, YamlNode node,
+ [List<String> arguments]) {
+ var span = node.span;
+ errorReporter.reportErrorForOffset(
+ code, span.start.offset, span.length, arguments);
+ }
+
+ /// Report that the value represented by the [node] does not have the
+ /// [expectedType], using the [context] to get the key to use in the message.
+ Null _reportInvalidValue(
+ YamlNode node, ErrorContext context, String expectedType) {
+ _reportError(TransformSetErrorCode.invalidValue, node,
+ [context.key, expectedType, _nodeType(node)]);
+ return null;
+ }
+
+ /// Report that a required key is missing, using the [context] to locate the
+ /// node associated with the diagnostic and the key to use in the message.
+ Null _reportMissingKey(ErrorContext context) {
+ _reportError(
+ TransformSetErrorCode.missingKey, context.parentNode, [context.key]);
+ return null;
+ }
+
+ /// Report any keys in the [map] whose values are not in [validKeys].
+ void _reportUnsupportedKeys(YamlMap map, Set<String> validKeys) {
+ for (var keyNode in map.nodes.keys) {
+ var key = _translateKey(keyNode);
+ if (key != null && !validKeys.contains(key)) {
+ _reportError(TransformSetErrorCode.unsupportedKey, keyNode, [key]);
+ }
+ }
+ }
+
+ /// Translate the [node] into a bool. Return the resulting bool, or `null`
+ /// if the [node] doesn't represent a valid bool. If the [node] isn't valid,
+ /// use the [context] to report the error. If the [node] doesn't exist and
+ /// [required] is `true`, then report an error.
+ bool _translateBool(YamlNode node, ErrorContext context,
+ {bool required = true}) {
+ if (node is YamlScalar) {
+ var value = node.value;
+ if (value is bool) {
+ return value;
+ }
+ return _reportInvalidValue(node, context, 'boolean');
+ } else if (node == null) {
+ if (required) {
+ return _reportMissingKey(context);
+ }
+ return null;
+ } else {
+ return _reportInvalidValue(node, context, 'boolean');
+ }
+ }
+
+ /// Translate the given [node] as a key.
+ String _translateKey(YamlNode node) {
+ String type;
+ if (node is YamlScalar) {
+ if (node.value is String) {
+ return node.value as String;
+ }
+ type = node.value.runtimeType.toString();
+ } else if (node is YamlList) {
+ type = 'List';
+ } else if (node is YamlMap) {
+ type = 'Map';
+ } else {
+ type = node.runtimeType.toString();
+ }
+ _reportError(TransformSetErrorCode.invalidKey, node, [type]);
+ return null;
+ }
+
+ /// Translate the [node] into a string. Return the resulting string, or `null`
+ /// if the [node] doesn't represent a valid string. If the [node] isn't valid,
+ /// use the [context] to report the error. If the [node] doesn't exist and
+ /// [required] is `true`, then report an error.
+ String _translateString(YamlNode node, ErrorContext context,
+ {bool required = true}) {
+ if (node is YamlScalar) {
+ var value = node.value;
+ if (value is String) {
+ return value;
+ }
+ return _reportInvalidValue(node, context, 'String');
+ } else if (node == null) {
+ if (required) {
+ return _reportMissingKey(context);
+ }
+ return null;
+ } else {
+ return _reportInvalidValue(node, context, 'String');
+ }
+ }
+
+ /// Translate the [node] into a transform override. Return the resulting
+ /// transform override, or `null` if the [node] does not represent a valid
+ /// transform override.
+ TransformOverride _translateTransformOverride(
+ YamlNode node, ErrorContext context, String title) {
+ assert(node != null);
+ if (node is YamlMap) {
+ _reportUnsupportedKeys(node, const {_bulkApplyKey});
+ var bulkApplyNode = node.valueAt(_bulkApplyKey);
+ if (bulkApplyNode != null) {
+ var bulkApplyValue = _translateBool(
+ bulkApplyNode, ErrorContext(key: _bulkApplyKey, parentNode: node),
+ required: true);
+ if (bulkApplyValue != null) {
+ return TransformOverride(title: title, bulkApply: bulkApplyValue);
+ }
+ }
+ return null;
+ } else {
+ return _reportInvalidValue(node, context, 'Map');
+ }
+ }
+
+ /// Translate the [node] into a transform override. Return the resulting
+ /// transform override, or `null` if the [node] does not represent a valid
+ /// transform override.
+ TransformOverrideSet _translateTransformOverrideSet(YamlNode node) {
+ assert(node != null);
+ if (node is YamlMap) {
+ var overrides = <TransformOverride>[];
+ for (var entry in node.nodes.entries) {
+ var keyNode = entry.key as YamlNode;
+ var errorContext = ErrorContext(key: 'file', parentNode: node);
+ var key = _translateString(keyNode, errorContext);
+ if (key != null) {
+ var valueNode = entry.value;
+ var override =
+ _translateTransformOverride(valueNode, errorContext, key);
+ if (override != null) {
+ overrides.add(override);
+ }
+ }
+ }
+ return TransformOverrideSet(overrides);
+ } else {
+ // TODO(brianwilkerson) Consider having a different error code for the
+ // top-level node (instead of using 'file' as the "key").
+ _reportError(TransformSetErrorCode.invalidValue, node,
+ ['file', 'Map', _nodeType(node)]);
+ return null;
+ }
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
index bbf0976..de1b7dd 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
@@ -4,6 +4,7 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
import 'package:meta/meta.dart';
/// A set of transforms used to aid in the construction of fixes for issues
@@ -18,6 +19,21 @@
_transforms.add(transform);
}
+ /// Return the result of applying the overrides in the [overrideSet] to the
+ /// transforms in this set.
+ TransformSet applyOverrides(TransformOverrideSet overrideSet) {
+ var overriddenSet = TransformSet();
+ for (var transform in _transforms) {
+ var override = overrideSet.overrideForTransform(transform.title);
+ if (override != null) {
+ overriddenSet.addTransform(transform.applyOverride(override));
+ } else {
+ overriddenSet.addTransform(transform);
+ }
+ }
+ return overriddenSet;
+ }
+
/// Return a list of the transforms that match the [matcher]. The flag
/// [applyingBulkFixes] indicates whether the transforms are being applied in
/// the context of a bulk fix.
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
index a239042..59ae8b8 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
@@ -30,6 +30,9 @@
/// Return the lint code being tested.
String get lintCode => null;
+ /// Return `true` if this test uses config files.
+ bool get useConfigFiles => false;
+
/// The workspace in which fixes contributor operates.
ChangeWorkspace get workspace {
return DartChangeWorkspace([session]);
@@ -58,7 +61,8 @@
var tracker = DeclarationsTracker(MemoryByteStore(), resourceProvider);
var analysisContext = contextFor(testFile);
tracker.addContext(analysisContext);
- var processor = BulkFixProcessor(TestInstrumentationService(), workspace);
+ var processor = BulkFixProcessor(TestInstrumentationService(), workspace,
+ useConfigFiles: useConfigFiles);
await processor.fixErrors([analysisContext]);
return processor;
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart
index f39851f..2a480a0 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart
@@ -29,6 +29,7 @@
defineReflectiveTests(UndefinedIdentifierTest);
defineReflectiveTests(UndefinedMethodTest);
defineReflectiveTests(UndefinedSetterTest);
+ defineReflectiveTests(WithConfigFileTest);
defineReflectiveTests(WrongNumberOfTypeArgumentsConstructorTest);
defineReflectiveTests(WrongNumberOfTypeArgumentsExtensionTest);
defineReflectiveTests(WrongNumberOfTypeArgumentsMethodTest);
@@ -1051,6 +1052,67 @@
}
@reflectiveTest
+class WithConfigFileTest extends _DataDrivenTest {
+ @override
+ bool get useConfigFiles => true;
+
+ Future<void> test_bulkApply_withConfig() async {
+ setPackageContent('''
+class New {}
+''');
+ addPackageDataFile('''
+version: 1
+transforms:
+- title: 'Rename to New'
+ date: 2021-21-01
+ bulkApply: false
+ element:
+ uris: ['$importUri']
+ class: 'Old'
+ changes:
+ - kind: 'rename'
+ newName: 'New'
+''');
+ addSource('/home/test/lib/test.config', '''
+'Rename to New':
+ bulkApply: true
+''');
+ await resolveTestCode('''
+import '$importUri';
+void f(Old p) {}
+''');
+ await assertHasFix('''
+import '$importUri';
+void f(New p) {}
+''');
+ }
+
+ Future<void> test_bulkApply_withoutConfig() async {
+ setPackageContent('''
+class New {}
+''');
+ addPackageDataFile('''
+version: 1
+transforms:
+- title: 'Rename to New'
+ date: 2021-21-01
+ bulkApply: false
+ element:
+ uris: ['$importUri']
+ class: 'Old'
+ changes:
+ - kind: 'rename'
+ newName: 'New'
+''');
+ await resolveTestCode('''
+import '$importUri';
+void f(Old p) {}
+''');
+ await assertNoFix();
+ }
+}
+
+@reflectiveTest
class WrongNumberOfTypeArgumentsConstructorTest extends _DataDrivenTest {
Future<void> test_addTypeParameter() async {
setPackageContent('''
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
index 22ee1a1..e449a9e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
@@ -14,6 +14,8 @@
import 'modify_parameters_test.dart' as modify_parameters;
import 'rename_parameter_test.dart' as rename_parameter;
import 'rename_test.dart' as rename;
+import 'transform_override_set_parser_test.dart'
+ as transform_override_set_parser;
import 'transform_set_manager_test.dart' as transform_set_manager;
import 'transform_set_parser_test.dart' as transform_set_parser;
@@ -29,6 +31,7 @@
modify_parameters.main();
rename_parameter.main();
rename.main();
+ transform_override_set_parser.main();
transform_set_manager.main();
transform_set_parser.main();
});
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_override_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_override_set_parser_test.dart
new file mode 100644
index 0000000..5aa8721
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_override_set_parser_test.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2021, 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:_fe_analyzer_shared/src/base/errors.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set_parser.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../../../mocks.dart';
+import '../../../../../utils/test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(TransformSetParserTest);
+ });
+}
+
+/// Utilities shared between tests of the [TransformOverrideSetParser].
+abstract class AbstractTransformOverrideSetParserTest {
+ /// The listener to which errors will be reported.
+ GatheringErrorListener errorListener;
+
+ /// The result of parsing the test file's content.
+ TransformOverrideSet result;
+
+ void assertErrors(String code, List<ExpectedError> expectedErrors) {
+ parse(code);
+ errorListener.assertErrors(expectedErrors);
+ }
+
+ void assertNoErrors(String content) {
+ parse(content);
+ errorListener.assertNoErrors();
+ }
+
+ void assertOverride(String title, {bool bulkApply}) {
+ var override = result.overrideForTransform(title);
+ expect(override, isNotNull);
+ if (bulkApply != null) {
+ expect(override.bulkApply, bulkApply);
+ }
+ }
+
+ ExpectedError error(ErrorCode code, int offset, int length,
+ {String text,
+ Pattern messageContains,
+ List<ExpectedContextMessage> contextMessages =
+ const <ExpectedContextMessage>[]}) =>
+ ExpectedError(code, offset, length,
+ message: text,
+ messageContains: messageContains,
+ expectedContextMessages: contextMessages);
+
+ void parse(String content) {
+ errorListener = GatheringErrorListener();
+ var errorReporter = ErrorReporter(errorListener, MockSource('data.yaml'));
+ var parser = TransformOverrideSetParser(errorReporter);
+ result = parser.parse(content);
+ }
+}
+
+@reflectiveTest
+class TransformSetParserTest extends AbstractTransformOverrideSetParserTest {
+ void test_emptyFile() {
+ assertErrors('''
+''', [error(TransformSetErrorCode.invalidValue, 0, 0)]);
+ expect(result, isNull);
+ }
+
+ void test_oneTransform() {
+ assertNoErrors('''
+"transform one":
+ bulkApply: true
+''');
+ assertOverride('transform one', bulkApply: true);
+ }
+
+ void test_twoTransforms() {
+ assertNoErrors('''
+"transform one":
+ bulkApply: false
+"transform two":
+ bulkApply: true
+''');
+ assertOverride('transform one', bulkApply: false);
+ assertOverride('transform two', bulkApply: true);
+ }
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 6a9b1ed..9792278 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -86,7 +86,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 120;
+ static const int DATA_VERSION = 122;
/// The length of the list returned by [_computeDeclaredVariablesSignature].
static const int _declaredVariablesSignatureLength = 4;
diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
index f778aef..43bd5eb 100644
--- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
@@ -365,10 +365,10 @@
return lower;
}
if (!identical(UnknownInferredType.instance, upper)) {
- return toKnownType ? _typeSystem.greatestClosure(upper) : upper;
+ return toKnownType ? _typeSystem.greatestClosureOfSchema(upper) : upper;
}
if (!identical(UnknownInferredType.instance, lower)) {
- return toKnownType ? _typeSystem.leastClosure(lower) : lower;
+ return toKnownType ? _typeSystem.leastClosureOfSchema(lower) : lower;
}
return upper;
} else {
@@ -379,10 +379,10 @@
return upper;
}
if (!identical(UnknownInferredType.instance, lower)) {
- return toKnownType ? _typeSystem.leastClosure(lower) : lower;
+ return toKnownType ? _typeSystem.leastClosureOfSchema(lower) : lower;
}
if (!identical(UnknownInferredType.instance, upper)) {
- return toKnownType ? _typeSystem.greatestClosure(upper) : upper;
+ return toKnownType ? _typeSystem.greatestClosureOfSchema(upper) : upper;
}
return lower;
}
diff --git a/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart b/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart
new file mode 100644
index 0000000..c800909
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, 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:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/replacement_visitor.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type_algebra.dart';
+import 'package:analyzer/src/dart/element/type_system.dart';
+import 'package:meta/meta.dart';
+
+class LeastGreatestClosureHelper extends ReplacementVisitor {
+ final TypeSystemImpl typeSystem;
+ final DartType topType;
+ final DartType topFunctionType;
+ final DartType bottomType;
+ final Set<TypeParameterElement> eliminationTargets;
+
+ bool _isLeastClosure;
+ bool _isCovariant = true;
+
+ LeastGreatestClosureHelper({
+ @required this.typeSystem,
+ @required this.topType,
+ @required this.topFunctionType,
+ @required this.bottomType,
+ @required this.eliminationTargets,
+ });
+
+ DartType get _functionReplacement {
+ return _isLeastClosure && _isCovariant ||
+ (!_isLeastClosure && !_isCovariant)
+ ? bottomType
+ : topFunctionType;
+ }
+
+ DartType get _typeParameterReplacement {
+ return _isLeastClosure && _isCovariant ||
+ (!_isLeastClosure && !_isCovariant)
+ ? bottomType
+ : topType;
+ }
+
+ @override
+ void changeVariance() {
+ _isCovariant = !_isCovariant;
+ }
+
+ /// Returns a supertype of [type] for all values of [eliminationTargets].
+ DartType eliminateToGreatest(DartType type) {
+ _isCovariant = true;
+ _isLeastClosure = false;
+ return type.accept(this) ?? type;
+ }
+
+ /// Returns a subtype of [type] for all values of [eliminationTargets].
+ DartType eliminateToLeast(DartType type) {
+ _isCovariant = true;
+ _isLeastClosure = true;
+ return type.accept(this) ?? type;
+ }
+
+ @override
+ DartType visitFunctionType(FunctionType node) {
+ // - if `S` is
+ // `T Function<X0 extends B0, ...., Xk extends Bk>(T0 x0, ...., Tn xn,
+ // [Tn+1 xn+1, ..., Tm xm])`
+ // or `T Function<X0 extends B0, ...., Xk extends Bk>(T0 x0, ...., Tn xn,
+ // {Tn+1 xn+1, ..., Tm xm})`
+ // and `L` contains any free type variables from any of the `Bi`:
+ // - The least closure of `S` with respect to `L` is `Never`
+ // - The greatest closure of `S` with respect to `L` is `Function`
+ for (var typeParameter in node.typeFormals) {
+ var bound = typeParameter.bound as TypeImpl;
+ if (bound.referencesAny(eliminationTargets)) {
+ return _functionReplacement;
+ }
+ }
+
+ return super.visitFunctionType(node);
+ }
+
+ @override
+ DartType visitTypeParameterType(TypeParameterType node) {
+ if (eliminationTargets.contains(node.element)) {
+ var replacement = _typeParameterReplacement as TypeImpl;
+ return replacement.withNullability(
+ uniteNullabilities(
+ replacement.nullabilitySuffix,
+ node.nullabilitySuffix,
+ ),
+ );
+ }
+ return super.visitTypeParameterType(node);
+ }
+}
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index d355eab..7a031c7 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -635,7 +635,7 @@
// UP(X1 extends B1, T2)
// UP(X1 & B1, T2)
- if (T1 is TypeParameterType) {
+ if (T1 is TypeParameterTypeImpl) {
// T2 if X1 <: T2
if (_typeSystem.isSubtypeOf2(T1, T2)) {
return T2;
@@ -644,14 +644,16 @@
if (_typeSystem.isSubtypeOf2(T2, T1)) {
return T1;
}
- // otherwise UP(B1[Object/X1], T2)
- var T1_toObject = _typeParameterResolveToObjectBounds(T1);
- return getLeastUpperBound(T1_toObject, T2);
+ // otherwise UP(B1a, T2)
+ // where B1a is the greatest closure of B1 with respect to X1
+ var bound = _typeParameterBound(T1);
+ var closure = _typeSystem.greatestClosure(bound, [T1.element]);
+ return getLeastUpperBound(closure, T2);
}
// UP(T1, X2 extends B2)
// UP(T1, X2 & B2)
- if (T2 is TypeParameterType) {
+ if (T2 is TypeParameterTypeImpl) {
// X2 if T1 <: X2
if (_typeSystem.isSubtypeOf2(T1, T2)) {
// TODO(scheglov) How to get here?
@@ -661,9 +663,11 @@
if (_typeSystem.isSubtypeOf2(T2, T1)) {
return T1;
}
- // otherwise UP(T1, B2[Object/X2])
- var T2_toObject = _typeParameterResolveToObjectBounds(T2);
- return getLeastUpperBound(T1, T2_toObject);
+ // otherwise UP(T1, B2a)
+ // where B2a is the greatest closure of B2 with respect to X2
+ var bound = _typeParameterBound(T2);
+ var closure = _typeSystem.greatestClosure(bound, [T2.element]);
+ return getLeastUpperBound(T1, closure);
}
// UP(T Function<...>(...), Function) = Function
@@ -880,16 +884,14 @@
return _typeSystem.getGreatestLowerBound(a.type, b.type);
}
- /// TODO(scheglov) Use greatest closure.
- /// See https://github.com/dart-lang/language/pull/1195
- DartType _typeParameterResolveToObjectBounds(DartType type) {
- var element = type.element;
-
- var objectType = _typeSystem.isNonNullableByDefault
+ /// Return the promoted or declared bound of the type parameter.
+ DartType _typeParameterBound(TypeParameterTypeImpl type) {
+ var bound = type.promotedBound ?? type.element.bound;
+ if (bound != null) {
+ return bound;
+ }
+ return _typeSystem.isNonNullableByDefault
? _typeSystem.objectQuestion
: _typeSystem.objectStar;
-
- type = type.resolveToBound(objectType);
- return Substitution.fromMap({element: objectType}).substituteType(type);
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/type_algebra.dart b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
index fdfffde..a06c562 100644
--- a/pkg/analyzer/lib/src/dart/element/type_algebra.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
@@ -104,6 +104,25 @@
return Substitution.fromMap(substitution).substituteType(type);
}
+/// 1. Substituting T=X! into T! yields X!
+/// 2. Substituting T=X* into T! yields X*
+/// 3. Substituting T=X? into T! yields X?
+/// 4. Substituting T=X! into T* yields X*
+/// 5. Substituting T=X* into T* yields X*
+/// 6. Substituting T=X? into T* yields X?
+/// 7. Substituting T=X! into T? yields X?
+/// 8. Substituting T=X* into T? yields X?
+/// 9. Substituting T=X? into T? yields X?
+NullabilitySuffix uniteNullabilities(NullabilitySuffix a, NullabilitySuffix b) {
+ if (a == NullabilitySuffix.question || b == NullabilitySuffix.question) {
+ return NullabilitySuffix.question;
+ }
+ if (a == NullabilitySuffix.star || b == NullabilitySuffix.star) {
+ return NullabilitySuffix.star;
+ }
+ return NullabilitySuffix.none;
+}
+
class FreshTypeParameters {
final List<TypeParameterElement> freshTypeParameters;
final Substitution substitution;
@@ -533,7 +552,7 @@
var parameterSuffix = type.nullabilitySuffix;
var argumentSuffix = argument.nullabilitySuffix;
- var nullability = _computeNullability(parameterSuffix, argumentSuffix);
+ var nullability = uniteNullabilities(parameterSuffix, argumentSuffix);
return (argument as TypeImpl).withNullability(nullability);
}
@@ -547,30 +566,6 @@
if (types == null) return null;
return types.map((e) => e.accept(this)).toList();
}
-
- /// 1. Substituting T=X! into T! yields X!
- /// 2. Substituting T=X* into T! yields X*
- /// 3. Substituting T=X? into T! yields X?
- /// 4. Substituting T=X! into T* yields X*
- /// 5. Substituting T=X* into T* yields X*
- /// 6. Substituting T=X? into T* yields X?
- /// 7. Substituting T=X! into T? yields X?
- /// 8. Substituting T=X* into T? yields X?
- /// 9. Substituting T=X? into T? yields X?
- static NullabilitySuffix _computeNullability(
- NullabilitySuffix parameterSuffix,
- NullabilitySuffix argumentSuffix,
- ) {
- if (parameterSuffix == NullabilitySuffix.question ||
- argumentSuffix == NullabilitySuffix.question) {
- return NullabilitySuffix.question;
- }
- if (parameterSuffix == NullabilitySuffix.star ||
- argumentSuffix == NullabilitySuffix.star) {
- return NullabilitySuffix.star;
- }
- return NullabilitySuffix.none;
- }
}
class _UpperLowerBoundsSubstitution extends Substitution {
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index 3acda34..9b2ab91 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -16,6 +16,7 @@
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/generic_inferrer.dart';
import 'package:analyzer/src/dart/element/greatest_lower_bound.dart';
+import 'package:analyzer/src/dart/element/least_greatest_closure.dart';
import 'package:analyzer/src/dart/element/least_upper_bound.dart';
import 'package:analyzer/src/dart/element/normalize.dart';
import 'package:analyzer/src/dart/element/nullability_eliminator.dart';
@@ -348,6 +349,36 @@
return _leastUpperBoundHelper.getLeastUpperBound(T1, T2);
}
+ /// Returns the greatest closure of [type] with respect to [typeParameters].
+ ///
+ /// https://github.com/dart-lang/language
+ /// See `resources/type-system/inference.md`
+ DartType greatestClosure(
+ DartType type,
+ List<TypeParameterElement> typeParameters,
+ ) {
+ var typeParameterSet = Set<TypeParameterElement>.identity();
+ typeParameterSet.addAll(typeParameters);
+
+ if (isNonNullableByDefault) {
+ return LeastGreatestClosureHelper(
+ typeSystem: this,
+ topType: objectQuestion,
+ topFunctionType: typeProvider.functionType,
+ bottomType: NeverTypeImpl.instance,
+ eliminationTargets: typeParameterSet,
+ ).eliminateToGreatest(type);
+ } else {
+ return LeastGreatestClosureHelper(
+ typeSystem: this,
+ topType: DynamicTypeImpl.instance,
+ topFunctionType: typeProvider.functionType,
+ bottomType: typeProvider.nullType,
+ eliminationTargets: typeParameterSet,
+ ).eliminateToGreatest(type);
+ }
+ }
+
/// Returns the greatest closure of the given type [schema] with respect to
/// `_`.
///
@@ -362,7 +393,7 @@
///
/// Note that the greatest closure of a type schema is always a supertype of
/// any type which matches the schema.
- DartType greatestClosure(DartType schema) {
+ DartType greatestClosureOfSchema(DartType schema) {
if (isNonNullableByDefault) {
return TypeSchemaEliminationVisitor.run(
topType: objectQuestion,
@@ -1170,6 +1201,36 @@
return false;
}
+ /// Returns the least closure of [type] with respect to [typeParameters].
+ ///
+ /// https://github.com/dart-lang/language
+ /// See `resources/type-system/inference.md`
+ DartType leastClosure(
+ DartType type,
+ List<TypeParameterElement> typeParameters,
+ ) {
+ var typeParameterSet = Set<TypeParameterElement>.identity();
+ typeParameterSet.addAll(typeParameters);
+
+ if (isNonNullableByDefault) {
+ return LeastGreatestClosureHelper(
+ typeSystem: this,
+ topType: objectQuestion,
+ topFunctionType: typeProvider.functionType,
+ bottomType: NeverTypeImpl.instance,
+ eliminationTargets: typeParameterSet,
+ ).eliminateToLeast(type);
+ } else {
+ return LeastGreatestClosureHelper(
+ typeSystem: this,
+ topType: DynamicTypeImpl.instance,
+ topFunctionType: typeProvider.functionType,
+ bottomType: typeProvider.nullType,
+ eliminationTargets: typeParameterSet,
+ ).eliminateToLeast(type);
+ }
+ }
+
/// Returns the least closure of the given type [schema] with respect to `_`.
///
/// The least closure of a type schema `P` with respect to `_` is defined as
@@ -1183,7 +1244,7 @@
///
/// Note that the least closure of a type schema is always a subtype of any
/// type which matches the schema.
- DartType leastClosure(DartType schema) {
+ DartType leastClosureOfSchema(DartType schema) {
if (isNonNullableByDefault) {
return TypeSchemaEliminationVisitor.run(
topType: objectQuestion,
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 6f59ded..1e46e61 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -397,6 +397,18 @@
}
@override
+ void visitBinaryExpression(BinaryExpression node) {
+ if (node.operator.type == TokenType.AMPERSAND_AMPERSAND) {
+ node.leftOperand.accept(this);
+ assignedVariables.beginNode();
+ node.rightOperand.accept(this);
+ assignedVariables.endNode(node);
+ } else {
+ super.visitBinaryExpression(node);
+ }
+ }
+
+ @override
void visitCatchClause(CatchClause node) {
for (var identifier in [
node.exceptionParameter,
@@ -411,6 +423,15 @@
}
@override
+ void visitConditionalExpression(ConditionalExpression node) {
+ node.condition.accept(this);
+ assignedVariables.beginNode();
+ node.thenExpression.accept(this);
+ assignedVariables.endNode(node);
+ node.elseExpression.accept(this);
+ }
+
+ @override
void visitConstructorDeclaration(ConstructorDeclaration node) {
throw StateError('Should not visit top level declarations');
}
@@ -458,6 +479,24 @@
}
@override
+ void visitIfElement(IfElement node) {
+ node.condition.accept(this);
+ assignedVariables.beginNode();
+ node.thenElement.accept(this);
+ assignedVariables.endNode(node);
+ node.elseElement?.accept(this);
+ }
+
+ @override
+ void visitIfStatement(IfStatement node) {
+ node.condition.accept(this);
+ assignedVariables.beginNode();
+ node.thenStatement.accept(this);
+ assignedVariables.endNode(node);
+ node.elseStatement?.accept(this);
+ }
+
+ @override
void visitMethodDeclaration(MethodDeclaration node) {
throw StateError('Should not visit top level declarations');
}
@@ -491,6 +530,17 @@
}
@override
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ var element = node.staticElement;
+ if (element is VariableElement &&
+ node.inGetterContext() &&
+ node.parent is! FormalParameter &&
+ node.parent is! CatchClause) {
+ assignedVariables.read(element);
+ }
+ }
+
+ @override
void visitSwitchStatement(SwitchStatement node) {
var expression = node.expression;
var members = node.members;
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
index aec4def..56332a4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
@@ -98,7 +98,7 @@
// corresponding parameter in the context type schema with type
// schema `K`, the parameter is given an inferred type `T` where `T`
// is derived from `K` as follows.
- inferredType = _typeSystem.greatestClosure(inferredType);
+ inferredType = _typeSystem.greatestClosureOfSchema(inferredType);
// If the greatest closure of `K` is `S` and `S` is a subtype of
// `Null`, then `T` is `Object?`. Otherwise, `T` is `S`.
diff --git a/pkg/analyzer/lib/src/summary2/apply_resolution.dart b/pkg/analyzer/lib/src/summary2/apply_resolution.dart
index f1094c6..ec7202d 100644
--- a/pkg/analyzer/lib/src/summary2/apply_resolution.dart
+++ b/pkg/analyzer/lib/src/summary2/apply_resolution.dart
@@ -141,6 +141,15 @@
}
@override
+ void visitAwaitExpression(AwaitExpression node) {
+ _expectMarker(MarkerTag.AwaitExpression_expression);
+ node.expression.accept(this);
+ _expectMarker(MarkerTag.AwaitExpression_expression2);
+ _expression(node);
+ _expectMarker(MarkerTag.AwaitExpression_end);
+ }
+
+ @override
void visitBinaryExpression(BinaryExpression node) {
_expectMarker(MarkerTag.BinaryExpression_leftOperand);
node.leftOperand.accept(this);
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
index 6313f8e..8be47e2 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
@@ -46,6 +46,8 @@
return _readAssertInitializer();
case Tag.AssignmentExpression:
return _readAssignmentExpression();
+ case Tag.AwaitExpression:
+ return _readAwaitExpression();
case Tag.BinaryExpression:
return _readBinaryExpression();
case Tag.BooleanLiteral:
@@ -309,6 +311,11 @@
);
}
+ AwaitExpression _readAwaitExpression() {
+ var expression = readNode() as Expression;
+ return astFactory.awaitExpression(Tokens.AWAIT, expression);
+ }
+
BinaryExpression _readBinaryExpression() {
var leftOperand = readNode() as Expression;
var rightOperand = readNode() as Expression;
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
index bcda462..43eee8a 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
@@ -20,6 +20,9 @@
AsExpression_type,
AsExpression_expression2,
AsExpression_end,
+ AwaitExpression_expression,
+ AwaitExpression_expression2,
+ AwaitExpression_end,
Expression_staticType,
AssertInitializer_condition,
AssertInitializer_message,
@@ -343,6 +346,7 @@
static const int AsExpression = 84;
static const int AssertInitializer = 82;
static const int AssignmentExpression = 96;
+ static const int AwaitExpression = 100;
static const int BinaryExpression = 52;
static const int BooleanLiteral = 4;
static const int CascadeExpression = 95;
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
index c805bbd..16e1177 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
@@ -146,6 +146,19 @@
}
@override
+ void visitAwaitExpression(AwaitExpression node) {
+ _writeByte(Tag.AwaitExpression);
+
+ _writeMarker(MarkerTag.AwaitExpression_expression);
+ _writeNode(node.expression);
+
+ _writeMarker(MarkerTag.AsExpression_expression2);
+ _storeExpression(node);
+
+ _writeMarker(MarkerTag.AwaitExpression_end);
+ }
+
+ @override
void visitBinaryExpression(BinaryExpression node) {
_writeByte(Tag.BinaryExpression);
diff --git a/pkg/analyzer/lib/src/util/ast_data_extractor.dart b/pkg/analyzer/lib/src/util/ast_data_extractor.dart
index 2147db8..a95a277 100644
--- a/pkg/analyzer/lib/src/util/ast_data_extractor.dart
+++ b/pkg/analyzer/lib/src/util/ast_data_extractor.dart
@@ -26,13 +26,13 @@
void computeForClass(Declaration node, Id id) {
if (id == null) return;
T value = computeNodeValue(id, node);
- registerValue(uri, node.offset, id, value, node);
+ registerValue(uri, _nodeOffset(node), id, value, node);
}
void computeForCollectionElement(CollectionElement node, NodeId id) {
if (id == null) return;
T value = computeNodeValue(id, node);
- registerValue(uri, node.offset, id, value, node);
+ registerValue(uri, _nodeOffset(node), id, value, node);
}
void computeForLibrary(LibraryElement library, Id id) {
@@ -44,13 +44,13 @@
void computeForMember(Declaration node, Id id) {
if (id == null) return;
T value = computeNodeValue(id, node);
- registerValue(uri, node.offset, id, value, node);
+ registerValue(uri, _nodeOffset(node), id, value, node);
}
void computeForStatement(Statement node, NodeId id) {
if (id == null) return;
T value = computeNodeValue(id, node);
- registerValue(uri, node.offset, id, value, node);
+ registerValue(uri, _nodeOffset(node), id, value, node);
}
/// Implement this to compute the data corresponding to [node].
@@ -174,7 +174,14 @@
}
int _nodeOffset(AstNode node) {
- var offset = node.offset;
+ int offset;
+ if (node is ConditionalExpression) {
+ offset = node.question.offset;
+ } else if (node is BinaryExpression) {
+ offset = node.operator.offset;
+ } else {
+ offset = node.offset;
+ }
assert(offset != null && offset >= 0,
"No fileOffset on $node (${node.runtimeType})");
return offset;
diff --git a/pkg/analyzer/test/generated/invalid_code_test.dart b/pkg/analyzer/test/generated/invalid_code_test.dart
index 4b043ca..0d4c949 100644
--- a/pkg/analyzer/test/generated/invalid_code_test.dart
+++ b/pkg/analyzer/test/generated/invalid_code_test.dart
@@ -20,6 +20,12 @@
/// and analysis finishes without exceptions.
@reflectiveTest
class InvalidCodeTest extends PubPackageResolutionTest {
+ test_const_AwaitExpression() async {
+ await _assertCanBeAnalyzed(r'''
+const a = await b();
+''');
+ }
+
test_const_ForPartsWithExpression() async {
await _assertCanBeAnalyzed(r'''
@A([for (;;) 0])
diff --git a/pkg/analyzer/test/id_tests/assigned_variables_test.dart b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
index f674c7c..00c8a11 100644
--- a/pkg/analyzer/test/id_tests/assigned_variables_test.dart
+++ b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
@@ -69,12 +69,16 @@
if (node == _currentDeclaration) {
return _Data(
_convertVars(_currentAssignedVariables.declaredAtTopLevel),
+ _convertVars(_currentAssignedVariables.readAnywhere),
+ _convertVars(_currentAssignedVariables.readCapturedAnywhere),
_convertVars(_currentAssignedVariables.writtenAnywhere),
_convertVars(_currentAssignedVariables.capturedAnywhere));
}
if (!_currentAssignedVariables.isTracked(node)) return null;
return _Data(
_convertVars(_currentAssignedVariables.declaredInNode(node)),
+ _convertVars(_currentAssignedVariables.readInNode(node)),
+ _convertVars(_currentAssignedVariables.readCapturedInNode(node)),
_convertVars(_currentAssignedVariables.writtenInNode(node)),
_convertVars(_currentAssignedVariables.capturedInNode(node)));
}
@@ -123,6 +127,12 @@
if (actualData.declared.isNotEmpty) {
parts.add('declared=${_setToString(actualData.declared)}');
}
+ if (actualData.read.isNotEmpty) {
+ parts.add('read=${_setToString(actualData.read)}');
+ }
+ if (actualData.readCaptured.isNotEmpty) {
+ parts.add('read=${_setToString(actualData.readCaptured)}');
+ }
if (actualData.assigned.isNotEmpty) {
parts.add('assigned=${_setToString(actualData.assigned)}');
}
@@ -156,9 +166,14 @@
class _Data {
final Set<String> declared;
+ final Set<String> read;
+
+ final Set<String> readCaptured;
+
final Set<String> assigned;
final Set<String> captured;
- _Data(this.declared, this.assigned, this.captured);
+ _Data(this.declared, this.read, this.readCaptured, this.assigned,
+ this.captured);
}
diff --git a/pkg/analyzer/test/src/dart/element/least_greatest_closure_test.dart b/pkg/analyzer/test/src/dart/element/least_greatest_closure_test.dart
new file mode 100644
index 0000000..2d92505
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/element/least_greatest_closure_test.dart
@@ -0,0 +1,275 @@
+// Copyright (c) 2021, 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:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:meta/meta.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../generated/type_system_test.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(GreatestClosureLegacyTest);
+ defineReflectiveTests(GreatestClosureNullSafetyTest);
+ });
+}
+
+@reflectiveTest
+class GreatestClosureLegacyTest extends AbstractTypeSystemTest {
+ TypeParameterElement T;
+ TypeParameterType T_none;
+ TypeParameterType T_question;
+ TypeParameterType T_star;
+
+ @override
+ void setUp() {
+ super.setUp();
+
+ T = typeParameter('T');
+ T_none = typeParameterTypeNone(T);
+ T_question = typeParameterTypeQuestion(T);
+ T_star = typeParameterTypeStar(T);
+ }
+
+ test_contravariant() {
+ _check(
+ functionTypeStar(returnType: voidNone, parameters: [
+ requiredParameter(type: T_star),
+ ]),
+ greatest: 'void Function(Null*)*',
+ least: 'void Function(dynamic)*',
+ );
+
+ _check(
+ functionTypeStar(
+ returnType: functionTypeStar(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: T_star),
+ ],
+ ),
+ ),
+ greatest: 'void Function(Null*)* Function()*',
+ least: 'void Function(dynamic)* Function()*',
+ );
+ }
+
+ test_covariant() {
+ _check(T_star, greatest: 'dynamic', least: 'Null*');
+
+ _check(
+ listStar(T_star),
+ greatest: 'List<dynamic>*',
+ least: 'List<Null*>*',
+ );
+
+ _check(
+ functionTypeStar(returnType: voidNone, parameters: [
+ requiredParameter(
+ type: functionTypeStar(returnType: intStar, parameters: [
+ requiredParameter(type: T_star),
+ ]),
+ ),
+ ]),
+ greatest: 'void Function(int* Function(dynamic)*)*',
+ least: 'void Function(int* Function(Null*)*)*',
+ );
+ }
+
+ test_function() {
+ // void Function<U extends T>()
+ _check(
+ functionTypeStar(
+ typeFormals: [
+ typeParameter('U', bound: T_star),
+ ],
+ returnType: voidNone,
+ ),
+ greatest: 'Function*',
+ least: 'Null*',
+ );
+ }
+
+ test_unrelated() {
+ _check1(intStar, 'int*');
+ _check1(listStar(intStar), 'List<int*>*');
+
+ _check1(objectStar, 'Object*');
+ _check1(neverStar, 'Never*');
+ _check1(nullStar, 'Null*');
+
+ _check1(dynamicNone, 'dynamic');
+
+ _check1(
+ functionTypeStar(returnType: stringStar, parameters: [
+ requiredParameter(type: intStar),
+ ]),
+ 'String* Function(int*)*',
+ );
+
+ _check1(
+ typeParameterTypeStar(
+ typeParameter('U'),
+ ),
+ 'U*',
+ );
+ }
+
+ void _check(
+ DartType type, {
+ @required String greatest,
+ @required String least,
+ }) {
+ var greatestResult = typeSystem.greatestClosure(type, [T]);
+ expect(
+ greatestResult.getDisplayString(withNullability: true),
+ greatest,
+ );
+
+ var leastResult = typeSystem.leastClosure(type, [T]);
+ expect(
+ leastResult.getDisplayString(withNullability: true),
+ least,
+ );
+ }
+
+ void _check1(DartType type, String expected) {
+ _check(type, greatest: expected, least: expected);
+ }
+}
+
+@reflectiveTest
+class GreatestClosureNullSafetyTest extends AbstractTypeSystemNullSafetyTest {
+ TypeParameterElement T;
+ TypeParameterType T_none;
+ TypeParameterType T_question;
+ TypeParameterType T_star;
+
+ @override
+ void setUp() {
+ super.setUp();
+
+ T = typeParameter('T');
+ T_none = typeParameterTypeNone(T);
+ T_question = typeParameterTypeQuestion(T);
+ T_star = typeParameterTypeStar(T);
+ }
+
+ test_contravariant() {
+ _check(
+ functionTypeNone(returnType: voidNone, parameters: [
+ requiredParameter(type: T_none),
+ ]),
+ greatest: 'void Function(Never)',
+ least: 'void Function(Object?)',
+ );
+
+ _check(
+ functionTypeNone(
+ returnType: functionTypeNone(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: T_none),
+ ],
+ ),
+ ),
+ greatest: 'void Function(Never) Function()',
+ least: 'void Function(Object?) Function()',
+ );
+ }
+
+ test_covariant() {
+ _check(T_none, greatest: 'Object?', least: 'Never');
+ _check(T_question, greatest: 'Object?', least: 'Never?');
+ _check(T_star, greatest: 'Object?', least: 'Never*');
+
+ _check(
+ listNone(T_none),
+ greatest: 'List<Object?>',
+ least: 'List<Never>',
+ );
+
+ _check(
+ functionTypeNone(returnType: voidNone, parameters: [
+ requiredParameter(
+ type: functionTypeNone(returnType: intNone, parameters: [
+ requiredParameter(type: T_none),
+ ]),
+ ),
+ ]),
+ greatest: 'void Function(int Function(Object?))',
+ least: 'void Function(int Function(Never))');
+ }
+
+ test_function() {
+ // void Function<U extends T>()
+ _check(
+ functionTypeNone(
+ typeFormals: [
+ typeParameter('U', bound: T_none),
+ ],
+ returnType: voidNone,
+ ),
+ greatest: 'Function',
+ least: 'Never',
+ );
+ }
+
+ test_unrelated() {
+ _check1(intNone, 'int');
+ _check1(intQuestion, 'int?');
+ _check1(intStar, 'int*');
+
+ _check1(listNone(intNone), 'List<int>');
+ _check1(listQuestion(intNone), 'List<int>?');
+
+ _check1(objectNone, 'Object');
+ _check1(objectQuestion, 'Object?');
+ _check1(objectStar, 'Object*');
+
+ _check1(neverNone, 'Never');
+ _check1(neverQuestion, 'Never?');
+ _check1(neverStar, 'Never*');
+
+ _check1(dynamicNone, 'dynamic');
+
+ _check1(
+ functionTypeNone(returnType: stringNone, parameters: [
+ requiredParameter(type: intNone),
+ ]),
+ 'String Function(int)',
+ );
+
+ _check1(
+ typeParameterTypeNone(
+ typeParameter('U'),
+ ),
+ 'U',
+ );
+ }
+
+ void _check(
+ DartType type, {
+ @required String greatest,
+ @required String least,
+ }) {
+ var greatestResult = typeSystem.greatestClosure(type, [T]);
+ expect(
+ greatestResult.getDisplayString(withNullability: true),
+ greatest,
+ );
+
+ var leastResult = typeSystem.leastClosure(type, [T]);
+ expect(
+ leastResult.getDisplayString(withNullability: true),
+ least,
+ );
+ }
+
+ void _check1(DartType type, String expected) {
+ _check(type, greatest: expected, least: expected);
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/element/subtype_test.dart b/pkg/analyzer/test/src/dart/element/subtype_test.dart
index 4cb55f1..18b28e1 100644
--- a/pkg/analyzer/test/src/dart/element/subtype_test.dart
+++ b/pkg/analyzer/test/src/dart/element/subtype_test.dart
@@ -5410,6 +5410,21 @@
);
}
+ @FailingTest(issue: 'https://github.com/dart-lang/language/issues/433')
+ test_typeParameter_44() {
+ var T = typeParameter('T');
+ var T_none = typeParameterTypeNone(T);
+ var FutureOr_T_none = futureOrNone(T_none);
+ T.bound = FutureOr_T_none;
+
+ isSubtype(
+ T_none,
+ FutureOr_T_none,
+ strT0: 'T, T extends FutureOr<T>',
+ strT1: 'FutureOr<T>, T extends FutureOr<T>',
+ );
+ }
+
void _defineType(String str, DartType type) {
for (var key in _types.keys) {
if (key == 'Never' || _typeStr(type) == 'Never') {
diff --git a/pkg/analyzer/test/src/dart/element/test_all.dart b/pkg/analyzer/test/src/dart/element/test_all.dart
index bac0ac6..f86065c 100644
--- a/pkg/analyzer/test/src/dart/element/test_all.dart
+++ b/pkg/analyzer/test/src/dart/element/test_all.dart
@@ -13,6 +13,7 @@
import 'future_value_type_test.dart' as future_value_type;
import 'generic_inferrer_test.dart' as generic_inferrer;
import 'inheritance_manager3_test.dart' as inheritance_manager3;
+import 'least_greatest_closure_test.dart' as least_greatest_closure_test;
import 'least_upper_bound_helper_test.dart' as least_upper_bound_helper;
import 'normalize_type_test.dart' as normalize_type;
import 'nullability_eliminator_test.dart' as nullability_eliminator;
@@ -41,6 +42,7 @@
future_value_type.main();
generic_inferrer.main();
inheritance_manager3.main();
+ least_greatest_closure_test.main();
least_upper_bound_helper.main();
normalize_type.main();
nullability_eliminator.main();
diff --git a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
index 77f2918..6e4b9fe 100644
--- a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
+++ b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
@@ -3092,6 +3092,63 @@
);
}
+ void test_typeParameter_greatestClosure_functionBounded() {
+ var T = typeParameter('T');
+ var T_none = typeParameterTypeNone(T);
+ T.bound = functionTypeNone(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: T_none),
+ ],
+ );
+
+ _checkLeastUpperBound(
+ T_none,
+ functionTypeNone(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: nullNone),
+ ],
+ ),
+ functionTypeNone(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: neverNone),
+ ],
+ ),
+ );
+ }
+
+ void test_typeParameter_greatestClosure_functionPromoted() {
+ var T = typeParameter('T');
+ var T_none = typeParameterTypeNone(T);
+ var T_none_promoted = typeParameterTypeNone(
+ T,
+ promotedBound: functionTypeNone(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: T_none),
+ ],
+ ),
+ );
+
+ _checkLeastUpperBound(
+ T_none_promoted,
+ functionTypeNone(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: nullNone),
+ ],
+ ),
+ functionTypeNone(
+ returnType: voidNone,
+ parameters: [
+ requiredParameter(type: neverNone),
+ ],
+ ),
+ );
+ }
+
void test_typeParameter_interface_bounded() {
var A = class_(name: 'A');
var A_none = interfaceTypeNone(A);
diff --git a/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart b/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart
index 051d9d0..3a90b0b 100644
--- a/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart
@@ -28,8 +28,6 @@
@override
Future<EditGetAssistsResult> handleEditGetAssists(
EditGetAssistsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var request = await getAssistRequest(parameters);
var generator = AssistGenerator(getAssistContributors(path));
@@ -50,8 +48,6 @@
@override
Future<AssistRequest> getAssistRequest(
EditGetAssistsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var result = await getResolvedUnitResult(path);
return DartAssistRequestImpl(
diff --git a/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart b/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart
index ed39aba..4bbf59a 100644
--- a/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart
@@ -29,8 +29,6 @@
@override
Future<CompletionGetSuggestionsResult> handleCompletionGetSuggestions(
CompletionGetSuggestionsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var request = await getCompletionRequest(parameters);
var generator = CompletionGenerator(getCompletionContributors(path));
@@ -52,8 +50,6 @@
@override
Future<CompletionRequest> getCompletionRequest(
CompletionGetSuggestionsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var result = await getResolvedUnitResult(parameters.file);
return DartCompletionRequestImpl(
resourceProvider, parameters.offset, result);
diff --git a/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart b/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart
index 1b8d651..1fde901 100644
--- a/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart
@@ -21,8 +21,6 @@
mixin DartFixesMixin implements FixesMixin {
@override
Future<FixesRequest> getFixesRequest(EditGetFixesParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var offset = parameters.offset;
var result = await getResolvedUnitResult(path);
@@ -59,8 +57,6 @@
@override
Future<EditGetFixesResult> handleEditGetFixes(
EditGetFixesParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var request = await getFixesRequest(parameters);
var generator = FixGenerator(getFixContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart b/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart
index ecae950..6bc5e1b 100644
--- a/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart
@@ -19,8 +19,6 @@
mixin DartFoldingMixin implements FoldingMixin {
@override
Future<FoldingRequest> getFoldingRequest(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var result = await getResolvedUnitResult(path);
return DartFoldingRequestImpl(resourceProvider, result);
}
@@ -44,8 +42,6 @@
@override
Future<void> sendFoldingNotification(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
try {
var request = await getFoldingRequest(path);
var generator = FoldingGenerator(getFoldingContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart b/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart
index a74deba..1a119bb 100644
--- a/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart
@@ -19,8 +19,6 @@
mixin DartHighlightsMixin implements HighlightsMixin {
@override
Future<HighlightsRequest> getHighlightsRequest(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var result = await getResolvedUnitResult(path);
return DartHighlightsRequestImpl(resourceProvider, result);
}
@@ -44,8 +42,6 @@
@override
Future<void> sendHighlightsNotification(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
try {
var request = await getHighlightsRequest(path);
var generator = HighlightsGenerator(getHighlightsContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart b/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart
index 353ec8d..d452e0d 100644
--- a/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart
@@ -21,8 +21,6 @@
@override
Future<EntryRequest> getEntryRequest(
KytheGetKytheEntriesParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var result = await getResolvedUnitResult(path);
return DartEntryRequestImpl(resourceProvider, result);
@@ -48,8 +46,6 @@
@override
Future<KytheGetKytheEntriesResult> handleKytheGetKytheEntries(
KytheGetKytheEntriesParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var request = await getEntryRequest(parameters);
var generator = EntryGenerator(getEntryContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart b/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart
index fb215d7..07dbddb 100644
--- a/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart
@@ -21,8 +21,6 @@
@override
Future<NavigationRequest> getNavigationRequest(
AnalysisGetNavigationParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var result = await getResolvedUnitResult(path);
var offset = parameters.offset;
@@ -55,8 +53,6 @@
@override
Future<AnalysisGetNavigationResult> handleAnalysisGetNavigation(
AnalysisGetNavigationParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var path = parameters.file;
var request = await getNavigationRequest(parameters);
var generator = NavigationGenerator(getNavigationContributors(path));
@@ -69,8 +65,6 @@
/// server.
@override
Future<void> sendNavigationNotification(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
try {
var request =
await getNavigationRequest(AnalysisGetNavigationParams(path, -1, -1));
diff --git a/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart b/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart
index 7c2fd1e..72a2c6c 100644
--- a/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart
@@ -19,8 +19,6 @@
mixin DartOccurrencesMixin implements OccurrencesMixin {
@override
Future<OccurrencesRequest> getOccurrencesRequest(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var result = await getResolvedUnitResult(path);
return DartOccurrencesRequestImpl(resourceProvider, result);
}
@@ -44,8 +42,6 @@
@override
Future<void> sendOccurrencesNotification(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
try {
var request = await getOccurrencesRequest(path);
var generator = OccurrencesGenerator(getOccurrencesContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart b/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart
index b249718..760103f 100644
--- a/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart
@@ -19,8 +19,6 @@
mixin DartOutlineMixin implements OutlineMixin {
@override
Future<OutlineRequest> getOutlineRequest(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var result = await getResolvedUnitResult(path);
return DartOutlineRequestImpl(resourceProvider, result);
}
@@ -44,8 +42,6 @@
@override
Future<void> sendOutlineNotification(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
try {
var request = await getOutlineRequest(path);
var generator = OutlineGenerator(getOutlineContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/plugin.dart b/pkg/analyzer_plugin/lib/plugin/plugin.dart
index 970d5d9..9759368 100644
--- a/pkg/analyzer_plugin/lib/plugin/plugin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/plugin.dart
@@ -153,8 +153,6 @@
/// Throw a [RequestFailure] is the file cannot be analyzed or if the driver
/// associated with the file is not an [AnalysisDriver].
Future<ResolvedUnitResult> getResolvedUnitResult(String path) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var driver = driverForPath(path);
if (driver is! AnalysisDriver) {
// Return an error from the request.
@@ -176,8 +174,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<AnalysisGetNavigationResult> handleAnalysisGetNavigation(
AnalysisGetNavigationParams params) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
return AnalysisGetNavigationResult(
<String>[], <NavigationTarget>[], <NavigationRegion>[]);
}
@@ -187,8 +183,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<AnalysisHandleWatchEventsResult> handleAnalysisHandleWatchEvents(
AnalysisHandleWatchEventsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
for (var event in parameters.events) {
switch (event.type) {
case WatchEventType.ADD:
@@ -213,8 +207,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<AnalysisSetContextRootsResult> handleAnalysisSetContextRoots(
AnalysisSetContextRootsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var contextRoots = parameters.roots;
var oldRoots = driverMap.keys.toList();
for (var contextRoot in contextRoots) {
@@ -244,8 +236,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<AnalysisSetPriorityFilesResult> handleAnalysisSetPriorityFiles(
AnalysisSetPriorityFilesParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var files = parameters.files;
var filesByDriver = <AnalysisDriverGeneric, List<String>>{};
for (var file in files) {
@@ -269,8 +259,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<AnalysisSetSubscriptionsResult> handleAnalysisSetSubscriptions(
AnalysisSetSubscriptionsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var subscriptions = parameters.subscriptions;
var newSubscriptions = subscriptionManager.setSubscriptions(subscriptions);
sendNotificationsForSubscriptions(newSubscriptions);
@@ -284,8 +272,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<AnalysisUpdateContentResult> handleAnalysisUpdateContent(
AnalysisUpdateContentParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
Map<String, Object> files = parameters.files;
files.forEach((String filePath, Object overlay) {
if (overlay is AddContentOverlay) {
@@ -319,8 +305,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<CompletionGetSuggestionsResult> handleCompletionGetSuggestions(
CompletionGetSuggestionsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
return CompletionGetSuggestionsResult(
-1, -1, const <CompletionSuggestion>[]);
}
@@ -330,8 +314,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<EditGetAssistsResult> handleEditGetAssists(
EditGetAssistsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
return EditGetAssistsResult(const <PrioritizedSourceChange>[]);
}
@@ -342,8 +324,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<EditGetAvailableRefactoringsResult> handleEditGetAvailableRefactorings(
EditGetAvailableRefactoringsParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
return EditGetAvailableRefactoringsResult(const <RefactoringKind>[]);
}
@@ -352,8 +332,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<EditGetFixesResult> handleEditGetFixes(
EditGetFixesParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
return EditGetFixesResult(const <AnalysisErrorFixes>[]);
}
@@ -362,8 +340,7 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<EditGetRefactoringResult> handleEditGetRefactoring(
EditGetRefactoringParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- return await null;
+ return null;
}
/// Handle a 'kythe.getKytheEntries' request.
@@ -371,8 +348,7 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<KytheGetKytheEntriesResult> handleKytheGetKytheEntries(
KytheGetKytheEntriesParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- return await null;
+ return null;
}
/// Handle a 'plugin.shutdown' request. Subclasses can override this method to
@@ -382,8 +358,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<PluginShutdownResult> handlePluginShutdown(
PluginShutdownParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
return PluginShutdownResult();
}
@@ -392,8 +366,6 @@
/// Throw a [RequestFailure] if the request could not be handled.
Future<PluginVersionCheckResult> handlePluginVersionCheck(
PluginVersionCheckParams parameters) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var byteStorePath = parameters.byteStorePath;
var sdkPath = parameters.sdkPath;
var versionString = parameters.version;
@@ -511,8 +483,6 @@
/// Compute the response that should be returned for the given [request], or
/// `null` if the response has already been sent.
Future<Response> _getResponse(Request request, int requestTime) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
ResponseResult result;
switch (request.method) {
case ANALYSIS_REQUEST_GET_NAVIGATION:
@@ -584,8 +554,6 @@
/// The method that is called when a [request] is received from the analysis
/// server.
Future<void> _onRequest(Request request) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var requestTime = DateTime.now().millisecondsSinceEpoch;
var id = request.id;
Response response;
diff --git a/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart b/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
index 664e448..2b35cc7 100644
--- a/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
+++ b/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
@@ -181,8 +181,6 @@
Future<void> listen(void Function(Response response) onResponse,
void Function(Notification notification) onNotification,
{void Function(dynamic error) onError, void Function() onDone}) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
if (_isolate != null) {
throw StateError('Cannot listen to the same channel more than once.');
}
diff --git a/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart b/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart
index 0b192c3..4c225fe 100644
--- a/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart
+++ b/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart
@@ -72,8 +72,6 @@
/// non-fatal 'plugin.error' notification.
Future<GeneratorResult<CompletionGetSuggestionsResult>>
generateCompletionResponse(CompletionRequest request) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var notifications = <Notification>[];
var collector = CompletionCollectorImpl();
try {
diff --git a/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart b/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart
index d18758d..bac46ee 100644
--- a/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart
+++ b/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart
@@ -34,8 +34,6 @@
@override
Future<void> computeSuggestions(
DartCompletionRequest request, CompletionCollector collector) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var target =
CompletionTarget.forOffset(request.result.unit, request.offset);
var optype = OpType.forCompletion(target, request.offset);
@@ -61,8 +59,6 @@
CompletionTarget target,
OpType optype,
}) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
target ??= CompletionTarget.forOffset(request.result.unit, request.offset,
entryPoint: entryPoint);
optype ??= OpType.forCompletion(target, request.offset);
diff --git a/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart b/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
index f5630f1..19b3cb0 100644
--- a/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
+++ b/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
@@ -26,8 +26,6 @@
@override
Future<void> computeSuggestions(
DartCompletionRequest request, CompletionCollector collector) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var containingLibrary = request.result.libraryElement;
// Gracefully degrade if the library element is not resolved
// e.g. detached part file or source change
@@ -46,8 +44,6 @@
/// Clients should not overload this function.
Future<void> computeSuggestionsWithEntryPoint(DartCompletionRequest request,
CompletionCollector collector, AstNode entryPoint) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
var containingLibrary = request.result.libraryElement;
// Gracefully degrade if the library element is not resolved
// e.g. detached part file or source change
diff --git a/pkg/compiler/lib/src/constants/values.dart b/pkg/compiler/lib/src/constants/values.dart
index 14b1867..f33c13b 100644
--- a/pkg/compiler/lib/src/constants/values.dart
+++ b/pkg/compiler/lib/src/constants/values.dart
@@ -27,6 +27,7 @@
INTERCEPTOR,
JS_NAME,
DUMMY_INTERCEPTOR,
+ LATE_SENTINEL,
UNREACHABLE,
INSTANTIATION,
DEFERRED_GLOBAL,
@@ -52,6 +53,8 @@
covariant InterceptorConstantValue constant, covariant A arg);
R visitDummyInterceptor(
covariant DummyInterceptorConstantValue constant, covariant A arg);
+ R visitLateSentinel(
+ covariant LateSentinelConstantValue constant, covariant A arg);
R visitUnreachable(
covariant UnreachableConstantValue constant, covariant A arg);
R visitJsName(covariant JsNameConstantValue constant, covariant A arg);
@@ -894,6 +897,34 @@
String toStructuredText(DartTypes dartTypes) => 'DummyInterceptorConstant()';
}
+/// A constant used to represent the sentinel for uninitialized late fields and
+/// variables.
+class LateSentinelConstantValue extends ConstantValue {
+ factory LateSentinelConstantValue() => const LateSentinelConstantValue._();
+
+ const LateSentinelConstantValue._();
+
+ @override
+ List<ConstantValue> getDependencies() => const <ConstantValue>[];
+
+ @override
+ accept(ConstantValueVisitor visitor, arg) {
+ return visitor.visitLateSentinel(this, arg);
+ }
+
+ @override
+ DartType getType(CommonElements types) => types.dynamicType;
+
+ @override
+ ConstantValueKind get kind => ConstantValueKind.LATE_SENTINEL;
+
+ @override
+ String toDartText(DartTypes dartTypes) => 'late_sentinel()';
+
+ @override
+ String toStructuredText(DartTypes dartTypes) => 'LateSentinelConstant()';
+}
+
// A constant with an empty type used in [HInstruction]s of an expression
// in an unreachable context.
class UnreachableConstantValue extends ConstantValue {
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/constants.dart b/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
index 935095e..7495c23 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
@@ -56,6 +56,11 @@
}
@override
+ TypeMask visitLateSentinel(
+ LateSentinelConstantValue constant, JClosedWorld closedWorld) =>
+ _abstractValueDomain.dynamicType;
+
+ @override
TypeMask visitUnreachable(
UnreachableConstantValue constant, JClosedWorld closedWorld) {
return _abstractValueDomain.emptyType;
diff --git a/pkg/compiler/lib/src/js_backend/constant_emitter.dart b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
index 95166e4..4d0546c 100644
--- a/pkg/compiler/lib/src/js_backend/constant_emitter.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
@@ -19,6 +19,7 @@
import '../js_model/type_recipe.dart' show TypeExpressionRecipe;
import '../options.dart';
import 'field_analysis.dart' show JFieldAnalysis;
+import 'namer.dart';
import 'runtime_types_new.dart' show RecipeEncoder;
import 'runtime_types_resolution.dart';
@@ -31,8 +32,9 @@
class ModularConstantEmitter
implements ConstantValueVisitor<jsAst.Expression, Null> {
final CompilerOptions _options;
+ final ModularNamer _namer;
- ModularConstantEmitter(this._options);
+ ModularConstantEmitter(this._options, this._namer);
/// Constructs a literal expression that evaluates to the constant. Uses a
/// canonical name unless the constant can be emitted multiple times (as for
@@ -159,6 +161,10 @@
}
@override
+ jsAst.Expression visitLateSentinel(LateSentinelConstantValue constant, [_]) =>
+ js('#', _namer.staticStateHolder);
+
+ @override
jsAst.Expression visitUnreachable(UnreachableConstantValue constant, [_]) {
// Unreachable constants should be rare in generated code, so we use
// `undefined` encoded as `void 1' to make them distinctive.
@@ -226,6 +232,7 @@
/// can be inlined.
ConstantEmitter(
CompilerOptions options,
+ ModularNamer _namer,
this._commonElements,
this._elementEnvironment,
this._rtiNeed,
@@ -234,7 +241,7 @@
this._emitter,
this._constantReferenceGenerator,
this._makeConstantList)
- : super(options);
+ : super(options, _namer);
@override
jsAst.Expression visitList(ListConstantValue constant, [_]) {
diff --git a/pkg/compiler/lib/src/js_backend/field_analysis.dart b/pkg/compiler/lib/src/js_backend/field_analysis.dart
index ac07284..8b56213 100644
--- a/pkg/compiler/lib/src/js_backend/field_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/field_analysis.dart
@@ -58,9 +58,16 @@
FieldEntity fieldElement = _elementMap.getField(field);
ir.Expression expression = field.initializer;
- ConstantValue value = _elementMap.getConstantValue(
- _elementMap.getStaticTypeContext(fieldElement), expression,
- requireConstant: false, implicitNull: true);
+ ConstantValue value;
+ if (expression is ir.StaticInvocation &&
+ identical(
+ expression.target, _elementMap.coreTypes.createSentinelMethod)) {
+ value = LateSentinelConstantValue();
+ } else {
+ value = _elementMap.getConstantValue(
+ _elementMap.getStaticTypeContext(fieldElement), expression,
+ requireConstant: false, implicitNull: true);
+ }
if (value != null && value.isConstant) {
fieldData[fieldElement] = new AllocatorData(value);
}
@@ -127,9 +134,16 @@
void registerStaticField(KField field, EvaluationComplexity complexity) {
ir.Field node = _elementMap.getMemberNode(field);
ir.Expression expression = node.initializer;
- ConstantValue value = _elementMap.getConstantValue(
- _elementMap.getStaticTypeContext(field), expression,
- requireConstant: node.isConst, implicitNull: true);
+ ConstantValue value;
+ if (expression is ir.StaticInvocation &&
+ identical(
+ expression.target, _elementMap.coreTypes.createSentinelMethod)) {
+ value = LateSentinelConstantValue();
+ } else {
+ value = _elementMap.getConstantValue(
+ _elementMap.getStaticTypeContext(field), expression,
+ requireConstant: node.isConst, implicitNull: true);
+ }
if (value != null && !value.isConstant) {
value = null;
}
@@ -358,7 +372,8 @@
} else if (value.isNull ||
value.isInt ||
value.isBool ||
- value.isString) {
+ value.isString ||
+ value is LateSentinelConstantValue) {
// TODO(johnniwinther,sra): Support non-primitive constants in
// allocators when it does cause allocators to deoptimized
// because of deferred loading.
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index da1e7f9..132100f 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -1910,6 +1910,11 @@
}
@override
+ void visitLateSentinel(LateSentinelConstantValue constant, [_]) {
+ add('late_sentinel');
+ }
+
+ @override
void visitUnreachable(UnreachableConstantValue constant, [_]) {
add('unreachable');
}
@@ -2046,6 +2051,13 @@
}
@override
+ int visitLateSentinel(LateSentinelConstantValue constant, [_]) =>
+ throw failedAt(
+ NO_LOCATION_SPANNABLE,
+ 'LateSentinelConstantValue should never be named and '
+ 'never be subconstant');
+
+ @override
int visitUnreachable(UnreachableConstantValue constant, [_]) {
throw failedAt(
NO_LOCATION_SPANNABLE,
diff --git a/pkg/compiler/lib/src/js_emitter/constant_ordering.dart b/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
index 48281f3..9d4c41a 100644
--- a/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
+++ b/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
@@ -161,6 +161,11 @@
}
@override
+ int visitLateSentinel(
+ LateSentinelConstantValue a, LateSentinelConstantValue b) =>
+ 0;
+
+ @override
int visitUnreachable(UnreachableConstantValue a, UnreachableConstantValue b) {
// Never emitted.
return 0;
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
index acf7e3c..ca28cdb 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
@@ -118,7 +118,7 @@
ModularEmitterImpl(
ModularNamer namer, this._registry, CompilerOptions options)
- : _constantEmitter = new ModularConstantEmitter(options),
+ : _constantEmitter = new ModularConstantEmitter(options, namer),
super(namer);
@override
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 60d8baa..e12027b 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -36,7 +36,11 @@
import '../../common.dart';
import '../../common/tasks.dart';
import '../../constants/values.dart'
- show ConstantValue, FunctionConstantValue, NullConstantValue;
+ show
+ ConstantValue,
+ FunctionConstantValue,
+ LateSentinelConstantValue,
+ NullConstantValue;
import '../../common_elements.dart' show CommonElements, JElementEnvironment;
import '../../deferred_load.dart' show OutputUnit;
import '../../dump_info.dart';
@@ -88,7 +92,7 @@
final Namer _namer;
final CompilerTask _task;
final Emitter _emitter;
- ConstantEmitter _constantEmitter;
+ ConstantEmitter constantEmitter;
final NativeEmitter _nativeEmitter;
final bool _shouldGenerateSourceMap;
final JClosedWorld _closedWorld;
@@ -125,8 +129,9 @@
RecipeEncoder rtiRecipeEncoder,
this._shouldGenerateSourceMap)
: _constantOrdering = new ConstantOrdering(_closedWorld.sorter) {
- this._constantEmitter = new ConstantEmitter(
+ this.constantEmitter = new ConstantEmitter(
_options,
+ _namer,
_closedWorld.commonElements,
_closedWorld.elementEnvironment,
_closedWorld.rtiNeed,
@@ -146,6 +151,7 @@
if (constant.isFunction) return true; // Already emitted.
if (constant.isPrimitive) return true; // Inlined.
if (constant.isDummy) return true; // Inlined.
+ if (constant is LateSentinelConstantValue) return true; // Inlined.
return false;
}
@@ -181,7 +187,7 @@
// We are only interested in the "isInlined" part, but it does not hurt to
// test for the other predicates.
if (isConstantInlinedOrAlreadyEmitted(value)) {
- return _constantEmitter.generate(value);
+ return constantEmitter.generate(value);
}
return js.js('#.#',
[_namer.globalObjectForConstant(value), _namer.constantName(value)]);
@@ -199,7 +205,7 @@
_dumpInfoTask,
_namer,
_emitter,
- _constantEmitter,
+ constantEmitter,
this,
_nativeEmitter,
_closedWorld,
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index db779c3..07086db 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -891,6 +891,9 @@
DummyInterceptorConstantValue constant, _) =>
constant;
@override
+ ConstantValue visitLateSentinel(LateSentinelConstantValue constant, _) =>
+ constant;
+ @override
ConstantValue visitUnreachable(UnreachableConstantValue constant, _) =>
constant;
@override
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 547be62..1bf5b01 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -74,7 +74,7 @@
int get enabledLateLowerings => LateLowering.all;
@override
- bool get supportsLateLoweringSentinel => false;
+ bool get supportsLateLoweringSentinel => true;
@override
bool get useStaticFieldLowering => false;
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index c127c695b..1bd824c 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -538,6 +538,8 @@
break;
case ConstantValueKind.DUMMY_INTERCEPTOR:
break;
+ case ConstantValueKind.LATE_SENTINEL:
+ break;
case ConstantValueKind.UNREACHABLE:
break;
case ConstantValueKind.JS_NAME:
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index c1e8ea1..f053835 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -546,6 +546,8 @@
return new DeferredGlobalConstantValue(constant, unit);
case ConstantValueKind.DUMMY_INTERCEPTOR:
return DummyInterceptorConstantValue();
+ case ConstantValueKind.LATE_SENTINEL:
+ return LateSentinelConstantValue();
case ConstantValueKind.UNREACHABLE:
return UnreachableConstantValue();
case ConstantValueKind.JS_NAME:
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 7e0a3af..20e835b 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -4147,6 +4147,10 @@
_handleForeignTypeRef(invocation);
} else if (name == 'LEGACY_TYPE_REF') {
_handleForeignLegacyTypeRef(invocation);
+ } else if (name == 'createJsSentinel') {
+ _handleForeignCreateJsSentinel(invocation);
+ } else if (name == 'isJsSentinel') {
+ _handleForeignIsJsSentinel(invocation);
} else {
reporter.internalError(
_elementMap.getSpannable(targetElement, invocation),
@@ -4863,6 +4867,22 @@
..sourceInformation = sourceInformation);
}
+ void _handleForeignCreateJsSentinel(ir.StaticInvocation invocation) {
+ SourceInformation sourceInformation =
+ _sourceInformationBuilder.buildCall(invocation, invocation);
+ stack.add(graph.addConstantLateSentinel(closedWorld,
+ sourceInformation: sourceInformation));
+ }
+
+ void _handleForeignIsJsSentinel(ir.StaticInvocation invocation) {
+ SourceInformation sourceInformation =
+ _sourceInformationBuilder.buildCall(invocation, invocation);
+ HInstruction checkedExpression =
+ _visitPositionalArguments(invocation.arguments).single;
+ push(HIsLateSentinel(checkedExpression, _abstractValueDomain.boolType)
+ ..sourceInformation = sourceInformation);
+ }
+
void _pushStaticInvocation(MemberEntity target, List<HInstruction> arguments,
AbstractValue typeMask, List<DartType> typeArguments,
{SourceInformation sourceInformation, InterfaceType instanceType}) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 098531f..81f1761 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -642,7 +642,8 @@
List<js.Expression> visitArguments(List<HInstruction> inputs,
{int start: HInvoke.ARGUMENTS_OFFSET}) {
assert(inputs.length >= start);
- List<js.Expression> result = new List<js.Expression>.filled(inputs.length - start, null);
+ List<js.Expression> result =
+ new List<js.Expression>.filled(inputs.length - start, null);
for (int i = start; i < inputs.length; i++) {
use(inputs[i]);
result[i - start] = pop();
@@ -2564,6 +2565,8 @@
use(input.inputs[0]);
} else if (input is HIdentity) {
emitIdentityComparison(input, sourceInformation, inverse: true);
+ } else if (input is HIsLateSentinel) {
+ _emitIsLateSentinel(input, sourceInformation, inverse: true);
} else if (canGenerateOptimizedComparison(input)) {
HRelational relational = input;
constant_system.BinaryOperation operation = relational.operation();
@@ -2710,7 +2713,9 @@
assert(over != null || under != null);
js.Expression underOver = under == null
? over
- : over == null ? under : new js.Binary("||", under, over);
+ : over == null
+ ? under
+ : new js.Binary("||", under, over);
js.Statement thenBody = new js.Block.empty();
js.Block oldContainer = currentContainer;
currentContainer = thenBody;
@@ -3272,4 +3277,18 @@
_registry.registerStaticUse(
new StaticUse.directInvoke(method, selector.callStructure, null));
}
+
+ _emitIsLateSentinel(HIsLateSentinel node, SourceInformation sourceInformation,
+ {inverse: false}) {
+ use(node.inputs[0]);
+ js.Expression value = pop();
+ js.Expression sentinel =
+ _emitter.constantReference(LateSentinelConstantValue());
+ push(js.Binary(mapRelationalOperator('===', inverse), value, sentinel)
+ .withSourceInformation(sourceInformation));
+ }
+
+ @override
+ visitIsLateSentinel(HIsLateSentinel node) =>
+ _emitIsLateSentinel(node, node.sourceInformation);
}
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 25b48a8..cf71f75 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -66,6 +66,7 @@
R visitInvokeSuper(HInvokeSuper node);
R visitInvokeConstructorBody(HInvokeConstructorBody node);
R visitInvokeGeneratorBody(HInvokeGeneratorBody node);
+ R visitIsLateSentinel(HIsLateSentinel node);
R visitLateValue(HLateValue node);
R visitLazyStatic(HLazyStatic node);
R visitLess(HLess node);
@@ -339,6 +340,11 @@
return addConstant(UnreachableConstantValue(), closedWorld);
}
+ HConstant addConstantLateSentinel(JClosedWorld closedWorld,
+ {SourceInformation sourceInformation}) =>
+ addConstant(LateSentinelConstantValue(), closedWorld,
+ sourceInformation: sourceInformation);
+
void finalize(AbstractValueDomain domain) {
addBlock(exit);
exit.open();
@@ -577,6 +583,8 @@
@override
visitTry(HTry node) => visitControlFlow(node);
@override
+ visitIsLateSentinel(HIsLateSentinel node) => visitInstruction(node);
+ @override
visitLateValue(HLateValue node) => visitInstruction(node);
@override
visitBoolConversion(HBoolConversion node) => visitCheck(node);
@@ -1093,6 +1101,8 @@
static const int TYPE_EVAL_TYPECODE = 56;
static const int TYPE_BIND_TYPECODE = 57;
+ static const int IS_LATE_SENTINEL_TYPECODE = 58;
+
HInstruction(this.inputs, this.instructionType) {
assert(inputs.every((e) => e != null), "inputs: $inputs");
}
@@ -4513,3 +4523,25 @@
@override
String toString() => 'HTypeBind()';
}
+
+class HIsLateSentinel extends HInstruction {
+ HIsLateSentinel(HInstruction value, AbstractValue type)
+ : super([value], type) {
+ setUseGvn();
+ }
+
+ @override
+ accept(HVisitor visitor) => visitor.visitIsLateSentinel(this);
+
+ @override
+ int typeCode() => HInstruction.IS_LATE_SENTINEL_TYPECODE;
+
+ @override
+ bool typeEquals(HInstruction other) => other is HIsLateSentinel;
+
+ @override
+ bool dataEquals(HIsLateSentinel other) => true;
+
+ @override
+ String toString() => 'HIsLateSentinel()';
+}
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index b855b90..5f0726f 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1186,6 +1186,20 @@
return newInstruction == null ? super.visitIdentity(node) : newInstruction;
}
+ @override
+ HInstruction visitIsLateSentinel(HIsLateSentinel node) {
+ HInstruction value = node.inputs[0];
+ if (value is HConstant) {
+ return _graph.addConstantBool(
+ value.constant is LateSentinelConstantValue, _closedWorld);
+ }
+
+ // TODO(fishythefish): Simplify to `false` when the input cannot evalute to
+ // the sentinel. This can be implemented in the powerset domain.
+
+ return super.visitIsLateSentinel(node);
+ }
+
void simplifyCondition(
HBasicBlock block, HInstruction condition, bool value) {
// `excludePhiOutEdges: true` prevents replacing a partially dominated phi
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index ef1ad6a..3eb079b 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -169,6 +169,10 @@
}
@override
+ String visitIsLateSentinel(HIsLateSentinel node) =>
+ 'IsLateSentinel: ${temporaryId(node.inputs[0])}';
+
+ @override
String visitLateValue(HLateValue node) {
return "LateValue: ${temporaryId(node.inputs[0])}";
}
diff --git a/pkg/compiler/test/helpers/shared_helper.dart b/pkg/compiler/test/helpers/shared_helper.dart
index 65ca216..4557ea7 100644
--- a/pkg/compiler/test/helpers/shared_helper.dart
+++ b/pkg/compiler/test/helpers/shared_helper.dart
@@ -232,37 +232,34 @@
sb.write(')');
}
+ void _unsupported(ConstantValue constant) => throw UnsupportedError(
+ 'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
+
@override
- void visitInterceptor(InterceptorConstantValue constant, StringBuffer sb) {
- throw new UnsupportedError(
- 'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
- }
+ void visitInterceptor(InterceptorConstantValue constant, StringBuffer sb) =>
+ _unsupported(constant);
@override
void visitDummyInterceptor(
- DummyInterceptorConstantValue constant, StringBuffer sb) {
- throw new UnsupportedError(
- 'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
- }
+ DummyInterceptorConstantValue constant, StringBuffer sb) =>
+ _unsupported(constant);
@override
- void visitUnreachable(UnreachableConstantValue constant, StringBuffer sb) {
- throw new UnsupportedError(
- 'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
- }
+ void visitLateSentinel(LateSentinelConstantValue constant, StringBuffer sb) =>
+ _unsupported(constant);
@override
- void visitJsName(JsNameConstantValue constant, StringBuffer sb) {
- throw new UnsupportedError(
- 'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
- }
+ void visitUnreachable(UnreachableConstantValue constant, StringBuffer sb) =>
+ _unsupported(constant);
+
+ @override
+ void visitJsName(JsNameConstantValue constant, StringBuffer sb) =>
+ _unsupported(constant);
@override
void visitDeferredGlobal(
- DeferredGlobalConstantValue constant, StringBuffer sb) {
- throw new UnsupportedError(
- 'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
- }
+ DeferredGlobalConstantValue constant, StringBuffer sb) =>
+ _unsupported(constant);
@override
void visitNonConstant(NonConstantValue constant, StringBuffer sb) {
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index 44a3ab9..8f2e8b8 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -356,4 +356,145 @@
expect(File(outFile).existsSync(), true,
reason: 'File not found: $outFile');
});
+
+ test('Compile AOT snapshot with sound null safety', () {
+ final p = project(mainSrc: '''void main() {}''');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'myaot'));
+
+ var result = p.runSync(
+ [
+ 'compile',
+ 'aot-snapshot',
+ '-o',
+ outFile,
+ inFile,
+ ],
+ );
+
+ expect(result.stdout, contains(soundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ });
+
+ test('Compile AOT snapshot with unsound null safety', () {
+ final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'myaot'));
+
+ var result = p.runSync(
+ [
+ 'compile',
+ 'aot-snapshot',
+ '-o',
+ outFile,
+ inFile,
+ ],
+ );
+
+ expect(result.stdout, contains(unsoundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ });
+
+ test('Compile kernel with sound null safety', () {
+ final p = project(mainSrc: '''void main() {}''');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'mydill'));
+
+ var result = p.runSync(
+ [
+ 'compile',
+ 'kernel',
+ '-o',
+ outFile,
+ inFile,
+ ],
+ );
+
+ expect(result.stdout, contains(soundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ });
+
+ test('Compile kernel with unsound null safety', () {
+ final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'mydill'));
+
+ var result = p.runSync(
+ [
+ 'compile',
+ 'kernel',
+ '-o',
+ outFile,
+ inFile,
+ ],
+ );
+
+ expect(result.stdout, contains(unsoundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ });
+
+ test('Compile JIT snapshot with sound null safety', () {
+ final p = project(mainSrc: '''void main() {}''');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'myjit'));
+
+ var result = p.runSync(
+ [
+ 'compile',
+ 'jit-snapshot',
+ '-o',
+ outFile,
+ inFile,
+ ],
+ );
+
+ expect(result.stdout, contains(soundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ });
+
+ test('Compile JIT snapshot with unsound null safety', () {
+ final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+ final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+ final outFile = path.canonicalize(path.join(p.dirPath, 'myjit'));
+
+ var result = p.runSync(
+ [
+ 'compile',
+ 'jit-snapshot',
+ '-o',
+ outFile,
+ inFile,
+ ],
+ );
+
+ expect(result.stdout, contains(unsoundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
+ expect(result.exitCode, 0);
+ expect(File(outFile).existsSync(), true,
+ reason: 'File not found: $outFile');
+ });
}
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 349d3f8..db638dc 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -3989,7 +3989,12 @@
[_visitExpression(node.iterable)]);
var iter = _emitTemporaryId('iter');
- return js.statement(
+
+ var savedContinueTargets = _currentContinueTargets;
+ var savedBreakTargets = _currentBreakTargets;
+ _currentContinueTargets = <LabeledStatement>[];
+ _currentBreakTargets = <LabeledStatement>[];
+ var awaitForStmt = js.statement(
'{'
' let # = #;'
' try {'
@@ -4007,6 +4012,9 @@
js_ast.Yield(js.call('#.cancel()', iter))
..sourceInformation = _nodeStart(node.variable)
]);
+ _currentContinueTargets = savedContinueTargets;
+ _currentBreakTargets = savedBreakTargets;
+ return awaitForStmt;
}
@override
@@ -5134,6 +5142,21 @@
return js_ast.PropertyAccess(js_ast.This(), jsMethod.name);
}
+ /// If [e] is a [TypeLiteral] or a [TypeLiteralConstant] expression, return
+ /// the underlying [DartType], otherwise returns null.
+ // TODO(sigmund,nshahan): remove all uses of type literals in the runtime
+ // libraries, so that this pattern can be deleted.
+ DartType getTypeLiteralType(Expression e) {
+ if (e is TypeLiteral) return e.type;
+ if (e is ConstantExpression) {
+ var constant = e.constant;
+ if (constant is TypeLiteralConstant) {
+ return constant.type.withDeclaredNullability(Nullability.nonNullable);
+ }
+ }
+ return null;
+ }
+
@override
js_ast.Expression visitStaticInvocation(StaticInvocation node) {
var target = node.target;
@@ -5143,18 +5166,14 @@
// Optimize some internal SDK calls.
if (isSdkInternalRuntime(target.enclosingLibrary)) {
var name = target.name.text;
- if (node.arguments.positional.isEmpty) {
- if (name == 'typeRep') {
- return _emitType(node.arguments.types.single);
- }
+ if (node.arguments.positional.isEmpty &&
+ node.arguments.types.length == 1) {
+ var type = node.arguments.types.single;
+ if (name == 'typeRep') return _emitType(type);
if (name == 'legacyTypeRep') {
- return _emitType(node.arguments.types.single
- .withDeclaredNullability(Nullability.legacy));
+ return _emitType(type.withDeclaredNullability(Nullability.legacy));
}
- } else if (node.arguments.positional.length == 1) {
- var firstArg = node.arguments.positional[0];
- if (name == 'getGenericClass' && firstArg is TypeLiteral) {
- var type = firstArg.type;
+ if (name == 'getGenericClassStatic') {
if (type is InterfaceType) {
return _emitTopLevelNameNoInterop(type.classNode, suffix: '\$');
}
@@ -5162,8 +5181,11 @@
return _emitFutureOrNameNoInterop(suffix: '\$');
}
}
- if (name == 'unwrapType' && firstArg is TypeLiteral) {
- return _emitType(firstArg.type);
+ } else if (node.arguments.positional.length == 1) {
+ var firstArg = node.arguments.positional[0];
+ var type = getTypeLiteralType(firstArg);
+ if (name == 'unwrapType' && type != null) {
+ return _emitType(type);
}
if (name == 'extensionSymbol' && firstArg is StringLiteral) {
return getSymbol(getExtensionSymbolInternal(firstArg.value));
@@ -5179,19 +5201,18 @@
} else if (node.arguments.positional.length == 2) {
var firstArg = node.arguments.positional[0];
var secondArg = node.arguments.positional[1];
- if (name == '_jsInstanceOf' && secondArg is TypeLiteral) {
+ var type = getTypeLiteralType(secondArg);
+ if (name == '_jsInstanceOf' && type != null) {
return js.call('# instanceof #', [
_visitExpression(firstArg),
- _emitType(
- secondArg.type.withDeclaredNullability(Nullability.nonNullable))
+ _emitType(type.withDeclaredNullability(Nullability.nonNullable))
]);
}
- if (name == '_equalType' && secondArg is TypeLiteral) {
+ if (name == '_equalType' && type != null) {
return js.call('# === #', [
_visitExpression(firstArg),
- _emitType(
- secondArg.type.withDeclaredNullability(Nullability.nonNullable))
+ _emitType(type.withDeclaredNullability(Nullability.nonNullable))
]);
}
}
@@ -6018,6 +6039,26 @@
'tearoffInterop(#)', [_emitStaticTarget(node.procedure)]);
}
}
+ if (node is TypeLiteralConstant) {
+ // We bypass the use of constants, since types are already canonicalized
+ // in the DDC output. DDC emits type literals in two contexts:
+ // * Foreign JS functions: we use the non-nullable version of some types
+ // directly in the runtime libraries (e.g. dart:_runtime). For
+ // correctness of those libraries, we need to remove the legacy marker
+ // that was added by the CFE normalization of type literals.
+ //
+ // * Regular user code: we need to emit a canonicalized type. We do so
+ // by calling `wrapType` on the type at runtime. By emitting the
+ // non-nullable version we save some redundant work at runtime.
+ // Technically, emitting a legacy type in this case would be correct,
+ // only more verbose and inefficient.
+ var type = node.type;
+ if (type.nullability == Nullability.legacy) {
+ type = type.withDeclaredNullability(Nullability.nonNullable);
+ }
+ assert(!_isInForeignJS || type.nullability == Nullability.nonNullable);
+ return _emitTypeLiteral(type);
+ }
if (isSdkInternalRuntime(_currentLibrary) || node is PrimitiveConstant) {
return super.visitConstant(node);
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 7cd5823..3bf29c0 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -450,6 +450,11 @@
}
@override
+ void registerVariableDeclaration(VariableDeclaration variable) {
+ typeInferrer?.assignedVariables?.declare(variable);
+ }
+
+ @override
void push(Object node) {
if (node is DartType) {
unhandled("DartType", "push", -1, uri);
@@ -1692,6 +1697,7 @@
}
VariableDeclaration variable =
forest.createVariableDeclarationForValue(expression);
+ registerVariableDeclaration(variable);
push(new Cascade(variable, isNullAware: isNullAware)
..fileOffset = expression.fileOffset);
push(_createReadOnlyVariableAccess(variable, token, expression.fileOffset,
@@ -1725,9 +1731,15 @@
@override
void beginBinaryExpression(Token token) {
- if (optional("&&", token) || optional("||", token)) {
+ bool isAnd = optional("&&", token);
+ if (isAnd || optional("||", token)) {
Expression lhs = popForValue();
typePromoter?.enterLogicalExpression(lhs, token.stringValue);
+ // This is matched by the call to [endNode] in
+ // [doLogicalExpression].
+ if (isAnd) {
+ typeInferrer?.assignedVariables?.beginNode();
+ }
push(lhs);
}
}
@@ -1812,6 +1824,11 @@
offsetForToken(token), receiver, token.stringValue, argument);
typePromoter?.exitLogicalExpression(argument, logicalExpression);
push(logicalExpression);
+ if (optional("&&", token)) {
+ // This is matched by the call to [beginNode] in
+ // [beginBinaryExpression].
+ typeInferrer?.assignedVariables?.endNode(logicalExpression);
+ }
}
/// Handle `a ?? b`.
@@ -2028,6 +2045,9 @@
@override
VariableGet createVariableGet(VariableDeclaration variable, int charOffset,
{bool forNullGuardedAccess: false}) {
+ if (!(variable as VariableDeclarationImpl).isLocalFunction) {
+ typeInferrer?.assignedVariables?.read(variable);
+ }
Object fact =
typePromoter?.getFactForAccess(variable, functionNestingLevel);
Object scope = typePromoter?.currentScope;
@@ -2395,6 +2415,9 @@
void beginThenStatement(Token token) {
Expression condition = popForValue();
enterThenForTypePromotion(condition);
+ // This is matched by the call to [deferNode] in
+ // [endThenStatement].
+ typeInferrer?.assignedVariables?.beginNode();
push(condition);
super.beginThenStatement(token);
}
@@ -2403,16 +2426,26 @@
void endThenStatement(Token token) {
typePromoter?.enterElse();
super.endThenStatement(token);
+ // This is matched by the call to [beginNode] in
+ // [beginThenStatement] and by the call to [storeInfo] in
+ // [endIfStatement].
+ push(typeInferrer?.assignedVariables?.deferNode());
}
@override
void endIfStatement(Token ifToken, Token elseToken) {
Statement elsePart = popStatementIfNotNull(elseToken);
+ AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
+ pop();
Statement thenPart = popStatement();
Expression condition = pop();
typePromoter?.exitConditional();
- push(forest.createIfStatement(
- offsetForToken(ifToken), condition, thenPart, elsePart));
+ Statement node = forest.createIfStatement(
+ offsetForToken(ifToken), condition, thenPart, elsePart);
+ // This is matched by the call to [deferNode] in
+ // [endThenStatement].
+ typeInferrer?.assignedVariables?.storeInfo(node, assignedVariablesInfo);
+ push(node);
}
@override
@@ -3456,6 +3489,9 @@
void beginConditionalExpression(Token question) {
Expression condition = popForValue();
typePromoter?.enterThen(condition);
+ // This is matched by the call to [deferNode] in
+ // [handleConditionalExpressionColon].
+ typeInferrer?.assignedVariables?.beginNode();
push(condition);
super.beginConditionalExpression(question);
}
@@ -3464,6 +3500,10 @@
void handleConditionalExpressionColon() {
Expression then = popForValue();
typePromoter?.enterElse();
+ // This is matched by the call to [beginNode] in
+ // [beginConditionalExpression] and by the call to [storeInfo] in
+ // [endConditionalExpression].
+ push(typeInferrer?.assignedVariables?.deferNode());
push(then);
super.handleConditionalExpressionColon();
}
@@ -3473,10 +3513,16 @@
debugEvent("ConditionalExpression");
Expression elseExpression = popForValue();
Expression thenExpression = pop();
+ AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
+ pop();
Expression condition = pop();
typePromoter?.exitConditional();
- push(forest.createConditionalExpression(
- offsetForToken(question), condition, thenExpression, elseExpression));
+ Expression node = forest.createConditionalExpression(
+ offsetForToken(question), condition, thenExpression, elseExpression);
+ push(node);
+ // This is matched by the call to [deferNode] in
+ // [handleConditionalExpressionColon].
+ typeInferrer?.assignedVariables?.storeInfo(node, assignedVariablesInfo);
}
@override
@@ -4618,6 +4664,10 @@
void handleThenControlFlow(Token token) {
Expression condition = popForValue();
enterThenForTypePromotion(condition);
+ // This is matched by the call to [deferNode] in
+ // [handleElseControlFlow] and by the call to [endNode] in
+ // [endIfControlFlow].
+ typeInferrer?.assignedVariables?.beginNode();
push(condition);
super.handleThenControlFlow(token);
}
@@ -4628,6 +4678,10 @@
// happens before we go into the else block.
Object node = pop();
if (node is! MapEntry) node = toValue(node);
+ // This is matched by the call to [beginNode] in
+ // [handleThenControlFlow] and by the call to [storeInfo] in
+ // [endIfElseControlFlow].
+ push(typeInferrer?.assignedVariables?.deferNode());
push(node);
typePromoter?.enterElse();
}
@@ -4640,15 +4694,20 @@
Token ifToken = pop();
transformCollections = true;
+ TreeNode node;
if (entry is MapEntry) {
- push(forest.createIfMapEntry(
- offsetForToken(ifToken), toValue(condition), entry));
+ node = forest.createIfMapEntry(
+ offsetForToken(ifToken), toValue(condition), entry);
} else {
- push(forest.createIfElement(
- offsetForToken(ifToken), toValue(condition), toValue(entry)));
+ node = forest.createIfElement(
+ offsetForToken(ifToken), toValue(condition), toValue(entry));
}
+ push(node);
typePromoter?.enterElse();
typePromoter?.exitConditional();
+ // This is matched by the call to [beginNode] in
+ // [handleThenControlFlow].
+ typeInferrer?.assignedVariables?.endNode(node);
}
@override
@@ -4656,72 +4715,79 @@
debugEvent("endIfElseControlFlow");
Object elseEntry = pop(); // else entry
Object thenEntry = pop(); // then entry
+ AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
+ pop();
Object condition = pop(); // parenthesized expression
Token ifToken = pop();
typePromoter?.exitConditional();
transformCollections = true;
+ TreeNode node;
if (thenEntry is MapEntry) {
if (elseEntry is MapEntry) {
- push(forest.createIfMapEntry(
- offsetForToken(ifToken), toValue(condition), thenEntry, elseEntry));
+ node = forest.createIfMapEntry(
+ offsetForToken(ifToken), toValue(condition), thenEntry, elseEntry);
} else if (elseEntry is ControlFlowElement) {
MapEntry elseMapEntry =
elseEntry.toMapEntry(typeInferrer?.assignedVariables?.reassignInfo);
if (elseMapEntry != null) {
- push(forest.createIfMapEntry(offsetForToken(ifToken),
- toValue(condition), thenEntry, elseMapEntry));
+ node = forest.createIfMapEntry(offsetForToken(ifToken),
+ toValue(condition), thenEntry, elseMapEntry);
} else {
int offset = elseEntry is Expression
? elseEntry.fileOffset
: offsetForToken(ifToken);
- push(new MapEntry(
+ node = new MapEntry(
buildProblem(
fasta.messageCantDisambiguateAmbiguousInformation, offset, 1),
new NullLiteral())
- ..fileOffset = offsetForToken(ifToken));
+ ..fileOffset = offsetForToken(ifToken);
}
} else {
int offset = elseEntry is Expression
? elseEntry.fileOffset
: offsetForToken(ifToken);
- push(new MapEntry(
+ node = new MapEntry(
buildProblem(fasta.templateExpectedAfterButGot.withArguments(':'),
offset, 1),
new NullLiteral())
- ..fileOffset = offsetForToken(ifToken));
+ ..fileOffset = offsetForToken(ifToken);
}
} else if (elseEntry is MapEntry) {
if (thenEntry is ControlFlowElement) {
MapEntry thenMapEntry =
thenEntry.toMapEntry(typeInferrer?.assignedVariables?.reassignInfo);
if (thenMapEntry != null) {
- push(forest.createIfMapEntry(offsetForToken(ifToken),
- toValue(condition), thenMapEntry, elseEntry));
+ node = forest.createIfMapEntry(offsetForToken(ifToken),
+ toValue(condition), thenMapEntry, elseEntry);
} else {
int offset = thenEntry is Expression
? thenEntry.fileOffset
: offsetForToken(ifToken);
- push(new MapEntry(
+ node = new MapEntry(
buildProblem(
fasta.messageCantDisambiguateAmbiguousInformation, offset, 1),
new NullLiteral())
- ..fileOffset = offsetForToken(ifToken));
+ ..fileOffset = offsetForToken(ifToken);
}
} else {
int offset = thenEntry is Expression
? thenEntry.fileOffset
: offsetForToken(ifToken);
- push(new MapEntry(
+ node = new MapEntry(
buildProblem(fasta.templateExpectedAfterButGot.withArguments(':'),
offset, 1),
new NullLiteral())
- ..fileOffset = offsetForToken(ifToken));
+ ..fileOffset = offsetForToken(ifToken);
}
} else {
- push(forest.createIfElement(offsetForToken(ifToken), toValue(condition),
- toValue(thenEntry), toValue(elseEntry)));
+ node = forest.createIfElement(offsetForToken(ifToken), toValue(condition),
+ toValue(thenEntry), toValue(elseEntry));
}
+ push(node);
+ // This is matched by the call to [deferNode] in
+ // [handleElseControlFlow].
+ typeInferrer?.assignedVariables?.storeInfo(node, assignedVariablesInfo);
}
@override
@@ -6253,6 +6319,7 @@
if (isNullAware) {
VariableDeclaration variable =
forest.createVariableDeclarationForValue(receiver);
+ registerVariableDeclaration(variable);
return new NullAwareMethodInvocation(
variable,
forest.createMethodInvocation(
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 24a3909..5fe56fe 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -407,6 +407,7 @@
}
VariableDeclaration read =
_helper.forest.createVariableDeclarationForValue(_createRead());
+ _helper.registerVariableDeclaration(read);
Expression binary = _helper.forest.createBinary(offset,
_helper.createVariableGet(read, fileOffset), binaryOperator, value);
VariableDeclaration write = _helper.forest
@@ -534,9 +535,11 @@
}
VariableDeclaration variable =
_helper.forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
VariableDeclaration read = _helper.forest.createVariableDeclarationForValue(
_forest.createPropertyGet(fileOffset,
_helper.createVariableGet(variable, receiver.fileOffset), name));
+ _helper.registerVariableDeclaration(read);
Expression binary = _helper.forest.createBinary(offset,
_helper.createVariableGet(read, fileOffset), binaryOperator, value);
VariableDeclaration write = _helper.forest
@@ -696,6 +699,7 @@
VariableDeclaration read = _helper.forest.createVariableDeclarationForValue(
_forest.createPropertyGet(
fileOffset, _forest.createThisExpression(fileOffset), name));
+ _helper.registerVariableDeclaration(read);
Expression binary = _helper.forest.createBinary(offset,
_helper.createVariableGet(read, fileOffset), binaryOperator, value);
VariableDeclaration write = _helper.forest
@@ -752,6 +756,7 @@
Expression buildSimpleRead() {
VariableDeclaration variable =
_helper.forest.createVariableDeclarationForValue(receiverExpression);
+ _helper.registerVariableDeclaration(variable);
PropertyGet read = _forest.createPropertyGet(
fileOffset,
_helper.createVariableGet(variable, receiverExpression.fileOffset,
@@ -765,6 +770,7 @@
Expression buildAssignment(Expression value, {bool voidContext = false}) {
VariableDeclaration variable =
_helper.forest.createVariableDeclarationForValue(receiverExpression);
+ _helper.registerVariableDeclaration(variable);
PropertySet read = _helper.forest.createPropertySet(
fileOffset,
_helper.createVariableGet(variable, receiverExpression.fileOffset,
@@ -899,6 +905,7 @@
}
VariableDeclaration read =
_helper.forest.createVariableDeclarationForValue(_createRead());
+ _helper.registerVariableDeclaration(read);
Expression binary = _helper.forest.createBinary(offset,
_helper.createVariableGet(read, fileOffset), binaryOperator, value);
VariableDeclaration write = _helper.forest
@@ -977,6 +984,7 @@
Expression receiverValue;
if (isNullAware) {
variable = _forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
receiverValue = _helper.createVariableGet(variable, fileOffset,
forNullGuardedAccess: true);
} else {
@@ -997,6 +1005,7 @@
Expression receiverValue;
if (isNullAware) {
variable = _forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
receiverValue = _helper.createVariableGet(variable, fileOffset,
forNullGuardedAccess: true);
} else {
@@ -1019,6 +1028,7 @@
Expression receiverValue;
if (isNullAware) {
variable = _forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
receiverValue = _helper.createVariableGet(variable, fileOffset,
forNullGuardedAccess: true);
} else {
@@ -1047,6 +1057,7 @@
Expression receiverValue;
if (isNullAware) {
variable = _forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
receiverValue = _helper.createVariableGet(variable, fileOffset,
forNullGuardedAccess: true);
} else {
@@ -1478,6 +1489,7 @@
}
VariableDeclaration read =
_helper.forest.createVariableDeclarationForValue(_createRead());
+ _helper.registerVariableDeclaration(read);
Expression binary = _helper.forest.createBinary(offset,
_helper.createVariableGet(read, fileOffset), binaryOperator, value);
VariableDeclaration write = _helper.forest
@@ -1988,6 +2000,7 @@
if (isNullAware) {
VariableDeclaration variable =
_helper.forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
return new NullAwareExtension(
variable,
_createRead(_helper.createVariableGet(variable, variable.fileOffset,
@@ -2020,6 +2033,7 @@
if (isNullAware) {
VariableDeclaration variable =
_helper.forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
return new NullAwareExtension(
variable,
_createWrite(
@@ -2054,6 +2068,7 @@
if (isNullAware) {
VariableDeclaration variable =
_helper.forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
Expression read = _createRead(_helper.createVariableGet(
variable, receiver.fileOffset,
forNullGuardedAccess: true));
@@ -2140,9 +2155,11 @@
} else {
VariableDeclaration variable =
_helper.forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
VariableDeclaration read = _helper.forest
.createVariableDeclarationForValue(_createRead(
_helper.createVariableGet(variable, receiver.fileOffset)));
+ _helper.registerVariableDeclaration(read);
Expression binary = _helper.forest.createBinary(offset,
_helper.createVariableGet(read, fileOffset), binaryOperator, value);
VariableDeclaration write = _helper.forest
@@ -2163,6 +2180,7 @@
if (isNullAware) {
receiverVariable =
_helper.forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(receiverVariable);
receiverExpression = _helper.createVariableGet(
receiverVariable, receiverVariable.fileOffset,
forNullGuardedAccess: true);
@@ -2337,6 +2355,7 @@
Expression receiverValue;
if (isNullAware) {
variable = _forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
receiverValue = _helper.createVariableGet(variable, fileOffset,
forNullGuardedAccess: true);
} else {
@@ -2402,6 +2421,7 @@
Expression receiverValue;
if (isNullAware) {
variable = _forest.createVariableDeclarationForValue(receiver);
+ _helper.registerVariableDeclaration(variable);
receiverValue = _helper.createVariableGet(variable, fileOffset,
forNullGuardedAccess: true);
} else {
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
index eaa2468..9a41855 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
@@ -163,4 +163,6 @@
///
/// This is needed for type promotion.
void registerVariableAssignment(VariableDeclaration variable);
+
+ void registerVariableDeclaration(VariableDeclaration variable);
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 1e80ebb..d0363bb 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1189,7 +1189,8 @@
variableGet.promotedType = nonNullableLhsType;
}
ConditionalExpression conditional = new ConditionalExpression(
- equalsNull, rhsResult.expression, variableGet, inferredType);
+ equalsNull, rhsResult.expression, variableGet, inferredType)
+ ..fileOffset = node.fileOffset;
replacement = new Let(variable, conditional)
..fileOffset = node.fileOffset;
}
@@ -5664,7 +5665,8 @@
Expression readEqualsNull =
inferrer.createEqualsNull(node.readOffset, read, readEqualsMember);
replacement = new ConditionalExpression(readEqualsNull, write,
- new NullLiteral()..fileOffset = node.writeOffset, inferredType);
+ new NullLiteral()..fileOffset = node.writeOffset, inferredType)
+ ..fileOffset = node.writeOffset;
} else {
// Encode `receiver?.name ??= value` as:
//
@@ -5684,7 +5686,8 @@
variableGet.promotedType = nonNullableReadType;
}
ConditionalExpression condition = new ConditionalExpression(
- readEqualsNull, write, variableGet, inferredType);
+ readEqualsNull, write, variableGet, inferredType)
+ ..fileOffset = receiverVariable.fileOffset;
replacement = createLet(readVariable, condition);
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
index b90df05..220157ef 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
@@ -257,8 +257,10 @@
? elseStatements.first
: _createBlock(elseStatements);
}
- body.add(_createIf(element.fileOffset,
- element.condition.accept<TreeNode>(this), thenBody, elseBody));
+ IfStatement ifStatement = _createIf(element.fileOffset,
+ element.condition.accept<TreeNode>(this), thenBody, elseBody);
+ _dataForTesting?.registerAlias(element, ifStatement);
+ body.add(ifStatement);
}
void _translateForElement(
@@ -510,8 +512,10 @@
elseStatement =
elseBody.length == 1 ? elseBody.first : _createBlock(elseBody);
}
- body.add(_createIf(entry.fileOffset, entry.condition.accept<TreeNode>(this),
- thenStatement, elseStatement));
+ IfStatement ifStatement = _createIf(entry.fileOffset,
+ entry.condition.accept<TreeNode>(this), thenStatement, elseStatement);
+ _dataForTesting?.registerAlias(entry, ifStatement);
+ body.add(ifStatement);
}
void _translateForEntry(
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index ec4dd29..0b50fbb 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -4421,7 +4421,8 @@
equalsNull,
new NullLiteral()..fileOffset = _nullAwareFileOffset,
nullAwareAction,
- inferredType);
+ inferredType)
+ ..fileOffset = _nullAwareFileOffset;
return new Let(_nullAwareVariable, condition)
..fileOffset = _nullAwareFileOffset;
}
diff --git a/pkg/front_end/lib/src/testing/id_extractor.dart b/pkg/front_end/lib/src/testing/id_extractor.dart
index 11247a0..93af7c6 100644
--- a/pkg/front_end/lib/src/testing/id_extractor.dart
+++ b/pkg/front_end/lib/src/testing/id_extractor.dart
@@ -617,4 +617,16 @@
node, computeDefaultNodeId(node, skipNodeWithNoOffset: true));
return super.visitBlock(node);
}
+
+ @override
+ visitConditionalExpression(ConditionalExpression node) {
+ computeForNode(node, computeDefaultNodeId(node));
+ return super.visitConditionalExpression(node);
+ }
+
+ @override
+ visitLogicalExpression(LogicalExpression node) {
+ computeForNode(node, computeDefaultNodeId(node));
+ return super.visitLogicalExpression(node);
+ }
}
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index c06e19b..783baee 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3853,10 +3853,10 @@
from the import.
analyzerCode: CONST_DEFERRED_CLASS
script: |
- import "dart:core" deferred as prefix;
+ import "dart:convert" deferred as prefix;
main() {
- const prefix.Object();
+ const prefix.JsonCodec();
}
CyclicRedirectingFactoryConstructors:
diff --git a/pkg/front_end/test/extensions/data/ambiguous/lib1.dart b/pkg/front_end/test/extensions/data/ambiguous/lib1.dart
index 486d104..3e96ec7 100644
--- a/pkg/front_end/test/extensions/data/ambiguous/lib1.dart
+++ b/pkg/front_end/test/extensions/data/ambiguous/lib1.dart
@@ -4,7 +4,11 @@
// @dart = 2.9
-/*library: scope=[AmbiguousExtension1,AmbiguousExtension2,UnambiguousExtension1]*/
+/*library: scope=[
+ AmbiguousExtension1,
+ AmbiguousExtension2,
+ UnambiguousExtension1,
+ async.dart.FutureExtensions]*/
/*class: AmbiguousExtension1:
builder-name=AmbiguousExtension1,
diff --git a/pkg/front_end/test/extensions/data/ambiguous/lib2.dart b/pkg/front_end/test/extensions/data/ambiguous/lib2.dart
index 859fc4c..60dab6f 100644
--- a/pkg/front_end/test/extensions/data/ambiguous/lib2.dart
+++ b/pkg/front_end/test/extensions/data/ambiguous/lib2.dart
@@ -4,7 +4,11 @@
// @dart = 2.9
-/*library: scope=[AmbiguousExtension1,AmbiguousExtension2,UnambiguousExtension2]*/
+/*library: scope=[
+ AmbiguousExtension1,
+ AmbiguousExtension2,
+ UnambiguousExtension2,
+ async.dart.FutureExtensions]*/
/*class: AmbiguousExtension1:
builder-name=AmbiguousExtension1,
diff --git a/pkg/front_end/test/extensions/data/ambiguous/main.dart b/pkg/front_end/test/extensions/data/ambiguous/main.dart
index 7fda5a5..76b412c 100644
--- a/pkg/front_end/test/extensions/data/ambiguous/main.dart
+++ b/pkg/front_end/test/extensions/data/ambiguous/main.dart
@@ -5,13 +5,13 @@
// @dart = 2.9
/*library: scope=[
- lib1.dart.AmbiguousExtension1,
- lib1.dart.AmbiguousExtension2,
- lib1.dart.UnambiguousExtension1,
- lib2.dart.AmbiguousExtension1,
- lib2.dart.AmbiguousExtension2,
- lib2.dart.UnambiguousExtension2]
-*/
+ async.dart.FutureExtensions,
+ lib1.dart.AmbiguousExtension1,
+ lib1.dart.AmbiguousExtension2,
+ lib1.dart.UnambiguousExtension1,
+ lib2.dart.AmbiguousExtension1,
+ lib2.dart.AmbiguousExtension2,
+ lib2.dart.UnambiguousExtension2]*/
import 'lib1.dart';
import 'lib2.dart';
diff --git a/pkg/front_end/test/extensions/data/as_show/lib.dart b/pkg/front_end/test/extensions/data/as_show/lib.dart
index 713c232..2511ba6 100644
--- a/pkg/front_end/test/extensions/data/as_show/lib.dart
+++ b/pkg/front_end/test/extensions/data/as_show/lib.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[Extension1]*/
+/*library: scope=[
+ Extension1,
+ async.dart.FutureExtensions]*/
/*class: Extension1:
builder-name=Extension1,
diff --git a/pkg/front_end/test/extensions/data/as_show/main.dart b/pkg/front_end/test/extensions/data/as_show/main.dart
index f214d01..c65f8c0 100644
--- a/pkg/front_end/test/extensions/data/as_show/main.dart
+++ b/pkg/front_end/test/extensions/data/as_show/main.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[lib.dart.Extension1,origin.dart.Extension2]*/
+/*library: scope=[
+ async.dart.FutureExtensions,
+ lib.dart.Extension1,
+ origin.dart.Extension2]*/
import 'lib.dart' as lib1;
import 'lib.dart' show Extension1;
diff --git a/pkg/front_end/test/extensions/data/as_show/origin.dart b/pkg/front_end/test/extensions/data/as_show/origin.dart
index 757fd60..abbeee6 100644
--- a/pkg/front_end/test/extensions/data/as_show/origin.dart
+++ b/pkg/front_end/test/extensions/data/as_show/origin.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[Extension2]*/
+/*library: scope=[
+ Extension2,
+ async.dart.FutureExtensions]*/
/*class: Extension2:
builder-name=Extension2,
diff --git a/pkg/front_end/test/extensions/data/explicit_this.dart b/pkg/front_end/test/extensions/data/explicit_this.dart
index b56b7fc..e1fcd49 100644
--- a/pkg/front_end/test/extensions/data/explicit_this.dart
+++ b/pkg/front_end/test/extensions/data/explicit_this.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[A2]*/
+/*library: scope=[
+ A2,
+ async.dart.FutureExtensions]*/
class A1 {
Object field;
diff --git a/pkg/front_end/test/extensions/data/export_twice/lib1.dart b/pkg/front_end/test/extensions/data/export_twice/lib1.dart
index ccfb0b9..14a21cd 100644
--- a/pkg/front_end/test/extensions/data/export_twice/lib1.dart
+++ b/pkg/front_end/test/extensions/data/export_twice/lib1.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[E]*/
+/*library: scope=[
+ E,
+ async.dart.FutureExtensions]*/
class A {}
diff --git a/pkg/front_end/test/extensions/data/export_twice/lib2.dart b/pkg/front_end/test/extensions/data/export_twice/lib2.dart
index f52e3cd1..e3d0760 100644
--- a/pkg/front_end/test/extensions/data/export_twice/lib2.dart
+++ b/pkg/front_end/test/extensions/data/export_twice/lib2.dart
@@ -2,6 +2,8 @@
// 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.
+/*library: scope=[async.dart.FutureExtensions]*/
+
// @dart = 2.9
export 'lib1.dart' show E;
diff --git a/pkg/front_end/test/extensions/data/export_twice/main.dart b/pkg/front_end/test/extensions/data/export_twice/main.dart
index 216536b..919a7ff 100644
--- a/pkg/front_end/test/extensions/data/export_twice/main.dart
+++ b/pkg/front_end/test/extensions/data/export_twice/main.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[lib1.dart.E]*/
+/*library: scope=[
+ async.dart.FutureExtensions,
+ lib1.dart.E]*/
import 'lib1.dart';
import 'lib2.dart';
diff --git a/pkg/front_end/test/extensions/data/export_unnamed/lib.dart b/pkg/front_end/test/extensions/data/export_unnamed/lib.dart
index 17d6f89..b6fab4b 100644
--- a/pkg/front_end/test/extensions/data/export_unnamed/lib.dart
+++ b/pkg/front_end/test/extensions/data/export_unnamed/lib.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[NamedExtension,_extension#0]*/
+/*library: scope=[
+ NamedExtension,
+ _extension#0,
+ async.dart.FutureExtensions]*/
/*class: _extension#0:
builder-name=_extension#0,
diff --git a/pkg/front_end/test/extensions/data/export_unnamed/main.dart b/pkg/front_end/test/extensions/data/export_unnamed/main.dart
index 132f4bb..5e37c32 100644
--- a/pkg/front_end/test/extensions/data/export_unnamed/main.dart
+++ b/pkg/front_end/test/extensions/data/export_unnamed/main.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[lib.dart.NamedExtension]*/
+/*library: scope=[
+ async.dart.FutureExtensions,
+ lib.dart.NamedExtension]*/
import 'lib.dart';
diff --git a/pkg/front_end/test/extensions/data/extension_on_type_variable.dart b/pkg/front_end/test/extensions/data/extension_on_type_variable.dart
index c4a88ce..5f7bb41 100644
--- a/pkg/front_end/test/extensions/data/extension_on_type_variable.dart
+++ b/pkg/front_end/test/extensions/data/extension_on_type_variable.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[GeneralGeneric]*/
+/*library: scope=[
+ GeneralGeneric,
+ async.dart.FutureExtensions]*/
/*class: GeneralGeneric:
builder-name=GeneralGeneric,
diff --git a/pkg/front_end/test/extensions/data/implicit_this.dart b/pkg/front_end/test/extensions/data/implicit_this.dart
index b50862b..733fb79 100644
--- a/pkg/front_end/test/extensions/data/implicit_this.dart
+++ b/pkg/front_end/test/extensions/data/implicit_this.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[A2]*/
+/*library: scope=[
+ A2,
+ async.dart.FutureExtensions]*/
class A1 {
Object field;
diff --git a/pkg/front_end/test/extensions/data/instance_members.dart b/pkg/front_end/test/extensions/data/instance_members.dart
index 3b3f248..6bd7d39 100644
--- a/pkg/front_end/test/extensions/data/instance_members.dart
+++ b/pkg/front_end/test/extensions/data/instance_members.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[A2,B2]*/
+/*library: scope=[
+ A2,
+ B2,
+ async.dart.FutureExtensions]*/
class A1 {}
diff --git a/pkg/front_end/test/extensions/data/named_declarations.dart b/pkg/front_end/test/extensions/data/named_declarations.dart
index ecde7e2..5f7d266 100644
--- a/pkg/front_end/test/extensions/data/named_declarations.dart
+++ b/pkg/front_end/test/extensions/data/named_declarations.dart
@@ -4,7 +4,12 @@
// @dart = 2.9
-/*library: scope=[A2,B2,B3,B4]*/
+/*library: scope=[
+ A2,
+ B2,
+ B3,
+ B4,
+ async.dart.FutureExtensions]*/
class A1 {}
diff --git a/pkg/front_end/test/extensions/data/other_kinds.dart b/pkg/front_end/test/extensions/data/other_kinds.dart
index 289e358..d38a105 100644
--- a/pkg/front_end/test/extensions/data/other_kinds.dart
+++ b/pkg/front_end/test/extensions/data/other_kinds.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[A2]*/
+/*library: scope=[
+ A2,
+ async.dart.FutureExtensions]*/
class A1 {
int _instanceField;
diff --git a/pkg/front_end/test/extensions/data/part/main.dart b/pkg/front_end/test/extensions/data/part/main.dart
index b014688..124b206 100644
--- a/pkg/front_end/test/extensions/data/part/main.dart
+++ b/pkg/front_end/test/extensions/data/part/main.dart
@@ -4,7 +4,8 @@
/*library: scope=[
Extension,
- _extension#0]*/
+ _extension#0,
+ async.dart.FutureExtensions]*/
part 'part.dart';
diff --git a/pkg/front_end/test/extensions/data/patching/main.dart b/pkg/front_end/test/extensions/data/patching/main.dart
index 692cfaa..43fab2c 100644
--- a/pkg/front_end/test/extensions/data/patching/main.dart
+++ b/pkg/front_end/test/extensions/data/patching/main.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[origin.dart.Extension,origin.dart.GenericExtension]*/
+/*library: scope=[
+ async.dart.FutureExtensions,
+ origin.dart.Extension,
+ origin.dart.GenericExtension]*/
// ignore: uri_does_not_exist
import 'dart:test';
diff --git a/pkg/front_end/test/extensions/data/patching/origin.dart b/pkg/front_end/test/extensions/data/patching/origin.dart
index c1c0176..f11eb98 100644
--- a/pkg/front_end/test/extensions/data/patching/origin.dart
+++ b/pkg/front_end/test/extensions/data/patching/origin.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[Extension,GenericExtension]*/
+/*library: scope=[
+ Extension,
+ GenericExtension,
+ async.dart.FutureExtensions]*/
/*class: Extension:
builder-name=Extension,
diff --git a/pkg/front_end/test/extensions/data/prefix/lib1.dart b/pkg/front_end/test/extensions/data/prefix/lib1.dart
index 3646dda..37dcd3b 100644
--- a/pkg/front_end/test/extensions/data/prefix/lib1.dart
+++ b/pkg/front_end/test/extensions/data/prefix/lib1.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[HiddenExtension1,ShownExtension1]*/
+/*library: scope=[
+ HiddenExtension1,
+ ShownExtension1,
+ async.dart.FutureExtensions]*/
/*class: ShownExtension1:
builder-name=ShownExtension1,
diff --git a/pkg/front_end/test/extensions/data/prefix/lib2.dart b/pkg/front_end/test/extensions/data/prefix/lib2.dart
index 8affe92..6c32e13 100644
--- a/pkg/front_end/test/extensions/data/prefix/lib2.dart
+++ b/pkg/front_end/test/extensions/data/prefix/lib2.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[HiddenExtension2,ShownExtension2]*/
+/*library: scope=[
+ HiddenExtension2,
+ ShownExtension2,
+ async.dart.FutureExtensions]*/
/*class: HiddenExtension2:
builder-name=HiddenExtension2,
diff --git a/pkg/front_end/test/extensions/data/prefix/lib3.dart b/pkg/front_end/test/extensions/data/prefix/lib3.dart
index 80105ee..a7245a0 100644
--- a/pkg/front_end/test/extensions/data/prefix/lib3.dart
+++ b/pkg/front_end/test/extensions/data/prefix/lib3.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[ShownExtension3]*/
+/*library: scope=[
+ ShownExtension3,
+ async.dart.FutureExtensions]*/
/*class: ShownExtension3:
builder-name=ShownExtension3,
diff --git a/pkg/front_end/test/extensions/data/prefix/main.dart b/pkg/front_end/test/extensions/data/prefix/main.dart
index e988aa1..db772d9 100644
--- a/pkg/front_end/test/extensions/data/prefix/main.dart
+++ b/pkg/front_end/test/extensions/data/prefix/main.dart
@@ -5,10 +5,10 @@
// @dart = 2.9
/*library: scope=[
- lib1.dart.ShownExtension1,
- lib2.dart.ShownExtension2,
- lib3.dart.ShownExtension3]
-*/
+ async.dart.FutureExtensions,
+ lib1.dart.ShownExtension1,
+ lib2.dart.ShownExtension2,
+ lib3.dart.ShownExtension3]*/
import 'lib1.dart' as lib1 show ShownExtension1;
import 'lib2.dart' as lib2 hide HiddenExtension2;
diff --git a/pkg/front_end/test/extensions/data/reexport/lib.dart b/pkg/front_end/test/extensions/data/reexport/lib.dart
index b787d79..c250d6a 100644
--- a/pkg/front_end/test/extensions/data/reexport/lib.dart
+++ b/pkg/front_end/test/extensions/data/reexport/lib.dart
@@ -2,6 +2,8 @@
// 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.
+/*library: scope=[async.dart.FutureExtensions]*/
+
// @dart = 2.9
export 'lib1.dart';
diff --git a/pkg/front_end/test/extensions/data/reexport/lib1.dart b/pkg/front_end/test/extensions/data/reexport/lib1.dart
index da11a1f..ecf38c5 100644
--- a/pkg/front_end/test/extensions/data/reexport/lib1.dart
+++ b/pkg/front_end/test/extensions/data/reexport/lib1.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[ClashingExtension,UniqueExtension1]*/
+/*library: scope=[
+ ClashingExtension,
+ UniqueExtension1,
+ async.dart.FutureExtensions]*/
/*class: ClashingExtension:
builder-name=ClashingExtension,
diff --git a/pkg/front_end/test/extensions/data/reexport/lib2.dart b/pkg/front_end/test/extensions/data/reexport/lib2.dart
index e30d364..56e8c50 100644
--- a/pkg/front_end/test/extensions/data/reexport/lib2.dart
+++ b/pkg/front_end/test/extensions/data/reexport/lib2.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[ClashingExtension,UniqueExtension2]*/
+/*library: scope=[
+ ClashingExtension,
+ UniqueExtension2,
+ async.dart.FutureExtensions]*/
/*class: ClashingExtension:
builder-name=ClashingExtension,
diff --git a/pkg/front_end/test/extensions/data/reexport/main.dart b/pkg/front_end/test/extensions/data/reexport/main.dart
index 1e06107..7238499 100644
--- a/pkg/front_end/test/extensions/data/reexport/main.dart
+++ b/pkg/front_end/test/extensions/data/reexport/main.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[lib1.dart.UniqueExtension1,lib2.dart.UniqueExtension2]*/
+/*library: scope=[
+ async.dart.FutureExtensions,
+ lib1.dart.UniqueExtension1,
+ lib2.dart.UniqueExtension2]*/
import 'lib.dart';
diff --git a/pkg/front_end/test/extensions/data/show_hide/lib1.dart b/pkg/front_end/test/extensions/data/show_hide/lib1.dart
index 3646dda..37dcd3b 100644
--- a/pkg/front_end/test/extensions/data/show_hide/lib1.dart
+++ b/pkg/front_end/test/extensions/data/show_hide/lib1.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[HiddenExtension1,ShownExtension1]*/
+/*library: scope=[
+ HiddenExtension1,
+ ShownExtension1,
+ async.dart.FutureExtensions]*/
/*class: ShownExtension1:
builder-name=ShownExtension1,
diff --git a/pkg/front_end/test/extensions/data/show_hide/lib2.dart b/pkg/front_end/test/extensions/data/show_hide/lib2.dart
index 8affe92..6c32e13 100644
--- a/pkg/front_end/test/extensions/data/show_hide/lib2.dart
+++ b/pkg/front_end/test/extensions/data/show_hide/lib2.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[HiddenExtension2,ShownExtension2]*/
+/*library: scope=[
+ HiddenExtension2,
+ ShownExtension2,
+ async.dart.FutureExtensions]*/
/*class: HiddenExtension2:
builder-name=HiddenExtension2,
diff --git a/pkg/front_end/test/extensions/data/show_hide/main.dart b/pkg/front_end/test/extensions/data/show_hide/main.dart
index 622bf60..60ba8f4 100644
--- a/pkg/front_end/test/extensions/data/show_hide/main.dart
+++ b/pkg/front_end/test/extensions/data/show_hide/main.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[lib1.dart.ShownExtension1,lib2.dart.ShownExtension2]*/
+/*library: scope=[
+ async.dart.FutureExtensions,
+ lib1.dart.ShownExtension1,
+ lib2.dart.ShownExtension2]*/
import 'lib1.dart' show ShownExtension1;
import 'lib2.dart' hide HiddenExtension2;
diff --git a/pkg/front_end/test/extensions/data/static_members.dart b/pkg/front_end/test/extensions/data/static_members.dart
index f11b66d..eab1942 100644
--- a/pkg/front_end/test/extensions/data/static_members.dart
+++ b/pkg/front_end/test/extensions/data/static_members.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[A2,B2]*/
+/*library: scope=[
+ A2,
+ B2,
+ async.dart.FutureExtensions]*/
class A1 {}
diff --git a/pkg/front_end/test/extensions/data/super.dart b/pkg/front_end/test/extensions/data/super.dart
index 0123d92..df22e8f 100644
--- a/pkg/front_end/test/extensions/data/super.dart
+++ b/pkg/front_end/test/extensions/data/super.dart
@@ -4,7 +4,9 @@
// @dart = 2.9
-/*library: scope=[A2]*/
+/*library: scope=[
+ A2,
+ async.dart.FutureExtensions]*/
class A1 {
method1() {}
diff --git a/pkg/front_end/test/extensions/data/type_variables.dart b/pkg/front_end/test/extensions/data/type_variables.dart
index e04f82d..8153930 100644
--- a/pkg/front_end/test/extensions/data/type_variables.dart
+++ b/pkg/front_end/test/extensions/data/type_variables.dart
@@ -4,7 +4,11 @@
// @dart = 2.9
-/*library: scope=[A2,A3,A4]*/
+/*library: scope=[
+ A2,
+ A3,
+ A4,
+ async.dart.FutureExtensions]*/
class A1<T> {}
diff --git a/pkg/front_end/test/extensions/data/unnamed_declarations.dart b/pkg/front_end/test/extensions/data/unnamed_declarations.dart
index 7577c5f..ffa10b4 100644
--- a/pkg/front_end/test/extensions/data/unnamed_declarations.dart
+++ b/pkg/front_end/test/extensions/data/unnamed_declarations.dart
@@ -4,7 +4,13 @@
// @dart = 2.9
-/*library: scope=[_extension#0,_extension#1,_extension#2,_extension#3,_extension#4]*/
+/*library: scope=[
+ _extension#0,
+ _extension#1,
+ _extension#2,
+ _extension#3,
+ _extension#4,
+ async.dart.FutureExtensions]*/
class A1 {}
diff --git a/pkg/front_end/test/extensions/data/use_as_type.dart b/pkg/front_end/test/extensions/data/use_as_type.dart
index b928105..5cdc007 100644
--- a/pkg/front_end/test/extensions/data/use_as_type.dart
+++ b/pkg/front_end/test/extensions/data/use_as_type.dart
@@ -4,7 +4,10 @@
// @dart = 2.9
-/*library: scope=[A2,B2]*/
+/*library: scope=[
+ A2,
+ B2,
+ async.dart.FutureExtensions]*/
class A1 {}
diff --git a/pkg/front_end/test/id_tests/assigned_variables_test.dart b/pkg/front_end/test/id_tests/assigned_variables_test.dart
index b0a7d7a..c74e39b 100644
--- a/pkg/front_end/test/id_tests/assigned_variables_test.dart
+++ b/pkg/front_end/test/id_tests/assigned_variables_test.dart
@@ -73,6 +73,8 @@
_Data computeMemberValue(Id id, Member member) {
return new _Data(
_convertVars(_assignedVariables.declaredAtTopLevel),
+ _convertVars(_assignedVariables.readAnywhere),
+ _convertVars(_assignedVariables.readCapturedAnywhere),
_convertVars(_assignedVariables.writtenAnywhere),
_convertVars(_assignedVariables.capturedAnywhere));
}
@@ -93,6 +95,8 @@
if (!_assignedVariables.isTracked(alias)) return null;
return new _Data(
_convertVars(_assignedVariables.declaredInNode(alias)),
+ _convertVars(_assignedVariables.readInNode(alias)),
+ _convertVars(_assignedVariables.readCapturedInNode(alias)),
_convertVars(_assignedVariables.writtenInNode(alias)),
_convertVars(_assignedVariables.capturedInNode(alias)));
}
@@ -107,6 +111,12 @@
if (actualData.declared.isNotEmpty) {
parts.add('declared=${_setToString(actualData.declared)}');
}
+ if (actualData.read.isNotEmpty) {
+ parts.add('read=${_setToString(actualData.read)}');
+ }
+ if (actualData.readCaptured.isNotEmpty) {
+ parts.add('readCaptured=${_setToString(actualData.readCaptured)}');
+ }
if (actualData.assigned.isNotEmpty) {
parts.add('assigned=${_setToString(actualData.assigned)}');
}
@@ -140,9 +150,14 @@
class _Data {
final Set<String> declared;
+ final Set<String> read;
+
+ final Set<String> readCaptured;
+
final Set<String> assigned;
final Set<String> captured;
- _Data(this.declared, this.assigned, this.captured);
+ _Data(this.declared, this.read, this.readCaptured, this.assigned,
+ this.captured);
}
diff --git a/pkg/front_end/test/static_types/data/if_null.dart b/pkg/front_end/test/static_types/data/if_null.dart
index 2f38469..c4e18b6 100644
--- a/pkg/front_end/test/static_types/data/if_null.dart
+++ b/pkg/front_end/test/static_types/data/if_null.dart
@@ -12,10 +12,11 @@
String? foo() => /*Null*/ null;
String bar() => /*String!*/ "bar";
- var s = /*invoke: String?*/ foo() ?? /*invoke: String!*/ bar();
+ var s = /*invoke: String?*/ foo() /*String!*/ ?? /*invoke: String!*/ bar();
/*String!*/ s;
- String s2 = /*invoke: String?*/ hest1/*<String?>*/() ?? /*String!*/ "fisk";
+ String
+ s2 = /*invoke: String?*/ hest1/*<String?>*/() /*String!*/ ?? /*String!*/ "fisk";
}
// ------------------------- If-null property set. -----------------------------
@@ -24,22 +25,24 @@
}
test2(A2 a) {
- var s =
- (/*A2!*/ a. /*String?*/ /*update: String!*/ foo ??= /*String!*/ "bar");
+ var s = (/*A2!*/ a
+ . /*String?*/ /*update: String!*/ foo /*String!*/ ??= /*String!*/ "bar");
/*String!*/ s;
}
// ------------------------------ If-null set. ---------------------------------
test3() {
String? s = /*Null*/ null;
- var s2 = (/*String?*/ /*update: String!*/ s ??= /*String!*/ "bar");
+ var s2 =
+ (/*String?*/ /*update: String!*/ s /*String!*/ ??= /*String!*/ "bar");
/*String!*/ s2;
}
// --------------------------- If-null index set. ------------------------------
test4() {
List<String?> list = /*List<String?>!*/ [/*Null*/ null];
- var s = (/*List<String?>!*/ list /*String?*/ /*update: void*/ [/*int!*/ 0] ??=
+ var s = (/*List<String?>!*/ list /*String?*/ /*update: void*/ [
+ /*int!*/ 0] /*String!*/ ??=
/*String!*/ "bar");
/*String!*/ s;
}
@@ -52,7 +55,7 @@
class B5 extends A5 {
test5() {
- var s = (super[/*int!*/ 0] ??= /*String!*/ "bar");
+ var s = (super[/*int!*/ 0] /*String!*/ ??= /*String!*/ "bar");
/*String!*/ s;
}
}
@@ -65,7 +68,7 @@
test6() {
var s = (E6(/*double!*/ 3.14) /*invoke: String?|void*/ [
- /*int!*/ 0] ??= /*String!*/ "bar");
+ /*int!*/ 0] /*String!*/ ??= /*String!*/ "bar");
/*String!*/ s;
}
@@ -76,12 +79,12 @@
}
test7(A7? a) {
- var s =
- (/*A7?*/ a?. /*String!*/ /*update: String!*/ foo ??= /*String!*/ "bar");
+ var s = (/*A7?|String?|String!*/ a
+ ?. /*String!*/ /*update: String!*/ foo ??= /*String!*/ "bar");
/*String?*/ s;
- var s2 =
- (/*A7?*/ a?. /*String?*/ /*update: String!*/ bar ??= /*String!*/ "bar");
+ var s2 = (/*A7?|String?|String!*/ a
+ ?. /*String?*/ /*update: String!*/ bar ??= /*String!*/ "bar");
/*String?*/ s2;
}
diff --git a/pkg/front_end/test/static_types/data/null_aware_for_in.dart b/pkg/front_end/test/static_types/data/null_aware_for_in.dart
index 48f0917..42c998b 100644
--- a/pkg/front_end/test/static_types/data/null_aware_for_in.dart
+++ b/pkg/front_end/test/static_types/data/null_aware_for_in.dart
@@ -17,5 +17,5 @@
Class c in
/*cfe.as: Iterable<dynamic>*/
/*cfe:nnbd.as: Iterable<dynamic>!*/
- /*dynamic*/ o?. /*dynamic*/ iterable) {}
+ /*dynamic|dynamic*/ o?. /*dynamic*/ iterable) {}
}
diff --git a/pkg/front_end/test/static_types/data/promoted_access.dart b/pkg/front_end/test/static_types/data/promoted_access.dart
index 3e87292..82085cb 100644
--- a/pkg/front_end/test/static_types/data/promoted_access.dart
+++ b/pkg/front_end/test/static_types/data/promoted_access.dart
@@ -13,9 +13,9 @@
/*cfe.T & Class<dynamic>*/
/*cfe:nnbd.T! & Class<dynamic>!*/
o. /*invoke: dynamic*/ method(/*Null*/ null);
- /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+ /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
?. /*invoke: dynamic*/ method(/*Null*/ null);
- /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+ /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
?. /*dynamic*/ property;
}
}
@@ -26,9 +26,9 @@
/*cfe.T & Class<dynamic>*/
/*cfe:nnbd.T! & Class<dynamic>!*/
o. /*invoke: dynamic*/ method(/*Null*/ null);
- /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+ /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
?. /*invoke: dynamic*/ method(/*Null*/ null);
- /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+ /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
?. /*dynamic*/ property;
}
}
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 5f30b51..c074a4a 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -2214,8 +2214,12 @@
}
return type;
} catch (exception, stackTrace) {
- listener.reportException(source, node, exception, stackTrace);
- return null;
+ if (listener != null) {
+ listener.reportException(source, node, exception, stackTrace);
+ return null;
+ } else {
+ rethrow;
+ }
}
}
@@ -3038,16 +3042,16 @@
}
}
- DecoratedType _handleTarget(Expression target, String name, Element method) {
+ DecoratedType _handleTarget(Expression target, String name, Element callee) {
if (isDeclaredOnObject(name)) {
return _dispatch(target);
- } else if (method is MethodElement &&
- method.enclosingElement is ExtensionElement) {
+ } else if ((callee is MethodElement || callee is PropertyAccessorElement) &&
+ callee.enclosingElement is ExtensionElement) {
// Extension methods can be called on a `null` target, when the `on` type
// of the extension is nullable.
return _handleAssignment(target,
destinationType:
- _variables.decoratedElementType(method.enclosingElement));
+ _variables.decoratedElementType(callee.enclosingElement));
} else {
return _checkExpressionNotNull(target);
}
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 7089d06..97bac6d 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -2289,7 +2289,6 @@
await _checkSingleFileChanges(content, expected);
}
- @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/42529')
Future<void> test_extension_nullable_target() async {
var content = '''
extension E on int {
@@ -2507,7 +2506,6 @@
await _checkSingleFileChanges(content, expected);
}
- @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/42529')
Future<void> test_extension_override_nullable_target() async {
var content = '''
extension E on int {
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 9558d4e..e50b091 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -6473,6 +6473,38 @@
expect(hasNullCheckHint(findNode.prefixed('m.pi/*!*/')), isTrue);
}
+ Future<void> test_prefixedIdentifier_extension_nullTarget_get() async {
+ await analyze('''
+class C {}
+extension on C /*1*/ {
+ int get x => 0;
+}
+void f() {
+ C c = null;
+ c.x;
+}
+''');
+ assertEdge(decoratedTypeAnnotation('C c').node,
+ decoratedTypeAnnotation('C /*1*/').node,
+ hard: true);
+ }
+
+ Future<void> test_prefixedIdentifier_extension_nullTarget_set() async {
+ await analyze('''
+class C {}
+extension on C /*1*/ {
+ set x(int value) {}
+}
+void f() {
+ C c = null;
+ c.x = 0;
+}
+''');
+ assertEdge(decoratedTypeAnnotation('C c').node,
+ decoratedTypeAnnotation('C /*1*/').node,
+ hard: true);
+ }
+
Future<void> test_prefixedIdentifier_field_type() async {
await analyze('''
class C {
@@ -6802,6 +6834,70 @@
hard: false);
}
+ Future<void> test_propertyAccess_extension_nullTarget_get() async {
+ await analyze('''
+class C {}
+extension on C /*1*/ {
+ int get x => 0;
+}
+void f() {
+ C g() => null;
+ g().x;
+}
+''');
+ assertEdge(decoratedTypeAnnotation('C g()').node,
+ decoratedTypeAnnotation('C /*1*/').node,
+ hard: false);
+ }
+
+ Future<void> test_propertyAccess_extension_nullTarget_get_explicit() async {
+ await analyze('''
+class C {}
+extension E on C /*1*/ {
+ int get x => 0;
+}
+void f() {
+ C g() => null;
+ E(g()).x;
+}
+''');
+ assertEdge(decoratedTypeAnnotation('C g()').node,
+ decoratedTypeAnnotation('C /*1*/').node,
+ hard: false);
+ }
+
+ Future<void> test_propertyAccess_extension_nullTarget_set() async {
+ await analyze('''
+class C {}
+extension on C /*1*/ {
+ set x(int value) {}
+}
+void f() {
+ C g() => null;
+ g().x = 0;
+}
+''');
+ assertEdge(decoratedTypeAnnotation('C g()').node,
+ decoratedTypeAnnotation('C /*1*/').node,
+ hard: false);
+ }
+
+ Future<void> test_propertyAccess_extension_nullTarget_set_explicit() async {
+ await analyze('''
+class C {}
+extension E on C /*1*/ {
+ set x(int value) {}
+}
+void f() {
+ C g() => null;
+ E(g()).x = 0;
+}
+''');
+ assertEdge(decoratedTypeAnnotation('C g()').node,
+ decoratedTypeAnnotation('C /*1*/').node,
+ hard: false);
+ }
+
Future<void> test_propertyAccess_object_property() async {
await analyze('''
int f(int i) => i.hashCode;
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index a0fc1de..5f226e7 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -23,7 +23,9 @@
import 'dart:async' show Future, ZoneSpecification, runZoned;
import 'dart:collection' show UnmodifiableMapBase;
import 'dart:convert' show utf8;
-import 'dart:io' show Directory, File, Platform, stderr hide FileSystemEntity;
+import 'dart:io'
+ show Directory, File, Platform, stderr, stdout
+ hide FileSystemEntity;
import 'dart:isolate';
import 'dart:typed_data' show Uint8List;
@@ -92,7 +94,8 @@
int nullSafety,
List<String> experimentalFlags,
Uri packagesUri,
- List<String> errors) {
+ List<String> errors,
+ String invocationModes) {
final expFlags = <String>[];
if (experimentalFlags != null) {
for (String flag in experimentalFlags) {
@@ -116,27 +119,33 @@
? NnbdMode.Strong
: NnbdMode.Weak
..onDiagnostic = (DiagnosticMessage message) {
- bool printMessage;
+ bool printToStdErr = false;
+ bool printToStdOut = false;
switch (message.severity) {
case Severity.error:
case Severity.internalProblem:
// TODO(sigmund): support emitting code with errors as long as they
// are handled in the generated code.
- printMessage = false; // errors are printed by VM
+ printToStdErr = false; // errors are printed by VM
errors.addAll(message.plainTextFormatted);
break;
case Severity.warning:
+ printToStdErr = !suppressWarnings;
+ break;
case Severity.info:
- printMessage = !suppressWarnings;
+ printToStdOut = !suppressWarnings;
break;
case Severity.context:
case Severity.ignored:
throw "Unexpected severity: ${message.severity}";
}
- if (printMessage) {
+ if (printToStdErr) {
printDiagnosticMessage(message, stderr.writeln);
+ } else if (printToStdOut) {
+ printDiagnosticMessage(message, stdout.writeln);
}
- };
+ }
+ ..invocationModes = InvocationMode.parseArguments(invocationModes);
}
abstract class Compiler {
@@ -148,6 +157,7 @@
final int nullSafety;
final List<String> experimentalFlags;
final String packageConfig;
+ final String invocationModes;
// Code coverage and hot reload are only supported by incremental compiler,
// which is used if vm-service is enabled.
@@ -165,7 +175,8 @@
this.experimentalFlags: null,
this.supportCodeCoverage: false,
this.supportHotReload: false,
- this.packageConfig: null}) {
+ this.packageConfig: null,
+ this.invocationModes: ''}) {
Uri packagesUri = null;
if (packageConfig != null) {
packagesUri = Uri.parse(packageConfig);
@@ -188,7 +199,8 @@
nullSafety,
experimentalFlags,
packagesUri,
- errors);
+ errors,
+ invocationModes);
}
Future<CompilerResult> compile(Uri script) {
@@ -278,7 +290,8 @@
bool enableAsserts: false,
int nullSafety: kNullSafetyOptionUnspecified,
List<String> experimentalFlags: null,
- String packageConfig: null})
+ String packageConfig: null,
+ String invocationModes: ''})
: super(isolateId, fileSystem, platformKernelPath,
suppressWarnings: suppressWarnings,
enableAsserts: enableAsserts,
@@ -286,7 +299,8 @@
experimentalFlags: experimentalFlags,
supportHotReload: true,
supportCodeCoverage: true,
- packageConfig: packageConfig);
+ packageConfig: packageConfig,
+ invocationModes: invocationModes);
factory IncrementalCompilerWrapper.forExpressionCompilationOnly(
Component component,
@@ -296,13 +310,15 @@
{bool suppressWarnings: false,
bool enableAsserts: false,
List<String> experimentalFlags: null,
- String packageConfig: null}) {
+ String packageConfig: null,
+ String invocationModes: ''}) {
IncrementalCompilerWrapper result = IncrementalCompilerWrapper(
isolateId, fileSystem, platformKernelPath,
suppressWarnings: suppressWarnings,
enableAsserts: enableAsserts,
experimentalFlags: experimentalFlags,
- packageConfig: packageConfig);
+ packageConfig: packageConfig,
+ invocationModes: invocationModes);
result.generator = new IncrementalCompiler.forExpressionCompilationOnly(
component,
result.options,
@@ -331,7 +347,8 @@
enableAsserts: enableAsserts,
nullSafety: nullSafety,
experimentalFlags: experimentalFlags,
- packageConfig: packageConfig);
+ packageConfig: packageConfig,
+ invocationModes: invocationModes);
generator.resetDeltaState();
Component fullComponent = await generator.compile();
@@ -361,13 +378,15 @@
bool enableAsserts: false,
int nullSafety: kNullSafetyOptionUnspecified,
List<String> experimentalFlags: null,
- String packageConfig: null})
+ String packageConfig: null,
+ String invocationModes: ''})
: super(isolateId, fileSystem, platformKernelPath,
suppressWarnings: suppressWarnings,
enableAsserts: enableAsserts,
nullSafety: nullSafety,
experimentalFlags: experimentalFlags,
- packageConfig: packageConfig);
+ packageConfig: packageConfig,
+ invocationModes: invocationModes);
@override
Future<CompilerResult> compileInternal(Uri script) async {
@@ -403,7 +422,8 @@
List<String> experimentalFlags: null,
String packageConfig: null,
String multirootFilepaths,
- String multirootScheme}) async {
+ String multirootScheme,
+ String invocationModes: ''}) async {
IncrementalCompilerWrapper compiler = lookupIncrementalCompiler(isolateId);
if (compiler != null) {
updateSources(compiler, sourceFiles);
@@ -432,7 +452,8 @@
enableAsserts: enableAsserts,
nullSafety: nullSafety,
experimentalFlags: experimentalFlags,
- packageConfig: packageConfig);
+ packageConfig: packageConfig,
+ invocationModes: invocationModes);
}
isolateCompilers[isolateId] = compiler;
}
@@ -652,10 +673,7 @@
return utf8.encode(uris.map(_escapeDependency).join(" "));
}
-Future _processListDependenciesRequest(request) async {
- final SendPort port = request[1];
- final int isolateId = request[6];
-
+Future _processListDependenciesRequest(SendPort port, int isolateId) async {
final List<Uri> dependencies = isolateDependencies[isolateId] ?? <Uri>[];
CompilationResult result;
@@ -709,32 +727,34 @@
return;
}
- if (tag == kListDependenciesTag) {
- await _processListDependenciesRequest(request);
- return;
- }
-
if (tag == kNotifyIsolateShutdownTag) {
await _processIsolateShutdownNotification(request);
return;
}
final SendPort port = request[1];
+ final int isolateId = request[7];
+
+ if (tag == kListDependenciesTag) {
+ await _processListDependenciesRequest(port, isolateId);
+ return;
+ }
+
final String inputFileUri = request[2];
final Uri script =
inputFileUri != null ? Uri.base.resolve(inputFileUri) : null;
final bool incremental = request[4];
- final int nullSafety = request[5];
- final int isolateId = request[6];
- final List sourceFiles = request[7];
- final bool suppressWarnings = request[8];
- final bool enableAsserts = request[9];
+ final bool snapshot = request[5];
+ final int nullSafety = request[6];
+ final List sourceFiles = request[8];
+ final bool suppressWarnings = request[9];
+ final bool enableAsserts = request[10];
final List<String> experimentalFlags =
- request[10] != null ? request[10].cast<String>() : null;
- final String packageConfig = request[11];
- final String multirootFilepaths = request[12];
- final String multirootScheme = request[13];
- final String workingDirectory = request[14];
+ request[11] != null ? request[11].cast<String>() : null;
+ final String packageConfig = request[12];
+ final String multirootFilepaths = request[13];
+ final String multirootScheme = request[14];
+ final String workingDirectory = request[15];
Uri platformKernelPath = null;
List<int> platformKernel = null;
@@ -748,6 +768,8 @@
computePlatformBinariesLocation().resolve('vm_platform_strong.dill');
}
+ final String invocationModes = snapshot ? 'compile' : '';
+
Compiler compiler;
// Update the in-memory file system with the provided sources. Currently, only
@@ -792,8 +814,16 @@
packagesUri = Uri.directory(workingDirectory).resolveUri(packagesUri);
}
final List<String> errors = <String>[];
- var options = setupCompilerOptions(fileSystem, platformKernelPath, false,
- false, nullSafety, experimentalFlags, packagesUri, errors);
+ var options = setupCompilerOptions(
+ fileSystem,
+ platformKernelPath,
+ false,
+ false,
+ nullSafety,
+ experimentalFlags,
+ packagesUri,
+ errors,
+ invocationModes);
// script should only be null for kUpdateSourcesTag.
assert(script != null);
@@ -819,7 +849,8 @@
experimentalFlags: experimentalFlags,
packageConfig: packageConfig,
multirootFilepaths: multirootFilepaths,
- multirootScheme: multirootScheme);
+ multirootScheme: multirootScheme,
+ invocationModes: invocationModes);
} else {
FileSystem fileSystem = _buildFileSystem(
sourceFiles, platformKernel, multirootFilepaths, multirootScheme);
@@ -830,7 +861,8 @@
enableAsserts: enableAsserts,
nullSafety: nullSafety,
experimentalFlags: experimentalFlags,
- packageConfig: packageConfig);
+ packageConfig: packageConfig,
+ invocationModes: invocationModes);
}
CompilationResult result;
@@ -974,6 +1006,7 @@
scriptUri,
platformKernelPath,
false /* incremental */,
+ false /* snapshot */,
kNullSafetyOptionUnspecified /* null safety */,
1 /* isolateId chosen randomly */,
[] /* source files */,
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 2013c32..4bd758c 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## 6.0.1-nullsafety.0
+- Fix versioning for pub.
+
+## 6.0.0-nullsafety.4
+- Fixed issue where response parsing could fail for `SourceReportRange.coverage`
+ if no coverage information was provided.
## 6.0.0-nullsafety.3
- Fixed issue where `Response.type` and classes which override `Response.type` were
returning the name of the `package:vm_service` reference object (e.g., InstanceRef) instead of
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 05cbc00..d7d9d48 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -6886,7 +6886,7 @@
compiled = json['compiled'] ?? false;
error = createServiceObject(json['error'], const ['ErrorRef']) as ErrorRef?;
coverage =
- _createSpecificObject(json['coverage']!, SourceReportCoverage.parse);
+ _createSpecificObject(json['coverage'], SourceReportCoverage.parse);
possibleBreakpoints = json['possibleBreakpoints'] == null
? null
: List<int>.from(json['possibleBreakpoints']);
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 84a7fe1..fc44e80 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -3,7 +3,7 @@
A library to communicate with a service implementing the Dart VM
service protocol.
-version: 6.0.0-nullsafety.3
+version: 6.0.1-nullsafety.0
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index 6f5fca6..b41d7ab 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -1580,7 +1580,7 @@
} else if (name == 'SourceReportRange' && field.name == 'coverage') {
// Special case `SourceReportRange.coverage`.
gen.writeln("coverage = _createSpecificObject("
- "json['coverage']!, SourceReportCoverage.parse);");
+ "json['coverage'], SourceReportCoverage.parse);");
} else if (name == 'Library' && field.name == 'dependencies') {
// Special case `Library.dependencies`.
gen.writeln("dependencies = List<LibraryDependency>.from("
diff --git a/runtime/bin/dfe.cc b/runtime/bin/dfe.cc
index f0aa87a..0a6bc48 100644
--- a/runtime/bin/dfe.cc
+++ b/runtime/bin/dfe.cc
@@ -182,14 +182,15 @@
Dart_KernelCompilationResult DFE::CompileScript(const char* script_uri,
bool incremental,
- const char* package_config) {
+ const char* package_config,
+ bool snapshot) {
// TODO(aam): When Frontend is ready, VM should be passing vm_outline.dill
// instead of vm_platform.dill to Frontend for compilation.
PathSanitizer path_sanitizer(script_uri);
const char* sanitized_uri = path_sanitizer.sanitized_uri();
return Dart_CompileToKernel(sanitized_uri, platform_strong_dill,
- platform_strong_dill_size, incremental,
+ platform_strong_dill_size, incremental, snapshot,
package_config);
}
@@ -198,9 +199,10 @@
intptr_t* kernel_buffer_size,
char** error,
int* exit_code,
- const char* package_config) {
- Dart_KernelCompilationResult result =
- CompileScript(script_uri, use_incremental_compiler(), package_config);
+ const char* package_config,
+ bool snapshot) {
+ Dart_KernelCompilationResult result = CompileScript(
+ script_uri, use_incremental_compiler(), package_config, snapshot);
switch (result.status) {
case Dart_KernelCompilationStatus_Ok:
*kernel_buffer = result.kernel;
diff --git a/runtime/bin/dfe.h b/runtime/bin/dfe.h
index eb475bf..0c38270 100644
--- a/runtime/bin/dfe.h
+++ b/runtime/bin/dfe.h
@@ -59,20 +59,28 @@
// Compiles specified script.
// Returns result from compiling the script.
+ //
+ // `snapshot` is used by the frontend to determine if compilation
+ // related information should be printed to console (e.g., null safety mode).
Dart_KernelCompilationResult CompileScript(const char* script_uri,
bool incremental,
- const char* package_config);
+ const char* package_config,
+ bool snapshot);
// Compiles specified script and reads the resulting kernel file.
// If the compilation is successful, returns a valid in memory kernel
// representation of the script, NULL otherwise
// 'error' and 'exit_code' have the error values in case of errors.
+ //
+ // `snapshot` is used by the frontend to determine if compilation
+ // related information should be printed to console (e.g., null safety mode).
void CompileAndReadScript(const char* script_uri,
uint8_t** kernel_buffer,
intptr_t* kernel_buffer_size,
char** error,
int* exit_code,
- const char* package_config);
+ const char* package_config,
+ bool snapshot);
// Reads the script kernel file if specified 'script_uri' is a kernel file.
// Returns an in memory kernel representation of the specified script is a
diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc
index 9f62982..e855baf 100644
--- a/runtime/bin/loader.cc
+++ b/runtime/bin/loader.cc
@@ -198,7 +198,7 @@
uint8_t* kernel_buffer = NULL;
intptr_t kernel_buffer_size = -1;
dfe.CompileAndReadScript(url_string, &kernel_buffer, &kernel_buffer_size,
- &error, &exit_code, NULL);
+ &error, &exit_code, NULL, false);
if (exit_code == 0) {
return Dart_LoadLibraryFromKernel(kernel_buffer, kernel_buffer_size);
} else if (exit_code == kCompilationErrorExitCode) {
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index fec166f..12e64808 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -298,9 +298,14 @@
}
uint8_t* application_kernel_buffer = NULL;
intptr_t application_kernel_buffer_size = 0;
+ // Only pass snapshot = true when generating an AppJIT snapshot to avoid
+ // duplicate null-safety info messages from the frontend when generating
+ // a kernel snapshot (this flag is instead set in
+ // Snapshot::GenerateKernel()).
+ const bool snapshot = Options::gen_snapshot_kind() == kAppJIT;
dfe.CompileAndReadScript(script_uri, &application_kernel_buffer,
&application_kernel_buffer_size, error, exit_code,
- resolved_packages_config);
+ resolved_packages_config, snapshot);
if (application_kernel_buffer == NULL) {
Dart_ExitScope();
Dart_ShutdownIsolate();
diff --git a/runtime/bin/secure_socket_filter.cc b/runtime/bin/secure_socket_filter.cc
index c7a89a5..f515b92 100644
--- a/runtime/bin/secure_socket_filter.cc
+++ b/runtime/bin/secure_socket_filter.cc
@@ -159,10 +159,14 @@
void FUNCTION_NAME(SecureSocket_NewX509CertificateWrapper)(
Dart_NativeArguments args) {
+// This is to be used only in conjunction with certificate trust evaluator
+// running asynchronously, which is only used on mac/ios at the moment.
+#if !defined(HOST_OS_MACOS)
+ FATAL("This is to be used only on mac/ios platforms");
+#endif
intptr_t x509_pointer = DartUtils::GetNativeIntptrArgument(args, 0);
ASSERT(x509_pointer != 0);
X509* x509 = reinterpret_cast<X509*>(x509_pointer);
- X509_up_ref(x509);
Dart_SetReturnValue(args, X509Helper::WrappedX509Certificate(x509));
}
diff --git a/runtime/bin/security_context_macos.cc b/runtime/bin/security_context_macos.cc
index 927e139..c56487d 100644
--- a/runtime/bin/security_context_macos.cc
+++ b/runtime/bin/security_context_macos.cc
@@ -136,6 +136,7 @@
int current_cert = 0;
cert_chain.set(CFArrayCreateMutable(NULL, num_certs, NULL));
X509* ca;
+ // Look for the last certificate in the chain - it's a root certificate.
while ((ca = sk_X509_shift(unverified)) != NULL) {
ScopedSecCertificateRef cert(CreateSecCertificateFromX509(ca));
if (cert == NULL) {
@@ -145,9 +146,12 @@
++current_cert;
if (current_cert == num_certs) {
- root_cert = ca;
+ break;
}
}
+ ASSERT(current_cert == num_certs);
+ root_cert = ca;
+ X509_up_ref(ca);
SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
X509_STORE* store = SSL_CTX_get_cert_store(ssl_ctx);
diff --git a/runtime/bin/snapshot_utils.cc b/runtime/bin/snapshot_utils.cc
index 4a19ba2..5168ac5 100644
--- a/runtime/bin/snapshot_utils.cc
+++ b/runtime/bin/snapshot_utils.cc
@@ -471,7 +471,7 @@
free(kernel_buffer);
} else {
Dart_KernelCompilationResult result =
- dfe.CompileScript(script_name, false, package_config);
+ dfe.CompileScript(script_name, false, package_config, true);
if (result.status != Dart_KernelCompilationStatus_Ok) {
ErrorExit(kErrorExitCode, "%s\n", result.error);
}
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 5cff8c3..aae9199 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -3537,6 +3537,10 @@
*
* \param platform_kernel_size The length of the platform_kernel buffer.
*
+ * \param snapshot_compile Set to `true` when the compilation is for a snapshot.
+ * This is used by the frontend to determine if compilation related information
+ * should be printed to console (e.g., null safety mode).
+ *
* \return Returns the result of the compilation.
*
* On a successful compilation the returned [Dart_KernelCompilationResult] has
@@ -3554,6 +3558,7 @@
const uint8_t* platform_kernel,
const intptr_t platform_kernel_size,
bool incremental_compile,
+ bool snapshot_compile,
const char* package_config);
typedef struct {
diff --git a/runtime/tests/vm/dart/sdk_hash_test.dart b/runtime/tests/vm/dart/sdk_hash_test.dart
index 8e31050..0ad4dba 100644
--- a/runtime/tests/vm/dart/sdk_hash_test.dart
+++ b/runtime/tests/vm/dart/sdk_hash_test.dart
@@ -35,7 +35,7 @@
]);
Expect.equals('', result.stderr);
Expect.equals(0, result.exitCode);
- Expect.equals('', result.stdout);
+ Expect.equals('$unsoundNullSafetyMessage\n', result.stdout);
}
{
diff --git a/runtime/tests/vm/dart/snapshot_test_helper.dart b/runtime/tests/vm/dart/snapshot_test_helper.dart
index 1b578b9..28e5c47 100644
--- a/runtime/tests/vm/dart/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart/snapshot_test_helper.dart
@@ -39,7 +39,10 @@
void expectOutput(String what, Result result) {
if (result.output != what) {
- reportError(result, 'Expected test to print \'${what}\' to stdout');
+ reportError(
+ result,
+ 'Expected test to print \'${what}\' to stdout. '
+ 'Actual: ${result.output}');
}
}
@@ -127,6 +130,12 @@
final snapshot1Path = p.join(temp, 'snapshot1');
final snapshot2Path = p.join(temp, 'snapshot2');
+ if (expectedStdout.isEmpty) {
+ expectedStdout = nullSafetyMessage;
+ } else {
+ expectedStdout = '$nullSafetyMessage\n$expectedStdout';
+ }
+
print("Version ${Platform.version}");
final generate1Result = await runDart('GENERATE SNAPSHOT 1', [
@@ -183,8 +192,15 @@
testPath,
'--train'
]);
- expectOutput("OK(Trained)", trainingResult);
+ expectOutput("$nullSafetyMessage\nOK(Trained)", trainingResult);
final runResult = await runSnapshot!(snapshotPath);
expectOutput("OK(Run)", runResult);
});
}
+
+final String nullSafetyMessage =
+ hasSoundNullSafety ? soundNullSafetyMessage : unsoundNullSafetyMessage;
+
+const String soundNullSafetyMessage = 'Info: Compiling with sound null safety';
+const String unsoundNullSafetyMessage =
+ 'Info: Compiling without sound null safety';
diff --git a/runtime/tests/vm/dart_2/sdk_hash_test.dart b/runtime/tests/vm/dart_2/sdk_hash_test.dart
index c73dc03..40f245d 100644
--- a/runtime/tests/vm/dart_2/sdk_hash_test.dart
+++ b/runtime/tests/vm/dart_2/sdk_hash_test.dart
@@ -35,7 +35,7 @@
]);
Expect.equals('', result.stderr);
Expect.equals(0, result.exitCode);
- Expect.equals('', result.stdout);
+ Expect.equals('$unsoundNullSafetyMessage\n', result.stdout);
}
{
diff --git a/runtime/tests/vm/dart_2/snapshot_test_helper.dart b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
index eef877f..ae68005 100644
--- a/runtime/tests/vm/dart_2/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
@@ -39,7 +39,10 @@
void expectOutput(String what, Result result) {
if (result.output != what) {
- reportError(result, 'Expected test to print \'${what}\' to stdout');
+ reportError(
+ result,
+ 'Expected test to print \'${what}\' to stdout. '
+ 'Actual: ${result.output}');
}
}
@@ -127,6 +130,12 @@
final snapshot1Path = p.join(temp, 'snapshot1');
final snapshot2Path = p.join(temp, 'snapshot2');
+ if (expectedStdout.isEmpty) {
+ expectedStdout = unsoundNullSafetyMessage;
+ } else {
+ expectedStdout = '$unsoundNullSafetyMessage\n$expectedStdout';
+ }
+
print("Version ${Platform.version}");
final generate1Result = await runDart('GENERATE SNAPSHOT 1', [
@@ -183,8 +192,11 @@
testPath,
'--train'
]);
- expectOutput("OK(Trained)", trainingResult);
+ expectOutput("$unsoundNullSafetyMessage\nOK(Trained)", trainingResult);
final runResult = await runSnapshot(snapshotPath);
expectOutput("OK(Run)", runResult);
});
}
+
+const String unsoundNullSafetyMessage =
+ 'Info: Compiling without sound null safety';
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 36e4687..2f53e5d 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6094,6 +6094,7 @@
const uint8_t* platform_kernel,
intptr_t platform_kernel_size,
bool incremental_compile,
+ bool snapshot_compile,
const char* package_config) {
API_TIMELINE_DURATION(Thread::Current());
@@ -6102,9 +6103,9 @@
result.status = Dart_KernelCompilationStatus_Unknown;
result.error = Utils::StrDup("Dart_CompileToKernel is unsupported.");
#else
- result = KernelIsolate::CompileToKernel(script_uri, platform_kernel,
- platform_kernel_size, 0, NULL,
- incremental_compile, package_config);
+ result = KernelIsolate::CompileToKernel(
+ script_uri, platform_kernel, platform_kernel_size, 0, NULL,
+ incremental_compile, snapshot_compile, package_config);
if (result.status == Dart_KernelCompilationStatus_Ok) {
Dart_KernelCompilationResult accept_result =
KernelIsolate::AcceptCompilation();
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 3d6a284..433af1e 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -1050,9 +1050,9 @@
{
const char* root_lib_url = root_lib_url_.ToCString();
TransitionVMToNative transition(Thread::Current());
- retval = KernelIsolate::CompileToKernel(root_lib_url, nullptr, 0,
- modified_scripts_count,
- modified_scripts, true, nullptr);
+ retval = KernelIsolate::CompileToKernel(
+ root_lib_url, nullptr, 0, modified_scripts_count, modified_scripts,
+ true, false, nullptr);
}
if (retval.status != Dart_KernelCompilationStatus_Ok) {
if (retval.kernel != nullptr) {
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index 9ab6116..36bfb1b 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -708,6 +708,7 @@
int source_files_count,
Dart_SourceFile source_files[],
bool incremental_compile,
+ bool snapshot_compile,
const char* package_config,
const char* multiroot_filepaths,
const char* multiroot_scheme,
@@ -755,6 +756,10 @@
dart_incremental.type = Dart_CObject_kBool;
dart_incremental.value.as_bool = incremental_compile;
+ Dart_CObject dart_snapshot;
+ dart_snapshot.type = Dart_CObject_kBool;
+ dart_snapshot.value.as_bool = snapshot_compile;
+
// TODO(aam): Assert that isolate exists once we move CompileAndReadScript
// compilation logic out of CreateIsolateAndSetupHelper and into
// IsolateSetupHelper in main.cc.
@@ -857,6 +862,7 @@
&uri,
&dart_platform_kernel,
&dart_incremental,
+ &dart_snapshot,
&null_safety,
&isolate_id,
&files,
@@ -1008,6 +1014,7 @@
int source_file_count,
Dart_SourceFile source_files[],
bool incremental_compile,
+ bool snapshot_compile,
const char* package_config,
const char* multiroot_filepaths,
const char* multiroot_scheme) {
@@ -1033,8 +1040,8 @@
return request.SendAndWaitForResponse(
kCompileTag, kernel_port, script_uri, platform_kernel,
platform_kernel_size, source_file_count, source_files,
- incremental_compile, package_config, multiroot_filepaths,
- multiroot_scheme, experimental_flags_, NULL);
+ incremental_compile, snapshot_compile, package_config,
+ multiroot_filepaths, multiroot_scheme, experimental_flags_, NULL);
}
bool KernelIsolate::DetectNullSafety(const char* script_uri,
@@ -1052,7 +1059,7 @@
KernelCompilationRequest request;
Dart_KernelCompilationResult result = request.SendAndWaitForResponse(
kDetectNullabilityTag, kernel_port, script_uri, nullptr, -1, 0, nullptr,
- false, package_config, nullptr, nullptr, experimental_flags_,
+ false, false, package_config, nullptr, nullptr, experimental_flags_,
original_working_directory);
return result.null_safety;
}
@@ -1068,8 +1075,8 @@
KernelCompilationRequest request;
return request.SendAndWaitForResponse(kListDependenciesTag, kernel_port, NULL,
- NULL, 0, 0, NULL, false, NULL, NULL,
- NULL, experimental_flags_, NULL);
+ NULL, 0, 0, NULL, false, false, NULL,
+ NULL, NULL, experimental_flags_, NULL);
}
Dart_KernelCompilationResult KernelIsolate::AcceptCompilation() {
@@ -1085,7 +1092,7 @@
KernelCompilationRequest request;
return request.SendAndWaitForResponse(kAcceptTag, kernel_port, NULL, NULL, 0,
- 0, NULL, true, NULL, NULL, NULL,
+ 0, NULL, true, false, NULL, NULL, NULL,
experimental_flags_, NULL);
}
@@ -1131,7 +1138,7 @@
KernelCompilationRequest request;
return request.SendAndWaitForResponse(
kUpdateSourcesTag, kernel_port, NULL, NULL, 0, source_files_count,
- source_files, true, NULL, NULL, NULL, experimental_flags_, NULL);
+ source_files, true, false, NULL, NULL, NULL, experimental_flags_, NULL);
}
void KernelIsolate::NotifyAboutIsolateShutdown(const Isolate* isolate) {
diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h
index 0aa5221..f312aee 100644
--- a/runtime/vm/kernel_isolate.h
+++ b/runtime/vm/kernel_isolate.h
@@ -51,6 +51,7 @@
int source_files_count = 0,
Dart_SourceFile source_files[] = NULL,
bool incremental_compile = true,
+ bool snapshot_compile = false,
const char* package_config = NULL,
const char* multiroot_filepaths = NULL,
const char* multiroot_scheme = NULL);
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index e1370f8..a65d61c 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -322,7 +322,8 @@
Zone* zone = Thread::Current()->zone();
Dart_KernelCompilationResult result = KernelIsolate::CompileToKernel(
url, platform_strong_dill, platform_strong_dill_size, sourcefiles_count,
- sourcefiles, incrementally, NULL, multiroot_filepaths, multiroot_scheme);
+ sourcefiles, incrementally, false, NULL, multiroot_filepaths,
+ multiroot_scheme);
if (result.status == Dart_KernelCompilationStatus_Ok) {
if (KernelIsolate::AcceptCompilation().status !=
Dart_KernelCompilationStatus_Ok) {
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart
index d9af93e..ca02b18 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart
@@ -155,11 +155,12 @@
// FutureOr<Never> --> Future<Never>
if (_equalType(typeArg, Never)) {
- return JS('!', '#(#)', getGenericClass(Future), typeArg);
+ return JS('!', '#(#)', getGenericClassStatic<Future>(), typeArg);
}
// FutureOr<Null> --> Future<Null>?
if (_equalType(typeArg, Null)) {
- return nullable(JS('!', '#(#)', getGenericClass(Future), typeArg));
+ return nullable(
+ JS('!', '#(#)', getGenericClassStatic<Future>(), typeArg));
}
// Otherwise, create the FutureOr<T> type as a normal generic type.
var genericType = JS('!', '#(#)', genericFutureOrType, typeArg);
@@ -171,7 +172,8 @@
// Add FutureOr specific is and as methods.
is_FutureOr(obj) =>
JS<bool>('!', '#.is(#)', typeArg, obj) ||
- JS<bool>('!', '#(#).is(#)', getGenericClass(Future), typeArg, obj);
+ JS<bool>(
+ '!', '#(#).is(#)', getGenericClassStatic<Future>(), typeArg, obj);
JS('!', '#.is = #', genericType, is_FutureOr);
as_FutureOr(obj) {
@@ -183,10 +185,12 @@
}
if (JS<bool>('!', '#.is(#)', typeArg, obj) ||
- JS<bool>('!', '#(#).is(#)', getGenericClass(Future), typeArg, obj)) {
+ JS<bool>('!', '#(#).is(#)', getGenericClassStatic<Future>(), typeArg,
+ obj)) {
return obj;
}
- return cast(obj, JS('!', '#(#)', getGenericClass(FutureOr), typeArg));
+ return cast(
+ obj, JS('!', '#(#)', getGenericClassStatic<FutureOr>(), typeArg));
}
JS('!', '#.as = #', genericType, as_FutureOr);
@@ -252,6 +256,19 @@
getGenericClass(type) => safeGetOwnProperty(type, _originalDeclaration);
+/// Extracts the type argument as the accessor for the JS class.
+///
+/// Should be used in place of [getGenericClass] when we know the class we want
+/// statically.
+///
+/// This value is extracted and inlined by the compiler without any runtime
+/// operations. The implementation here is only provided as a theoretical fall
+/// back and shouldn't actually be run.
+///
+/// For example `getGenericClassStatic<FutureOr>` emits `async.FutureOr$`
+/// directly.
+external getGenericClassStatic<T>();
+
// TODO(markzipan): Make this non-nullable if we can ensure this returns
// an empty list or if null and the empty list are semantically the same.
List? getGenericArgs(type) =>
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
index af8e2e1..174b2ff 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
@@ -665,7 +665,7 @@
let value = map.get($elementType);
if (value) return value;
- ${getGenericClass(JSArray)}($elementType).unmodifiable($elements);
+ ${getGenericClassStatic<JSArray>()}($elementType).unmodifiable($elements);
map.set($elementType, elements);
return elements;
})()''');
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
index 7fa6bbe..7874940 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
@@ -1206,7 +1206,7 @@
let args = ${getGenericArgs(type)};
if (args == null) return name;
- if (${getGenericClass(type)} == ${getGenericClass(JSArray)}) name = 'List';
+ if (${getGenericClass(type)} == ${getGenericClassStatic<JSArray>()}) name = 'List';
let result = name;
result += '<';
@@ -1391,7 +1391,7 @@
bool _isFutureOr(type) {
var genericClass = getGenericClass(type);
return JS<bool>('!', '# && # === #', genericClass, genericClass,
- getGenericClass(FutureOr));
+ getGenericClassStatic<FutureOr>());
}
@notNull
@@ -1476,7 +1476,7 @@
// given t1 is Future<A> | A, then:
// (Future<A> | A) <: t2 iff Future<A> <: t2 and A <: t2.
- let t1Future = ${getGenericClass(Future)}(t1TypeArg);
+ let t1Future = ${getGenericClassStatic<Future>()}(t1TypeArg);
// Known to handle the case FutureOr<Null> <: Future<Null>.
return $_isSubtype(t1Future, $t2, $strictMode) &&
$_isSubtype(t1TypeArg, $t2, $strictMode);
@@ -1493,7 +1493,7 @@
// given t2 is Future<A> | A, then:
// t1 <: (Future<A> | A) iff t1 <: Future<A> or t1 <: A
let t2TypeArg = ${getGenericArgs(t2)}[0];
- let t2Future = ${getGenericClass(Future)}(t2TypeArg);
+ let t2Future = ${getGenericClassStatic<Future>()}(t2TypeArg);
// TODO(nshahan) Need to handle type variables on the left.
// https://github.com/dart-lang/sdk/issues/38816
return $_isSubtype($t1, t2Future, $strictMode) || $_isSubtype($t1, t2TypeArg, $strictMode);
@@ -1894,7 +1894,7 @@
// - And `P` is a subtype match for `Q` with respect to `L` under
// constraints `C1`.
var subtypeFuture =
- JS<Object>('!', '#(#)', getGenericClass(Future), subtypeArg);
+ JS<Object>('!', '#(#)', getGenericClassStatic<Future>(), subtypeArg);
return _isSubtypeMatch(subtypeFuture, supertype) &&
_isSubtypeMatch(subtypeArg!, supertype);
}
@@ -1909,8 +1909,8 @@
// - And `P` is a subtype match for `Q` with respect to `L` under
// constraints `C`
var supertypeArg = getGenericArgs(supertype)![0];
- var supertypeFuture =
- JS<Object>('!', '#(#)', getGenericClass(Future), supertypeArg);
+ var supertypeFuture = JS<Object>(
+ '!', '#(#)', getGenericClassStatic<Future>(), supertypeArg);
return _isSubtypeMatch(subtype, supertypeFuture) ||
_isSubtypeMatch(subtype, supertypeArg);
}
diff --git a/sdk/lib/_internal/js_dev_runtime/private/js_array.dart b/sdk/lib/_internal/js_dev_runtime/private/js_array.dart
index 3d5e3d7..a6522bd 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/js_array.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/js_array.dart
@@ -598,7 +598,7 @@
}
Type get runtimeType =>
- dart.wrapType(JS('', '#(#)', dart.getGenericClass(List), E));
+ dart.wrapType(JS('', '#(#)', dart.getGenericClassStatic<List>(), E));
Iterable<E> followedBy(Iterable<E> other) =>
FollowedByIterable<E>.firstEfficient(this, other);
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 317c1fe..c8c1403 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -698,6 +698,80 @@
Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
}
+/// Convenience methods on futures.
+///
+/// Adds functionality to futures which makes it easier to
+/// write well-typed asynchronous code.
+@Since("2.12")
+extension FutureExtensions<T> on Future<T> {
+ /// Handles errors on this future.
+ ///
+ /// Catches errors of type [E] that this future complete with.
+ /// If [test] is supplied, only catches errors of type [E]
+ /// where [test] returns `true`.
+ /// If [E] is [Object], then all errors are potentially caught,
+ /// depending only on a supplied [test].toString()
+ ///
+ /// If the error is caught,
+ /// the returned future completes with the result of calling [handleError]
+ /// with the error and stack trace.
+ /// This result must be a value of the same type that this future
+ /// could otherwise complete with.
+ /// For example, if this future cannot complete with `null`,
+ /// then [handleError] also cannot return `null`.
+ /// Example:
+ /// ```dart
+ /// Future<T> retryOperation<T>(Future<T> operation(), T onFailure()) =>
+ /// operation().onError<RetryException>((e, s) {
+ /// if (e.canRetry) {
+ /// return retryOperation(operation, onFailure);
+ /// }
+ /// return onFailure();
+ /// });
+ /// ```
+ ///
+ /// If [handleError] throws, the returned future completes
+ /// with the thrown error and stack trace,
+ /// except that if it throws the *same* error object again,
+ /// then it is considered a "rethrow"
+ /// and the original stack trace is retained.
+ /// This can be used as an alternative to skipping the
+ /// error in [test].
+ /// Example:
+ /// ```dart
+ /// // Unwraps an an exceptions cause, if it has one.
+ /// someFuture.onError<SomeException>((e, _) {
+ /// throw e.cause ?? e;
+ /// });
+ /// // vs.
+ /// someFuture.onError<SomeException>((e, _) {
+ /// throw e.cause!;
+ /// }, test: (e) => e.cause != null);
+ /// ```
+ ///
+ /// If the error is not caught, the returned future
+ /// completes with the same result, value or error,
+ /// as this future.
+ ///
+ /// This method is effectively a more precisely typed version
+ /// of [Future.catchError].
+ /// It makes it easy to catch specific error types,
+ /// and requires a correctly typed error handler function,
+ /// rather than just [Function].
+ /// Because of this, the error handlers must accept
+ /// the stack trace argument.
+ Future<T> onError<E extends Object>(
+ FutureOr<T> handleError(E error, StackTrace stackTrace),
+ {bool test(E error)?}) {
+ // There are various ways to optimize this to avoid the double is E/as E
+ // type check, but for now we are not optimizing the error path.
+ return this.catchError(
+ (Object error, StackTrace stackTrace) =>
+ handleError(error as E, stackTrace),
+ test: (Object error) => error is E && (test == null || test(error)));
+ }
+}
+
/// Thrown when a scheduled timeout happens while waiting for an async result.
class TimeoutException implements Exception {
/// Description of the cause of the timeout.
diff --git a/sdk/lib/core/core.dart b/sdk/lib/core/core.dart
index e0b0124..7e96d5c 100644
--- a/sdk/lib/core/core.dart
+++ b/sdk/lib/core/core.dart
@@ -168,6 +168,8 @@
@Since("2.1")
export "dart:async" show Future, Stream;
+@Since("2.12")
+export "dart:async" show FutureExtensions;
part "annotations.dart";
part "bigint.dart";
diff --git a/tests/language/constants_2018/constant_type_literal_test.dart b/tests/language/constants_2018/constant_type_literal_test.dart
index b886d77..924bcab 100644
--- a/tests/language/constants_2018/constant_type_literal_test.dart
+++ b/tests/language/constants_2018/constant_type_literal_test.dart
@@ -8,7 +8,7 @@
import "dart:core" as core;
// No reloading support for deferred loading.
// See https://github.com/dart-lang/sdk/issues/33118.
-import "dart:core" deferred as dcore; //# 01: compile-time error
+import "dart:core" deferred as dcore show int //# 01: compile-time error
// Declares F function type alias, M mixin and C class.
import "constant_type_literal_types.dart";
diff --git a/tests/language/deferred/import_core_test.dart b/tests/language/deferred/import_core_test.dart
index f3d6644..3c72766 100644
--- a/tests/language/deferred/import_core_test.dart
+++ b/tests/language/deferred/import_core_test.dart
@@ -5,7 +5,7 @@
// Nothing in the language spec explicitly prohibits a deferred import of
// 'dart:core'. Make sure it doesn't lead to any strange behavior.
-import "dart:core" deferred as core;
+import "dart:core" deferred as core show Object;
main() {
core.loadLibrary().then((_) => null);
diff --git a/tests/language_2/constants_2018/constant_type_literal_test.dart b/tests/language_2/constants_2018/constant_type_literal_test.dart
index b886d77..99a26db 100644
--- a/tests/language_2/constants_2018/constant_type_literal_test.dart
+++ b/tests/language_2/constants_2018/constant_type_literal_test.dart
@@ -8,7 +8,7 @@
import "dart:core" as core;
// No reloading support for deferred loading.
// See https://github.com/dart-lang/sdk/issues/33118.
-import "dart:core" deferred as dcore; //# 01: compile-time error
+import "dart:core" deferred as dcore show int; //# 01: compile-time error
// Declares F function type alias, M mixin and C class.
import "constant_type_literal_types.dart";
diff --git a/tests/language_2/deferred/import_core_test.dart b/tests/language_2/deferred/import_core_test.dart
index f3d6644..3c72766 100644
--- a/tests/language_2/deferred/import_core_test.dart
+++ b/tests/language_2/deferred/import_core_test.dart
@@ -5,7 +5,7 @@
// Nothing in the language spec explicitly prohibits a deferred import of
// 'dart:core'. Make sure it doesn't lead to any strange behavior.
-import "dart:core" deferred as core;
+import "dart:core" deferred as core show Object;
main() {
core.loadLibrary().then((_) => null);
diff --git a/tests/lib/async/catch_errors.dart b/tests/lib/async/catch_errors.dart
index 02c403c..f2a5bc4 100644
--- a/tests/lib/async/catch_errors.dart
+++ b/tests/lib/async/catch_errors.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2013, 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.
+
library catch_errors;
import 'dart:async';
diff --git a/tests/lib/async/future_onerror_test.dart b/tests/lib/async/future_onerror_test.dart
new file mode 100644
index 0000000..ea5ffab
--- /dev/null
+++ b/tests/lib/async/future_onerror_test.dart
@@ -0,0 +1,115 @@
+// Copyright (c) 2020, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+
+main() {
+ var stack = StackTrace.fromString("for testing");
+ var error = Object();
+ var stateError = StateError("test");
+
+ asyncStart();
+
+ {
+ // Match any error.
+ asyncStart();
+ var future = Future.error(error, stack);
+ future.onError((e, s) {
+ Expect.identical(error, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ });
+ }
+
+ {
+ // With matching test.
+ asyncStart();
+ var future = Future.error(error, stack);
+ future.onError((e, s) {
+ Expect.identical(error, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ }, test: (o) => true);
+ }
+
+ {
+ // With failing test.
+ asyncStart();
+ var future = Future.error(error, stack);
+ var onErrorFuture = future.onError((e, s) {
+ Expect.fail('unreachable');
+ }, test: (o) => false);
+ onErrorFuture.catchError((e, s) {
+ Expect.identical(error, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ });
+ }
+
+ {
+ // With matching type.
+ asyncStart();
+ var future = Future.error(stateError, stack);
+ future.onError<StateError>((e, s) {
+ Expect.identical(stateError, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ });
+ }
+
+ {
+ // With non-matching type.
+ asyncStart();
+ var future = Future.error(stateError, stack);
+ var onErrorFuture = future.onError<ArgumentError>((e, s) {
+ Expect.fail('unreachable');
+ });
+ onErrorFuture.catchError((e, s) {
+ Expect.identical(stateError, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ });
+ }
+
+ {
+ // With non-matching type and matching test.
+ asyncStart();
+ var future = Future.error(stateError, stack);
+ var onErrorFuture = future.onError<ArgumentError>((e, s) {
+ Expect.fail('unreachable');
+ }, test: (ArgumentError e) => true);
+ onErrorFuture.catchError((e, s) {
+ Expect.identical(stateError, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ });
+ }
+
+ {
+ // With matching type and matching test.
+ asyncStart();
+ var future = Future.error(stateError, stack);
+ future.onError<StateError>((e, s) {
+ Expect.identical(stateError, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ }, test: (StateError e) => true);
+ }
+
+ {
+ // With matching type and non-matching test.
+ asyncStart();
+ var future = Future.error(stateError, stack);
+ var onErrorFuture = future.onError<StateError>((e, s) {
+ Expect.fail('unreachable');
+ }, test: (StateError e) => false);
+ onErrorFuture.catchError((e, s) {
+ Expect.identical(stateError, e);
+ Expect.identical(stack, s);
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tools/VERSION b/tools/VERSION
index b96128e..a9ef1ca 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 250
+PRERELEASE 251
PRERELEASE_PATCH 0
\ No newline at end of file