Version 2.17.0-56.0.dev
Merge commit '1708703d159b97d950b152a698afb53317cf2f78' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
index 252ccf6..acceb63 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
@@ -144,12 +144,26 @@
_MatcherBuilder(this.importedUris);
void buildMatchersForNode(AstNode? node) {
- var components = _componentsForNode(node);
- if (components == null) {
- return;
+ if (node is ArgumentList) {
+ _buildFromArgumentList(node);
+ } else if (node is BinaryExpression) {
+ _buildFromBinaryExpression(node);
+ } else if (node is ConstructorName) {
+ _buildFromConstructorName(node);
+ } else if (node is Literal) {
+ var parent = node.parent;
+ if (parent is ArgumentList) {
+ _buildFromArgumentList(parent);
+ }
+ } else if (node is NamedType) {
+ _buildFromNamedType(node);
+ } else if (node is PrefixedIdentifier) {
+ _buildFromPrefixedIdentifier(node);
+ } else if (node is SimpleIdentifier) {
+ _buildFromSimpleIdentifier(node);
+ } else if (node is TypeArgumentList) {
+ _buildFromTypeArgumentList(node);
}
- var kinds = _kindsForNode(node) ?? [];
- _addMatcher(components: components, kinds: kinds);
}
void _addMatcher(
@@ -158,58 +172,311 @@
importedUris: importedUris, components: components, kinds: kinds));
}
- /// Return the components of the path of the element associated with the given
- /// [node]. The components are ordered from the most local to the most global.
- /// For example, for a constructor this would be the name of the constructor
- /// followed by the name of the class in which the constructor is declared
- /// (with an empty string for the unnamed constructor).
- static List<String>? _componentsForNode(AstNode? node) {
- if (node is SimpleIdentifier) {
- var parent = node.parent;
- if (parent is Label && parent.parent is NamedExpression) {
- // The parent of the named expression is an argument list. Because we
- // don't represent parameters as elements, the element we need to match
- // against is the invocation containing those arguments.
- return _componentsFromParent(parent.parent!.parent!);
- } else if (parent is NamedType && parent.parent is ConstructorName) {
- return ['', node.name];
- } else if (parent is MethodDeclaration && node == parent.name) {
- return [node.name];
- } else if ((parent is MethodInvocation &&
- node == parent.methodName &&
- !_isPrefix(parent.target)) ||
- (parent is PrefixedIdentifier &&
- node == parent.identifier &&
- !_isPrefix(parent.prefix)) ||
- (parent is PropertyAccess &&
- node == parent.propertyName &&
- !_isPrefix(parent.target))) {
- return _componentsFromParent(node);
+ /// Build a matcher for the element being invoked.
+ void _buildFromArgumentList(ArgumentList node) {
+ var parent = node.parent;
+ if (parent is Annotation) {
+ _addMatcher(
+ components: [parent.constructorName?.name ?? '', parent.name.name],
+ kinds: [ElementKind.constructorKind],
+ );
+ // } else if (parent is ExtensionOverride) {
+ // // TODO(brianwilkerson) Determine whether this branch can be reached.
+ // _buildFromExtensionOverride(parent);
+ } else if (parent is FunctionExpressionInvocation) {
+ _buildFromFunctionExpressionInvocation(parent);
+ } else if (parent is InstanceCreationExpression) {
+ _buildFromInstanceCreationExpression(parent);
+ } else if (parent is MethodInvocation) {
+ _buildFromMethodInvocation(parent);
+ } else if (parent is RedirectingConstructorInvocation) {
+ var grandparent = parent.parent;
+ if (grandparent is ConstructorDeclaration) {
+ _addMatcher(
+ components: [
+ parent.constructorName?.name ?? '',
+ grandparent.returnType.name
+ ],
+ kinds: [ElementKind.constructorKind],
+ );
}
- return _componentsFromIdentifier(node);
- } else if (node is PrefixedIdentifier) {
- var parent = node.parent;
- if (parent is NamedType && parent.parent is ConstructorName) {
- return ['', node.identifier.name];
+ } else if (parent is SuperConstructorInvocation) {
+ var superclassName = parent.staticElement?.enclosingElement.name;
+ if (superclassName != null) {
+ _addMatcher(
+ components: [parent.constructorName?.name ?? '', superclassName],
+ kinds: [ElementKind.constructorKind],
+ );
}
- return [node.identifier.name];
- } else if (node is ConstructorName) {
- var constructorName = node.name;
- if (constructorName != null) {
- return [constructorName.name];
- }
- } else if (node is NamedType) {
- return [node.name.name];
- } else if (node is TypeArgumentList) {
- return _componentsFromParent(node);
- } else if (node is ArgumentList) {
- return _componentsFromParent(node);
}
- var parent = node?.parent;
- if (parent is ArgumentList) {
- return _componentsFromParent(parent);
+ }
+
+ /// Build a matcher for the operator being invoked.
+ void _buildFromBinaryExpression(BinaryExpression node) {
+ // TODO(brianwilkerson) Implement this method in order to support changes to
+ // operators.
+ }
+
+ /// Build a matcher for the constructor being referenced.
+ void _buildFromConstructorName(ConstructorName node) {
+ // TODO(brianwilkerson) Use the static element, if there is one, in order to
+ // get a more exact matcher.
+ // TODO(brianwilkerson) Use 'new' for the name of the unnamed constructor.
+ var constructorName = node.name?.name ?? ''; // ?? 'new';
+ var className = node.type2.name.simpleName;
+ _addMatcher(
+ components: [constructorName, className],
+ kinds: const [ElementKind.constructorKind],
+ );
+ _addMatcher(
+ components: [className],
+ kinds: const [ElementKind.classKind],
+ );
+ }
+
+ /// Build a matcher for the extension.
+ void _buildFromExtensionOverride(ExtensionOverride node) {
+ _addMatcher(
+ components: [node.extensionName.name],
+ kinds: [ElementKind.extensionKind],
+ );
+ }
+
+ /// Build a matcher for the function being invoked.
+ void _buildFromFunctionExpressionInvocation(
+ FunctionExpressionInvocation node) {
+ // TODO(brianwilkerson) This case was missed in the original implementation
+ // and there are no tests for it at this point, but it ought to be supported.
+ }
+
+ /// Build a matcher for the constructor being invoked.
+ void _buildFromInstanceCreationExpression(InstanceCreationExpression node) {
+ _buildFromConstructorName(node.constructorName);
+ }
+
+ /// Build a matcher for the method being declared.
+ void _buildFromMethodDeclaration(MethodDeclaration node) {
+ _addMatcher(
+ components: [node.name.name],
+ kinds: [ElementKind.methodKind],
+ );
+ }
+
+ /// Build a matcher for the method being invoked.
+ void _buildFromMethodInvocation(MethodInvocation node) {
+ // TODO(brianwilkerson) Use the static element, if there is one, in order to
+ // get a more exact matcher.
+ // var element = node.methodName.staticElement;
+ // if (element != null) {
+ // return _buildFromElement(element);
+ // }
+ var methodName = node.methodName;
+ var targetName = _nameOfTarget(node.realTarget);
+ if (targetName != null) {
+ // If there is a target, and we know the type of the target, then we know
+ // that a method is being invoked.
+ _addMatcher(
+ components: [methodName.name, targetName],
+ kinds: [
+ ElementKind.constructorKind,
+ ElementKind.methodKind,
+ ],
+ );
+ } else if (node.realTarget != null) {
+ // If there is a target, but we don't know the type of the target, then
+ // the target type might be undefined and this might have been either a
+ // method invocation, an invocation of a function returned by a getter, or
+ // a constructor invocation prior to the type having been deleted.
+ _addMatcher(
+ components: _componentsFromIdentifier(methodName),
+ kinds: [
+ ElementKind.constructorKind,
+ ElementKind.getterKind,
+ ElementKind.methodKind,
+ ],
+ );
+ } else {
+ // If there is no target, then this might have been either a method
+ // invocation, a function invocation (of either a function or a function
+ // returned from a getter), a constructor invocation, or an extension
+ // override. If it's a constructor, then the change might have been to the
+ // class rather than an individual constructor.
+ _addMatcher(
+ components: _componentsFromIdentifier(methodName),
+ kinds: [
+ ElementKind.classKind,
+ ElementKind.constructorKind,
+ ElementKind.extensionKind,
+ ElementKind.functionKind,
+ ElementKind.getterKind,
+ ElementKind.methodKind,
+ ],
+ );
}
- return null;
+ }
+
+ /// Build a matcher for the type.
+ void _buildFromNamedType(NamedType node) {
+ var parent = node.parent;
+ if (parent is ConstructorName) {
+ return _buildFromConstructorName(parent);
+ }
+ // TODO(brianwilkerson) Use the static element, if there is one, in order to
+ // get a more exact matcher.
+ _addMatcher(
+ components: [node.name.simpleName],
+ kinds: const [
+ ElementKind.classKind,
+ ElementKind.enumKind,
+ ElementKind.mixinKind,
+ ElementKind.typedefKind
+ ],
+ );
+ // TODO(brianwilkerson) Determine whether we can ever get here as a result
+ // of having a removed unnamed constructor.
+ // _addMatcher(
+ // components: ['', node.name.name],
+ // kinds: const [ElementKind.constructorKind],
+ // );
+ }
+
+ /// Build a matcher for the element represented by the prefixed identifier.
+ void _buildFromPrefixedIdentifier(PrefixedIdentifier node) {
+ var parent = node.parent;
+ if (parent is NamedType) {
+ return _buildFromNamedType(parent);
+ }
+ // TODO(brianwilkerson) Use the static element, if there is one, in order to
+ // get a more exact matcher.
+ var prefix = node.prefix;
+ if (prefix.staticElement is PrefixElement) {
+ var parent = node.parent;
+ if ((parent is NamedType && parent.parent is! ConstructorName) ||
+ (parent is PropertyAccess && parent.target == node)) {
+ _addMatcher(components: [
+ node.identifier.name
+ ], kinds: const [
+ ElementKind.classKind,
+ ElementKind.enumKind,
+ ElementKind.extensionKind,
+ ElementKind.mixinKind,
+ ElementKind.typedefKind
+ ]);
+ }
+ _addMatcher(components: [
+ node.identifier.name
+ ], kinds: const [
+ // If the old class has been removed then this might have been a
+ // constructor invocation.
+ ElementKind.constructorKind,
+ ElementKind.functionKind, // tear-off
+ ElementKind.getterKind,
+ ElementKind.setterKind,
+ ElementKind.variableKind
+ ]);
+ }
+ // It looks like we're accessing a member, so try to figure out the
+ // name of the type defining the member.
+ var targetType = node.prefix.staticType;
+ if (targetType is InterfaceType) {
+ _addMatcher(
+ components: [node.identifier.name, targetType.element.name],
+ kinds: const [
+ ElementKind.constantKind,
+ ElementKind.fieldKind,
+ ElementKind.functionKind, // tear-off
+ ElementKind.getterKind,
+ ElementKind.methodKind, // tear-off
+ ElementKind.setterKind
+ ],
+ );
+ }
+ // It looks like we're accessing a member, but we don't know what kind of
+ // member, so we include all of the member kinds.
+ var container = node.prefix.staticElement;
+ if (container is ClassElement) {
+ _addMatcher(
+ components: [node.identifier.name, container.name],
+ kinds: const [
+ ElementKind.constantKind,
+ ElementKind.fieldKind,
+ ElementKind.functionKind, // tear-off
+ ElementKind.getterKind,
+ ElementKind.methodKind, // tear-off
+ ElementKind.setterKind
+ ],
+ );
+ }
+ }
+
+ /// Build a matcher for the property being accessed.
+ void _buildFromPropertyAccess(PropertyAccess node) {
+ // TODO(brianwilkerson) Use the static element, if there is one, in order to
+ // get a more exact matcher.
+ var propertyName = node.propertyName;
+ var targetName = _nameOfTarget(node.realTarget);
+ List<String> components;
+ if (targetName != null) {
+ components = [propertyName.name, targetName];
+ } else {
+ components = _componentsFromIdentifier(propertyName);
+ }
+ _addMatcher(
+ components: components,
+ kinds: const [
+ ElementKind.constantKind,
+ ElementKind.fieldKind,
+ ElementKind.functionKind, // tear-off, prefixed
+ ElementKind.getterKind,
+ ElementKind.methodKind, // tear-off, prefixed
+ ElementKind.setterKind
+ ],
+ );
+ }
+
+ /// Build a matcher for the element referenced by the identifier.
+ void _buildFromSimpleIdentifier(SimpleIdentifier node) {
+ // TODO(brianwilkerson) Use the static element, if there is one, in order to
+ // get a more exact matcher.
+ var parent = node.parent;
+ if (parent is Label && parent.parent is NamedExpression) {
+ // The parent of the named expression is an argument list. Because we
+ // don't represent parameters as elements, the element we need to match
+ // against is the invocation containing those arguments.
+ _buildFromArgumentList(parent.parent!.parent as ArgumentList);
+ } else if (parent is NamedType) {
+ _buildFromNamedType(parent);
+ } else if (parent is MethodDeclaration && node == parent.name) {
+ _buildFromMethodDeclaration(parent);
+ } else if (parent is MethodInvocation &&
+ node == parent.methodName &&
+ !_isPrefix(parent.target)) {
+ _buildFromMethodInvocation(parent);
+ } else if (parent is PrefixedIdentifier && node == parent.identifier) {
+ _buildFromPrefixedIdentifier(parent);
+ } else if (parent is PropertyAccess &&
+ node == parent.propertyName &&
+ !_isPrefix(parent.target)) {
+ _buildFromPropertyAccess(parent);
+ } else {
+ // TODO(brianwilkerson) See whether the list of kinds can be specified.
+ _addMatcher(components: [node.name], kinds: []);
+ }
+ }
+
+ /// Build a matcher for the element with which the type arguments are
+ /// associated.
+ void _buildFromTypeArgumentList(TypeArgumentList node) {
+ var parent = node.parent;
+ if (parent is ExtensionOverride) {
+ _buildFromExtensionOverride(parent);
+ } else if (parent is FunctionExpressionInvocation) {
+ _buildFromFunctionExpressionInvocation(parent);
+ } else if (parent is InstanceCreationExpression) {
+ _buildFromInstanceCreationExpression(parent);
+ } else if (parent is MethodInvocation) {
+ _buildFromMethodInvocation(parent);
+ }
}
/// Return the components associated with the [identifier] when there is no
@@ -236,162 +503,11 @@
return [identifier.name];
}
- /// Return the components for the element associated with the given [node] by
- /// looking at the parent of the [node].
- static List<String>? _componentsFromParent(AstNode node) {
- var parent = node.parent;
- if (parent is ArgumentList) {
- parent = parent.parent;
- }
- if (parent is Annotation) {
- return [parent.constructorName?.name ?? '', parent.name.name];
- } else if (parent is ExtensionOverride) {
- return [parent.extensionName.name];
- } else if (parent is InstanceCreationExpression) {
- var constructorName = parent.constructorName;
- return [
- constructorName.name?.name ?? '',
- constructorName.type2.name.name
- ];
- } else if (parent is MethodInvocation) {
- var methodName = parent.methodName;
- var targetName = _nameOfTarget(parent.realTarget);
- if (targetName != null) {
- return [methodName.name, targetName];
- }
- return _componentsFromIdentifier(methodName);
- } else if (parent is PrefixedIdentifier) {
- var identifier = parent.identifier;
- var targetName = _nameOfTarget(parent.prefix);
- if (targetName != null) {
- return [identifier.name, targetName];
- }
- return _componentsFromIdentifier(identifier);
- } else if (parent is PropertyAccess) {
- var propertyName = parent.propertyName;
- var targetName = _nameOfTarget(parent.realTarget);
- if (targetName != null) {
- return [propertyName.name, targetName];
- }
- return _componentsFromIdentifier(propertyName);
- } else if (parent is RedirectingConstructorInvocation) {
- var ancestor = parent.parent;
- if (ancestor is ConstructorDeclaration) {
- return [parent.constructorName?.name ?? '', ancestor.returnType.name];
- }
- } else if (parent is SuperConstructorInvocation) {
- var ancestor = parent.parent;
- if (ancestor is ConstructorDeclaration) {
- return [parent.constructorName?.name ?? '', ancestor.returnType.name];
- }
- }
- return null;
- }
-
/// Return `true` if the [node] is a prefix
static bool _isPrefix(AstNode? node) {
return node is SimpleIdentifier && node.staticElement is PrefixElement;
}
- /// Return the kinds of elements that could reasonably be referenced at the
- /// location of the [node]. If [child] is not `null` then the [node] is a
- /// parent of the [child].
- static List<ElementKind>? _kindsForNode(AstNode? node, {AstNode? child}) {
- if (node is ConstructorName) {
- return const [ElementKind.constructorKind];
- } else if (node is ExtensionOverride) {
- return const [ElementKind.extensionKind];
- } else if (node is InstanceCreationExpression) {
- return const [ElementKind.constructorKind];
- } else if (node is Label) {
- var argumentList = node.parent?.parent;
- return _kindsForNode(argumentList?.parent, child: argumentList);
- } else if (node is MethodInvocation) {
- assert(child != null);
- if (node.target == child) {
- return const [
- ElementKind.classKind,
- ElementKind.enumKind,
- ElementKind.mixinKind
- ];
- }
- var realTarget = node.realTarget;
- if (realTarget != null && !_isPrefix(realTarget)) {
- return const [ElementKind.constructorKind, ElementKind.methodKind];
- }
- return const [
- ElementKind.classKind,
- ElementKind.extensionKind,
- ElementKind.functionKind,
- ElementKind.methodKind
- ];
- } else if (node is NamedType) {
- var parent = node.parent;
- if (parent is ConstructorName && parent.name == null) {
- return const [ElementKind.classKind, ElementKind.constructorKind];
- }
- return const [
- ElementKind.classKind,
-// ElementKind.constructorKind,
- ElementKind.enumKind,
- ElementKind.mixinKind,
- ElementKind.typedefKind
- ];
- } else if (node is PrefixedIdentifier) {
- var prefix = node.prefix;
- if (prefix == child) {
- return const [
- ElementKind.classKind,
- ElementKind.enumKind,
- ElementKind.extensionKind,
- ElementKind.mixinKind,
- ElementKind.typedefKind
- ];
- } else if (prefix.staticElement is PrefixElement) {
- var parent = node.parent;
- if ((parent is NamedType && parent.parent is! ConstructorName) ||
- (parent is PropertyAccess && parent.target == node)) {
- return const [
- ElementKind.classKind,
- ElementKind.enumKind,
- ElementKind.extensionKind,
- ElementKind.mixinKind,
- ElementKind.typedefKind
- ];
- }
- return const [
- // If the old class has been removed then this might have been a
- // constructor invocation.
- ElementKind.constructorKind,
- ElementKind.functionKind, // tear-off
- ElementKind.getterKind,
- ElementKind.setterKind,
- ElementKind.variableKind
- ];
- }
- return const [
- ElementKind.constantKind,
- ElementKind.fieldKind,
- ElementKind.functionKind, // tear-off
- ElementKind.getterKind,
- ElementKind.methodKind, // tear-off
- ElementKind.setterKind
- ];
- } else if (node is PropertyAccess) {
- return const [
- ElementKind.constantKind,
- ElementKind.fieldKind,
- ElementKind.functionKind, // tear-off, prefixed
- ElementKind.getterKind,
- ElementKind.methodKind, // tear-off, prefixed
- ElementKind.setterKind
- ];
- } else if (node is SimpleIdentifier) {
- return _kindsForNode(node.parent, child: node);
- }
- return null;
- }
-
/// Return the name of the class associated with the given [target].
static String? _nameOfTarget(Expression? target) {
if (target is SimpleIdentifier) {
@@ -416,3 +532,14 @@
return null;
}
}
+
+extension on Identifier {
+ String get simpleName {
+ var identifier = this;
+ if (identifier is PrefixedIdentifier) {
+ // The prefix isn't part of the name of the class.
+ return identifier.identifier.name;
+ }
+ return name;
+ }
+}
diff --git a/pkg/analysis_server/test/analysis/get_hover_test.dart b/pkg/analysis_server/test/analysis/get_hover_test.dart
index 980ec6e..6d19ce9 100644
--- a/pkg/analysis_server/test/analysis/get_hover_test.dart
+++ b/pkg/analysis_server/test/analysis/get_hover_test.dart
@@ -933,6 +933,54 @@
expect(hover.elementKind, 'parameter');
}
+ Future<void>
+ test_parameter_ofConstructor_optionalPositional_super_defaultValue_explicit() async {
+ addTestFile('''
+class A {
+ A([int a = 1]);
+}
+class B extends A {
+ B([super.a = 2]);
+}
+''');
+ var hover = await prepareHover('a = 2]');
+ // element
+ expect(hover.elementDescription, '[int a = 2]');
+ expect(hover.elementKind, 'parameter');
+ }
+
+ Future<void>
+ test_parameter_ofConstructor_optionalPositional_super_defaultValue_inherited() async {
+ addTestFile('''
+class A {
+ A([int a = 1]);
+}
+class B extends A {
+ B([super.a]);
+}
+''');
+ var hover = await prepareHover('a]');
+ // element
+ expect(hover.elementDescription, '[int a = 1]');
+ expect(hover.elementKind, 'parameter');
+ }
+
+ Future<void>
+ test_parameter_ofConstructor_optionalPositional_super_defaultValue_inherited2() async {
+ addTestFile('''
+class A {
+ A([num a = 1.2]);
+}
+class B extends A{
+ B([int super.a]);
+}
+''');
+ var hover = await prepareHover('a]');
+ // element
+ expect(hover.elementDescription, '[int a]');
+ expect(hover.elementKind, 'parameter');
+ }
+
Future<void> test_parameter_reference_fieldFormal() async {
addTestFile('''
class A {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
index dff9c05..6839e8f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
@@ -33,7 +33,7 @@
void C() {}
}
''');
- setPackageData(_add(0, components: ['C', 'C']));
+ setPackageData(_add(0, components: ['C']));
await resolveTestCode('''
import '$importUri';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
index 716cd55..e7a0f03 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
@@ -54,8 +54,10 @@
/// The kinds that are expected where an invocation is allowed.
static List<ElementKind> invocationKinds = [
ElementKind.classKind,
+ ElementKind.constructorKind,
ElementKind.extensionKind,
ElementKind.functionKind,
+ ElementKind.getterKind,
ElementKind.methodKind,
];
@@ -109,7 +111,7 @@
}
}
''');
- _assertMatcher('g;', expectedComponents: ['g', 'C']);
+ _assertMatcher('g;', expectedComponents: ['g']);
}
Future<void> test_getter_withoutTarget_unresolved() async {
@@ -149,17 +151,7 @@
s.length;
}
''');
- // TODO(brianwilkerson) Several of these kinds don't seem to be appropriate,
- // so we might want to narrow down the list.
- _assertMatcher('s', expectedComponents: [
- 's'
- ], expectedKinds: [
- ElementKind.classKind,
- ElementKind.enumKind,
- ElementKind.extensionKind,
- ElementKind.mixinKind,
- ElementKind.typedefKind,
- ]);
+ _assertMatcher('s', expectedComponents: ['s'], expectedKinds: []);
}
Future<void> test_method_withoutTarget_resolved() async {
@@ -217,7 +209,7 @@
}
}
''');
- _assertMatcher('s =', expectedComponents: ['s', 'C']);
+ _assertMatcher('s =', expectedComponents: ['s']);
}
Future<void> test_setter_withoutTarget_unresolved() async {
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 6101710..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);
}
@@ -1584,23 +1584,19 @@
@override
String? get defaultValueCode {
- return constantInitializer?.toSource();
- }
-
- @override
- bool get hasDefaultValue {
- if (super.hasDefaultValue) {
- return true;
- }
- return computeConstantValue() != null;
- }
-
- @override
- DartObject? computeConstantValue() {
+ final constantInitializer = this.constantInitializer;
if (constantInitializer != null) {
- return super.computeConstantValue();
+ return constantInitializer.toSource();
}
+ if (_superConstructorParameterDefaultValue != null) {
+ return superConstructorParameter?.defaultValueCode;
+ }
+
+ return null;
+ }
+
+ DartObject? get _superConstructorParameterDefaultValue {
var superDefault = superConstructorParameter?.computeConstantValue();
var superDefaultType = superDefault?.type;
var libraryElement = library;
@@ -1612,6 +1608,15 @@
return null;
}
+
+ @override
+ DartObject? computeConstantValue() {
+ if (constantInitializer != null) {
+ return super.computeConstantValue();
+ }
+
+ return _superConstructorParameterDefaultValue;
+ }
}
/// The synthetic element representing the declaration of the type `dynamic`.
@@ -2768,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',
diff --git a/tools/VERSION b/tools/VERSION
index a4cf01a..5d43e34 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 55
+PRERELEASE 56
PRERELEASE_PATCH 0
\ No newline at end of file