| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/error/listener.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/ast/extensions.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/resolver/method_invocation_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/scope.dart'; |
| import 'package:analyzer/src/dart/resolver/type_property_resolver.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/generated/migratable_ast_info_provider.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/super_context.dart'; |
| |
| /// An object used by instances of [ResolverVisitor] to resolve references |
| /// within the AST structure to the elements being referenced. The requirements |
| /// for the element resolver are: |
| /// |
| /// 1. Every [SimpleIdentifier] should be resolved to the element to which it |
| /// refers. Specifically: |
| /// * An identifier within the declaration of that name should resolve to the |
| /// element being declared. |
| /// * An identifier denoting a prefix should resolve to the element |
| /// representing the import that defines the prefix (an [ImportElement]). |
| /// * An identifier denoting a variable should resolve to the element |
| /// representing the variable (a [VariableElement]). |
| /// * An identifier denoting a parameter should resolve to the element |
| /// representing the parameter (a [ParameterElement]). |
| /// * An identifier denoting a field should resolve to the element |
| /// representing the getter or setter being invoked (a |
| /// [PropertyAccessorElement]). |
| /// * An identifier denoting the name of a method or function being invoked |
| /// should resolve to the element representing the method or function (an |
| /// [ExecutableElement]). |
| /// * An identifier denoting a label should resolve to the element |
| /// representing the label (a [LabelElement]). |
| /// The identifiers within directives are exceptions to this rule and are |
| /// covered below. |
| /// 2. Every node containing a token representing an operator that can be |
| /// overridden ( [BinaryExpression], [PrefixExpression], [PostfixExpression]) |
| /// should resolve to the element representing the method invoked by that |
| /// operator (a [MethodElement]). |
| /// 3. Every [FunctionExpressionInvocation] should resolve to the element |
| /// representing the function being invoked (a [FunctionElement]). This will |
| /// be the same element as that to which the name is resolved if the function |
| /// has a name, but is provided for those cases where an unnamed function is |
| /// being invoked. |
| /// 4. Every [LibraryDirective] and [PartOfDirective] should resolve to the |
| /// element representing the library being specified by the directive (a |
| /// [LibraryElement]) unless, in the case of a part-of directive, the |
| /// specified library does not exist. |
| /// 5. Every [ImportDirective] and [ExportDirective] should resolve to the |
| /// element representing the library being specified by the directive unless |
| /// the specified library does not exist (an [ImportElement] or |
| /// [ExportElement]). |
| /// 6. The identifier representing the prefix in an [ImportDirective] should |
| /// resolve to the element representing the prefix (a [PrefixElement]). |
| /// 7. The identifiers in the hide and show combinators in [ImportDirective]s |
| /// and [ExportDirective]s should resolve to the elements that are being |
| /// hidden or shown, respectively, unless those names are not defined in the |
| /// specified library (or the specified library does not exist). |
| /// 8. Every [PartDirective] should resolve to the element representing the |
| /// compilation unit being specified by the string unless the specified |
| /// compilation unit does not exist (a [CompilationUnitElement]). |
| /// |
| /// Note that AST nodes that would represent elements that are not defined are |
| /// not resolved to anything. This includes such things as references to |
| /// undeclared variables (which is an error) and names in hide and show |
| /// combinators that are not defined in the imported library (which is not an |
| /// error). |
| class ElementResolver extends SimpleAstVisitor<void> { |
| /// The resolver driving this participant. |
| final ResolverVisitor _resolver; |
| |
| /// The element for the library containing the compilation unit being visited. |
| final LibraryElement _definingLibrary; |
| |
| /// Helper for resolving properties on types. |
| final TypePropertyResolver _typePropertyResolver; |
| |
| final MethodInvocationResolver _methodInvocationResolver; |
| |
| /// Initialize a newly created visitor to work for the given [_resolver] to |
| /// resolve the nodes in a compilation unit. |
| ElementResolver(this._resolver, |
| {MigratableAstInfoProvider migratableAstInfoProvider = |
| const MigratableAstInfoProvider()}) |
| : _definingLibrary = _resolver.definingLibrary, |
| _typePropertyResolver = _resolver.typePropertyResolver, |
| _methodInvocationResolver = MethodInvocationResolver( |
| _resolver, |
| migratableAstInfoProvider, |
| inferenceHelper: _resolver.inferenceHelper, |
| ); |
| |
| /// Return `true` iff the current enclosing function is a constant constructor |
| /// declaration. |
| bool get isInConstConstructor { |
| var function = _resolver.enclosingFunction; |
| if (function is ConstructorElement) { |
| return function.isConst; |
| } |
| return false; |
| } |
| |
| ErrorReporter get _errorReporter => _resolver.errorReporter; |
| |
| TypeProviderImpl get _typeProvider => _resolver.typeProvider; |
| |
| @override |
| void visitBreakStatement(covariant BreakStatementImpl node) { |
| node.target = _lookupBreakOrContinueTarget(node, node.label, false); |
| } |
| |
| @override |
| void visitClassDeclaration(ClassDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitCommentReference(covariant CommentReferenceImpl node) { |
| var identifier = node.identifier; |
| if (identifier is SimpleIdentifierImpl) { |
| var element = _resolveSimpleIdentifier(identifier); |
| if (element == null) { |
| // TODO(brianwilkerson) Report this error? |
| // resolver.reportError( |
| // CompileTimeErrorCode.UNDEFINED_IDENTIFIER, |
| // simpleIdentifier, |
| // simpleIdentifier.getName()); |
| } else { |
| if (element.library == null || element.library != _definingLibrary) { |
| // TODO(brianwilkerson) Report this error? |
| } |
| identifier.staticElement = element; |
| if (node.newKeyword != null) { |
| if (element is ClassElement) { |
| var constructor = element.unnamedConstructor; |
| if (constructor == null) { |
| // TODO(brianwilkerson) Report this error. |
| } else { |
| identifier.staticElement = constructor; |
| } |
| } else { |
| // TODO(brianwilkerson) Report this error. |
| } |
| } |
| } |
| } else if (identifier is PrefixedIdentifierImpl) { |
| var prefix = identifier.prefix; |
| var prefixElement = _resolveSimpleIdentifier(prefix); |
| prefix.staticElement = prefixElement; |
| |
| var name = identifier.identifier; |
| |
| if (prefixElement == null) { |
| // resolver.reportError(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, prefix, prefix.getName()); |
| return; |
| } |
| |
| if (prefixElement is PrefixElement) { |
| var prefixScope = prefixElement.scope; |
| var lookupResult = prefixScope.lookup(name.name); |
| var element = lookupResult.getter ?? lookupResult.setter; |
| element = _resolver.toLegacyElement(element); |
| name.staticElement = element; |
| return; |
| } |
| |
| var library = prefixElement.library; |
| if (library != _definingLibrary) { |
| // TODO(brianwilkerson) Report this error. |
| } |
| |
| if (node.newKeyword == null) { |
| if (prefixElement is ClassElement) { |
| name.staticElement = prefixElement.getMethod(name.name) ?? |
| prefixElement.getGetter(name.name) ?? |
| prefixElement.getSetter(name.name) ?? |
| prefixElement.getNamedConstructor(name.name); |
| } else if (prefixElement is ExtensionElement) { |
| name.staticElement = prefixElement.getMethod(name.name) ?? |
| prefixElement.getGetter(name.name) ?? |
| prefixElement.getSetter(name.name); |
| } else { |
| // TODO(brianwilkerson) Report this error. |
| } |
| } else if (prefixElement is ClassElement) { |
| var constructor = prefixElement.getNamedConstructor(name.name); |
| if (constructor == null) { |
| // TODO(brianwilkerson) Report this error. |
| } else { |
| name.staticElement = constructor; |
| } |
| } else { |
| // TODO(brianwilkerson) Report this error. |
| } |
| } |
| } |
| |
| @override |
| void visitConstructorDeclaration(ConstructorDeclaration node) { |
| super.visitConstructorDeclaration(node); |
| ConstructorElement element = node.declaredElement!; |
| if (element is ConstructorElementImpl) { |
| var redirectedNode = node.redirectedConstructor; |
| if (redirectedNode != null) { |
| // set redirected factory constructor |
| var redirectedElement = redirectedNode.staticElement; |
| element.redirectedConstructor = redirectedElement; |
| } else { |
| // set redirected generative constructor |
| for (ConstructorInitializer initializer in node.initializers) { |
| if (initializer is RedirectingConstructorInvocation) { |
| var redirectedElement = initializer.staticElement; |
| element.redirectedConstructor = redirectedElement; |
| } |
| } |
| } |
| _resolveAnnotations(node.metadata); |
| } |
| } |
| |
| @override |
| void visitConstructorFieldInitializer( |
| covariant ConstructorFieldInitializerImpl node) { |
| var fieldName = node.fieldName; |
| ClassElement enclosingClass = _resolver.enclosingClass!; |
| var fieldElement = enclosingClass.getField(fieldName.name); |
| fieldName.staticElement = fieldElement; |
| } |
| |
| @override |
| void visitConstructorName(covariant ConstructorNameImpl node) { |
| DartType type = node.type.typeOrThrow; |
| if (type.isDynamic) { |
| // Nothing to do. |
| } else if (type is InterfaceType) { |
| // look up ConstructorElement |
| ConstructorElement? constructor; |
| var name = node.name; |
| if (name == null) { |
| constructor = type.lookUpConstructor(null, _definingLibrary); |
| constructor = _resolver.toLegacyElement(constructor); |
| } else { |
| constructor = type.lookUpConstructor(name.name, _definingLibrary); |
| constructor = _resolver.toLegacyElement(constructor); |
| name.staticElement = constructor; |
| } |
| node.staticElement = constructor; |
| } else { |
| // TODO(brianwilkerson) Report these errors. |
| // ASTNode parent = node.getParent(); |
| // if (parent instanceof InstanceCreationExpression) { |
| // if (((InstanceCreationExpression) parent).isConst()) { |
| // // CompileTimeErrorCode.CONST_WITH_NON_TYPE |
| // } else { |
| // // CompileTimeErrorCode.NEW_WITH_NON_TYPE |
| // } |
| // } else { |
| // // This is part of a redirecting factory constructor; not sure which error code to use |
| // } |
| } |
| } |
| |
| @override |
| void visitContinueStatement(covariant ContinueStatementImpl node) { |
| node.target = _lookupBreakOrContinueTarget(node, node.label, true); |
| } |
| |
| @override |
| void visitDeclaredIdentifier(DeclaredIdentifier node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitEnumConstantDeclaration(EnumConstantDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitEnumDeclaration(EnumDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitExportDirective(ExportDirective node) { |
| var exportElement = node.element; |
| if (exportElement != null) { |
| // The element is null when the URI is invalid |
| // TODO(brianwilkerson) Figure out whether the element can ever be |
| // something other than an ExportElement |
| _resolveCombinators(exportElement.exportedLibrary, node.combinators); |
| _resolveAnnotations(node.metadata); |
| } |
| } |
| |
| @override |
| void visitExtensionDeclaration(ExtensionDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitFieldDeclaration(FieldDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitFieldFormalParameter(FieldFormalParameter node) { |
| _resolveMetadataForParameter(node); |
| super.visitFieldFormalParameter(node); |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| _resolveMetadataForParameter(node); |
| } |
| |
| @override |
| void visitGenericTypeAlias(GenericTypeAlias node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitImportDirective(covariant ImportDirectiveImpl node) { |
| var prefixNode = node.prefix; |
| if (prefixNode != null) { |
| String prefixName = prefixNode.name; |
| List<PrefixElement> prefixes = _definingLibrary.prefixes; |
| int count = prefixes.length; |
| for (int i = 0; i < count; i++) { |
| PrefixElement prefixElement = prefixes[i]; |
| if (prefixElement.displayName == prefixName) { |
| prefixNode.staticElement = prefixElement; |
| break; |
| } |
| } |
| } |
| var importElement = node.element; |
| if (importElement != null) { |
| // The element is null when the URI is invalid |
| var library = importElement.importedLibrary; |
| if (library != null) { |
| _resolveCombinators(library, node.combinators); |
| } |
| _resolveAnnotations(node.metadata); |
| } |
| } |
| |
| @override |
| void visitInstanceCreationExpression( |
| covariant InstanceCreationExpressionImpl node) { |
| var invokedConstructor = node.constructorName.staticElement; |
| var argumentList = node.argumentList; |
| var parameters = |
| _resolveArgumentsToFunction(argumentList, invokedConstructor); |
| if (parameters != null) { |
| argumentList.correspondingStaticParameters = parameters; |
| } |
| } |
| |
| @override |
| void visitLibraryDirective(LibraryDirective node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitMethodDeclaration(MethodDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitMethodInvocation(MethodInvocation node, |
| {List<WhyNotPromotedGetter>? whyNotPromotedList}) { |
| whyNotPromotedList ??= []; |
| _methodInvocationResolver.resolve( |
| node as MethodInvocationImpl, whyNotPromotedList); |
| } |
| |
| @override |
| void visitMixinDeclaration(MixinDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitPartDirective(PartDirective node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitPartOfDirective(PartOfDirective node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitRedirectingConstructorInvocation( |
| covariant RedirectingConstructorInvocationImpl node) { |
| var enclosingClass = _resolver.enclosingClass; |
| if (enclosingClass == null) { |
| // TODO(brianwilkerson) Report this error. |
| return; |
| } |
| ConstructorElement? element; |
| var name = node.constructorName; |
| if (name == null) { |
| element = enclosingClass.unnamedConstructor; |
| } else { |
| element = enclosingClass.getNamedConstructor(name.name); |
| } |
| if (element == null) { |
| // TODO(brianwilkerson) Report this error and decide what element to |
| // associate with the node. |
| return; |
| } |
| if (name != null) { |
| name.staticElement = element; |
| } |
| node.staticElement = element; |
| var argumentList = node.argumentList; |
| var parameters = _resolveArgumentsToFunction(argumentList, element); |
| if (parameters != null) { |
| argumentList.correspondingStaticParameters = parameters; |
| } |
| } |
| |
| @override |
| void visitSimpleFormalParameter(SimpleFormalParameter node) { |
| _resolveMetadataForParameter(node); |
| } |
| |
| @override |
| void visitSuperConstructorInvocation( |
| covariant SuperConstructorInvocationImpl node) { |
| var enclosingClass = _resolver.enclosingClass; |
| if (enclosingClass == null) { |
| // TODO(brianwilkerson) Report this error. |
| return; |
| } |
| var superType = enclosingClass.supertype; |
| if (superType == null) { |
| // TODO(brianwilkerson) Report this error. |
| return; |
| } |
| var name = node.constructorName; |
| var superName = name?.name; |
| var element = superType.lookUpConstructor(superName, _definingLibrary); |
| element = _resolver.toLegacyElement(element); |
| if (element == null || !element.isAccessibleIn(_definingLibrary)) { |
| if (name != null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER, |
| node, |
| [superType, name]); |
| } else { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, |
| node, |
| [superType]); |
| } |
| return; |
| } else { |
| if (element.isFactory && |
| // Check if we've reported [NO_GENERATIVE_CONSTRUCTORS_IN_SUPERCLASS]. |
| !element.enclosingElement.constructors |
| .every((constructor) => constructor.isFactory)) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node, [element]); |
| } |
| } |
| if (name != null) { |
| name.staticElement = element; |
| } |
| node.staticElement = element; |
| // TODO(brianwilkerson) Defer this check until we know there's an error (by |
| // in-lining _resolveArgumentsToFunction below). |
| var declaration = node.thisOrAncestorOfType<ClassDeclaration>(); |
| var superclassName = declaration?.extendsClause?.superclass.name; |
| if (superclassName != null && |
| _resolver.nameScope.shouldIgnoreUndefined(superclassName)) { |
| return; |
| } |
| var argumentList = node.argumentList; |
| var parameters = _resolveArgumentsToFunction(argumentList, element); |
| if (parameters != null) { |
| argumentList.correspondingStaticParameters = parameters; |
| } |
| } |
| |
| @override |
| void visitSuperExpression(SuperExpression node) { |
| var context = SuperContext.of(node); |
| if (context == SuperContext.annotation || context == SuperContext.static) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT, node); |
| } else if (context == SuperContext.extension) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.SUPER_IN_EXTENSION, node); |
| } |
| super.visitSuperExpression(node); |
| } |
| |
| @override |
| void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitTypeParameter(TypeParameter node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| @override |
| void visitVariableDeclaration(VariableDeclaration node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| /// Return the target of a break or continue statement, and update the static |
| /// element of its label (if any). The [parentNode] is the AST node of the |
| /// break or continue statement. The [labelNode] is the label contained in |
| /// that statement (if any). The flag [isContinue] is `true` if the node being |
| /// visited is a continue statement. |
| AstNode? _lookupBreakOrContinueTarget( |
| AstNode parentNode, SimpleIdentifierImpl? labelNode, bool isContinue) { |
| if (labelNode == null) { |
| return _resolver.implicitLabelScope.getTarget(isContinue); |
| } else { |
| var labelScope = _resolver.labelScope; |
| if (labelScope == null) { |
| // There are no labels in scope, so by definition the label is |
| // undefined. |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]); |
| return null; |
| } |
| var definingScope = labelScope.lookup(labelNode.name); |
| if (definingScope == null) { |
| // No definition of the given label name could be found in any |
| // enclosing scope. |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]); |
| return null; |
| } |
| // The target has been found. |
| labelNode.staticElement = definingScope.element; |
| ExecutableElement? labelContainer = |
| definingScope.element.thisOrAncestorOfType(); |
| if (!identical(labelContainer, _resolver.enclosingFunction)) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE, |
| labelNode, |
| [labelNode.name]); |
| } |
| return definingScope.node; |
| } |
| } |
| |
| /// Given an [argumentList] and the [executableElement] that will be invoked |
| /// using those argument, compute the list of parameters that correspond to |
| /// the list of arguments. An error will be reported if any of the arguments |
| /// cannot be matched to a parameter. Return the parameters that correspond to |
| /// the arguments, or `null` if no correspondence could be computed. |
| List<ParameterElement?>? _resolveArgumentsToFunction( |
| ArgumentList argumentList, ExecutableElement? executableElement) { |
| if (executableElement == null) { |
| return null; |
| } |
| List<ParameterElement> parameters = executableElement.parameters; |
| return _resolveArgumentsToParameters(argumentList, parameters); |
| } |
| |
| /// Given an [argumentList] and the [parameters] related to the element that |
| /// will be invoked using those arguments, compute the list of parameters that |
| /// correspond to the list of arguments. An error will be reported if any of |
| /// the arguments cannot be matched to a parameter. Return the parameters that |
| /// correspond to the arguments. |
| List<ParameterElement?> _resolveArgumentsToParameters( |
| ArgumentList argumentList, List<ParameterElement> parameters) { |
| return ResolverVisitor.resolveArgumentsToParameters( |
| argumentList, parameters, _errorReporter.reportErrorForNode); |
| } |
| |
| /// Resolve the names in the given [combinators] in the scope of the given |
| /// [library]. |
| void _resolveCombinators( |
| LibraryElement? library, NodeList<Combinator> combinators) { |
| if (library == null) { |
| // |
| // The library will be null if the directive containing the combinators |
| // has a URI that is not valid. |
| // |
| return; |
| } |
| Namespace namespace = |
| NamespaceBuilder().createExportNamespaceForLibrary(library); |
| for (Combinator combinator in combinators) { |
| NodeList<SimpleIdentifier> names; |
| if (combinator is HideCombinator) { |
| names = combinator.hiddenNames; |
| } else { |
| names = (combinator as ShowCombinator).shownNames; |
| } |
| for (var name in names) { |
| name as SimpleIdentifierImpl; |
| String nameStr = name.name; |
| var element = namespace.get(nameStr) ?? namespace.get("$nameStr="); |
| if (element != null) { |
| // Ensure that the name always resolves to a top-level variable |
| // rather than a getter or setter |
| if (element is PropertyAccessorElement) { |
| name.staticElement = element.variable; |
| } else { |
| name.staticElement = element; |
| } |
| } |
| } |
| } |
| } |
| |
| /// Given a [node] that can have annotations associated with it, resolve the |
| /// annotations in the element model representing annotations to the node. |
| void _resolveMetadataForParameter(NormalFormalParameter node) { |
| _resolveAnnotations(node.metadata); |
| } |
| |
| /// Resolve the given simple [identifier] if possible. Return the element to |
| /// which it could be resolved, or `null` if it could not be resolved. This |
| /// does not record the results of the resolution. |
| Element? _resolveSimpleIdentifier(SimpleIdentifier identifier) { |
| var lookupResult = _resolver.nameScope.lookup(identifier.name); |
| |
| var element = lookupResult.getter; |
| element = _resolver.toLegacyElement(element); |
| |
| if (element is PropertyAccessorElement && identifier.inSetterContext()) { |
| var setter = lookupResult.setter; |
| if (setter == null) { |
| // |
| // Check to see whether there might be a locally defined getter and |
| // an inherited setter. |
| // |
| var enclosingClass = _resolver.enclosingClass; |
| if (enclosingClass != null) { |
| var result = _typePropertyResolver.resolve( |
| receiver: null, |
| receiverType: enclosingClass.thisType, |
| name: identifier.name, |
| propertyErrorEntity: identifier, |
| nameErrorEntity: identifier, |
| ); |
| setter = result.setter; |
| } |
| } |
| if (setter != null) { |
| setter = _resolver.toLegacyElement(setter); |
| element = setter; |
| } |
| } else if (element == null && |
| (identifier.inSetterContext() || |
| identifier.parent is CommentReference)) { |
| element = lookupResult.setter; |
| element = _resolver.toLegacyElement(element); |
| } |
| if (element == null) { |
| InterfaceType enclosingType; |
| var enclosingClass = _resolver.enclosingClass; |
| if (enclosingClass == null) { |
| var enclosingExtension = _resolver.enclosingExtension; |
| if (enclosingExtension == null) { |
| return null; |
| } |
| DartType extendedType = |
| _resolveTypeParameter(enclosingExtension.extendedType); |
| if (extendedType is InterfaceType) { |
| enclosingType = extendedType; |
| } else if (extendedType is FunctionType) { |
| enclosingType = _typeProvider.functionType; |
| } else { |
| return null; |
| } |
| } else { |
| enclosingType = enclosingClass.thisType; |
| } |
| if (element == null) { |
| var result = _typePropertyResolver.resolve( |
| receiver: null, |
| receiverType: enclosingType, |
| name: identifier.name, |
| propertyErrorEntity: identifier, |
| nameErrorEntity: identifier, |
| ); |
| if (identifier.inSetterContext() || |
| identifier.parent is CommentReference) { |
| element = result.setter; |
| } |
| element ??= result.getter; |
| } |
| } |
| return element; |
| } |
| |
| /// If the given [type] is a type parameter, resolve it to the type that |
| /// should be used when looking up members. Otherwise, return the original |
| /// type. |
| DartType _resolveTypeParameter(DartType type) => |
| type.resolveToBound(_typeProvider.objectType); |
| |
| /// Checks whether the given [expression] is a reference to a class. If it is |
| /// then the element representing the class is returned, otherwise `null` is |
| /// returned. |
| static ClassElement? getTypeReference(Expression expression) { |
| if (expression is Identifier) { |
| var element = expression.staticElement; |
| if (element is ClassElement) { |
| return element; |
| } else if (element is TypeAliasElement) { |
| var aliasedType = element.aliasedType; |
| if (aliasedType is InterfaceType) { |
| return aliasedType.element; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /// Resolve each of the annotations in the given list of [annotations]. |
| static void _resolveAnnotations(NodeList<Annotation> annotations) { |
| for (Annotation annotation in annotations) { |
| var elementAnnotation = |
| annotation.elementAnnotation as ElementAnnotationImpl?; |
| if (elementAnnotation != null) { |
| elementAnnotation.element = annotation.element; |
| } |
| } |
| } |
| } |
| |
| /// An identifier that can be used to look up names in the lexical scope when |
| /// there is no identifier in the AST structure. There is no identifier in the |
| /// AST when the parser could not distinguish between a method invocation and an |
| /// invocation of a top-level function imported with a prefix. |
| class SyntheticIdentifier implements SimpleIdentifier { |
| @override |
| final String name; |
| |
| SyntheticIdentifier(this.name); |
| |
| @override |
| dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |