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);
 }