Evaluate enum constants and report errors.
Change-Id: Ie3e307ae667cf5e0679a9750047b81ca66c3323c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/234141
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
index 92f15c0..d51f9d9 100644
--- a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
+++ b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
@@ -124,13 +124,19 @@
}
@override
- visitEnumConstantDeclaration(node) {
+ visitEnumConstantDeclaration(EnumConstantDeclaration node) {
super.visitEnumConstantDeclaration(node);
var argumentList = node.arguments?.argumentList;
if (argumentList != null) {
_validateConstantArguments(argumentList);
}
+
+ var element = node.declaredElement as ConstFieldElementImpl;
+ var result = element.evaluationResult;
+ if (result != null) {
+ _reportErrors(result.errors, null);
+ }
}
@override
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index 49ca24e..ba68d5e 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -28,6 +28,12 @@
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/task/api/model.dart';
+/// During evaluation of enum constants we might need to report an error
+/// that is associated with the [InstanceCreationExpression], but this
+/// expression is synthetic. Instead, we remember the corresponding
+/// [EnumConstantDeclaration] and report the error on it.
+final enumConstantErrorNodes = Expando<EnumConstantDeclaration>();
+
/// Helper class encapsulating the methods for evaluating constants and
/// constant instance creation expressions.
class ConstantEvaluationEngine {
@@ -187,6 +193,18 @@
/// [callback].
void computeDependencies(
ConstantEvaluationTarget constant, ReferenceFinderCallback callback) {
+ if (constant is ConstFieldElementImpl && constant.isEnumConstant) {
+ var enclosing = constant.enclosingElement;
+ if (enclosing is EnumElementImpl) {
+ if (enclosing.name == 'values') {
+ return;
+ }
+ if (constant.name == enclosing.name) {
+ return;
+ }
+ }
+ }
+
ReferenceFinder referenceFinder = ReferenceFinder(callback);
if (constant is ConstructorElement) {
constant = constant.declaration;
@@ -2541,7 +2559,7 @@
declaredVariables,
errorReporter,
library,
- node,
+ enumConstantErrorNodes[node] ?? node,
constructor,
typeArguments,
namedNodes: namedNodes,
diff --git a/pkg/analyzer/lib/src/dart/constant/utilities.dart b/pkg/analyzer/lib/src/dart/constant/utilities.dart
index 62f7b47..38b14dc 100644
--- a/pkg/analyzer/lib/src/dart/constant/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/constant/utilities.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
+import 'package:analyzer/src/dart/element/element.dart';
/// Callback used by [ReferenceFinder] to report that a dependency was found.
typedef ReferenceFinderCallback = void Function(
@@ -141,6 +142,17 @@
}
@override
+ void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+ super.visitEnumConstantDeclaration(node);
+
+ var element = node.declaredElement as ConstFieldElementImpl;
+ constantsToCompute.add(element);
+
+ var constantInitializer = element.constantInitializer!;
+ enumConstantErrorNodes[constantInitializer] = node;
+ }
+
+ @override
void visitVariableDeclaration(VariableDeclaration node) {
super.visitVariableDeclaration(node);
var initializer = node.initializer;
diff --git a/pkg/analyzer/test/generated/constant_test.dart b/pkg/analyzer/test/generated/constant_test.dart
index 2387698b..9be808b 100644
--- a/pkg/analyzer/test/generated/constant_test.dart
+++ b/pkg/analyzer/test/generated/constant_test.dart
@@ -5,6 +5,7 @@
@deprecated
library analyzer.test.constant_test;
+import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/generated/constant.dart';
import 'package:test/test.dart';
@@ -392,6 +393,27 @@
''');
}
+ /// Enum constants can reference other constants.
+ test_object_enum_enhanced_constants() async {
+ await assertNoErrorsInCode('''
+enum E {
+ v1(42), v2(v1);
+ final Object? a;
+ const E([this.a]);
+}
+''');
+
+ assertDartObjectText(findElement.field('v2').evaluationResult.value, r'''
+E
+ _name: String v2
+ a: E
+ _name: String v1
+ a: int 42
+ index: int 0
+ index: int 1
+''');
+ }
+
test_object_enum_enhanced_named() async {
await resolveTestCode('''
enum E<T> {
@@ -586,3 +608,14 @@
return element.evaluationResult!;
}
}
+
+extension on VariableElement {
+ EvaluationResultImpl get evaluationResult {
+ var constVariable = this as ConstVariableElement;
+ var evaluationResult = constVariable.evaluationResult;
+ if (evaluationResult == null) {
+ fail('Not evaluated: $this');
+ }
+ return evaluationResult;
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/constant/utilities_test.dart b/pkg/analyzer/test/src/dart/constant/utilities_test.dart
index 906c7e0..99bd23a 100644
--- a/pkg/analyzer/test/src/dart/constant/utilities_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/utilities_test.dart
@@ -6,7 +6,6 @@
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type_provider.dart';
-import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/generated/constant.dart';
@@ -53,15 +52,6 @@
expect(_findAnnotations(), contains(_node));
}
- void test_visitAnnotation_enumConstant() {
- // Analyzer ignores annotations on enum constant declarations.
- Annotation annotation = AstTestFactory.annotation2(
- AstTestFactory.identifier3('A'), null, AstTestFactory.argumentList());
- _node = astFactory.enumConstantDeclaration(
- null, <Annotation>[annotation], AstTestFactory.identifier3('C'));
- expect(_findConstants(), isEmpty);
- }
-
/// Test an annotation that represents the invocation of a constant
/// constructor.
void test_visitAnnotation_invocation() {
diff --git a/pkg/analyzer/test/src/dart/resolution/metadata_test.dart b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
index b28cf4d..458a44b 100644
--- a/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
@@ -95,6 +95,32 @@
''');
}
+ test_onEnumConstant() async {
+ await assertNoErrorsInCode(r'''
+enum E {
+ @v
+ v;
+}
+''');
+
+ var annotation = findNode.annotation('@v');
+ assertResolvedNodeText(annotation, '''
+Annotation
+ atSign: @
+ name: SimpleIdentifier
+ token: v
+ staticElement: self::@enum::E::@getter::v
+ staticType: null
+ element: self::@enum::E::@getter::v
+''');
+
+ _assertAnnotationValueText(annotation, '''
+E
+ _name: String v
+ index: int 0
+''');
+ }
+
test_onFieldFormal() async {
await assertNoErrorsInCode(r'''
class A {
diff --git a/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
index 432c0e1..59d97c5 100644
--- a/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
@@ -93,6 +93,7 @@
}
''', [
error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 13, 1),
+ error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 13, 1),
]);
}
diff --git a/pkg/analyzer/test/src/diagnostics/assert_in_redirecting_constructor_test.dart b/pkg/analyzer/test/src/diagnostics/assert_in_redirecting_constructor_test.dart
index a254d03..93ccda1 100644
--- a/pkg/analyzer/test/src/diagnostics/assert_in_redirecting_constructor_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/assert_in_redirecting_constructor_test.dart
@@ -58,17 +58,17 @@
test_enum_assertBeforeRedirection() async {
await assertErrorsInCode(r'''
enum E {
- v(0);
+ v(42);
const E(int x) : assert(x > 0), this.name();
const E.name();
}
-''', [error(CompileTimeErrorCode.ASSERT_IN_REDIRECTING_CONSTRUCTOR, 36, 13)]);
+''', [error(CompileTimeErrorCode.ASSERT_IN_REDIRECTING_CONSTRUCTOR, 37, 13)]);
}
test_enum_justAssert() async {
await assertNoErrorsInCode(r'''
enum E {
- v(0);
+ v(42);
const E(int x) : assert(x > 0);
}
''');
@@ -87,10 +87,10 @@
test_enum_redirectionBeforeAssert() async {
await assertErrorsInCode(r'''
enum E {
- v(0);
+ v(42);
const E(int x) : this.name(), assert(x > 0);
const E.name();
}
-''', [error(CompileTimeErrorCode.ASSERT_IN_REDIRECTING_CONSTRUCTOR, 49, 13)]);
+''', [error(CompileTimeErrorCode.ASSERT_IN_REDIRECTING_CONSTRUCTOR, 50, 13)]);
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/const_constructor_param_type_mismatch_test.dart b/pkg/analyzer/test/src/diagnostics/const_constructor_param_type_mismatch_test.dart
index cdd3c81..5cb3c4e 100644
--- a/pkg/analyzer/test/src/diagnostics/const_constructor_param_type_mismatch_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_constructor_param_type_mismatch_test.dart
@@ -104,6 +104,32 @@
]);
}
+ test_enum_int_null() async {
+ await assertErrorsInCode(r'''
+const dynamic a = null;
+
+enum E {
+ v(a);
+ const E(int a);
+}
+''', [
+ error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 38, 1),
+ ]);
+ }
+
+ test_enum_int_String() async {
+ await assertErrorsInCode(r'''
+const dynamic a = '0';
+
+enum E {
+ v(a);
+ const E(int a);
+}
+''', [
+ error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 37, 1),
+ ]);
+ }
+
test_int_to_double_reference_from_other_library_other_file_after() async {
newFile('$testPackageLibPath/other.dart', content: '''
import 'test.dart';
diff --git a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
index 6c17ac2..f5911ee 100644
--- a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
@@ -53,6 +53,18 @@
error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 129, 18),
]);
}
+
+ test_enum_constructor_initializer_asExpression() async {
+ await assertErrorsInCode(r'''
+enum E {
+ v();
+ final int x;
+ const E({int? x}) : x = x as int;
+}
+''', [
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 11, 3),
+ ]);
+ }
}
@reflectiveTest
diff --git a/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart b/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart
index e066e13..e089c34 100644
--- a/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart
@@ -121,6 +121,7 @@
}
''', [
error(CompileTimeErrorCode.MISSING_REQUIRED_ARGUMENT, 11, 1),
+ error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 11, 3),
]);
}
@@ -132,6 +133,7 @@
}
''', [
error(CompileTimeErrorCode.MISSING_REQUIRED_ARGUMENT, 11, 1),
+ error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 11, 1),
]);
}
diff --git a/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart b/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart
index af89c90..78a8782 100644
--- a/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart
@@ -64,6 +64,7 @@
const E(int a);
}
''', [
+ error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 11, 3),
error(CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS, 12, 2),
]);
}
@@ -75,6 +76,7 @@
const E(int a);
}
''', [
+ error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 11, 1),
error(CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS, 11, 1),
]);
}
diff --git a/pkg/analyzer/test/src/diagnostics/recursive_compile_time_constant_test.dart b/pkg/analyzer/test/src/diagnostics/recursive_compile_time_constant_test.dart
index 325c407..e65cbff 100644
--- a/pkg/analyzer/test/src/diagnostics/recursive_compile_time_constant_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/recursive_compile_time_constant_test.dart
@@ -25,6 +25,42 @@
]);
}
+ test_enum_constant_values() async {
+ await assertErrorsInCode(r'''
+enum E {
+ v(values);
+ const E(Object a);
+}
+''', [
+ error(CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, 11, 1),
+ ]);
+ }
+
+ test_enum_constants() async {
+ await assertErrorsInCode(r'''
+enum E {
+ v1(v2), v2(v1);
+ const E(E other);
+}
+''', [
+ error(CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, 11, 2),
+ error(CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, 19, 2),
+ ]);
+ }
+
+ test_enum_fields() async {
+ await assertErrorsInCode(r'''
+enum E {
+ v;
+ static const x = y + 1;
+ static const y = x + 1;
+}
+''', [
+ error(CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, 29, 1),
+ error(CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, 55, 1),
+ ]);
+ }
+
test_field() async {
await assertErrorsInCode(r'''
class A {
diff --git a/tests/language/enum/enhanced_enums_error_test.dart b/tests/language/enum/enhanced_enums_error_test.dart
index 10a94e4..e812324 100644
--- a/tests/language/enum/enhanced_enums_error_test.dart
+++ b/tests/language/enum/enhanced_enums_error_test.dart
@@ -661,11 +661,11 @@
e1(e2),
//^
// [cfe] Constant evaluation error:
-// ^^
-// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT
+//^^
+// [analyzer] COMPILE_TIME_ERROR.RECURSIVE_COMPILE_TIME_CONSTANT
e2(e1);
- // ^^
- // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT
+//^^
+// [analyzer] COMPILE_TIME_ERROR.RECURSIVE_COMPILE_TIME_CONSTANT
final CyclicReference other;
const CyclicReference(this.other);
}
@@ -676,8 +676,8 @@
// ^
// [cfe] Constant evaluation error:
e1(values);
- // ^^^^^^
- // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT
+//^^
+// [analyzer] COMPILE_TIME_ERROR.RECURSIVE_COMPILE_TIME_CONSTANT
final List<CyclicReferenceValues> list;
const CyclicReferenceValues(this.list);
}