From 0851b9ae4cd2de81a9d75d45abb3ac7f3fd035a4 Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Mon, 9 Sep 2019 19:13:06 +0000 Subject: [PATCH] Consider both getter and setter when deciding whether an extension reference is ambiguous. Change-Id: I8acbabda7f748e7415bd38a957e96f906afd7f6b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116250 Reviewed-by: Brian Wilkerson Commit-Queue: Konstantin Shcheglov --- pkg/analyzer/lib/src/dart/element/member.dart | 20 +++ .../resolver/extension_member_resolver.dart | 165 +++++++++--------- .../resolver/method_invocation_resolver.dart | 25 ++- .../src/dart/resolver/resolution_result.dart | 34 +++- .../lib/src/generated/element_resolver.dart | 128 +++++++------- .../resolution/extension_method_test.dart | 42 ----- ...mbiguous_extension_member_access_test.dart | 91 +++++++--- 7 files changed, 267 insertions(+), 238 deletions(-) diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart index d5f1df9674d81..fda6a4d15c14d 100644 --- a/pkg/analyzer/lib/src/dart/element/member.dart +++ b/pkg/analyzer/lib/src/dart/element/member.dart @@ -340,6 +340,16 @@ class FieldMember extends VariableMember implements FieldElement { Substitution.fromInterfaceType(definingType), ); } + + static FieldElement from2( + FieldElement element, + MapSubstitution substitution, + ) { + if (substitution.map.isEmpty) { + return element; + } + return FieldMember(element, substitution); + } } /** @@ -643,6 +653,16 @@ class MethodMember extends ExecutableMember implements MethodElement { Substitution.fromInterfaceType(definingType), ); } + + static MethodElement from2( + MethodElement element, + MapSubstitution substitution, + ) { + if (substitution.map.isEmpty) { + return element; + } + return MethodMember(element, substitution); + } } /** diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart index 367d112a053e4..931287a62a310 100644 --- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart @@ -31,26 +31,30 @@ class ExtensionMemberResolver { TypeSystem get _typeSystem => _resolver.typeSystem; /// Return the most specific extension in the current scope for this [type], - /// that defines the member with the the [name] and [kind]. + /// that defines the member with the given [name]. /// - /// If no applicable extensions, return `null`. + /// If no applicable extensions, return [ResolutionResult.none]. /// - /// If the match is ambiguous, report an error and return `null`. + /// If the match is ambiguous, report an error and return + /// [ResolutionResult.ambiguous]. ResolutionResult findExtension( - DartType type, String name, Expression target, ElementKind kind) { - var extensions = _getApplicable(type, name, kind); + DartType type, + String name, + Expression target, + ) { + var extensions = _getApplicable(type, name); if (extensions.isEmpty) { return ResolutionResult.none; } if (extensions.length == 1) { - return ResolutionResult(extensions[0].instantiatedMember); + return extensions[0].asResolutionResult; } var extension = _chooseMostSpecific(extensions); if (extension != null) { - return ResolutionResult(extension.instantiatedMember); + return extension.asResolutionResult; } _errorReporter.reportErrorForNode( @@ -58,29 +62,33 @@ class ExtensionMemberResolver { target, [ name, - extensions[0].element.name, - extensions[1].element.name, + extensions[0].extension.name, + extensions[1].extension.name, ], ); return ResolutionResult.ambiguous; } - /// Return the member with the [name] (without `=`) of the given [kind]. + /// Return the member with the [name] (without `=`). /// /// The [node] is fully resolved, and its type arguments are set. ExecutableElement getOverrideMember( - ExtensionOverride node, String name, ElementKind kind) { + ExtensionOverride node, + String name, { + bool setter = false, + }) { ExtensionElement element = node.extensionName.staticElement; ExecutableElement member; - if (kind == ElementKind.GETTER) { - member = element.getGetter(name); - } else if (kind == ElementKind.METHOD) { - member = element.getMethod(name); - } else if (kind == ElementKind.SETTER) { + if (setter) { member = element.getSetter(name); + } else { + member = element.getGetter(name) ?? element.getMethod(name); + } + + if (member == null) { + return null; } - if (member == null) return null; return ExecutableMember.from2( member, @@ -232,9 +240,8 @@ class ExtensionMemberResolver { /// Return extensions for the [type] that match the given [name] in the /// current scope. - List<_InstantiatedExtension> _getApplicable( - DartType type, String name, ElementKind kind) { - var candidates = _getExtensionsWithMember(name, kind); + List<_InstantiatedExtension> _getApplicable(DartType type, String name) { + var candidates = _getExtensionsWithMember(name); var instantiatedExtensions = <_InstantiatedExtension>[]; for (var candidate in candidates) { @@ -266,14 +273,7 @@ class ExtensionMemberResolver { } instantiatedExtensions.add( - _InstantiatedExtension( - candidate.extension, - extendedType, - ExecutableMember.from2( - candidate.member, - substitution, - ), - ), + _InstantiatedExtension(candidate, substitution, extendedType), ); } @@ -281,54 +281,27 @@ class ExtensionMemberResolver { } /// Return extensions from the current scope, that define a member with the - /// given[name]. - List<_CandidateExtension> _getExtensionsWithMember( - String name, - ElementKind kind, - ) { + /// given [name]. + List<_CandidateExtension> _getExtensionsWithMember(String name) { var candidates = <_CandidateExtension>[]; - /// Return `true` if the [elementName] matches the target [name], taking - /// into account the `=` on the end of the names of setters. - bool matchesName(String elementName) { - if (elementName.endsWith('=') && !name.endsWith('=')) { - elementName = elementName.substring(0, elementName.length - 1); - } - return elementName == name; - } - /// Add the given [extension] to the list of [candidates] if it defined a /// member whose name matches the target [name]. void checkExtension(ExtensionElement extension) { - if (kind == ElementKind.GETTER) { - for (var accessor in extension.accessors) { - if (accessor.isGetter && matchesName(accessor.name)) { - candidates.add(_CandidateExtension(extension, accessor)); - return; - } - } - } else if (kind == ElementKind.SETTER) { - for (var accessor in extension.accessors) { - if (accessor.isSetter && matchesName(accessor.name)) { - candidates.add(_CandidateExtension(extension, accessor)); - return; - } + for (var field in extension.fields) { + if (field.name == name) { + candidates.add( + _CandidateExtension(extension, field: field), + ); + return; } - } else if (kind == ElementKind.METHOD) { - for (var method in extension.methods) { - if (matchesName(method.name)) { - candidates.add(_CandidateExtension(extension, method)); - return; - } - } - // Check for a getter that matches a function type. - for (var accessor in extension.accessors) { - if (accessor.type is FunctionType && - accessor.isGetter && - matchesName(accessor.name)) { - candidates.add(_CandidateExtension(extension, accessor)); - return; - } + } + for (var method in extension.methods) { + if (method.name == name) { + candidates.add( + _CandidateExtension(extension, method: method), + ); + return; } } } @@ -400,16 +373,16 @@ class ExtensionMemberResolver { // former extension is not. // 2. They are both declared in platform libraries, or both declared in // non-platform libraries. - var e1_isInSdk = e1.element.library.isInSdk; - var e2_isInSdk = e2.element.library.isInSdk; + var e1_isInSdk = e1.extension.library.isInSdk; + var e2_isInSdk = e2.extension.library.isInSdk; if (e1_isInSdk && !e2_isInSdk) { return false; } else if (!e1_isInSdk && e2_isInSdk) { return true; } - var extendedType1 = e1._extendedType; - var extendedType2 = e2._extendedType; + var extendedType1 = e1.extendedType; + var extendedType2 = e2.extendedType; // 3. The instantiated type (the type after applying type inference from // the receiver) of T1 is a subtype of the instantiated type of T2, @@ -426,8 +399,8 @@ class ExtensionMemberResolver { // 5. ...the instantiate-to-bounds type of T1 is a subtype of the // instantiate-to-bounds type of T2 and not vice versa. // TODO(scheglov) store instantiated types - var extendedTypeBound1 = _instantiateToBounds(e1.element); - var extendedTypeBound2 = _instantiateToBounds(e2.element); + var extendedTypeBound1 = _instantiateToBounds(e1.extension); + var extendedTypeBound2 = _instantiateToBounds(e2.extension); return _isSubtypeOf(extendedTypeBound1, extendedTypeBound2) && !_isSubtypeOf(extendedTypeBound2, extendedTypeBound1); } @@ -455,19 +428,37 @@ class ExtensionMemberResolver { class _CandidateExtension { final ExtensionElement extension; - final ExecutableElement member; + final FieldElement field; + final MethodElement method; - _CandidateExtension(this.extension, this.member); + _CandidateExtension(this.extension, {this.field, this.method}) + : assert(field != null || method != null); } class _InstantiatedExtension { - final ExtensionElement element; - final DartType _extendedType; - final ExecutableElement instantiatedMember; - - _InstantiatedExtension( - this.element, - this._extendedType, - this.instantiatedMember, - ); + final _CandidateExtension candidate; + final MapSubstitution substitution; + final DartType extendedType; + + _InstantiatedExtension(this.candidate, this.substitution, this.extendedType); + + ResolutionResult get asResolutionResult { + return ResolutionResult(function: method, property: field); + } + + ExtensionElement get extension => candidate.extension; + + FieldElement get field { + if (candidate.field == null) { + return null; + } + return FieldMember.from2(candidate.field, substitution); + } + + MethodElement get method { + if (candidate.method == null) { + return null; + } + return MethodMember.from2(candidate.method, substitution); + } } diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart index ba24c79a67df6..39a27a4040b70 100644 --- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart @@ -342,7 +342,6 @@ class MethodInvocationResolver { receiverType, name, nameNode, - ElementKind.METHOD, ); if (!result.isSingle) { @@ -350,7 +349,7 @@ class MethodInvocationResolver { return result; } - ExecutableElement member = result.element; + ExecutableElement member = result.getter; nameNode.staticElement = member; if (member.isStatic) { @@ -392,10 +391,7 @@ class MethodInvocationResolver { void _resolveExtensionOverride(MethodInvocation node, ExtensionOverride override, SimpleIdentifier nameNode, String name) { - var member = _extensionResolver.getOverrideMember( - override, name, ElementKind.METHOD) ?? - _extensionResolver.getOverrideMember( - override, name, ElementKind.GETTER); + var member = _extensionResolver.getOverrideMember(override, name); if (member == null) { _setDynamicResolution(node); @@ -439,11 +435,11 @@ class MethodInvocationResolver { return; } - ResolutionResult result = _extensionResolver.findExtension( - receiverType, name, nameNode, ElementKind.METHOD); + ResolutionResult result = + _extensionResolver.findExtension(receiverType, name, nameNode); if (result.isSingle) { - nameNode.staticElement = result.element; - var calleeType = _getCalleeType(node, result.element); + nameNode.staticElement = result.getter; + var calleeType = _getCalleeType(node, result.getter); return _setResolution(node, calleeType); } else if (result.isAmbiguous) { return; @@ -578,10 +574,9 @@ class MethodInvocationResolver { return; } - var result = _extensionResolver.findExtension( - receiverType, name, nameNode, ElementKind.METHOD); + var result = _extensionResolver.findExtension(receiverType, name, nameNode); if (result.isSingle) { - var target = result.element; + var target = result.getter; if (target != null) { nameNode.staticElement = target; var calleeType = _getCalleeType(node, target); @@ -744,9 +739,9 @@ class MethodInvocationResolver { var call = _inheritance.getMember(type, _nameCall); if (call == null) { var result = _extensionResolver.findExtension( - type, _nameCall.name, node.methodName, ElementKind.METHOD); + type, _nameCall.name, node.methodName); if (result.isSingle) { - call = result.element; + call = result.function; } else if (result.isAmbiguous) { return; } diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart index 1d91bf83400d4..d3b7288e705bf 100644 --- a/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart +++ b/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart @@ -17,18 +17,27 @@ class ResolutionResult { /// The state of the result. final _ResolutionResultState state; - /// The element that was found, or `null` if the [state] is not - /// [_ResolutionResultState.single]. - final ExecutableElement element; + /// The function that was found, or `null` if the [state] is not + /// [_ResolutionResultState.single], or a [property] was found. + final ExecutableElement function; + + /// The property that was found, or `null` if the [state] is not + /// [_ResolutionResultState.single], or a [function] was found. + final PropertyInducingElement property; /// Initialize a newly created result to represent resolving to a single - /// [element]. - ResolutionResult(this.element) - : assert(element != null), + /// [function] or [property]. + ResolutionResult({this.function, this.property}) + : assert(function != null || property != null), state = _ResolutionResultState.single; /// Initialize a newly created result with no element and the given [state]. - const ResolutionResult._(this.state) : element = null; + const ResolutionResult._(this.state) + : function = null, + property = null; + + /// Return the getter of the [property], or the [function]. + ExecutableElement get getter => function ?? property?.getter; /// Return `true` if this result represents the case where multiple ambiguous /// elements were found. @@ -41,6 +50,17 @@ class ResolutionResult { /// Return `true` if this result represents the case where a single element /// was found. bool get isSingle => state == _ResolutionResultState.single; + + /// If this is a property, return `true` is the property is static. + /// If this is a function, return `true` is the function is static. + /// Otherwise return `false`. + bool get isStatic { + return function?.isStatic ?? property?.isStatic ?? false; + } + + /// Return the setter of the [property], or `null` if this is not a property, + /// or the property does not have a setter. + ExecutableElement get setter => property?.setter; } /// The state of a [ResolutionResult]. diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart index e8da884fbe9c1..8518284d17f16 100644 --- a/pkg/analyzer/lib/src/generated/element_resolver.dart +++ b/pkg/analyzer/lib/src/generated/element_resolver.dart @@ -175,7 +175,7 @@ class ElementResolver extends SimpleAstVisitor { // side to the operator. ResolutionResult result = _lookUpMethod(leftHandSide, staticType, methodName, leftHandSide); - node.staticElement = result.element; + node.staticElement = result.function; if (_shouldReportInvalidMember(staticType, result)) { _recordUndefinedToken( staticType.element, @@ -272,8 +272,7 @@ class ElementResolver extends SimpleAstVisitor { memberElement = element.getNamedConstructor(name.name); if (memberElement == null) { memberElement = - _lookUpSetter(prefix, element.type, name.name, name) - .element; + _lookUpSetter(prefix, element.type, name.name, name).setter; } } if (memberElement == null) { @@ -406,8 +405,7 @@ class ElementResolver extends SimpleAstVisitor { Expression function = node.function; DartType functionType; if (function is ExtensionOverride) { - var member = _extensionResolver.getOverrideMember( - function, 'call', ElementKind.METHOD); + var member = _extensionResolver.getOverrideMember(function, 'call'); if (member != null && member.isStatic) { _resolver.errorReporter.reportErrorForNode( CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER, @@ -486,7 +484,7 @@ class ElementResolver extends SimpleAstVisitor { ResolutionResult setterResult = _lookUpMethod(target, staticType, setterMethodName, target); // set setter element - node.staticElement = setterResult.element; + node.staticElement = setterResult.function; // generate undefined method warning _checkForUndefinedIndexOperator( node, target, setterMethodName, setterResult, staticType); @@ -495,7 +493,7 @@ class ElementResolver extends SimpleAstVisitor { _lookUpMethod(target, staticType, getterMethodName, target); // set getter element AuxiliaryElements auxiliaryElements = - new AuxiliaryElements(getterResult.element, null); + new AuxiliaryElements(getterResult.function, null); node.auxiliaryElements = auxiliaryElements; // generate undefined method warning _checkForUndefinedIndexOperator( @@ -505,7 +503,7 @@ class ElementResolver extends SimpleAstVisitor { ResolutionResult methodResult = _lookUpMethod(target, staticType, getterMethodName, target); // set getter element - node.staticElement = methodResult.element; + node.staticElement = methodResult.function; // generate undefined method warning _checkForUndefinedIndexOperator( node, target, getterMethodName, methodResult, staticType); @@ -514,7 +512,7 @@ class ElementResolver extends SimpleAstVisitor { ResolutionResult methodResult = _lookUpMethod(target, staticType, setterMethodName, target); // set setter element - node.staticElement = methodResult.element; + node.staticElement = methodResult.function; // generate undefined method warning _checkForUndefinedIndexOperator( node, target, setterMethodName, methodResult, staticType); @@ -572,7 +570,7 @@ class ElementResolver extends SimpleAstVisitor { // the operator. ResolutionResult result = _lookUpMethod(operand, staticType, methodName, operand); - node.staticElement = result.element; + node.staticElement = result.function; if (_shouldReportInvalidMember(staticType, result)) { if (operand is SuperExpression) { _recordUndefinedToken( @@ -680,7 +678,7 @@ class ElementResolver extends SimpleAstVisitor { // the operator. ResolutionResult result = _lookUpMethod(operand, staticType, methodName, operand); - node.staticElement = result.element; + node.staticElement = result.function; if (_shouldReportInvalidMember(staticType, result)) { if (operand is SuperExpression) { _recordUndefinedToken( @@ -716,7 +714,10 @@ class ElementResolver extends SimpleAstVisitor { ExecutableElement member; if (propertyName.inSetterContext()) { member = _extensionResolver.getOverrideMember( - target, memberName, ElementKind.SETTER); + target, + memberName, + setter: true, + ); if (member == null) { _resolver.errorReporter.reportErrorForNode( CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER, @@ -724,8 +725,8 @@ class ElementResolver extends SimpleAstVisitor { [memberName, element.name]); } if (propertyName.inGetterContext()) { - PropertyAccessorElement getter = _extensionResolver.getOverrideMember( - target, memberName, ElementKind.GETTER); + PropertyAccessorElement getter = + _extensionResolver.getOverrideMember(target, memberName); if (getter == null) { _resolver.errorReporter.reportErrorForNode( CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER, @@ -735,10 +736,7 @@ class ElementResolver extends SimpleAstVisitor { propertyName.auxiliaryElements = AuxiliaryElements(getter, null); } } else if (propertyName.inGetterContext()) { - member = _extensionResolver.getOverrideMember( - target, memberName, ElementKind.GETTER) ?? - _extensionResolver.getOverrideMember( - target, memberName, ElementKind.METHOD); + member = _extensionResolver.getOverrideMember(target, memberName); if (member == null) { _resolver.errorReporter.reportErrorForNode( CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER, @@ -877,7 +875,7 @@ class ElementResolver extends SimpleAstVisitor { enclosingClass != null) { InterfaceType enclosingType = enclosingClass.type; AuxiliaryElements auxiliaryElements = new AuxiliaryElements( - _lookUpGetter(null, enclosingType, node.name, node).element, null); + _lookUpGetter(null, enclosingType, node.name, node).getter, null); node.auxiliaryElements = auxiliaryElements; } // @@ -1227,12 +1225,8 @@ class ElementResolver extends SimpleAstVisitor { } var result = _extensionResolver.findExtension( - type, - FunctionElement.CALL_METHOD_NAME, - node, - ElementKind.METHOD, - ); - var instantiatedMember = result.element; + type, FunctionElement.CALL_METHOD_NAME, node); + var instantiatedMember = result.function; if (instantiatedMember is MethodElement) { return instantiatedMember; } @@ -1254,7 +1248,7 @@ class ElementResolver extends SimpleAstVisitor { var getter = type.lookUpInheritedGetter(name, library: _definingLibrary, thisType: target is! SuperExpression); if (getter != null) { - result = ResolutionResult(getter); + result = ResolutionResult(property: getter.variable); } } @@ -1266,8 +1260,7 @@ class ElementResolver extends SimpleAstVisitor { return ResolutionResult.none; } if (result.isNone) { - result = _extensionResolver.findExtension( - type, name, nameNode, ElementKind.GETTER); + result = _extensionResolver.findExtension(type, name, nameNode); } return result; } @@ -1300,7 +1293,7 @@ class ElementResolver extends SimpleAstVisitor { var method = type.lookUpInheritedMethod(name, library: _definingLibrary, thisType: target is! SuperExpression); if (method != null) { - result = ResolutionResult(method); + result = ResolutionResult(function: method); } } @@ -1312,8 +1305,7 @@ class ElementResolver extends SimpleAstVisitor { return ResolutionResult.none; } if (result.isNone) { - result = _extensionResolver.findExtension( - type, name, nameNode, ElementKind.METHOD); + result = _extensionResolver.findExtension(type, name, nameNode); } return result; } @@ -1332,7 +1324,7 @@ class ElementResolver extends SimpleAstVisitor { var setter = type.lookUpInheritedSetter(name, library: _definingLibrary, thisType: target is! SuperExpression); if (setter != null) { - result = ResolutionResult(setter); + result = ResolutionResult(property: setter.variable); } } @@ -1344,8 +1336,7 @@ class ElementResolver extends SimpleAstVisitor { return ResolutionResult.none; } if (result.isNone) { - result = _extensionResolver.findExtension( - type, name, nameNode, ElementKind.SETTER); + result = _extensionResolver.findExtension(type, name, nameNode); } return result; } @@ -1593,7 +1584,7 @@ class ElementResolver extends SimpleAstVisitor { forSuper: isSuper, ); if (invokeElement != null) { - result = ResolutionResult(invokeElement); + result = ResolutionResult(function: invokeElement); } } @@ -1604,12 +1595,11 @@ class ElementResolver extends SimpleAstVisitor { } if (result.isNone) { - result = _extensionResolver.findExtension( - leftType, methodName, node, ElementKind.METHOD); + result = _extensionResolver.findExtension(leftType, methodName, node); } - node.staticElement = result.element; - node.staticInvokeType = result.element?.type; + node.staticElement = result.function; + node.staticInvokeType = result.function?.type; if (_shouldReportInvalidMember(leftType, result)) { if (isSuper) { _recordUndefinedToken( @@ -1670,23 +1660,28 @@ class ElementResolver extends SimpleAstVisitor { * Given that we are accessing a property of the given [classElement] with the * given [propertyName], return the element that represents the property. */ - Element _resolveElement( + ResolutionResult _resolveElement( ClassElement classElement, SimpleIdentifier propertyName) { String name = propertyName.name; - Element element = null; if (propertyName.inSetterContext()) { - element = classElement.getSetter(name); - } - if (element == null) { - element = classElement.getGetter(name); + var element = classElement.getSetter(name); + if (element != null && element.isAccessibleIn(_definingLibrary)) { + return ResolutionResult(property: element.variable); + } } - if (element == null) { - element = classElement.getMethod(name); + { + var element = classElement.getGetter(name); + if (element != null && element.isAccessibleIn(_definingLibrary)) { + return ResolutionResult(property: element.variable); + } } - if (element != null && element.isAccessibleIn(_definingLibrary)) { - return element; + { + var element = classElement.getMethod(name); + if (element != null && element.isAccessibleIn(_definingLibrary)) { + return ResolutionResult(function: element); + } } - return null; + return ResolutionResult.none; } /** @@ -1757,7 +1752,11 @@ class ElementResolver extends SimpleAstVisitor { [memberName]); } if (staticElement != null) { - result = ResolutionResult(staticElement); + if (staticElement is PropertyAccessorElement) { + result = ResolutionResult(property: staticElement.variable); + } else if (staticElement is MethodElement) { + result = ResolutionResult(function: staticElement); + } } } // @@ -1767,18 +1766,15 @@ class ElementResolver extends SimpleAstVisitor { // does not apply to conditional property accesses (i.e. 'C?.m'). // if (result.isNone) { - Element staticElement; ClassElement typeReference = getTypeReference(target); if (typeReference != null) { if (isCascaded) { typeReference = _typeType.element; } - staticElement = _resolveElement(typeReference, propertyName); - if (staticElement != null) { - result = ResolutionResult(staticElement); - } + result = _resolveElement(typeReference, propertyName); } else { if (target is SuperExpression) { + ExecutableElement staticElement; if (staticType is InterfaceTypeImpl) { staticElement = staticType.lookUpInheritedMember( propertyName.name, _definingLibrary, @@ -1806,8 +1802,10 @@ class ElementResolver extends SimpleAstVisitor { } } } - if (staticElement != null) { - result = ResolutionResult(staticElement); + if (staticElement is PropertyAccessorElement) { + result = ResolutionResult(property: staticElement.variable); + } else if (staticElement is MethodElement) { + result = ResolutionResult(function: staticElement); } } else { result = _resolveProperty(target, staticType, propertyName); @@ -1818,11 +1816,13 @@ class ElementResolver extends SimpleAstVisitor { // Error was already reported in validateAnnotationElement(). if (target.parent.parent is Annotation) { if (result.isSingle) { - propertyName.staticElement = result.element; + propertyName.staticElement = result.getter; } return; } - propertyName.staticElement = result.element; + propertyName.staticElement = + (propertyName.inSetterContext() ? result.setter : null) ?? + result.getter; if (_shouldReportInvalidMember(staticType, result)) { if (staticType is FunctionType && propertyName.name == FunctionElement.CALL_METHOD_NAME) { @@ -1909,7 +1909,7 @@ class ElementResolver extends SimpleAstVisitor { if (enclosingClass != null) { setter = _lookUpSetter( null, enclosingClass.type, identifier.name, identifier) - .element; + .setter; } } if (setter != null) { @@ -1949,17 +1949,17 @@ class ElementResolver extends SimpleAstVisitor { identifier.parent is CommentReference)) { element = _lookUpSetter(null, enclosingType, identifier.name, identifier) - .element; + .setter; } if (element == null && identifier.inGetterContext()) { element = _lookUpGetter(null, enclosingType, identifier.name, identifier) - .element; + .getter; } if (element == null) { element = _lookUpMethod(null, enclosingType, identifier.name, identifier) - .element; + .getter; } } } diff --git a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart index d5d3d3c400e1f..7c00d1f13581f 100644 --- a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart @@ -646,27 +646,6 @@ f(B b) { assertType(access, 'int'); } - test_instance_getter_with_setter() async { - await assertNoErrorsInCode(''' -class C {} - -extension E on C { - int get a => 1; -} - -extension E2 on C { - set a(int v) { } -} - -f(C c) { - print(c.a); -} -'''); - var access = findNode.prefixed('c.a'); - assertElement(access, findElement.getter('a')); - assertType(access, 'int'); - } - test_instance_getterInvoked_fromExtension_functionType() async { await assertNoErrorsInCode(''' extension E on int Function(int) { @@ -1127,27 +1106,6 @@ f(C c) { assertElement(access, findElement.setter('a')); } - test_instance_setter_with_getter() async { - await assertNoErrorsInCode(''' -class C {} - -extension E on C { - int get a => 1; -} - -extension E2 on C { - set a(int v) { } -} - -f(C c) { - print(c.a = 1); -} -'''); - var access = findNode.prefixed('c.a'); - assertElement(access, findElement.setter('a')); - assertType(access, 'int'); - } - test_instance_tearoff_fromExtension_functionType() async { await assertNoErrorsInCode(''' extension E on int Function(int) { diff --git a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart index be7ec1e711136..a6894c5ab5bbe 100644 --- a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart +++ b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart @@ -40,44 +40,88 @@ int f(A a) => a(); ]); } - test_getter() async { + test_getter_getter() async { await assertErrorsInCode(''' -class A {} - -extension E1 on A { +extension E1 on int { void get a => 1; } -extension E2 on A { +extension E2 on int { void get a => 2; } -f(A a) { - a.a; +f() { + 0.a; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 109, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 98, 1), ]); + var access = findNode.propertyAccess('0.a'); + assertElementNull(access); + assertTypeDynamic(access); } - test_method() async { + test_getter_method() async { await assertErrorsInCode(''' -class A {} +extension E on int { + int get a => 1; +} -extension E1 on A { +extension E2 on int { void a() {} } -extension E2 on A { +f() { + 0.a; +} +''', [ + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 91, 1), + ]); + var access = findNode.propertyAccess('0.a'); + assertElementNull(access); + assertTypeDynamic(access); + } + + test_getter_setter() async { + await assertErrorsInCode(''' +extension E on int { + int get a => 1; +} + +extension E2 on int { + set a(int v) { } +} + +f() { + 0.a; +} +''', [ + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 96, 1), + ]); + var access = findNode.propertyAccess('0.a'); + assertElementNull(access); + assertTypeDynamic(access); + } + + test_method_method() async { + await assertErrorsInCode(''' +extension E1 on int { void a() {} } -f(A a) { - a.a(); +extension E2 on int { + void a() {} +} + +f() { + 0.a(); } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 99, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1), ]); + var invocation = findNode.methodInvocation('0.a()'); + assertElementNull(invocation); + assertTypeDynamic(invocation); } test_noMoreSpecificExtension() async { @@ -160,23 +204,24 @@ int f(A a) => -a; ]); } - test_setter() async { + test_setter_setter() async { await assertErrorsInCode(''' -class A {} - -extension E1 on A { +extension E1 on int { set a(x) {} } -extension E2 on A { +extension E2 on int { set a(x) {} } -f(A a) { - a.a = 3; +f() { + 0.a = 3; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 99, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1), ]); + var access = findNode.propertyAccess('0.a'); + assertElementNull(access); + assertTypeDynamic(access); } }