Resolve enum constants to constructors.

Change-Id: Iace6dd63bd2071830436b070299991f4e811cdf6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/229620
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 17a3d4b..b1d7dd6 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -130,6 +130,16 @@
   }
 
   @override
+  ConstructorElement? get unnamedConstructor {
+    for (ConstructorElement element in constructors) {
+      if (element.name.isEmpty) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  @override
   T? accept<T>(ElementVisitor<T> visitor) => visitor.visitClassElement(this);
 
   @override
@@ -778,16 +788,6 @@
   }
 
   @override
-  ConstructorElement? get unnamedConstructor {
-    for (ConstructorElement element in constructors) {
-      if (element.name.isEmpty) {
-        return element;
-      }
-    }
-    return null;
-  }
-
-  @override
   void appendTo(ElementDisplayStringBuilder builder) {
     builder.writeClassElement(this);
   }
@@ -2773,9 +2773,6 @@
   }
 
   @override
-  ConstructorElement? get unnamedConstructor => null;
-
-  @override
   void appendTo(ElementDisplayStringBuilder builder) {
     builder.writeEnumElement(this);
   }
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index ad00f5e..decbd59 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -103,6 +103,12 @@
   }
 }
 
+extension ExecutableElementExtension on ExecutableElement {
+  bool get isEnumConstructor {
+    return this is ConstructorElement && enclosingElement is EnumElementImpl;
+  }
+}
+
 extension ParameterElementExtensions on ParameterElement {
   /// Return [ParameterElement] with the specified properties replaced.
   ParameterElement copyWith({
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index 0f6da71..7145bd0 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -394,6 +394,15 @@
 
     node.metadata.accept(this);
     _setElementAnnotations(node.metadata, element.metadata);
+
+    var arguments = node.arguments;
+    if (arguments != null) {
+      _withElementWalker(null, () {
+        _withElementHolder(ElementHolder(element), () {
+          arguments.accept(this);
+        });
+      });
+    }
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/generated/declaration_resolver.dart b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
index 650fde4..19b68b9 100644
--- a/pkg/analyzer/lib/src/generated/declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
@@ -4,6 +4,7 @@
 
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/extensions.dart';
 
 /// Keeps track of the set of non-synthetic child elements of an element,
 /// yielding them one at a time in response to "get" method calls.
@@ -67,7 +68,9 @@
   ElementWalker.forExecutable(ExecutableElement element)
       : element = element,
         _functions = const <ExecutableElement>[],
-        _parameters = element.parameters,
+        _parameters = element.isEnumConstructor
+            ? element.parameters.skip(2).toList()
+            : element.parameters,
         _typeParameters = element.typeParameters;
 
   /// Creates an [ElementWalker] which walks the child elements of an extension
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 1e84b07..d7284f2 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1539,9 +1539,39 @@
 
   @override
   void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    node as EnumConstantDeclarationImpl;
+
+    node.documentationComment?.accept(this);
     node.metadata.accept(this);
     checkUnreachableNode(node);
-    node.visitChildren(this);
+
+    var element = node.declaredElement as ConstFieldElementImpl;
+    var initializer = element.constantInitializer;
+    if (initializer is InstanceCreationExpression) {
+      var constructor = initializer.constructorName.staticElement;
+      node.constructorElement = constructor;
+      var arguments = node.arguments;
+      if (arguments != null) {
+        var argumentList = arguments.argumentList;
+        if (constructor != null) {
+          InferenceContext.setType(argumentList, constructor.type);
+          argumentList.correspondingStaticParameters =
+              ResolverVisitor.resolveArgumentsToParameters(
+            argumentList: argumentList,
+            parameters: constructor.parameters.skip(2).toList(),
+          );
+          for (var argument in argumentList.arguments) {
+            var parameter = argument.staticParameterElement;
+            if (parameter != null) {
+              InferenceContext.setType(argument, parameter.type);
+            }
+            argument.accept(this);
+          }
+        }
+        arguments.typeArguments?.accept(this);
+      }
+    }
+
     elementResolver.visitEnumConstantDeclaration(node);
   }
 
@@ -3039,6 +3069,7 @@
   void visitEnumMembersInScope(EnumDeclaration node) {
     node.documentationComment?.accept(this);
     node.constants.accept(this);
+    node.members.accept(this);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index 8bad34e..e201c44 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -268,6 +268,7 @@
             name: astFactory.simpleIdentifier(
               StringToken(TokenType.STRING, element.name, -1),
             ),
+            typeArguments: constant.arguments?.typeArguments,
           ),
           Tokens.period(),
           astFactory.simpleIdentifier(
diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart
index 1ae0a1c..c7f58df 100644
--- a/pkg/analyzer/lib/src/summary2/informative_data.dart
+++ b/pkg/analyzer/lib/src/summary2/informative_data.dart
@@ -1299,7 +1299,7 @@
       _writeDocumentationComment(node);
       _writeOffsets(
         metadata: node.metadata,
-        enumConstantArguments: node.arguments?.argumentList,
+        enumConstantArguments: node.arguments,
       );
     }
 
@@ -1433,7 +1433,7 @@
     NodeList<ConstructorInitializer>? constructorInitializers,
     NodeList<EnumConstantDeclaration>? enumConstants,
     TypeAnnotation? aliasedType,
-    ArgumentList? enumConstantArguments,
+    EnumConstantArguments? enumConstantArguments,
   }) {
     var collector = _OffsetsCollector();
 
@@ -1485,7 +1485,8 @@
       addTypeParameters(aliasedType.typeParameters);
       addFormalParameters(aliasedType.parameters);
     }
