Cherry-pick 028ac521805dde0d71a1c97d1063b091fcddfe25 from analyzer to master.
This fixes "Evaluation of this constant expression throws an exception"
during Flutter analysis.
Initial CL: https://dart-review.googlesource.com/c/sdk/+/81750
R=brianwilkerson@google.com, paulberry@google.com
Title: Enum values are always computed, compute even implicit null values of default parameters.
Change-Id: If38dde0490f04bf39119087b5f15a36272e58cab
Reviewed-on: https://dart-review.googlesource.com/c/84041
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index 76ee093..3cab468 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -183,15 +183,20 @@
void computeConstantValue(ConstantEvaluationTarget constant) {
validator.beforeComputeValue(constant);
if (constant is ParameterElementImpl) {
- Expression defaultValue = constant.constantInitializer;
- if (defaultValue != null) {
- RecordingErrorListener errorListener = new RecordingErrorListener();
- ErrorReporter errorReporter =
- new ErrorReporter(errorListener, constant.source);
- DartObjectImpl dartObject =
- defaultValue.accept(new ConstantVisitor(this, errorReporter));
- constant.evaluationResult =
- new EvaluationResultImpl(dartObject, errorListener.errors);
+ if (constant.isOptional) {
+ Expression defaultValue = constant.constantInitializer;
+ if (defaultValue != null) {
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ ErrorReporter errorReporter =
+ new ErrorReporter(errorListener, constant.source);
+ DartObjectImpl dartObject =
+ defaultValue.accept(new ConstantVisitor(this, errorReporter));
+ constant.evaluationResult =
+ new EvaluationResultImpl(dartObject, errorListener.errors);
+ } else {
+ constant.evaluationResult =
+ EvaluationResultImpl(typeProvider.nullObject);
+ }
}
} else if (constant is VariableElementImpl) {
Expression constantInitializer = constant.constantInitializer;
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index e6cab4e..513799f 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1784,6 +1784,9 @@
}
@override
+ bool get isConstantEvaluated => true;
+
+ @override
void set isFinal(bool isFinal) {
assert(false);
}
diff --git a/pkg/analyzer/test/src/dart/resolution/constant_test.dart b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
new file mode 100644
index 0000000..53a5c74
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2018, 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/element/element.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'driver_resolution.dart';
+import 'resolution.dart';
+import 'task_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ConstantDriverTest);
+ defineReflectiveTests(ConstantTaskTest);
+ });
+}
+
+@reflectiveTest
+class ConstantDriverTest extends DriverResolutionTest with ConstantMixin {}
+
+abstract class ConstantMixin implements ResolutionTest {
+ test_constantValue_defaultParameter_noDefaultValue() async {
+ newFile('/test/lib/a.dart', content: r'''
+class A {
+ const A({int p});
+}
+''');
+ addTestFile(r'''
+import 'a.dart';
+const a = const A();
+''');
+ await resolveTestFile();
+ assertNoTestErrors();
+
+ var aLib = findElement.import('package:test/a.dart').importedLibrary;
+ var aConstructor = aLib.getType('A').constructors.single;
+ DefaultParameterElementImpl p = aConstructor.parameters.single;
+
+ // To evaluate `const A()` we have to evaluate `{int p}`.
+ // Even if its value is `null`.
+ expect(p.isConstantEvaluated, isTrue);
+ expect(p.constantValue.isNull, isTrue);
+ }
+
+ test_constFactoryRedirection_super() async {
+ addTestFile(r'''
+class I {
+ const factory I(int f) = B;
+}
+
+class A implements I {
+ final int f;
+
+ const A(this.f);
+}
+
+class B extends A {
+ const B(int f) : super(f);
+}
+
+@I(42)
+main() {}
+''');
+ await resolveTestFile();
+ assertNoTestErrors();
+
+ var node = findNode.annotation('@I');
+ var value = node.elementAnnotation.constantValue;
+ expect(value.getField('(super)').getField('f').toIntValue(), 42);
+ }
+}
+
+@reflectiveTest
+class ConstantTaskTest extends TaskResolutionTest with ConstantMixin {}
diff --git a/pkg/analyzer/test/src/dart/resolution/enum_test.dart b/pkg/analyzer/test/src/dart/resolution/enum_test.dart
index d30bf67..930ab7c 100644
--- a/pkg/analyzer/test/src/dart/resolution/enum_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/enum_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/src/error/codes.dart';
+import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'driver_resolution.dart';
@@ -44,6 +45,20 @@
var v = findElement.topVar('v');
assertElementTypeString(v.type, 'List<Object>');
}
+
+ test_isConstantEvaluated() async {
+ addTestFile(r'''
+enum E {
+ aaa, bbb
+}
+''');
+ await resolveTestFile();
+ assertNoTestErrors();
+
+ expect(findElement.field('aaa').isConstantEvaluated, isTrue);
+ expect(findElement.field('bbb').isConstantEvaluated, isTrue);
+ expect(findElement.field('values').isConstantEvaluated, isTrue);
+ }
}
@reflectiveTest
diff --git a/pkg/analyzer/test/src/dart/resolution/find_element.dart b/pkg/analyzer/test/src/dart/resolution/find_element.dart
index dff0500..4eadf43 100644
--- a/pkg/analyzer/test/src/dart/resolution/find_element.dart
+++ b/pkg/analyzer/test/src/dart/resolution/find_element.dart
@@ -73,21 +73,28 @@
}
FieldElement field(String name) {
- for (var type in unitElement.mixins) {
- for (var field in type.fields) {
+ for (var enum_ in unitElement.enums) {
+ for (var field in enum_.fields) {
if (field.name == name) {
return field;
}
}
}
- for (var type in unitElement.types) {
- for (var field in type.fields) {
+ for (var mixin in unitElement.mixins) {
+ for (var field in mixin.fields) {
if (field.name == name) {
return field;
}
}
}
- fail('Not found class field: $name');
+ for (var class_ in unitElement.types) {
+ for (var field in class_.fields) {
+ if (field.name == name) {
+ return field;
+ }
+ }
+ }
+ fail('Not found field: $name');
}
FunctionElement function(String name) {