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',