Version 2.17.0-242.0.dev
Merge commit '2a1dba465c4e38106702e0108c223fb15190bb77' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart
new file mode 100644
index 0000000..818a11c
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToCascade extends CorrectionProducer {
+ @override
+ FixKind get fixKind => DartFixKind.CONVERT_TO_CASCADE;
+
+ @override
+ Future<void> compute(ChangeBuilder builder) async {
+ var node = this.node;
+ if (node is! ExpressionStatement) return;
+
+ var block = node.parent;
+ if (block is! Block) return;
+
+ var previous = _getPrevious(block, node);
+ if (previous is! ExpressionStatement) return;
+ var previousOperator = _getTargetAndOperator(previous.expression)?.operator;
+
+ var expression = node.expression;
+ var target = _getTargetAndOperator(expression)?.target;
+ if (target == null) return;
+
+ var targetReplacement = expression is CascadeExpression ? '' : '.';
+
+ await builder.addDartFileEdit(file, (builder) {
+ if (previousOperator != null) {
+ builder.addSimpleInsertion(previousOperator.offset, '.');
+ }
+ builder.addDeletion(range.token(previous.semicolon!));
+ builder.addSimpleReplacement(range.node(target), targetReplacement);
+ });
+ }
+
+ Statement? _getPrevious(Block block, Statement statement) {
+ var statements = block.statements;
+ var index = statements.indexOf(statement);
+ return index > 0 ? statements[index - 1] : null;
+ }
+
+ _TargetAndOperator? _getTargetAndOperator(Expression expression) {
+ if (expression is AssignmentExpression) {
+ var lhs = expression.leftHandSide;
+ if (lhs is PrefixedIdentifier) {
+ return _TargetAndOperator(lhs.prefix, lhs.period);
+ }
+ } else if (expression is MethodInvocation) {
+ return _TargetAndOperator(expression.target, expression.operator);
+ } else if (expression is CascadeExpression) {
+ return _TargetAndOperator(expression.target, null);
+ }
+ return null;
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+ static ConvertToCascade newInstance() => ConvertToCascade();
+}
+
+class _TargetAndOperator {
+ AstNode? target;
+ Token? operator;
+ _TargetAndOperator(this.target, this.operator);
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
index c571123..3fd0c99 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
@@ -5,6 +5,7 @@
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -61,12 +62,22 @@
List<Object> get fixArguments {
var buffer = StringBuffer();
buffer.write('super');
- var constructorName = _constructor.displayName;
- if (constructorName.isNotEmpty) {
- buffer.write('.');
- buffer.write(constructorName);
+ var constructorName = _constructor.name;
+ if (libraryElement.featureSet.isEnabled(Feature.super_parameters)) {
+ if (constructorName.isNotEmpty) {
+ buffer.write('.');
+ buffer.write(constructorName);
+ buffer.write('()');
+ } else {
+ buffer.write('.');
+ }
+ } else {
+ if (constructorName.isNotEmpty) {
+ buffer.write('.');
+ buffer.write(constructorName);
+ }
+ buffer.write('(...)');
}
- buffer.write('(...)');
return [buffer.toString()];
}
@@ -75,6 +86,14 @@
@override
Future<void> compute(ChangeBuilder builder) async {
+ if (libraryElement.featureSet.isEnabled(Feature.super_parameters)) {
+ await _computeWithSuperParameters(builder);
+ } else {
+ await _computeWithoutSuperParameters(builder);
+ }
+ }
+
+ Future<void> _computeWithoutSuperParameters(ChangeBuilder builder) async {
var constructorName = _constructor.name;
var requiredPositionalParameters = _constructor.parameters
.where((parameter) => parameter.isRequiredPositional);
@@ -154,4 +173,70 @@
});
});
}
+
+ Future<void> _computeWithSuperParameters(ChangeBuilder builder) async {
+ var constructorName = _constructor.name;
+ var requiredPositionalParameters = _constructor.parameters
+ .where((parameter) => parameter.isRequiredPositional);
+ var requiredNamedParameters =
+ _constructor.parameters.where((parameter) => parameter.isRequiredNamed);
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addInsertion(_targetLocation.offset, (builder) {
+ void writeParameter(ParameterElement parameter) {
+ var parameterName = parameter.displayName;
+
+ if (parameterName.length > 1 && parameterName.startsWith('_')) {
+ parameterName = parameterName.substring(1);
+ }
+
+ if (parameter.isRequiredNamed) {
+ builder.write('required ');
+ }
+
+ builder.write('super.');
+ builder.write(parameterName);
+ }
+
+ builder.write(_targetLocation.prefix);
+ builder.write(_targetClassName);
+ if (constructorName.isNotEmpty) {
+ builder.write('.');
+ builder.addSimpleLinkedEdit('NAME', constructorName);
+ }
+ builder.write('(');
+
+ var firstParameter = true;
+ void writeComma() {
+ if (firstParameter) {
+ firstParameter = false;
+ } else {
+ builder.write(', ');
+ }
+ }
+
+ for (var parameter in requiredPositionalParameters) {
+ writeComma();
+ writeParameter(parameter);
+ }
+ if (requiredNamedParameters.isNotEmpty) {
+ writeComma();
+ firstParameter = true; // Reset since we just included a comma.
+ builder.write('{');
+ for (var parameter in requiredNamedParameters) {
+ writeComma();
+ writeParameter(parameter);
+ }
+ builder.write('}');
+ }
+
+ if (constructorName.isNotEmpty) {
+ builder.write(') : super.');
+ builder.addSimpleLinkedEdit('NAME', constructorName);
+ builder.write('(');
+ }
+ builder.write(');');
+ builder.write(_targetLocation.suffix);
+ });
+ });
+ }
}
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 1ea1e2e..1be33f8 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1587,7 +1587,7 @@
LintCode.cancel_subscriptions:
status: needsEvaluation
LintCode.cascade_invocations:
- status: needsEvaluation
+ status: hasFix
LintCode.cast_nullable_to_non_nullable:
status: needsEvaluation
LintCode.close_sinks:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 94f206f..a9b52c3 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -373,6 +373,11 @@
DartFixKindPriority.IN_FILE,
'Convert the quotes and remove escapes everywhere in file',
);
+ static const CONVERT_TO_CASCADE = FixKind(
+ 'dart.fix.convert.toCascade',
+ DartFixKindPriority.DEFAULT,
+ 'Convert to cascade notation',
+ );
static const CONVERT_TO_CONTAINS = FixKind(
'dart.fix.convert.toContains',
DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 9fb3607..7239a67 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -48,6 +48,7 @@
import 'package:analysis_server/src/services/correction/dart/convert_into_is_not.dart';
import 'package:analysis_server/src/services/correction/dart/convert_map_from_iterable_to_for_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_quotes.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_cascade.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_contains.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_expression_function_body.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_for_loop.dart';
@@ -424,6 +425,9 @@
LintNames.await_only_futures: [
RemoveAwait.newInstance,
],
+ LintNames.cascade_invocations: [
+ ConvertToCascade.newInstance,
+ ],
LintNames.curly_braces_in_flow_control_structures: [
UseCurlyBraces.newInstance,
],
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index 60448e7..fe9d319 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -45,6 +45,7 @@
'avoid_unnecessary_containers';
static const String avoid_void_async = 'avoid_void_async';
static const String await_only_futures = 'await_only_futures';
+ static const String cascade_invocations = 'cascade_invocations';
static const String curly_braces_in_flow_control_structures =
'curly_braces_in_flow_control_structures';
static const String diagnostic_describe_all_properties =
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_cascade_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_cascade_test.dart
new file mode 100644
index 0000000..66c1a2a
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_cascade_test.dart
@@ -0,0 +1,169 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ConvertToCascadeTest);
+ });
+}
+
+@reflectiveTest
+class ConvertToCascadeTest extends FixProcessorLintTest {
+ @override
+ FixKind get kind => DartFixKind.CONVERT_TO_CASCADE;
+
+ @override
+ String get lintCode => LintNames.cascade_invocations;
+
+ Future<void> test_cascade_method() async {
+ await resolveTestCode('''
+class A {
+ void m() {}
+ int? x;
+}
+
+void f(A a) {
+ a..x = 1
+ ..x = 2;
+ a..m();
+}
+''');
+ await assertHasFix('''
+class A {
+ void m() {}
+ int? x;
+}
+
+void f(A a) {
+ a..x = 1
+ ..x = 2
+ ..m();
+}
+''');
+ }
+
+ Future<void> test_method_method() async {
+ await resolveTestCode('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a.m();
+ a.m();
+}
+''');
+ await assertHasFix('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a..m()
+ ..m();
+}
+''');
+ }
+
+ Future<void> test_method_property() async {
+ await resolveTestCode('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a.m();
+ a.x = 1;
+}
+''');
+ await assertHasFix('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a..m()
+ ..x = 1;
+}
+''');
+ }
+
+ Future<void> test_property_cascade() async {
+ await resolveTestCode('''
+class A {
+ void m() {}
+ int? x;
+}
+
+void f(A a) {
+ a.x = 1;
+ a..m();
+}
+''');
+ await assertHasFix('''
+class A {
+ void m() {}
+ int? x;
+}
+
+void f(A a) {
+ a..x = 1
+ ..m();
+}
+''');
+ }
+
+ Future<void> test_property_method() async {
+ await resolveTestCode('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a.x = 1;
+ a.m();
+}
+''');
+ await assertHasFix('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a..x = 1
+ ..m();
+}
+''');
+ }
+
+ Future<void> test_property_property() async {
+ await resolveTestCode('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a.x = 1;
+ a.x = 2;
+}
+''');
+ await assertHasFix('''
+class A {
+ void m() {}
+ int? x;
+}
+void f(A a) {
+ a..x = 1
+ ..x = 2;
+}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
index 6095520..d5a3275 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
@@ -12,6 +12,7 @@
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(CreateConstructorSuperTest);
+ defineReflectiveTests(CreateConstructorSuperWithoutSuperParametersTest);
});
}
@@ -42,18 +43,271 @@
class B extends A {
int existingField = 0;
- B(int field) : super(field);
+ B(super.field);
+
+ void existingMethod() {}
+}
+''', matchFixMessage: 'Create constructor to call super.');
+ }
+
+ Future<void> test_importType() async {
+ addSource('$testPackageLibPath/a.dart', '''
+class A {}
+''');
+ addSource('$testPackageLibPath/b.dart', '''
+import 'package:test/a.dart';
+
+class B {
+ B(A a);
+}
+''');
+ await resolveTestCode('''
+import 'package:test/b.dart';
+
+class C extends B {
+}
+''');
+ await assertHasFix('''
+import 'package:test/b.dart';
+
+class C extends B {
+ C(super.a);
+}
+''');
+ }
+
+ Future<void> test_lint_sortConstructorsFirst() async {
+ createAnalysisOptionsFile(lints: [LintNames.sort_constructors_first]);
+ await resolveTestCode('''
+class A {
+ A(this.field);
+
+ int field;
+}
+class B extends A {
+ int existingField = 0;
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ A(this.field);
+
+ int field;
+}
+class B extends A {
+ B(super.field);
+
+ int existingField = 0;
+ void existingMethod() {}
+}
+''');
+ }
+
+ Future<void> test_namedConstructor() async {
+ await resolveTestCode('''
+class A {
+ A.named(p1, int p2);
+}
+class B extends A {
+ int existingField = 0;
+
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ A.named(p1, int p2);
+}
+class B extends A {
+ int existingField = 0;
+
+ B.named(super.p1, super.p2) : super.named();
+
+ void existingMethod() {}
+}
+''', matchFixMessage: 'Create constructor to call super.named()');
+ }
+
+ Future<void> test_namedOptionalParams() async {
+ await resolveTestCode('''
+class A {
+ A(p1, int p2, List<String> p3, {int? p4});
+}
+class B extends A {
+ int existingField = 0;
+
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ A(p1, int p2, List<String> p3, {int? p4});
+}
+class B extends A {
+ int existingField = 0;
+
+ B(super.p1, super.p2, super.p3);
void existingMethod() {}
}
''');
}
+ Future<void> test_namedRequiredParams() async {
+ await resolveTestCode('''
+class A {
+ A(p1, int p2, List<String> p3, {required int p4, required int p5});
+}
+class B extends A {
+ int existingField = 0;
+
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ A(p1, int p2, List<String> p3, {required int p4, required int p5});
+}
+class B extends A {
+ int existingField = 0;
+
+ B(super.p1, super.p2, super.p3, {required super.p4, required super.p5});
+
+ void existingMethod() {}
+}
+''');
+ }
+
+ Future<void> test_optional() async {
+ await resolveTestCode('''
+class A {
+ A(p1, int p2, List<String> p3, [int p4 = 0]);
+}
+class B extends A {
+ int existingField = 0;
+
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ A(p1, int p2, List<String> p3, [int p4 = 0]);
+}
+class B extends A {
+ int existingField = 0;
+
+ B(super.p1, super.p2, super.p3);
+
+ void existingMethod() {}
+}
+''');
+ }
+
+ Future<void> test_private() async {
+ await resolveTestCode('''
+class A {
+ A._named(p);
+}
+class B extends A {
+}
+''');
+ await assertNoFix();
+ }
+
+ Future<void> test_typeArgument() async {
+ await resolveTestCode('''
+class C<T> {
+ final T x;
+ C(this.x);
+}
+class D extends C<int> {
+}
+''');
+ await assertHasFix('''
+class C<T> {
+ final T x;
+ C(this.x);
+}
+class D extends C<int> {
+ D(super.x);
+}
+''');
+ }
+
+ Future<void> test_underscore() async {
+ await resolveTestCode('''
+class A {
+ int _;
+ A(this._);
+ int get field => _;
+}
+class B extends A {
+ int existingField = 0;
+
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ int _;
+ A(this._);
+ int get field => _;
+}
+class B extends A {
+ int existingField = 0;
+
+ B(super._);
+
+ void existingMethod() {}
+}
+''');
+ }
+}
+
+@reflectiveTest
+class CreateConstructorSuperWithoutSuperParametersTest
+ extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.CREATE_CONSTRUCTOR_SUPER;
+
+ @override
+ String get testPackageLanguageVersion => '2.16';
+
+ Future<void> test_fieldInitializer() async {
+ await resolveTestCode('''
+class A {
+ int _field;
+ A(this._field);
+ int get field => _field;
+}
+class B extends A {
+ int existingField = 0;
+
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ int _field;
+ A(this._field);
+ int get field => _field;
+}
+class B extends A {
+ int existingField = 0;
+
+ B(int field) : super(field);
+
+ void existingMethod() {}
+}
+''', matchFixMessage: 'Create constructor to call super(...)');
+ }
+
Future<void> test_importType() async {
- addSource('$testPackageLibPath/a.dart', r'''
+ addSource('$testPackageLibPath/a.dart', '''
class A {}
''');
- addSource('$testPackageLibPath/b.dart', r'''
+ addSource('$testPackageLibPath/b.dart', '''
import 'package:test/a.dart';
class B {
@@ -126,7 +380,7 @@
void existingMethod() {}
}
-''');
+''', matchFixMessage: 'Create constructor to call super.named(...)');
}
Future<void> test_namedOptionalParams() async {
@@ -222,7 +476,8 @@
C(this.x);
}
class D extends C<int> {
-}''');
+}
+''');
await assertHasFix('''
class C<T> {
final T x;
@@ -230,6 +485,36 @@
}
class D extends C<int> {
D(int x) : super(x);
-}''');
+}
+''');
+ }
+
+ Future<void> test_underscore() async {
+ await resolveTestCode('''
+class A {
+ int _;
+ A(this._);
+ int get field => _;
+}
+class B extends A {
+ int existingField = 0;
+
+ void existingMethod() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ int _;
+ A(this._);
+ int get field => _;
+}
+class B extends A {
+ int existingField = 0;
+
+ B(int _) : super(_);
+
+ void existingMethod() {}
+}
+''');
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 8bd5f64..4f1543a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -58,6 +58,7 @@
import 'convert_into_expression_body_test.dart' as convert_into_expression_body;
import 'convert_into_is_not_test.dart' as convert_into_is_not;
import 'convert_quotes_test.dart' as convert_quotes;
+import 'convert_to_cascade_test.dart' as convert_to_cascade;
import 'convert_to_contains_test.dart' as convert_to_contains;
import 'convert_to_double_quoted_string_test.dart'
as convert_to_double_quoted_string;
@@ -274,6 +275,7 @@
convert_into_expression_body.main();
convert_into_is_not.main();
convert_quotes.main();
+ convert_to_cascade.main();
convert_to_contains.main();
convert_to_double_quoted_string.main();
convert_to_for_element.main();
diff --git a/pkg/analyzer/lib/dart/analysis/features.dart b/pkg/analyzer/lib/dart/analysis/features.dart
index bb407f0..211cc17 100644
--- a/pkg/analyzer/lib/dart/analysis/features.dart
+++ b/pkg/analyzer/lib/dart/analysis/features.dart
@@ -37,6 +37,9 @@
/// Feature information for generic metadata.
static final generic_metadata = ExperimentalFeatures.generic_metadata;
+ /// Feature information for inference-update-1.
+ static final inference_update_1 = ExperimentalFeatures.inference_update_1;
+
/// Feature information for macros.
static final macros = ExperimentalFeatures.macros;
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
index 6867d3b..52fb27d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
@@ -163,7 +163,7 @@
List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo =
_isIdentical(node) ? [] : null;
- _visitArguments(
+ var deferredClosures = _visitArguments(
resolver: resolver,
node: node,
argumentList: argumentList,
@@ -173,6 +173,18 @@
identicalInfo: identicalInfo,
substitution: substitution,
inferrer: inferrer);
+ if (deferredClosures != null) {
+ _resolveDeferredClosures(
+ resolver: resolver,
+ node: node,
+ argumentList: argumentList,
+ rawType: rawType,
+ contextType: contextType,
+ deferredClosures: deferredClosures,
+ identicalInfo: identicalInfo,
+ substitution: substitution,
+ inferrer: inferrer);
+ }
if (inferrer != null) {
typeArgumentTypes = inferrer.upwardsInfer();
@@ -335,13 +347,22 @@
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList,
}) {
- _visitArguments(
+ var deferredClosures = _visitArguments(
resolver: resolver,
node: node,
argumentList: argumentList,
rawType: rawType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
+ if (deferredClosures != null) {
+ _resolveDeferredClosures(
+ resolver: resolver,
+ node: node,
+ argumentList: argumentList,
+ rawType: rawType,
+ contextType: contextType,
+ deferredClosures: deferredClosures);
+ }
}
/// Computes the type context that should be used when evaluating a particular
@@ -371,10 +392,49 @@
}
}
+ /// Resolves any closures that were deferred by [_visitArguments].
+ void _resolveDeferredClosures(
+ {required ResolverVisitor resolver,
+ required Node node,
+ required ArgumentListImpl argumentList,
+ required FunctionType? rawType,
+ required DartType? contextType,
+ required List<_DeferredClosure> deferredClosures,
+ List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
+ Substitution? substitution,
+ GenericInferrer? inferrer}) {
+ var flow = resolver.flowAnalysis.flow;
+ var arguments = argumentList.arguments;
+ for (var deferredArgument in deferredClosures) {
+ var parameter = deferredArgument.parameter;
+ DartType? parameterContextType;
+ if (parameter != null) {
+ var parameterType = parameter.type;
+ if (substitution != null) {
+ parameterType = substitution.substituteType(parameterType);
+ }
+ parameterContextType = _computeContextForArgument(
+ resolver, node, parameterType, contextType);
+ }
+ var argument = arguments[deferredArgument.index];
+ resolver.analyzeExpression(argument, parameterContextType);
+ // In case of rewrites, we need to grab the argument again.
+ argument = arguments[deferredArgument.index];
+ if (flow != null) {
+ identicalInfo?[deferredArgument.index] =
+ flow.equalityOperand_end(argument, argument.typeOrThrow);
+ }
+ if (parameter != null) {
+ inferrer?.constrainArgument(
+ argument.typeOrThrow, parameter.type, parameter.name);
+ }
+ }
+ }
+
/// Visits [argumentList], resolving each argument. If any arguments need to
/// be deferred due to the `inference-update-1` feature, a list of them is
/// returned.
- void _visitArguments(
+ List<_DeferredClosure>? _visitArguments(
{required ResolverVisitor resolver,
required Node node,
required ArgumentListImpl argumentList,
@@ -385,6 +445,7 @@
Substitution? substitution,
GenericInferrer? inferrer}) {
assert(whyNotPromotedList.isEmpty);
+ List<_DeferredClosure>? deferredClosures;
var parameters = rawType?.parameters;
var namedParameters = <String, ParameterElement>{};
if (parameters != null) {
@@ -401,40 +462,56 @@
var arguments = argumentList.arguments;
for (int i = 0; i < arguments.length; i++) {
var argument = arguments[i];
+ Expression value;
ParameterElement? parameter;
if (argument is NamedExpression) {
+ value = argument.expression;
parameter = namedParameters[argument.name.label.name];
- } else if (parameters != null) {
- while (positionalParameterIndex < parameters.length) {
- var candidate = parameters[positionalParameterIndex++];
- if (!candidate.isNamed) {
- parameter = candidate;
- break;
+ } else {
+ value = argument;
+ if (parameters != null) {
+ while (positionalParameterIndex < parameters.length) {
+ var candidate = parameters[positionalParameterIndex++];
+ if (!candidate.isNamed) {
+ parameter = candidate;
+ break;
+ }
}
}
}
- DartType? parameterContextType;
- if (parameter != null) {
- var parameterType = parameter.type;
- if (substitution != null) {
- parameterType = substitution.substituteType(parameterType);
+ if (resolver.isInferenceUpdate1Enabled &&
+ value is FunctionExpressionImpl) {
+ (deferredClosures ??= []).add(_DeferredClosure(parameter, value, i));
+ identicalInfo?.add(null);
+ // The "why not promoted" list isn't really relevant for closures
+ // because promoting a closure doesn't even make sense. So we store an
+ // innocuous value in the list.
+ whyNotPromotedList.add(() => const {});
+ } else {
+ DartType? parameterContextType;
+ if (parameter != null) {
+ var parameterType = parameter.type;
+ if (substitution != null) {
+ parameterType = substitution.substituteType(parameterType);
+ }
+ parameterContextType = _computeContextForArgument(
+ resolver, node, parameterType, contextType);
}
- parameterContextType = _computeContextForArgument(
- resolver, node, parameterType, contextType);
- }
- resolver.analyzeExpression(argument, parameterContextType);
- // In case of rewrites, we need to grab the argument again.
- argument = arguments[i];
- if (flow != null) {
- identicalInfo
- ?.add(flow.equalityOperand_end(argument, argument.typeOrThrow));
- whyNotPromotedList.add(flow.whyNotPromoted(argument));
- }
- if (parameter != null) {
- inferrer?.constrainArgument(
- argument.typeOrThrow, parameter.type, parameter.name);
+ resolver.analyzeExpression(argument, parameterContextType);
+ // In case of rewrites, we need to grab the argument again.
+ argument = arguments[i];
+ if (flow != null) {
+ identicalInfo
+ ?.add(flow.equalityOperand_end(argument, argument.typeOrThrow));
+ whyNotPromotedList.add(flow.whyNotPromoted(argument));
+ }
+ if (parameter != null) {
+ inferrer?.constrainArgument(
+ argument.typeOrThrow, parameter.type, parameter.name);
+ }
}
}
+ return deferredClosures;
}
/// Computes the return type of the method or function represented by the
@@ -498,3 +575,19 @@
return returnType;
}
}
+
+/// Information about an invocation argument that needs to be resolved later due
+/// to the fact that it's a closure and the `inference-update-1` feature is
+/// enabled.
+class _DeferredClosure {
+ /// The [ParameterElement] the closure is being passed to.
+ final ParameterElement? parameter;
+
+ /// The closure expression.
+ final FunctionExpression value;
+
+ /// The index into the argument list of the closure expression.
+ final int index;
+
+ _DeferredClosure(this.parameter, this.value, this.index);
+}
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 94c717d..64e21e7 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -1657,6 +1657,7 @@
"Concrete classes can't have 'Enum' as a superinterface.",
correctionMessage:
"Try specifying a different interface, or remove it from the list.",
+ hasPublishedDocs: true,
);
/**
@@ -3850,6 +3851,7 @@
'ENUM_CONSTANT_WITH_NON_CONST_CONSTRUCTOR',
"The invoked constructor isn't a 'const' constructor.",
correctionMessage: "Try invoking a 'const' generative constructor.",
+ hasPublishedDocs: true,
);
static const CompileTimeErrorCode
@@ -3908,6 +3910,7 @@
'ENUM_MIXIN_WITH_INSTANCE_VARIABLE',
"Mixins applied to enums can't have instance variables.",
correctionMessage: "Try replacing the instance variables with getters.",
+ hasPublishedDocs: true,
);
/**
@@ -3950,6 +3953,7 @@
'ENUM_WITH_ABSTRACT_MEMBER',
"'{0}' must have a method body because '{1}' is an enum.",
correctionMessage: "Try adding a body to '{0}'.",
+ hasPublishedDocs: true,
);
// #### Description
@@ -3977,6 +3981,7 @@
'ENUM_WITH_NAME_VALUES',
"The name 'values' is not a valid name for an enum.",
correctionMessage: "Try using a different name.",
+ hasPublishedDocs: true,
);
/**
@@ -6182,6 +6187,7 @@
"A concrete instance member named '{0}' can't be declared in a class that "
"implements 'Enum'.",
correctionMessage: "Try using a different name.",
+ hasPublishedDocs: true,
uniqueName: 'ILLEGAL_CONCRETE_ENUM_MEMBER_DECLARATION',
);
@@ -6196,6 +6202,7 @@
"A concrete instance member named '{0}' can't be inherited from '{1}' in a "
"class that implements 'Enum'.",
correctionMessage: "Try using a different name.",
+ hasPublishedDocs: true,
uniqueName: 'ILLEGAL_CONCRETE_ENUM_MEMBER_INHERITANCE',
);
@@ -6242,6 +6249,7 @@
"An instance member named 'values' can't be declared in a class that "
"implements 'Enum'.",
correctionMessage: "Try using a different name.",
+ hasPublishedDocs: true,
uniqueName: 'ILLEGAL_ENUM_VALUES_DECLARATION',
);
@@ -6255,6 +6263,7 @@
"An instance member named 'values' can't be inherited from '{0}' in a "
"class that implements 'Enum'.",
correctionMessage: "Try using a different name.",
+ hasPublishedDocs: true,
uniqueName: 'ILLEGAL_ENUM_VALUES_INHERITANCE',
);
@@ -6565,6 +6574,7 @@
"parameters.",
correctionMessage:
"Try adding an explicit super parameter with the required arguments.",
+ hasPublishedDocs: true,
);
/**
@@ -8265,6 +8275,7 @@
'INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR',
"Generative enum constructors can only be used as targets of redirection.",
correctionMessage: "Try using an enum constant, or a factory constructor.",
+ hasPublishedDocs: true,
);
/**
@@ -8396,6 +8407,7 @@
correctionMessage:
"Try removing the 'super' modifier, or changing the constructor to be "
"non-redirecting and generative.",
+ hasPublishedDocs: true,
);
/**
@@ -11195,6 +11207,7 @@
'NON_CONST_GENERATIVE_ENUM_CONSTRUCTOR',
"Generative enum constructors must be 'const'.",
correctionMessage: "Try adding the keyword 'const'.",
+ hasPublishedDocs: true,
);
/**
@@ -11255,6 +11268,7 @@
'NON_FINAL_FIELD_IN_ENUM',
"Enums can only declare final fields.",
correctionMessage: "Try making the field final.",
+ hasPublishedDocs: true,
);
/**
@@ -12778,6 +12792,7 @@
correctionMessage:
"Try making all the positional parameters passed to the super "
"constructor be either all super parameters or all normal parameters.",
+ hasPublishedDocs: true,
);
/**
@@ -14507,6 +14522,7 @@
"associated super constructor parameter.",
correctionMessage:
"Try removing the explicit type annotation from the parameter.",
+ hasPublishedDocs: true,
);
/**
@@ -14584,6 +14600,7 @@
correctionMessage:
"Try changing the name to the name of an existing named super "
"constructor parameter, or creating such named parameter.",
+ hasPublishedDocs: true,
);
/**
@@ -14667,6 +14684,7 @@
correctionMessage:
"Try using a normal parameter, or adding more positional parameters to "
"the super constructor.",
+ hasPublishedDocs: true,
);
/**
@@ -14763,6 +14781,7 @@
'SUPER_IN_ENUM_CONSTRUCTOR',
"The enum constructor can't have a 'super' initializer.",
correctionMessage: "Try removing the 'super' invocation.",
+ hasPublishedDocs: true,
);
/**
@@ -15915,6 +15934,7 @@
correctionMessage:
"Try correcting the name to the name of an existing constructor, or "
"defining constructor with the name '{0}'.",
+ hasPublishedDocs: true,
uniqueName: 'UNDEFINED_ENUM_CONSTRUCTOR_NAMED',
);
@@ -15925,6 +15945,7 @@
correctionMessage:
"Try adding the name of an existing constructor, or defining an "
"unnamed constructor.",
+ hasPublishedDocs: true,
uniqueName: 'UNDEFINED_ENUM_CONSTRUCTOR_UNNAMED',
);
@@ -17194,6 +17215,7 @@
'VALUES_DECLARATION_IN_ENUM',
"A member named 'values' can't be declared in an enum.",
correctionMessage: "Try using a different name.",
+ hasPublishedDocs: true,
);
/**
@@ -17555,6 +17577,7 @@
"The enum is declared with {0} type parameters, but {1} type arguments "
"were given.",
correctionMessage: "Try adjusting the number of type arguments.",
+ hasPublishedDocs: true,
);
/**
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 6b2ba66..3c61b28 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -382,6 +382,9 @@
bool get isConstructorTearoffsEnabled =>
_featureSet.isEnabled(Feature.constructor_tearoffs);
+ bool get isInferenceUpdate1Enabled =>
+ _featureSet.isEnabled(Feature.inference_update_1);
+
/// Return the object providing promoted or declared types of variables.
LocalVariableTypeProvider get localVariableTypeProvider {
return flowAnalysis.localVariableTypeProvider;
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 67a45e2..c4921ef 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -1559,6 +1559,7 @@
NON_CONST_GENERATIVE_ENUM_CONSTRUCTOR:
problemMessage: Generative enum constructors must be 'const'.
correctionMessage: Try adding the keyword 'const'.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -1673,6 +1674,7 @@
CONCRETE_CLASS_HAS_ENUM_SUPERINTERFACE:
problemMessage: "Concrete classes can't have 'Enum' as a superinterface."
correctionMessage: Try specifying a different interface, or remove it from the list.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -3535,6 +3537,7 @@
ENUM_CONSTANT_WITH_NON_CONST_CONSTRUCTOR:
problemMessage: The invoked constructor isn't a 'const' constructor.
correctionMessage: Try invoking a 'const' generative constructor.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -3574,6 +3577,7 @@
ENUM_MIXIN_WITH_INSTANCE_VARIABLE:
problemMessage: Mixins applied to enums can't have instance variables.
correctionMessage: Try replacing the instance variables with getters.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -3622,6 +3626,7 @@
ENUM_WITH_ABSTRACT_MEMBER:
problemMessage: "'{0}' must have a method body because '{1}' is an enum."
correctionMessage: "Try adding a body to '{0}'."
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the name of the abstract method
@@ -3660,6 +3665,7 @@
ENUM_WITH_NAME_VALUES:
problemMessage: The name 'values' is not a valid name for an enum.
correctionMessage: Try using a different name.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -5422,6 +5428,7 @@
sharedName: ILLEGAL_ENUM_VALUES
problemMessage: An instance member named 'values' can't be declared in a class that implements 'Enum'.
correctionMessage: Try using a different name.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -5464,6 +5471,7 @@
sharedName: ILLEGAL_ENUM_VALUES
problemMessage: An instance member named 'values' can't be inherited from '{0}' in a class that implements 'Enum'.
correctionMessage: Try using a different name.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the name of the class that declares 'values'
@@ -5474,6 +5482,7 @@
sharedName: ILLEGAL_CONCRETE_ENUM_MEMBER
problemMessage: A concrete instance member named '{0}' can't be declared in a class that implements 'Enum'.
correctionMessage: Try using a different name.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the name of member that cannot be declared
@@ -5543,6 +5552,7 @@
sharedName: ILLEGAL_CONCRETE_ENUM_MEMBER
problemMessage: A concrete instance member named '{0}' can't be inherited from '{1}' in a class that implements 'Enum'.
correctionMessage: Try using a different name.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the name of member that cannot be inherited
@@ -5753,6 +5763,7 @@
IMPLICIT_SUPER_INITIALIZER_MISSING_ARGUMENTS:
problemMessage: The implicitly invoked unnamed constructor from '{0}' has required parameters.
correctionMessage: Try adding an explicit super parameter with the required arguments.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the name of the superclass
@@ -7208,6 +7219,7 @@
INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR:
problemMessage: Generative enum constructors can only be used as targets of redirection.
correctionMessage: Try using an enum constant, or a factory constructor.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -7296,6 +7308,7 @@
INVALID_SUPER_FORMAL_PARAMETER_LOCATION:
problemMessage: Super parameters can only be used in non-redirecting generative constructors.
correctionMessage: Try removing the 'super' modifier, or changing the constructor to be non-redirecting and generative.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the super modifier
@@ -9600,6 +9613,7 @@
NON_FINAL_FIELD_IN_ENUM:
problemMessage: Enums can only declare final fields.
correctionMessage: Try making the field final.
+ hasPublishedDocs: true
comment: No parameters.
documentation: |-
#### Description
@@ -10913,6 +10927,7 @@
POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT:
problemMessage: Positional super parameters can't be used when the super constructor invocation has a positional argument.
correctionMessage: Try making all the positional parameters passed to the super constructor be either all super parameters or all normal parameters.
+ hasPublishedDocs: true
comment: No parameters.
documentation: |-
#### Description
@@ -12414,6 +12429,7 @@
SUPER_FORMAL_PARAMETER_TYPE_IS_NOT_SUBTYPE_OF_ASSOCIATED:
problemMessage: The type '{0}' of this parameter isn't a subtype of the type '{1}' of the associated super constructor parameter.
correctionMessage: Try removing the explicit type annotation from the parameter.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the type of super-parameter
@@ -12485,6 +12501,7 @@
SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED:
problemMessage: No associated named super constructor parameter.
correctionMessage: Try changing the name to the name of an existing named super constructor parameter, or creating such named parameter.
+ hasPublishedDocs: true
comment: No parameters.
documentation: |-
#### Description
@@ -12555,6 +12572,7 @@
SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL:
problemMessage: No associated positional super constructor parameter.
correctionMessage: Try using a normal parameter, or adding more positional parameters to the super constructor.
+ hasPublishedDocs: true
comment: No parameters.
documentation: |-
#### Description
@@ -12872,6 +12890,7 @@
SUPER_IN_ENUM_CONSTRUCTOR:
problemMessage: The enum constructor can't have a 'super' initializer.
correctionMessage: Try removing the 'super' invocation.
+ hasPublishedDocs: true
comment: No parameters.
documentation: |-
#### Description
@@ -13801,6 +13820,7 @@
sharedName: UNDEFINED_ENUM_CONSTRUCTOR
problemMessage: The enum doesn't have a constructor named '{0}'.
correctionMessage: Try correcting the name to the name of an existing constructor, or defining constructor with the name '{0}'.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the name of the constructor that is undefined
@@ -13891,6 +13911,7 @@
sharedName: UNDEFINED_ENUM_CONSTRUCTOR
problemMessage: The enum doesn't have an unnamed constructor.
correctionMessage: Try adding the name of an existing constructor, or defining an unnamed constructor.
+ hasPublishedDocs: true
UNDEFINED_EXTENSION_GETTER:
problemMessage: "The getter '{0}' isn't defined for the extension '{1}'."
correctionMessage: "Try correcting the name to the name of an existing getter, or defining a getter named '{0}'."
@@ -14967,6 +14988,7 @@
VALUES_DECLARATION_IN_ENUM:
problemMessage: A member named 'values' can't be declared in an enum.
correctionMessage: Try using a different name.
+ hasPublishedDocs: true
documentation: |-
#### Description
@@ -15258,6 +15280,7 @@
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ENUM:
problemMessage: The enum is declared with {0} type parameters, but {1} type arguments were given.
correctionMessage: Try adjusting the number of type arguments.
+ hasPublishedDocs: true
comment: |-
Parameters:
0: the number of type parameters that were declared
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart
new file mode 100644
index 0000000..209ee0f
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(HorizontalInferenceEnabledTest);
+ defineReflectiveTests(HorizontalInferenceDisabledTest);
+ });
+}
+
+@reflectiveTest
+class HorizontalInferenceDisabledTest extends PubPackageResolutionTest
+ with HorizontalInferenceTestCases {
+ @override
+ String get testPackageLanguageVersion => '2.17';
+}
+
+@reflectiveTest
+class HorizontalInferenceEnabledTest extends PubPackageResolutionTest
+ with HorizontalInferenceTestCases {
+ @override
+ List<String> get experiments =>
+ [...super.experiments, EnableString.inference_update_1];
+}
+
+mixin HorizontalInferenceTestCases on PubPackageResolutionTest {
+ bool get _isEnabled => experiments.contains(EnableString.inference_update_1);
+
+ test_closure_passed_to_identical() async {
+ await assertNoErrorsInCode('''
+test() => identical(() {}, () {});
+''');
+ // No further assertions; we just want to make sure the interaction between
+ // flow analysis for `identical` and deferred analysis of closures doesn't
+ // lead to a crash.
+ }
+
+ test_write_capture_deferred() async {
+ await assertNoErrorsInCode('''
+test(int? i) {
+ if (i != null) {
+ f(() { i = null; }, i); // (1)
+ i; // (2)
+ }
+}
+void f(void Function() g, Object? x) {}
+''');
+ // With the feature enabled, analysis of the closure is deferred until after
+ // all the other arguments to `f`, so the `i` at (1) is not yet write
+ // captured and retains its promoted value. With the experiment disabled,
+ // it is write captured immediately.
+ assertType(findNode.simple('i); // (1)'), _isEnabled ? 'int' : 'int?');
+ // At (2), after the call to `f`, the write capture has taken place
+ // regardless of whether the experiment is enabled.
+ assertType(findNode.simple('i; // (2)'), 'int?');
+ }
+
+ test_write_capture_deferred_named() async {
+ await assertNoErrorsInCode('''
+test(int? i) {
+ if (i != null) {
+ f(g: () { i = null; }, x: i); // (1)
+ i; // (2)
+ }
+}
+void f({required void Function() g, Object? x}) {}
+''');
+ // With the feature enabled, analysis of the closure is deferred until after
+ // all the other arguments to `f`, so the `i` at (1) is not yet write
+ // captured and retains its promoted value. With the experiment disabled,
+ // it is write captured immediately.
+ assertType(findNode.simple('i); // (1)'), _isEnabled ? 'int' : 'int?');
+ // At (2), after the call to `f`, the write capture has taken place
+ // regardless of whether the experiment is enabled.
+ assertType(findNode.simple('i; // (2)'), 'int?');
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart
index 6c94826..ade0cef 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart
@@ -11,6 +11,7 @@
import 'extension_methods_test.dart' as extension_methods;
import 'function_expression_test.dart' as function_expression;
import 'function_test.dart' as function;
+import 'inference_update_1_test.dart' as inference_update_1;
import 'list_literal_test.dart' as list_literal;
import 'local_variable_test.dart' as local_variable;
import 'logical_boolean_expressions_test.dart' as logical_boolean_expressions;
@@ -29,6 +30,7 @@
extension_methods.main();
function.main();
function_expression.main();
+ inference_update_1.main();
list_literal.main();
local_variable.main();
logical_boolean_expressions.main();
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 7abb1f8..54d8b99 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -24,7 +24,7 @@
show ConstraintData;
import 'deferred_load/program_split_constraints/parser.dart' as psc show Parser;
import 'diagnostics/code_location.dart';
-import 'diagnostics/messages.dart' show Message, MessageTemplate;
+import 'diagnostics/messages.dart' show Message;
import 'dump_info.dart' show DumpInfoTask;
import 'elements/entities.dart';
import 'enqueue.dart' show Enqueuer, ResolutionEnqueuer;
@@ -36,7 +36,6 @@
import 'inferrer/typemasks/masks.dart' show TypeMaskStrategy;
import 'inferrer/types.dart'
show GlobalTypeInferenceResults, GlobalTypeInferenceTask;
-import 'io/source_information.dart' show SourceInformation;
import 'ir/modular.dart';
import 'js_backend/backend.dart' show CodegenInputs;
import 'js_backend/enqueuer.dart';
@@ -54,16 +53,12 @@
import 'serialization/task.dart';
import 'serialization/serialization.dart';
import 'serialization/strategies.dart';
-import 'ssa/nodes.dart' show HInstruction;
import 'universe/selector.dart' show Selector;
import 'universe/codegen_world_builder.dart';
import 'universe/resolution_world_builder.dart';
import 'universe/world_impact.dart' show WorldImpact, WorldImpactBuilderImpl;
import 'world.dart' show JClosedWorld;
-typedef MakeReporterFunction = CompilerDiagnosticReporter Function(
- Compiler compiler, CompilerOptions options);
-
/// Implementation of the compiler using a [api.CompilerInput] for supplying
/// the sources.
class Compiler {
@@ -73,7 +68,7 @@
KernelFrontendStrategy frontendStrategy;
JsBackendStrategy backendStrategy;
- CompilerDiagnosticReporter _reporter;
+ DiagnosticReporter _reporter;
Map<Entity, WorldImpact> _impactCache;
GenericTask userHandlerTask;
GenericTask userProviderTask;
@@ -122,8 +117,6 @@
DumpInfoTask dumpInfoTask;
SerializationTask serializationTask;
- bool get hasCrashed => _reporter.hasCrashed;
-
Progress progress = const Progress();
static const int PHASE_SCANNING = 0;
@@ -142,8 +135,7 @@
// Callback function used for testing codegen enqueuing.
void Function() onCodegenQueueEmptyForTesting;
- Compiler(this.provider, this._outputProvider, this.handler, this.options,
- {MakeReporterFunction makeReporter})
+ Compiler(this.provider, this._outputProvider, this.handler, this.options)
// NOTE: allocating measurer is done upfront to ensure the wallclock is
// started before other computations.
: measurer = Measurer(enableTaskMeasurements: options.verbose),
@@ -164,11 +156,7 @@
CompilerTask kernelFrontEndTask;
selfTask = GenericTask('self', measurer);
_outputProvider = _CompilerOutput(this, outputProvider);
- if (makeReporter != null) {
- _reporter = makeReporter(this, options);
- } else {
- _reporter = CompilerDiagnosticReporter(this);
- }
+ _reporter = DiagnosticReporter(this);
kernelFrontEndTask = GenericTask('Front end', measurer);
frontendStrategy = KernelFrontendStrategy(
kernelFrontEndTask, options, reporter, environment);
@@ -950,293 +938,6 @@
}
}
-/// Information about suppressed warnings and hints for a given library.
-class SuppressionInfo {
- int warnings = 0;
- int hints = 0;
-}
-
-class CompilerDiagnosticReporter extends DiagnosticReporter {
- final Compiler compiler;
- @override
- CompilerOptions get options => compiler.options;
-
- Entity _currentElement;
- bool hasCrashed = false;
-
- /// `true` if the last diagnostic was filtered, in which case the
- /// accompanying info message should be filtered as well.
- bool lastDiagnosticWasFiltered = false;
-
- /// Map containing information about the warnings and hints that have been
- /// suppressed for each library.
- Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{};
-
- CompilerDiagnosticReporter(this.compiler);
-
- Entity get currentElement => _currentElement;
-
- @override
- DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
- [Map<String, String> arguments = const {}]) {
- SourceSpan span = spanFromSpannable(spannable);
- MessageTemplate template = MessageTemplate.TEMPLATES[messageKind];
- Message message = template.message(arguments, options);
- return DiagnosticMessage(span, spannable, message);
- }
-
- @override
- void reportError(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR);
- }
-
- @override
- void reportWarning(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reportDiagnosticInternal(message, infos, api.Diagnostic.WARNING);
- }
-
- @override
- void reportHint(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reportDiagnosticInternal(message, infos, api.Diagnostic.HINT);
- }
-
- @override
- void reportInfo(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reportDiagnosticInternal(message, infos, api.Diagnostic.INFO);
- }
-
- void reportDiagnosticInternal(DiagnosticMessage message,
- List<DiagnosticMessage> infos, api.Diagnostic kind) {
- if (!options.showAllPackageWarnings &&
- message.spannable != NO_LOCATION_SPANNABLE) {
- switch (kind) {
- case api.Diagnostic.WARNING:
- case api.Diagnostic.HINT:
- Entity element = elementFromSpannable(message.spannable);
- if (!compiler.inUserCode(element, assumeInUserCode: true)) {
- Uri uri = compiler.getCanonicalUri(element);
- if (options.showPackageWarningsFor(uri)) {
- reportDiagnostic(message, infos, kind);
- return;
- }
- SuppressionInfo info =
- suppressedWarnings.putIfAbsent(uri, () => SuppressionInfo());
- if (kind == api.Diagnostic.WARNING) {
- info.warnings++;
- } else {
- info.hints++;
- }
- lastDiagnosticWasFiltered = true;
- return;
- }
- break;
- case api.Diagnostic.INFO:
- if (lastDiagnosticWasFiltered) {
- return;
- }
- break;
- }
- }
- lastDiagnosticWasFiltered = false;
- reportDiagnostic(message, infos, kind);
- }
-
- void reportDiagnostic(DiagnosticMessage message,
- List<DiagnosticMessage> infos, api.Diagnostic kind) {
- compiler.reportDiagnostic(message, infos, kind);
- if (kind == api.Diagnostic.ERROR ||
- kind == api.Diagnostic.CRASH ||
- (options.fatalWarnings && kind == api.Diagnostic.WARNING)) {
- compiler.fatalDiagnosticReported(message, infos, kind);
- }
- }
-
- @override
- bool get hasReportedError => compiler.compilationFailed;
-
- /// Perform an operation, [f], returning the return value from [f]. If an
- /// error occurs then report it as having occurred during compilation of
- /// [element]. Can be nested.
- @override
- withCurrentElement(Entity element, f()) {
- Entity old = currentElement;
- _currentElement = element;
- try {
- return f();
- } on SpannableAssertionFailure catch (ex) {
- if (!hasCrashed) {
- reportAssertionFailure(ex);
- pleaseReportCrash();
- }
- hasCrashed = true;
- rethrow;
- } on StackOverflowError {
- // We cannot report anything useful in this case, because we
- // do not have enough stack space.
- rethrow;
- } catch (ex) {
- if (hasCrashed) rethrow;
- try {
- unhandledExceptionOnElement(element);
- } catch (doubleFault) {
- // Ignoring exceptions in exception handling.
- }
- rethrow;
- } finally {
- _currentElement = old;
- }
- }
-
- void reportAssertionFailure(SpannableAssertionFailure ex) {
- String message =
- (ex.message != null) ? tryToString(ex.message) : tryToString(ex);
- reportDiagnosticInternal(
- createMessage(ex.node, MessageKind.GENERIC, {'text': message}),
- const <DiagnosticMessage>[],
- api.Diagnostic.CRASH);
- }
-
- /// Using [frontendStrategy] to compute a [SourceSpan] from spannable using
- /// the [currentElement] as context.
- SourceSpan _spanFromStrategy(Spannable spannable) {
- SourceSpan span;
- if (compiler.phase == Compiler.PHASE_COMPILING) {
- span =
- compiler.backendStrategy.spanFromSpannable(spannable, currentElement);
- } else {
- span = compiler.frontendStrategy
- .spanFromSpannable(spannable, currentElement);
- }
- if (span != null) return span;
- throw 'No error location.';
- }
-
- @override
- SourceSpan spanFromSpannable(Spannable spannable) {
- if (spannable == CURRENT_ELEMENT_SPANNABLE) {
- spannable = currentElement;
- } else if (spannable == NO_LOCATION_SPANNABLE) {
- if (currentElement == null) return null;
- spannable = currentElement;
- }
- if (spannable is SourceSpan) {
- return spannable;
- } else if (spannable is HInstruction) {
- Entity element = spannable.sourceElement;
- if (element == null) element = currentElement;
- SourceInformation position = spannable.sourceInformation;
- if (position != null) return position.sourceSpan;
- return _spanFromStrategy(element);
- } else {
- return _spanFromStrategy(spannable);
- }
- }
-
- @override
- internalError(Spannable spannable, reason) {
- String message = tryToString(reason);
- reportDiagnosticInternal(
- createMessage(spannable, MessageKind.GENERIC, {'text': message}),
- const <DiagnosticMessage>[],
- api.Diagnostic.CRASH);
- throw 'Internal Error: $message';
- }
-
- void unhandledExceptionOnElement(Entity element) {
- if (hasCrashed) return;
- hasCrashed = true;
- reportDiagnostic(createMessage(element, MessageKind.COMPILER_CRASHED),
- const <DiagnosticMessage>[], api.Diagnostic.CRASH);
- pleaseReportCrash();
- }
-
- void pleaseReportCrash() {
- print(MessageTemplate.TEMPLATES[MessageKind.PLEASE_REPORT_THE_CRASH]
- .message({'buildId': compiler.options.buildId}, options));
- }
-
- /// Finds the approximate [Element] for [node]. [currentElement] is used as
- /// the default value.
- Entity elementFromSpannable(Spannable node) {
- Entity element;
- if (node is Entity) {
- element = node;
- } else if (node is HInstruction) {
- element = node.sourceElement;
- }
- return element ?? currentElement;
- }
-
- @override
- void log(message) {
- Message msg = MessageTemplate.TEMPLATES[MessageKind.GENERIC]
- .message({'text': '$message'}, options);
- reportDiagnostic(DiagnosticMessage(null, null, msg),
- const <DiagnosticMessage>[], api.Diagnostic.VERBOSE_INFO);
- }
-
- String tryToString(object) {
- try {
- return object.toString();
- } catch (_) {
- return '<exception in toString()>';
- }
- }
-
- onError(Uri uri, error, StackTrace stackTrace) {
- try {
- if (!hasCrashed) {
- hasCrashed = true;
- if (error is SpannableAssertionFailure) {
- reportAssertionFailure(error);
- } else {
- reportDiagnostic(
- createMessage(
- SourceSpan(uri, 0, 0), MessageKind.COMPILER_CRASHED),
- const <DiagnosticMessage>[],
- api.Diagnostic.CRASH);
- }
- pleaseReportCrash();
- }
- } catch (doubleFault) {
- // Ignoring exceptions in exception handling.
- }
- return Future.error(error, stackTrace);
- }
-
- @override
- void onCrashInUserCode(String message, exception, stackTrace) {
- hasCrashed = true;
- print('$message: ${tryToString(exception)}');
- print(tryToString(stackTrace));
- }
-
- void reportSuppressedMessagesSummary() {
- if (!options.showAllPackageWarnings && !options.suppressWarnings) {
- suppressedWarnings.forEach((Uri uri, SuppressionInfo info) {
- MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS;
- if (info.warnings == 0) {
- kind = MessageKind.HIDDEN_HINTS;
- } else if (info.hints == 0) {
- kind = MessageKind.HIDDEN_WARNINGS;
- }
- MessageTemplate template = MessageTemplate.TEMPLATES[kind];
- Message message = template.message({
- 'warnings': info.warnings.toString(),
- 'hints': info.hints.toString(),
- 'uri': uri.toString(),
- }, options);
- reportDiagnostic(DiagnosticMessage(null, null, message),
- const <DiagnosticMessage>[], api.Diagnostic.HINT);
- });
- }
- }
-}
-
class _TimingData {
final String description;
final int milliseconds;
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 11c2d38..b78f0fc 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -1527,6 +1527,7 @@
fromCodegenAndData,
fromCodegenAndClosedWorldAndData,
}
+
enum WriteStrategy {
toKernel,
toModularAnalysis,
diff --git a/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart b/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
index c425106..4b895a4 100644
--- a/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
+++ b/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
@@ -4,72 +4,313 @@
library dart2js.diagnostic_listener;
+import '../../compiler.dart' as api;
+import '../compiler.dart' show Compiler;
import '../elements/entities.dart';
-import '../options.dart' show DiagnosticOptions;
+import '../io/source_information.dart';
+import '../options.dart';
+import '../ssa/nodes.dart' show HInstruction;
import 'messages.dart';
import 'source_span.dart' show SourceSpan;
-import 'spannable.dart' show Spannable;
+import 'spannable.dart';
-// TODO(johnniwinther): Rename and cleanup this interface. Add severity enum.
-abstract class DiagnosticReporter {
- DiagnosticOptions get options;
+class DiagnosticReporter {
+ final Compiler _compiler;
- // TODO(karlklose): rename log to something like reportInfo.
- void log(message);
+ CompilerOptions get options => _compiler.options;
- internalError(Spannable spannable, message);
+ Entity _currentElement;
+ bool _hasCrashed = false;
- /// Creates a [SourceSpan] for [node] in scope of the current element.
- ///
- /// If [node] is a [Node] we assert in checked mode that the corresponding
- /// tokens can be found within the tokens of the current element.
- SourceSpan spanFromSpannable(Spannable node);
+ /// `true` if the last diagnostic was filtered, in which case the
+ /// accompanying info message should be filtered as well.
+ bool _lastDiagnosticWasFiltered = false;
+
+ /// Map containing information about the warnings and hints that have been
+ /// suppressed for each library.
+ final Map<Uri, SuppressionInfo> _suppressedWarnings = {};
+
+ DiagnosticReporter(this._compiler);
+
+ Entity get currentElement => _currentElement;
+
+ DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
+ [Map<String, String> arguments = const {}]) {
+ SourceSpan span = spanFromSpannable(spannable);
+ MessageTemplate template = MessageTemplate.TEMPLATES[messageKind];
+ Message message = template.message(arguments, options);
+ return DiagnosticMessage(span, spannable, message);
+ }
+
+ void reportError(DiagnosticMessage message,
+ [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+ _reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR);
+ }
void reportErrorMessage(Spannable spannable, MessageKind messageKind,
[Map<String, String> arguments = const {}]) {
reportError(createMessage(spannable, messageKind, arguments));
}
- void reportError(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+ void reportWarning(DiagnosticMessage message,
+ [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+ _reportDiagnosticInternal(message, infos, api.Diagnostic.WARNING);
+ }
void reportWarningMessage(Spannable spannable, MessageKind messageKind,
[Map<String, String> arguments = const {}]) {
reportWarning(createMessage(spannable, messageKind, arguments));
}
- void reportWarning(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+ void reportHint(DiagnosticMessage message,
+ [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+ _reportDiagnosticInternal(message, infos, api.Diagnostic.HINT);
+ }
void reportHintMessage(Spannable spannable, MessageKind messageKind,
[Map<String, String> arguments = const {}]) {
reportHint(createMessage(spannable, messageKind, arguments));
}
- void reportHint(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+ void reportInfo(DiagnosticMessage message,
+ [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+ _reportDiagnosticInternal(message, infos, api.Diagnostic.INFO);
+ }
void reportInfoMessage(Spannable node, MessageKind errorCode,
[Map<String, String> arguments = const {}]) {
reportInfo(createMessage(node, errorCode, arguments));
}
- void reportInfo(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+ void _reportDiagnosticInternal(DiagnosticMessage message,
+ List<DiagnosticMessage> infos, api.Diagnostic kind) {
+ if (!options.showAllPackageWarnings &&
+ message.spannable != NO_LOCATION_SPANNABLE) {
+ switch (kind) {
+ case api.Diagnostic.WARNING:
+ case api.Diagnostic.HINT:
+ Entity element = _elementFromSpannable(message.spannable);
+ if (!_compiler.inUserCode(element, assumeInUserCode: true)) {
+ Uri uri = _compiler.getCanonicalUri(element);
+ if (options.showPackageWarningsFor(uri)) {
+ _reportDiagnostic(message, infos, kind);
+ return;
+ }
+ SuppressionInfo info =
+ _suppressedWarnings.putIfAbsent(uri, () => SuppressionInfo());
+ if (kind == api.Diagnostic.WARNING) {
+ info.warnings++;
+ } else {
+ info.hints++;
+ }
+ _lastDiagnosticWasFiltered = true;
+ return;
+ }
+ break;
+ case api.Diagnostic.INFO:
+ if (_lastDiagnosticWasFiltered) {
+ return;
+ }
+ break;
+ }
+ }
+ _lastDiagnosticWasFiltered = false;
+ _reportDiagnostic(message, infos, kind);
+ }
- /// Set current element of this reporter to [element]. This is used for
- /// creating [SourceSpan] in [spanFromSpannable].
- withCurrentElement(Entity element, f());
-
- DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
- [Map<String, String> arguments = const {}]);
+ void _reportDiagnostic(DiagnosticMessage message,
+ List<DiagnosticMessage> infos, api.Diagnostic kind) {
+ _compiler.reportDiagnostic(message, infos, kind);
+ if (kind == api.Diagnostic.ERROR ||
+ kind == api.Diagnostic.CRASH ||
+ (options.fatalWarnings && kind == api.Diagnostic.WARNING)) {
+ _compiler.fatalDiagnosticReported(message, infos, kind);
+ }
+ }
/// Returns `true` if a crash, an error or a fatal warning has been reported.
- bool get hasReportedError;
+ bool get hasReportedError => _compiler.compilationFailed;
+
+ /// Set current element of this reporter to [element]. This is used for
+ /// creating [SourceSpan] in [spanFromSpannable]. That is,
+ /// [withCurrentElement] performs an operation, [f], returning the return
+ /// value from [f]. If an error occurs then report it as having occurred
+ /// during compilation of [element]. Can be nested.
+ dynamic withCurrentElement(Entity element, dynamic f()) {
+ Entity old = currentElement;
+ _currentElement = element;
+ try {
+ return f();
+ } on SpannableAssertionFailure catch (ex) {
+ if (!_hasCrashed) {
+ _reportAssertionFailure(ex);
+ _pleaseReportCrash();
+ }
+ _hasCrashed = true;
+ rethrow;
+ } on StackOverflowError {
+ // We cannot report anything useful in this case, because we
+ // do not have enough stack space.
+ rethrow;
+ } catch (ex) {
+ if (_hasCrashed) rethrow;
+ try {
+ _unhandledExceptionOnElement(element);
+ } catch (doubleFault) {
+ // Ignoring exceptions in exception handling.
+ }
+ rethrow;
+ } finally {
+ _currentElement = old;
+ }
+ }
+
+ void _reportAssertionFailure(SpannableAssertionFailure ex) {
+ String message =
+ (ex.message != null) ? tryToString(ex.message) : tryToString(ex);
+ _reportDiagnosticInternal(
+ createMessage(ex.node, MessageKind.GENERIC, {'text': message}),
+ const <DiagnosticMessage>[],
+ api.Diagnostic.CRASH);
+ }
+
+ /// Using [frontendStrategy] to compute a [SourceSpan] from spannable using
+ /// the [currentElement] as context.
+ SourceSpan _spanFromStrategy(Spannable spannable) {
+ SourceSpan span;
+ if (_compiler.phase == Compiler.PHASE_COMPILING) {
+ span = _compiler.backendStrategy
+ .spanFromSpannable(spannable, currentElement);
+ } else {
+ span = _compiler.frontendStrategy
+ .spanFromSpannable(spannable, currentElement);
+ }
+ if (span != null) return span;
+ throw 'No error location.';
+ }
+
+ /// Creates a [SourceSpan] for [node] in scope of the current element.
+ ///
+ /// If [node] is a [Node] we assert in checked mode that the corresponding
+ /// tokens can be found within the tokens of the current element.
+ SourceSpan spanFromSpannable(Spannable spannable) {
+ if (spannable == CURRENT_ELEMENT_SPANNABLE) {
+ spannable = currentElement;
+ } else if (spannable == NO_LOCATION_SPANNABLE) {
+ if (currentElement == null) return null;
+ spannable = currentElement;
+ }
+ if (spannable is SourceSpan) {
+ return spannable;
+ } else if (spannable is HInstruction) {
+ Entity element = spannable.sourceElement;
+ if (element == null) element = currentElement;
+ SourceInformation position = spannable.sourceInformation;
+ if (position != null) return position.sourceSpan;
+ return _spanFromStrategy(element);
+ } else {
+ return _spanFromStrategy(spannable);
+ }
+ }
+
+ dynamic internalError(Spannable spannable, reason) {
+ String message = tryToString(reason);
+ _reportDiagnosticInternal(
+ createMessage(spannable, MessageKind.GENERIC, {'text': message}),
+ const <DiagnosticMessage>[],
+ api.Diagnostic.CRASH);
+ throw 'Internal Error: $message';
+ }
+
+ void _unhandledExceptionOnElement(Entity element) {
+ if (_hasCrashed) return;
+ _hasCrashed = true;
+ _reportDiagnostic(createMessage(element, MessageKind.COMPILER_CRASHED),
+ const <DiagnosticMessage>[], api.Diagnostic.CRASH);
+ _pleaseReportCrash();
+ }
+
+ void _pleaseReportCrash() {
+ print(MessageTemplate.TEMPLATES[MessageKind.PLEASE_REPORT_THE_CRASH]
+ .message({'buildId': _compiler.options.buildId}, options));
+ }
+
+ /// Finds the approximate [Element] for [node]. [currentElement] is used as
+ /// the default value.
+ Entity _elementFromSpannable(Spannable node) {
+ Entity element;
+ if (node is Entity) {
+ element = node;
+ } else if (node is HInstruction) {
+ element = node.sourceElement;
+ }
+ return element ?? currentElement;
+ }
+
+ void log(message) {
+ Message msg = MessageTemplate.TEMPLATES[MessageKind.GENERIC]
+ .message({'text': '$message'}, options);
+ _reportDiagnostic(DiagnosticMessage(null, null, msg),
+ const <DiagnosticMessage>[], api.Diagnostic.VERBOSE_INFO);
+ }
+
+ String tryToString(object) {
+ try {
+ return object.toString();
+ } catch (_) {
+ return '<exception in toString()>';
+ }
+ }
+
+ Future onError(Uri uri, error, StackTrace stackTrace) {
+ try {
+ if (!_hasCrashed) {
+ _hasCrashed = true;
+ if (error is SpannableAssertionFailure) {
+ _reportAssertionFailure(error);
+ } else {
+ _reportDiagnostic(
+ createMessage(
+ SourceSpan(uri, 0, 0), MessageKind.COMPILER_CRASHED),
+ const <DiagnosticMessage>[],
+ api.Diagnostic.CRASH);
+ }
+ _pleaseReportCrash();
+ }
+ } catch (doubleFault) {
+ // Ignoring exceptions in exception handling.
+ }
+ return Future.error(error, stackTrace);
+ }
/// Called when an [exception] is thrown from user-provided code, like from
/// the input provider or diagnostics handler.
- void onCrashInUserCode(String message, exception, stackTrace) {}
+ void onCrashInUserCode(String message, exception, stackTrace) {
+ _hasCrashed = true;
+ print('$message: ${tryToString(exception)}');
+ print(tryToString(stackTrace));
+ }
+
+ void reportSuppressedMessagesSummary() {
+ if (!options.showAllPackageWarnings && !options.suppressWarnings) {
+ _suppressedWarnings.forEach((Uri uri, SuppressionInfo info) {
+ MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS;
+ if (info.warnings == 0) {
+ kind = MessageKind.HIDDEN_HINTS;
+ } else if (info.hints == 0) {
+ kind = MessageKind.HIDDEN_WARNINGS;
+ }
+ MessageTemplate template = MessageTemplate.TEMPLATES[kind];
+ Message message = template.message({
+ 'warnings': info.warnings.toString(),
+ 'hints': info.hints.toString(),
+ 'uri': uri.toString(),
+ }, options);
+ _reportDiagnostic(DiagnosticMessage(null, null, message),
+ const <DiagnosticMessage>[], api.Diagnostic.HINT);
+ });
+ }
+ }
}
class DiagnosticMessage {
@@ -79,3 +320,9 @@
DiagnosticMessage(this.sourceSpan, this.spannable, this.message);
}
+
+/// Information about suppressed warnings and hints for a given library.
+class SuppressionInfo {
+ int warnings = 0;
+ int hints = 0;
+}
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 18d2044..b24e672 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -81,6 +81,10 @@
/// Whether to emit JavaScript encoded as UTF-8.
FeatureOption writeUtf8 = FeatureOption('utf8');
+ /// Experimental instrumentation to add tree shaking information to
+ /// dump-info's output.
+ FeatureOption newDumpInfo = FeatureOption('new-dump-info');
+
/// [FeatureOption]s which are shipped and cannot be toggled.
late final List<FeatureOption> shipped = [
newHolders,
@@ -93,9 +97,7 @@
];
/// [FeatureOption]s which default to disabled.
- late final List<FeatureOption> canary = [
- writeUtf8,
- ];
+ late final List<FeatureOption> canary = [writeUtf8, newDumpInfo];
/// Forces canary feature on. This must run after [Option].parse.
void forceCanary() {
diff --git a/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart b/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
index 435002d..92c1cb6 100644
--- a/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
+++ b/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
@@ -341,7 +341,7 @@
/// corresponding to [object] at location [sourceSpan]. We also perform error
/// checking to ensure that the same [id] isn't added twice.
void _registerValue<T>(Id id, T value, Object object, SourceSpan sourceSpan,
- Map<Id, ActualData<T>> actualMap, CompilerDiagnosticReporter reporter) {
+ Map<Id, ActualData<T>> actualMap, DiagnosticReporter reporter) {
if (actualMap.containsKey(id)) {
ActualData<T> existingData = actualMap[id];
reportHere(reporter, sourceSpan,
diff --git a/pkg/compiler/test/end_to_end/diagnostic_reporter_helper.dart b/pkg/compiler/test/end_to_end/diagnostic_reporter_helper.dart
deleted file mode 100644
index ba061e2..0000000
--- a/pkg/compiler/test/end_to_end/diagnostic_reporter_helper.dart
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2015, 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.
-
-// @dart = 2.7
-
-library dart2js.diagnostic_reporter.helper;
-
-import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
-import 'package:compiler/src/diagnostics/messages.dart';
-import 'package:compiler/src/diagnostics/source_span.dart';
-import 'package:compiler/src/diagnostics/spannable.dart';
-import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/options.dart';
-
-abstract class DiagnosticReporterWrapper extends DiagnosticReporter {
- DiagnosticReporter get reporter;
-
- @override
- DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
- [Map<String, String> arguments = const {}]) {
- return reporter.createMessage(spannable, messageKind, arguments);
- }
-
- @override
- internalError(Spannable spannable, message) {
- return reporter.internalError(spannable, message);
- }
-
- @override
- void log(message) {
- return reporter.log(message);
- }
-
- @override
- DiagnosticOptions get options => reporter.options;
-
- @override
- void reportError(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reporter.reportError(message, infos);
- }
-
- @override
- void reportHint(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reporter.reportHint(message, infos);
- }
-
- @override
- void reportInfo(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reporter.reportInfo(message, infos);
- }
-
- @override
- void reportInfoMessage(Spannable node, MessageKind errorCode,
- [Map<String, String> arguments = const {}]) {
- // ignore: deprecated_member_use_from_same_package
- reporter.reportInfoMessage(node, errorCode, arguments);
- }
-
- @override
- void reportWarning(DiagnosticMessage message,
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
- reporter.reportWarning(message, infos);
- }
-
- @override
- SourceSpan spanFromSpannable(Spannable node) {
- return reporter.spanFromSpannable(node);
- }
-
- @override
- withCurrentElement(Entity element, f()) {
- return reporter.withCurrentElement(element, f);
- }
-
- @override
- bool get hasReportedError => reporter.hasReportedError;
-}
diff --git a/pkg/compiler/test/end_to_end/exit_code_test.dart b/pkg/compiler/test/end_to_end/exit_code_test.dart
index be0974f..b42b5fd 100644
--- a/pkg/compiler/test/end_to_end/exit_code_test.dart
+++ b/pkg/compiler/test/end_to_end/exit_code_test.dart
@@ -28,7 +28,6 @@
import 'package:compiler/src/options.dart' show CompilerOptions;
import 'package:compiler/src/universe/world_impact.dart';
import 'package:compiler/src/world.dart';
-import 'diagnostic_reporter_helper.dart';
import '../helpers/memory_compiler.dart';
class TestCompiler extends Compiler {
@@ -46,10 +45,8 @@
String this.testMarker,
String this.testType,
Function this.onTest)
- : reporter = new TestDiagnosticReporter(),
- super(inputProvider, outputProvider, handler, options) {
- reporter.compiler = this;
- reporter.reporter = super.reporter;
+ : super(inputProvider, outputProvider, handler, options) {
+ reporter = new TestDiagnosticReporter(this);
test('Compiler');
}
@@ -122,10 +119,10 @@
}
}
-class TestDiagnosticReporter extends DiagnosticReporterWrapper {
+class TestDiagnosticReporter extends DiagnosticReporter {
TestCompiler compiler;
- @override
- DiagnosticReporter reporter;
+
+ TestDiagnosticReporter(this.compiler) : super(compiler);
@override
withCurrentElement(Entity element, f()) {
diff --git a/pkg/compiler/test/js/js_spec_string_test.dart b/pkg/compiler/test/js/js_spec_string_test.dart
index ad274e5..8ae300c 100644
--- a/pkg/compiler/test/js/js_spec_string_test.dart
+++ b/pkg/compiler/test/js/js_spec_string_test.dart
@@ -18,6 +18,8 @@
const NULL = 'Null';
class Listener extends DiagnosticReporter {
+ Listener() : super(null);
+
String errorMessage;
@override
internalError(spannable, message) {
diff --git a/pkg/dds/test/devtools_server/devtools_server_driver.dart b/pkg/dds/test/devtools_server/devtools_server_driver.dart
index 3a287ca..a5d44b8 100644
--- a/pkg/dds/test/devtools_server/devtools_server_driver.dart
+++ b/pkg/dds/test/devtools_server/devtools_server_driver.dart
@@ -210,6 +210,7 @@
.where((e) => e.kind == EventKind.kServiceRegistered)
.listen((e) => registeredServices[e.service!] = e.method!);
await appFixture.serviceConnection.streamListen(EventStreams.kService);
+ await appFixture.onAppStarted;
}
int nextId = 0;
diff --git a/pkg/js_ast/lib/src/characters.dart b/pkg/js_ast/lib/src/characters.dart
index 5fd1196..aaa068d 100644
--- a/pkg/js_ast/lib/src/characters.dart
+++ b/pkg/js_ast/lib/src/characters.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.
+// @dart=2.15
+
library js_character_codes;
const int $EOF = 0;
diff --git a/pkg/js_ast/lib/src/equivalence_visitor.dart b/pkg/js_ast/lib/src/equivalence_visitor.dart
index ac23703..5f67e72 100644
--- a/pkg/js_ast/lib/src/equivalence_visitor.dart
+++ b/pkg/js_ast/lib/src/equivalence_visitor.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.
+// @dart=2.15
+
library js_ast.equivalence_visitor;
import 'nodes.dart';
@@ -13,17 +15,17 @@
/// Called when [node1] and [node2] are not equivalent.
///
/// Override this to collect or report information on the inequivalent nodes.
- bool failAt(Node node1, Node node2) => false;
+ bool failAt(Node? node1, Node? node2) => false;
/// Returns whether the non-node values [value1] and [value2] are equivalent.
- bool testValues(Node node1, Object value1, Node node2, Object value2) =>
+ bool testValues(Node node1, Object? value1, Node node2, Object? value2) =>
value1 == value2;
/// Returns whether the labels [label1] and [label2] are equivalent.
- bool testLabels(Node node1, String label1, Node node2, String label2) =>
+ bool testLabels(Node node1, String? label1, Node node2, String? label2) =>
label1 == label2;
- bool testNodes(Node node1, Node node2) {
+ bool testNodes(Node? node1, Node? node2) {
if (identical(node1, node2)) return true;
if (node1 == null || node2 == null) return failAt(node1, node2);
return node1.accept1(this, node2);
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index b8fe27a..6d5d5ac 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.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.
+// @dart=2.15
+
library js_ast.nodes;
import 'precedence.dart';
@@ -561,9 +563,9 @@
}
abstract class Node {
- JavaScriptNodeSourceInformation get sourceInformation => _sourceInformation;
+ JavaScriptNodeSourceInformation? get sourceInformation => _sourceInformation;
- JavaScriptNodeSourceInformation _sourceInformation;
+ JavaScriptNodeSourceInformation? _sourceInformation;
T accept<T>(NodeVisitor<T> visitor);
void visitChildren<T>(NodeVisitor<T> visitor);
@@ -696,9 +698,7 @@
class ExpressionStatement extends Statement {
final Expression expression;
- ExpressionStatement(this.expression) {
- assert(this.expression != null);
- }
+ ExpressionStatement(this.expression);
@override
T accept<T>(NodeVisitor<T> visitor) => visitor.visitExpressionStatement(this);
@@ -779,14 +779,13 @@
abstract class Loop extends Statement {
final Statement body;
-
Loop(this.body);
}
class For extends Loop {
- final Expression init;
- final Expression condition;
- final Expression update;
+ final Expression? init;
+ final Expression? condition;
+ final Expression? update;
For(this.init, this.condition, this.update, Statement body) : super(body);
@@ -851,7 +850,7 @@
}
class While extends Loop {
- final Node condition;
+ final Expression condition;
While(this.condition, Statement body) : super(body);
@@ -907,7 +906,8 @@
}
class Continue extends Statement {
- final String targetLabel; // Can be null.
+ /// Name of the label L for `continue L;` or `null` for `continue;`.
+ final String? targetLabel;
Continue(this.targetLabel);
@@ -929,7 +929,8 @@
}
class Break extends Statement {
- final String targetLabel; // Can be null.
+ /// Name of the label L for `break L;` or `null` for `break;`.
+ final String? targetLabel;
Break(this.targetLabel);
@@ -951,7 +952,8 @@
}
class Return extends Statement {
- final Expression value; // Can be null.
+ /// The expression for `return expression;`, or `null` for `return;`.
+ final Expression? value;
Return([this.value = null]);
@@ -1004,8 +1006,8 @@
class Try extends Statement {
final Block body;
- final Catch catchPart; // Can be null if [finallyPart] is non-null.
- final Block finallyPart; // Can be null if [catchPart] is non-null.
+ final Catch? catchPart; // Can be null if [finallyPart] is non-null.
+ final Block? finallyPart; // Can be null if [catchPart] is non-null.
Try(this.body, this.catchPart, this.finallyPart) {
assert(catchPart != null || finallyPart != null);
@@ -1310,7 +1312,7 @@
class LiteralStringFromName extends LiteralString {
final Name name;
- LiteralStringFromName(this.name) : super(null) {
+ LiteralStringFromName(this.name) : super('') {
ArgumentError.checkNotNull(name, 'name');
}
@@ -1430,14 +1432,13 @@
class Assignment extends Expression {
final Expression leftHandSide;
- final String op; // Null, if the assignment is not compound.
+ final String? op; // `null` if the assignment is not compound.
final Expression value;
- Assignment(leftHandSide, value) : this.compound(leftHandSide, null, value);
+ Assignment(this.leftHandSide, this.value) : op = null;
// If `this.op == null` this will be a non-compound assignment.
- Assignment.compound(this.leftHandSide, this.op, this.value)
- : assert(value != null);
+ Assignment.compound(this.leftHandSide, this.op, this.value);
@override
int get precedenceLevel => ASSIGNMENT;
@@ -1471,7 +1472,8 @@
// TODO(sra): Can [VariableInitialization] be a non-expression?
final Declaration declaration;
- final Expression value; // [value] may be null.
+ // The initializing value can be missing, e.g. for `a` in `var a, b=1;`.
+ final Expression? value;
VariableInitialization(this.declaration, this.value);
@@ -1542,7 +1544,7 @@
List<Expression> arguments;
Call(this.target, this.arguments,
- {JavaScriptNodeSourceInformation sourceInformation}) {
+ {JavaScriptNodeSourceInformation? sourceInformation}) {
this._sourceInformation = sourceInformation;
}
@@ -1843,9 +1845,9 @@
}
abstract class FunctionExpression extends Expression {
- Node body;
- List<Parameter> params;
- AsyncModifier asyncModifier;
+ Node get body;
+ List<Parameter> get params;
+ AsyncModifier get asyncModifier;
}
class Fun extends FunctionExpression {
@@ -1935,7 +1937,7 @@
final String description;
const AsyncModifier(this.index, this.description,
- {this.isAsync, this.isYielding});
+ {required this.isAsync, required this.isYielding});
static const AsyncModifier sync =
AsyncModifier(0, "sync", isAsync: false, isYielding: false);
diff --git a/pkg/js_ast/lib/src/precedence.dart b/pkg/js_ast/lib/src/precedence.dart
index 222f4a8..19c5c9f 100644
--- a/pkg/js_ast/lib/src/precedence.dart
+++ b/pkg/js_ast/lib/src/precedence.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.
+// @dart=2.15
+
library precedence;
const EXPRESSION = 0;
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index bcfe5a8..67d0f9b 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.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.
+// @dart=2.15
+
library js_ast.printer;
import 'characters.dart' as charCodes;
@@ -49,7 +51,7 @@
///
/// [enterNode] is called in post-traversal order.
void exitNode(
- Node node, int startPosition, int endPosition, int closingPosition) {}
+ Node node, int startPosition, int endPosition, int? closingPosition) {}
/// Should return `true` if the printing tolerates unfinalized deferred AST
/// nodes.
@@ -150,11 +152,10 @@
if (!shouldCompressOutput) out(" ", isWhitespace: true);
}
- String lastAddedString = null;
+ String lastAddedString = '\u0000';
int get lastCharCode {
- if (lastAddedString == null) return 0;
- assert(lastAddedString.length != 0);
+ assert(lastAddedString.isNotEmpty);
return lastAddedString.codeUnitAt(lastAddedString.length - 1);
}
@@ -227,7 +228,7 @@
}
}
- EnterExitNode currentNode;
+ EnterExitNode? currentNode;
void _emit(String text) {
context.emit(text);
@@ -244,7 +245,7 @@
}
void enterNode() {
- currentNode.addToNode(context, _charCount);
+ currentNode!.addToNode(context, _charCount);
}
void endNode(Node node) {
@@ -253,8 +254,8 @@
endNode(node.value);
}
}
- assert(currentNode.node == node);
- currentNode = currentNode.exitNode(context, _charCount);
+ assert(currentNode!.node == node);
+ currentNode = currentNode!.exitNode(context, _charCount);
}
void visit(Node node) {
@@ -263,8 +264,8 @@
endNode(node);
}
- void visitCommaSeparated(List<Node> nodes, int hasRequiredType,
- {bool newInForInit, bool newAtStatementBegin}) {
+ void visitCommaSeparated(List<Expression> nodes, int hasRequiredType,
+ {required bool newInForInit, required bool newAtStatementBegin}) {
for (int i = 0; i < nodes.length; i++) {
if (i != 0) {
atStatementBegin = false;
@@ -280,7 +281,7 @@
nodes.forEach(visit);
}
- Node _undefer(Node node) {
+ Expression _undefer(Expression node) {
if (isDebugContext && !node.isFinalized) return node;
if (node is DeferredExpression) return _undefer(node.value);
return node;
@@ -303,7 +304,8 @@
return result;
}
- bool blockBody(Statement body, {bool needsSeparation, bool needsNewline}) {
+ bool blockBody(Statement body,
+ {required bool needsSeparation, required bool needsNewline}) {
if (body is Block) {
spaceOut();
blockOut(body, shouldIndent: false, needsNewline: needsNewline);
@@ -338,7 +340,8 @@
}
}
- int blockOut(Block node, {bool shouldIndent, bool needsNewline}) {
+ int blockOut(Block node,
+ {required bool shouldIndent, required bool needsNewline}) {
if (shouldIndent) indent();
startNode(node);
out("{");
@@ -425,19 +428,19 @@
spaceOut();
out("(");
if (loop.init != null) {
- visitNestedExpression(loop.init, EXPRESSION,
+ visitNestedExpression(loop.init!, EXPRESSION,
newInForInit: true, newAtStatementBegin: false);
}
out(";");
if (loop.condition != null) {
spaceOut();
- visitNestedExpression(loop.condition, EXPRESSION,
+ visitNestedExpression(loop.condition!, EXPRESSION,
newInForInit: false, newAtStatementBegin: false);
}
out(";");
if (loop.update != null) {
spaceOut();
- visitNestedExpression(loop.update, EXPRESSION,
+ visitNestedExpression(loop.update!, EXPRESSION,
newInForInit: false, newAtStatementBegin: false);
}
out(")");
@@ -513,16 +516,17 @@
@override
void visitReturn(Return node) {
- if (node.value == null) {
+ final value = node.value;
+ if (value == null) {
outIndent("return");
} else {
outIndent("return");
pendingSpace = true;
- visitNestedExpression(node.value, EXPRESSION,
+ visitNestedExpression(value, EXPRESSION,
newInForInit: false, newAtStatementBegin: false);
}
// Set the closing position to be before the optional semicolon.
- currentNode.closingPosition = _charCount;
+ currentNode!.closingPosition = _charCount;
outSemicolonLn();
}
@@ -553,12 +557,12 @@
outIndent("try");
blockBody(node.body, needsSeparation: true, needsNewline: false);
if (node.catchPart != null) {
- visit(node.catchPart);
+ visit(node.catchPart!);
}
if (node.finallyPart != null) {
spaceOut();
out("finally");
- blockBody(node.finallyPart, needsSeparation: true, needsNewline: true);
+ blockBody(node.finallyPart!, needsSeparation: true, needsNewline: true);
} else {
lineOut();
}
@@ -632,7 +636,7 @@
blockBody(body, needsSeparation: false, needsNewline: true);
}
- int functionOut(Fun fun, Node name, VarCollector vars) {
+ int functionOut(Fun fun, Expression? name, VarCollector vars) {
out("function");
if (name != null) {
out(" ");
@@ -642,10 +646,8 @@
}
localNamer.enterScope(vars);
out("(");
- if (fun.params != null) {
- visitCommaSeparated(fun.params, PRIMARY,
- newInForInit: false, newAtStatementBegin: false);
- }
+ visitCommaSeparated(fun.params, PRIMARY,
+ newInForInit: false, newAtStatementBegin: false);
out(")");
switch (fun.asyncModifier) {
case AsyncModifier.sync:
@@ -676,14 +678,14 @@
vars.visitFunctionDeclaration(declaration);
indent();
startNode(declaration.function);
- currentNode.closingPosition =
+ currentNode!.closingPosition =
functionOut(declaration.function, declaration.name, vars);
endNode(declaration.function);
lineOut();
}
visitNestedExpression(Expression node, int requiredPrecedence,
- {bool newInForInit, bool newAtStatementBegin}) {
+ {required bool newInForInit, required bool newAtStatementBegin}) {
int precedenceLevel =
(isDebugContext && !node.isFinalized) ? CALL : node.precedenceLevel;
bool needsParentheses =
@@ -714,7 +716,7 @@
@override
visitVariableDeclarationList(VariableDeclarationList list) {
out("var ");
- List<Node> nodes = list.declarations;
+ final nodes = list.declarations;
if (inForInit) {
visitCommaSeparated(nodes, ASSIGNMENT,
newInForInit: inForInit, newAtStatementBegin: false);
@@ -727,7 +729,7 @@
}
bool lastWasBig = false;
for (int i = 0; i < nodes.length; i++) {
- Node node = nodes[i];
+ Expression node = nodes[i];
bool thisIsBig = !_isSmallInitialization(node);
if (i > 0) {
atStatementBegin = false;
@@ -752,8 +754,7 @@
bool _isSmallInitialization(Node node) {
if (node is VariableInitialization) {
if (node.value == null) return true;
- Node value = _undefer(node.value);
- if (value == null) return true;
+ Node value = _undefer(node.value!);
if (value is This) return true;
if (value is LiteralNull) return true;
if (value is LiteralNumber) return true;
@@ -765,7 +766,7 @@
return false;
}
- void _outputIncDec(String op, Expression variable, [Expression alias]) {
+ void _outputIncDec(String op, Expression variable, [Expression? alias]) {
if (op == '+') {
if (lastCharCode == charCodes.$PLUS) out(" ", isWhitespace: true);
out('++');
@@ -784,24 +785,23 @@
/// To print assignments like `a = a + 1` and `a = a + b` compactly as
/// `++a` and `a += b` in the face of [DeferredExpression]s we detect the
/// pattern of the undeferred assignment.
- String op = assignment.op;
+ String? op = assignment.op;
Node leftHandSide = _undefer(assignment.leftHandSide);
- Node value = assignment.value;
- Node rightHandSide = value == null ? value : _undefer(value);
+ Node rightHandSide = _undefer(assignment.value);
if ((op == '+' || op == '-') &&
leftHandSide is VariableUse &&
rightHandSide is LiteralNumber &&
rightHandSide.value == "1") {
// Output 'a += 1' as '++a' and 'a -= 1' as '--a'.
- _outputIncDec(op, assignment.leftHandSide);
+ _outputIncDec(op!, assignment.leftHandSide);
return;
}
if (!assignment.isCompound &&
leftHandSide is VariableUse &&
rightHandSide is Binary) {
- Node rLeft = _undefer(rightHandSide.left);
- Node rRight = _undefer(rightHandSide.right);
- String op = rightHandSide.op;
+ Expression rLeft = _undefer(rightHandSide.left);
+ Expression rRight = _undefer(rightHandSide.right);
+ String? op = rightHandSide.op;
if (op == '+' ||
op == '-' ||
op == '/' ||
@@ -835,14 +835,13 @@
}
visitNestedExpression(assignment.leftHandSide, CALL,
newInForInit: inForInit, newAtStatementBegin: atStatementBegin);
- if (value != null) {
- spaceOut();
- if (op != null) out(op);
- out("=");
- spaceOut();
- visitNestedExpression(value, ASSIGNMENT,
- newInForInit: inForInit, newAtStatementBegin: false);
- }
+
+ spaceOut();
+ if (op != null) out(op);
+ out("=");
+ spaceOut();
+ visitNestedExpression(assignment.value, ASSIGNMENT,
+ newInForInit: inForInit, newAtStatementBegin: false);
}
@override
@@ -853,7 +852,7 @@
spaceOut();
out("=");
spaceOut();
- visitNestedExpression(initialization.value, ASSIGNMENT,
+ visitNestedExpression(initialization.value!, ASSIGNMENT,
newInForInit: inForInit, newAtStatementBegin: false);
}
}
@@ -976,6 +975,8 @@
rightPrecedenceRequirement = UNARY;
break;
default:
+ leftPrecedenceRequirement = EXPRESSION;
+ rightPrecedenceRequirement = EXPRESSION;
context.error("Forgot operator: $op");
}
@@ -1111,7 +1112,7 @@
out(']');
}
- void _dotString(Node selector, Node receiver, String selectorValue,
+ void _dotString(Node selector, Expression receiver, String selectorValue,
{bool assumeValid = false}) {
if (assumeValid || isValidJavaScriptId(selectorValue)) {
if (_undefer(receiver) is LiteralNumber &&
@@ -1134,41 +1135,41 @@
VarCollector vars = VarCollector();
vars.visitNamedFunction(namedFunction);
startNode(namedFunction.function);
- int closingPosition = currentNode.closingPosition =
+ int closingPosition = currentNode!.closingPosition =
functionOut(namedFunction.function, namedFunction.name, vars);
endNode(namedFunction.function);
// Use closing position of `namedFunction.function` as the closing position
// of the named function itself.
- currentNode.closingPosition = closingPosition;
+ currentNode!.closingPosition = closingPosition;
}
@override
void visitFun(Fun fun) {
VarCollector vars = VarCollector();
vars.visitFun(fun);
- currentNode.closingPosition = functionOut(fun, null, vars);
+ currentNode!.closingPosition = functionOut(fun, null, vars);
}
@override
void visitArrowFunction(ArrowFunction fun) {
VarCollector vars = VarCollector();
vars.visitArrowFunction(fun);
- currentNode.closingPosition = arrowFunctionOut(fun, vars);
+ currentNode!.closingPosition = arrowFunctionOut(fun, vars);
}
+ static bool _isIdentifierParameter(Node node) => node is VariableReference;
+
int arrowFunctionOut(ArrowFunction fun, VarCollector vars) {
// TODO: support static, get/set, async, and generators.
localNamer.enterScope(vars);
final List<Parameter> params = fun.params;
- if (params.length == 1 && params.first is VariableReference) {
+ if (params.length == 1 && _isIdentifierParameter(params.first)) {
visitNestedExpression(params.single, ASSIGNMENT,
newInForInit: false, newAtStatementBegin: false);
} else {
out("(");
- if (params != null) {
- visitCommaSeparated(fun.params, PRIMARY,
- newInForInit: false, newAtStatementBegin: false);
- }
+ visitCommaSeparated(fun.params, PRIMARY,
+ newInForInit: false, newAtStatementBegin: false);
out(")");
}
spaceOut();
@@ -1182,7 +1183,7 @@
if (fun.implicitReturnAllowed && body is Block) {
final statement = unwrapBlockIfSingleStatement(body);
if (statement is Return) {
- body = statement.value;
+ body = statement.value!;
}
}
if (body is Block) {
@@ -1194,7 +1195,7 @@
// https://tc39.github.io/ecma262/#sec-arrow-function-definitions
bool needsParens = body is ObjectInitializer;
if (needsParens) out("(");
- visitNestedExpression(body, ASSIGNMENT,
+ visitNestedExpression(body as Expression, ASSIGNMENT,
newInForInit: false, newAtStatementBegin: false);
if (needsParens) out(")");
closingPosition = _charCount;
@@ -1344,7 +1345,7 @@
out("{");
indentMore();
for (int i = 0; i < properties.length; i++) {
- Node value = properties[i].value;
+ Expression value = properties[i].value;
if (isOneLiner && exitOneLinerMode(value)) isOneLiner = false;
if (i != 0) {
out(",");
@@ -1379,7 +1380,7 @@
VarCollector vars = VarCollector();
vars.visitMethodDefinition(node);
startNode(node.function);
- currentNode.closingPosition = methodOut(node, vars);
+ currentNode!.closingPosition = methodOut(node, vars);
endNode(node.function);
}
@@ -1388,10 +1389,8 @@
Fun fun = node.function;
localNamer.enterScope(vars);
out("(");
- if (fun.params != null) {
- visitCommaSeparated(fun.params, PRIMARY,
- newInForInit: false, newAtStatementBegin: false);
- }
+ visitCommaSeparated(fun.params, PRIMARY,
+ newInForInit: false, newAtStatementBegin: false);
out(")");
spaceOut();
int closingPosition =
@@ -1586,10 +1585,8 @@
void collectVarsInFunction(FunctionExpression fun) {
if (!nested) {
nested = true;
- if (fun.params != null) {
- for (int i = 0; i < fun.params.length; i++) {
- params.add(fun.params[i].name);
- }
+ for (int i = 0; i < fun.params.length; i++) {
+ params.add(fun.params[i].name);
}
fun.body.accept(this);
nested = false;
@@ -1697,9 +1694,9 @@
@override
bool visitTry(Try node) {
if (node.finallyPart != null) {
- return node.finallyPart.accept(this);
+ return node.finallyPart!.accept(this);
} else {
- return node.catchPart.accept(this);
+ return node.catchPart!.accept(this);
}
}
@@ -1874,30 +1871,28 @@
/// Information pertaining the enter and exit callbacks for [node].
class EnterExitNode {
- final EnterExitNode parent;
+ final EnterExitNode? parent;
final Node node;
- int startPosition;
- int closingPosition;
+ int? startPosition;
+ int? closingPosition;
EnterExitNode(this.parent, this.node);
void addToNode(JavaScriptPrintingContext context, int position) {
if (startPosition == null) {
// [position] is the start position of [node].
- if (parent != null) {
- // This might be the start position of the parent as well.
- parent.addToNode(context, position);
- }
+ // This might be the start position of the parent as well.
+ parent?.addToNode(context, position);
startPosition = position;
context.enterNode(node, position);
}
}
- EnterExitNode exitNode(JavaScriptPrintingContext context, int position) {
+ EnterExitNode? exitNode(JavaScriptPrintingContext context, int position) {
// Enter must happen before exit.
addToNode(context, position);
- context.exitNode(node, startPosition, position, closingPosition);
+ context.exitNode(node, startPosition!, position, closingPosition);
return parent;
}
}
diff --git a/pkg/js_ast/lib/src/strings.dart b/pkg/js_ast/lib/src/strings.dart
index 91c2bae..871eea7 100644
--- a/pkg/js_ast/lib/src/strings.dart
+++ b/pkg/js_ast/lib/src/strings.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.
+// @dart=2.15
+
// Utilities for converting between JavaScript source-code Strings and the
// String value they represent.
@@ -15,7 +17,7 @@
/// [true] if contents require no escaping with the preferred quoting.
final bool simple;
- const StringToSourceKind({this.doubleQuotes, this.simple});
+ const StringToSourceKind({required this.doubleQuotes, required this.simple});
String get quote => doubleQuotes ? '"' : "'";
}
@@ -23,7 +25,7 @@
class StringToSource {
const StringToSource();
- static StringToSourceKind analyze(String value, {/*required*/ bool utf8}) {
+ static StringToSourceKind analyze(String value, {required bool utf8}) {
final ascii = !utf8;
int singleQuotes = 0;
int doubleQuotes = 0;
@@ -73,9 +75,9 @@
static void writeString(
StringBuffer sb, String string, StringToSourceKind kind,
- {/*required*/ bool utf8}) {
+ {required bool utf8}) {
for (int rune in string.runes) {
- String escape = _irregularEscape(rune, kind.doubleQuotes);
+ String? escape = _irregularEscape(rune, kind.doubleQuotes);
if (escape != null) {
sb.write(escape);
continue;
@@ -109,7 +111,7 @@
static bool _isUnpairedSurrogate(int code) => (code & 0xFFFFF800) == 0xD800;
- static String _irregularEscape(int code, bool useDoubleQuotes) {
+ static String? _irregularEscape(int code, bool useDoubleQuotes) {
switch (code) {
case charCodes.$SQ:
return useDoubleQuotes ? r"'" : r"\'";
diff --git a/pkg/test_runner/lib/src/test_file.dart b/pkg/test_runner/lib/src/test_file.dart
index 3c71e5b..b39ba90 100644
--- a/pkg/test_runner/lib/src/test_file.dart
+++ b/pkg/test_runner/lib/src/test_file.dart
@@ -12,7 +12,7 @@
final _vmOptionsRegExp = RegExp(r"// VMOptions=(.*)");
final _environmentRegExp = RegExp(r"// Environment=(.*)");
final _packagesRegExp = RegExp(r"// Packages=(.*)");
-final _experimentRegExp = RegExp(r"^--enable-experiment=([a-z,-]+)$");
+final _experimentRegExp = RegExp(r"^--enable-experiment=([a-z0-9,-]+)$");
List<String> _splitWords(String s) =>
s.split(' ').where((e) => e != '').toList();
diff --git a/tests/language/inference_update_1/write_capture_deferral_disabled_test.dart b/tests/language/inference_update_1/write_capture_deferral_disabled_test.dart
new file mode 100644
index 0000000..1083524
--- /dev/null
+++ b/tests/language/inference_update_1/write_capture_deferral_disabled_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests that when the feature is disabled, if an invocation argument is a
+// closure, write captures made by that closure take effect immediately after
+// the closure is visited
+
+// @dart=2.17
+
+import '../static_type_helper.dart';
+
+withUnnamedArguments(
+ int? i, void Function(Object?, void Function(), Object?) f) {
+ if (i != null) {
+ f(i..expectStaticType<Exactly<int>>(), () {
+ i = null;
+ }, i..expectStaticType<Exactly<int?>>());
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+withNamedArguments(int? i,
+ void Function({Object? x, required void Function() g, Object? y}) f) {
+ if (i != null) {
+ f(
+ x: i..expectStaticType<Exactly<int>>(),
+ g: () {
+ i = null;
+ },
+ y: i..expectStaticType<Exactly<int?>>());
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+withIdentical_lhs(int? i) {
+ if (i != null) {
+ i..expectStaticType<Exactly<int>>();
+ identical(() {
+ i = null;
+ }, i..expectStaticType<Exactly<int?>>());
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+withIdentical_rhs(int? i) {
+ if (i != null) {
+ identical(i..expectStaticType<Exactly<int>>(), () {
+ i = null;
+ });
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {}
diff --git a/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart b/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart
new file mode 100644
index 0000000..3a93caa
--- /dev/null
+++ b/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests that when the feature is enabled, if an invocation argument is a
+// closure, write captures made by that closure do not take effect until after
+// the invocation. This is a minor improvement to flow analysis that falls
+// naturally out of the fact that closures are analyzed last (so that their
+// types can depend on the types of other arguments).
+
+// SharedOptions=--enable-experiment=inference-update-1
+
+import '../static_type_helper.dart';
+
+withUnnamedArguments(int? i, void Function(void Function(), Object?) f) {
+ if (i != null) {
+ f(() {
+ i = null;
+ }, i..expectStaticType<Exactly<int>>());
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+withNamedArguments(
+ int? i, void Function({required void Function() g, Object? x}) f) {
+ if (i != null) {
+ f(
+ g: () {
+ i = null;
+ },
+ x: i..expectStaticType<Exactly<int>>());
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+withIdentical_lhs(int? i) {
+ if (i != null) {
+ i..expectStaticType<Exactly<int>>();
+ identical(() {
+ i = null;
+ }, i..expectStaticType<Exactly<int>>());
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+withIdentical_rhs(int? i) {
+ if (i != null) {
+ identical(i..expectStaticType<Exactly<int>>(), () {
+ i = null;
+ });
+ i..expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {}
diff --git a/tools/VERSION b/tools/VERSION
index 78b37fe..f0cd312 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 241
+PRERELEASE 242
PRERELEASE_PATCH 0
\ No newline at end of file