-    enumConstantArguments?.arguments.accept(collector);
+    enumConstantArguments?.typeArguments?.accept(collector);
+    enumConstantArguments?.argumentList.arguments.accept(collector);
     sink.writeUint30List(collector.offsets);
   }
 
@@ -1662,14 +1663,7 @@
 
   void applyToConstantInitializer(Element element) {
     if (element is ConstFieldElementImpl && element.isEnumConstant) {
-      var initializer = element.constantInitializer;
-      if (initializer is InstanceCreationExpression) {
-        var arguments = initializer.argumentList.arguments;
-        // Skip synthetic `index` and `name` arguments.
-        for (var argument in arguments.skip(2)) {
-          argument.accept(this);
-        }
-      }
+      _applyToEnumConstantInitializer(element);
     } else if (element is ConstVariableElement) {
       element.constantInitializer?.accept(this);
     }
@@ -1744,6 +1738,18 @@
       element.nameOffset = identifier.offset;
     }
   }
+
+  void _applyToEnumConstantInitializer(ConstFieldElementImpl element) {
+    var initializer = element.constantInitializer;
+    if (initializer is InstanceCreationExpression) {
+      initializer.constructorName.type2.typeArguments?.accept(this);
+      var arguments = initializer.argumentList.arguments;
+      // Skip synthetic `index` and `name` arguments.
+      for (var argument in arguments.skip(2)) {
+        argument.accept(this);
+      }
+    }
+  }
 }
 
 abstract class _OffsetsAstVisitor extends RecursiveAstVisitor<void> {
diff --git a/pkg/analyzer/lib/src/test_utilities/find_element.dart b/pkg/analyzer/lib/src/test_utilities/find_element.dart
index 25dfccc..7159665 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_element.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_element.dart
@@ -355,21 +355,34 @@
 
   ConstructorElement constructor(String name, {String? of}) {
     assert(name != '');
+
     ConstructorElement? result;
-    for (var class_ in unitElement.classes) {
-      if (of == null || class_.name == of) {
-        for (var constructor in class_.constructors) {
-          if (constructor.name == name) {
-            if (result != null) {
-              throw StateError('Not unique: $name');
-            }
-            result = constructor;
+
+    void findIn(List<ConstructorElement> constructors) {
+      for (var constructor in constructors) {
+        if (constructor.name == name) {
+          if (result != null) {
+            throw StateError('Not unique: $name');
           }
+          result = constructor;
         }
       }
     }
+
+    for (var class_ in unitElement.classes) {
+      if (of == null || class_.name == of) {
+        findIn(class_.constructors);
+      }
+    }
+
+    for (var enum_ in unitElement.enums) {
+      if (of == null || enum_.name == of) {
+        findIn(enum_.constructors);
+      }
+    }
+
     if (result != null) {
-      return result;
+      return result!;
     }
     throw StateError('Not found: $name');
   }
diff --git a/pkg/analyzer/test/src/dart/resolution/enum_test.dart b/pkg/analyzer/test/src/dart/resolution/enum_test.dart
index 1e71eb6..ca17f7c 100644
--- a/pkg/analyzer/test/src/dart/resolution/enum_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/enum_test.dart
@@ -15,9 +15,212 @@
 
 @reflectiveTest
 class EnumDriverResolutionTest extends PubPackageResolutionTest {
-  test_field() async {
+  test_constructor_argumentList_contextType() async {
+    await assertNoErrorsInCode(r'''
+enum E {
+  v([]);
+  E(List<int> a);
+}
+''');
+
+    assertType(findNode.listLiteral('[]'), 'List<int>');
+  }
+
+  test_constructor_argumentList_namedType() async {
+    await assertNoErrorsInCode(r'''
+enum E {
+  v(<void Function(double)>[]);
+  E(Object a);
+}
+''');
+
+    assertNamedType(
+      findNode.namedType('double'),
+      doubleElement,
+      'double',
+    );
+
+    assertType(
+      findNode.genericFunctionType('void Function'),
+      'void Function(double)',
+    );
+  }
+
+  test_constructor_generic_noTypeArguments_named() async {
     await assertNoErrorsInCode(r'''
 enum E<T> {
+  v.named(42);
+  E.named(T a);
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: elementMatcher(
+        findElement.constructor('named'),
+        substitution: {'T': 'int'},
+      ),
+    );
+
+    assertParameterElement(
+      findNode.integerLiteral('42'),
+      elementMatcher(
+        findElement.parameter('a'),
+        substitution: {'T': 'int'},
+      ),
+    );
+  }
+
+  test_constructor_generic_noTypeArguments_unnamed() async {
+    await assertNoErrorsInCode(r'''
+enum E<T> {
+  v(42);
+  E(T a);
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: elementMatcher(
+        findElement.enum_('E').unnamedConstructor,
+        substitution: {'T': 'int'},
+      ),
+    );
+
+    assertParameterElement(
+      findNode.integerLiteral('42'),
+      elementMatcher(
+        findElement.parameter('a'),
+        substitution: {'T': 'int'},
+      ),
+    );
+  }
+
+  test_constructor_generic_typeArguments_named() async {
+    await assertNoErrorsInCode(r'''
+enum E<T> {
+  v<double>.named(42);
+  E.named(T a);
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: elementMatcher(
+        findElement.constructor('named'),
+        substitution: {'T': 'double'},
+      ),
+    );
+
+    assertNamedType(
+      findNode.namedType('double'),
+      doubleElement,
+      'double',
+    );
+
+    assertParameterElement(
+      findNode.integerLiteral('42'),
+      elementMatcher(
+        findElement.parameter('a'),
+        substitution: {'T': 'double'},
+      ),
+    );
+  }
+
+  test_constructor_notGeneric_named() async {
+    await assertNoErrorsInCode(r'''
+enum E {
+  v.named(42);
+  E.named(int a);
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: findElement.constructor('named'),
+    );
+
+    assertParameterElement(
+      findNode.integerLiteral('42'),
+      findElement.parameter('a'),
+    );
+  }
+
+  test_constructor_notGeneric_unnamed() async {
+    await assertNoErrorsInCode(r'''
+enum E {
+  v(42);
+  E(int a);
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: findElement.enum_('E').unnamedConstructor,
+    );
+
+    assertParameterElement(
+      findNode.integerLiteral('42'),
+      findElement.parameter('a'),
+    );
+  }
+
+  test_constructor_notGeneric_unnamed_implicit() async {
+    await assertNoErrorsInCode(r'''
+enum E {
+  v
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: findElement.enum_('E').unnamedConstructor,
+    );
+  }
+
+  test_constructor_unresolved_named() async {
+    await assertNoErrorsInCode(r'''
+enum E {
+  v.named(42);
+  E(int a);
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: null,
+    );
+
+    assertParameterElement(findNode.integerLiteral('42'), null);
+  }
+
+  test_constructor_unresolved_unnamed() async {
+    await assertNoErrorsInCode(r'''
+enum E {
+  v(42);
+  E.named(int a);
+}
+''');
+
+    assertEnumConstant(
+      findNode.enumConstantDeclaration('v'),
+      element: findElement.field('v'),
+      constructorElement: null,
+    );
+
+    assertParameterElement(findNode.integerLiteral('42'), null);
+  }
+
+  test_field() async {
+    await assertNoErrorsInCode(r'''
+enum E {
   v;
   final foo = 42;
 }
@@ -88,7 +291,7 @@
 
   test_method_toString() async {
     await assertNoErrorsInCode(r'''
-enum E<T> {
+enum E {
   v;
   String toString() => 'E';
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/index_expression_test.dart b/pkg/analyzer/test/src/dart/resolution/index_expression_test.dart
index c02261d..384b9be 100644
--- a/pkg/analyzer/test/src/dart/resolution/index_expression_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/index_expression_test.dart
@@ -122,7 +122,10 @@
     );
     assertParameterElement(
       indexExpression.index,
-      indexElement.parameters[0],
+      elementMatcher(
+        indexElement.parameters[0],
+        substitution: {'T': 'double'},
+      ),
     );
   }
 
@@ -230,7 +233,10 @@
     }
     assertParameterElement(
       indexExpression.index,
-      indexEqElement.parameters[0],
+      elementMatcher(
+        indexEqElement.parameters[0],
+        substitution: {'T': 'double'},
+      ),
     );
 
     var assignment = indexExpression.parent as AssignmentExpression;
@@ -426,7 +432,10 @@
     }
     assertParameterElement(
       indexExpression.index,
-      indexEqElement.parameters[0],
+      elementMatcher(
+        indexEqElement.parameters[0],
+        substitution: {'T': 'double'},
+      ),
     );
 
     var assignment = indexExpression.parent as AssignmentExpression;
@@ -444,7 +453,10 @@
     );
     assertParameterElement(
       assignment.rightHandSide,
-      indexEqElement.parameters[1],
+      elementMatcher(
+        indexEqElement.parameters[1],
+        substitution: {'T': 'double'},
+      ),
     );
   }
 
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 35aaf17..e76b324 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -285,6 +285,15 @@
     expect(element.enclosingElement, expectedEnclosing);
   }
 
+  void assertEnumConstant(
+    EnumConstantDeclaration node, {
+    required FieldElement element,
+    required Object? constructorElement,
+  }) {
+    assertElement(node.declaredElement, element);
+    assertElement(node.constructorElement, constructorElement);
+  }
+
   Future<void> assertErrorsInCode(
       String code, List<ExpectedError> expectedErrors) async {
     addTestFile(code);
@@ -627,9 +636,9 @@
 
   void assertParameterElement(
     Expression expression,
-    ParameterElement expected,
+    Object? elementOrMatcher,
   ) {
-    expect(expression.staticParameterElement, expected);
+    assertElement(expression.staticParameterElement, elementOrMatcher);
   }
 
   void assertParameterElementType(FormalParameter node, String expected) {
diff --git a/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart b/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart
index 0fd804c..4fc7b70 100644
--- a/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart
@@ -22,7 +22,10 @@
   return const E();
 }
 ''', [
+      error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 43, 9),
+      error(CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, 43, 9),
       error(CompileTimeErrorCode.INSTANTIATE_ENUM, 49, 1),
+      error(CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS, 50, 2),
     ]);
   }
 
@@ -34,6 +37,7 @@
 }
 ''', [
       error(CompileTimeErrorCode.INSTANTIATE_ENUM, 47, 1),
+      error(CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS, 48, 2),
     ]);
   }
 }
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 3e35554..4d9cd96 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -17748,6 +17748,109 @@
 ''');
   }
 
+  test_enum_constant_typeArguments() async {
+    var library = await checkLibrary(r'''
+enum E<T> {
+  v<double>(42);
+  E(T a);
+}
+''');
+    checkElementText(library, r'''
+library
+  definingUnit
+    enums
+      enum E @5
+        typeParameters
+          covariant T @7
+            defaultType: dynamic
+        supertype: Enum
+        fields
+          static const enumConstant v @14
+            type: E<double>
+            constantInitializer
+              InstanceCreationExpression
+                argumentList: ArgumentList
+                  arguments
+                    IntegerLiteral
+                      literal: 0 @0
+                      staticType: int
+                    SimpleStringLiteral
+                      literal: 'v' @0
+                    IntegerLiteral
+                      literal: 42 @24
+                      staticType: double
+                  leftParenthesis: ( @0
+                  rightParenthesis: ) @0
+                constructorName: ConstructorName
+                  name: SimpleIdentifier
+                    staticElement: ConstructorMember
+                      base: self::@enum::E::@constructor::•
+                      substitution: {T: double}
+                    staticType: null
+                    token:  @-1
+                  period: . @0
+                  staticElement: ConstructorMember
+                    base: self::@enum::E::@constructor::•
+                    substitution: {T: double}
+                  type: NamedType
+                    name: SimpleIdentifier
+                      staticElement: self::@enum::E
+                      staticType: null
+                      token: E @-1
+                    type: E<double>
+                    typeArguments: TypeArgumentList
+                      arguments
+                        NamedType
+                          name: SimpleIdentifier
+                            staticElement: dart:core::@class::double
+                            staticType: null
+                            token: double @16
+                          type: double
+                      leftBracket: < @15
+                      rightBracket: > @22
+                staticType: E<double>
+          synthetic static const values @-1
+            type: List<E<dynamic>>
+            constantInitializer
+              ListLiteral
+                elements
+                  SimpleIdentifier
+                    staticElement: self::@enum::E::@getter::v
+                    staticType: E<double>
+                    token: v @-1
+                leftBracket: [ @0
+                rightBracket: ] @0
+                staticType: List<E<dynamic>>
+          synthetic final index @-1
+            type: int
+          synthetic final _name @-1
+            type: String
+        constructors
+          @31
+            parameters
+              requiredPositional final this.index @-1
+                type: int
+                field: self::@enum::E::@field::index
+              requiredPositional final this._name @-1
+                type: String
+                field: self::@enum::E::@field::_name
+              requiredPositional a @35
+                type: T
+        accessors
+          synthetic static get v @-1
+            returnType: E<double>
+          synthetic static get values @-1
+            returnType: List<E<dynamic>>
+          synthetic get index @-1
+            returnType: int
+          synthetic get _name @-1
+            returnType: String
+        methods
+          synthetic toString @-1
+            returnType: String
+''');
+  }
+
   test_enum_constant_underscore() async {
     var library = await checkLibrary('''
 enum E {
diff --git a/pkg/analyzer/test/verify_diagnostics_test.dart b/pkg/analyzer/test/verify_diagnostics_test.dart
index 873f700..84ce4b3 100644
--- a/pkg/analyzer/test/verify_diagnostics_test.dart
+++ b/pkg/analyzer/test/verify_diagnostics_test.dart
@@ -50,6 +50,8 @@
     // (such as `JSBool b;`), but that would complicate the example.
     'CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY',
     // Produces two diagnostics when it should only produce one.
+    'CompileTimeErrorCode.INSTANTIATE_ENUM',
+    // Produces two diagnostics when it should only produce one.
     'CompileTimeErrorCode.INVALID_URI',
     // Produces two diagnostics when it should only produce one.
     'CompileTimeErrorCode.INVALID_USE_OF_NULL_VALUE',