/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.internal.resolver; import com.google.dart.engine.AnalysisEngine; import com.google.dart.engine.ast.AnnotatedNode; import com.google.dart.engine.ast.Annotation; import com.google.dart.engine.ast.ArgumentList; import com.google.dart.engine.ast.AssignmentExpression; import com.google.dart.engine.ast.AstNode; import com.google.dart.engine.ast.AstVisitor; import com.google.dart.engine.ast.BinaryExpression; import com.google.dart.engine.ast.BreakStatement; import com.google.dart.engine.ast.ClassDeclaration; import com.google.dart.engine.ast.ClassTypeAlias; import com.google.dart.engine.ast.Combinator; import com.google.dart.engine.ast.CommentReference; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.ast.ConstructorDeclaration; import com.google.dart.engine.ast.ConstructorFieldInitializer; import com.google.dart.engine.ast.ConstructorInitializer; import com.google.dart.engine.ast.ConstructorName; import com.google.dart.engine.ast.ContinueStatement; import com.google.dart.engine.ast.DeclaredIdentifier; import com.google.dart.engine.ast.ExportDirective; import com.google.dart.engine.ast.Expression; import com.google.dart.engine.ast.FieldDeclaration; import com.google.dart.engine.ast.FieldFormalParameter; import com.google.dart.engine.ast.FunctionDeclaration; import com.google.dart.engine.ast.FunctionExpression; import com.google.dart.engine.ast.FunctionExpressionInvocation; import com.google.dart.engine.ast.FunctionTypeAlias; import com.google.dart.engine.ast.FunctionTypedFormalParameter; import com.google.dart.engine.ast.HideCombinator; import com.google.dart.engine.ast.Identifier; import com.google.dart.engine.ast.ImportDirective; import com.google.dart.engine.ast.IndexExpression; import com.google.dart.engine.ast.InstanceCreationExpression; import com.google.dart.engine.ast.LibraryDirective; import com.google.dart.engine.ast.MethodDeclaration; import com.google.dart.engine.ast.MethodInvocation; import com.google.dart.engine.ast.NamedExpression; import com.google.dart.engine.ast.NodeList; import com.google.dart.engine.ast.NormalFormalParameter; import com.google.dart.engine.ast.NullLiteral; import com.google.dart.engine.ast.PartDirective; import com.google.dart.engine.ast.PartOfDirective; import com.google.dart.engine.ast.PostfixExpression; import com.google.dart.engine.ast.PrefixExpression; import com.google.dart.engine.ast.PrefixedIdentifier; import com.google.dart.engine.ast.PropertyAccess; import com.google.dart.engine.ast.RedirectingConstructorInvocation; import com.google.dart.engine.ast.ShowCombinator; import com.google.dart.engine.ast.SimpleFormalParameter; import com.google.dart.engine.ast.SimpleIdentifier; import com.google.dart.engine.ast.SuperConstructorInvocation; import com.google.dart.engine.ast.SuperExpression; import com.google.dart.engine.ast.TopLevelVariableDeclaration; import com.google.dart.engine.ast.TypeParameter; import com.google.dart.engine.ast.VariableDeclaration; import com.google.dart.engine.ast.VariableDeclarationList; import com.google.dart.engine.ast.visitor.SimpleAstVisitor; import com.google.dart.engine.context.AnalysisOptions; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.ConstructorElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.ExecutableElement; import com.google.dart.engine.element.ExportElement; import com.google.dart.engine.element.FieldElement; import com.google.dart.engine.element.FunctionElement; import com.google.dart.engine.element.ImportElement; import com.google.dart.engine.element.LabelElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.MethodElement; import com.google.dart.engine.element.MultiplyDefinedElement; import com.google.dart.engine.element.ParameterElement; import com.google.dart.engine.element.PrefixElement; import com.google.dart.engine.element.PropertyAccessorElement; import com.google.dart.engine.element.PropertyInducingElement; import com.google.dart.engine.element.VariableElement; import com.google.dart.engine.error.CompileTimeErrorCode; import com.google.dart.engine.error.ErrorCode; import com.google.dart.engine.error.HintCode; import com.google.dart.engine.error.StaticTypeWarningCode; import com.google.dart.engine.error.StaticWarningCode; import com.google.dart.engine.internal.element.AuxiliaryElements; import com.google.dart.engine.internal.element.ClassElementImpl; import com.google.dart.engine.internal.element.ConstructorElementImpl; import com.google.dart.engine.internal.element.ElementAnnotationImpl; import com.google.dart.engine.internal.element.ElementImpl; import com.google.dart.engine.internal.element.ExecutableElementImpl; import com.google.dart.engine.internal.element.LabelElementImpl; import com.google.dart.engine.internal.element.MethodElementImpl; import com.google.dart.engine.internal.element.MultiplyDefinedElementImpl; import com.google.dart.engine.internal.element.ParameterElementImpl; import com.google.dart.engine.internal.scope.LabelScope; import com.google.dart.engine.internal.scope.Namespace; import com.google.dart.engine.internal.scope.NamespaceBuilder; import com.google.dart.engine.internal.scope.Scope; import com.google.dart.engine.internal.type.FunctionTypeImpl; import com.google.dart.engine.internal.type.InterfaceTypeImpl; import com.google.dart.engine.internal.type.UnionTypeImpl; import com.google.dart.engine.scanner.Token; import com.google.dart.engine.scanner.TokenType; import com.google.dart.engine.type.FunctionType; import com.google.dart.engine.type.InterfaceType; import com.google.dart.engine.type.Type; import com.google.dart.engine.type.TypeParameterType; import com.google.dart.engine.type.UnionType; import com.google.dart.engine.utilities.dart.ParameterKind; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Set; /** * Instances of the class {@code ElementResolver} are used by instances of {@link ResolverVisitor} * to resolve references within the AST structure to the elements being referenced. The requirements * for the element resolver are: * <ol> * <li>Every {@link SimpleIdentifier} should be resolved to the element to which it refers. * Specifically: * <ul> * <li>An identifier within the declaration of that name should resolve to the element being * declared.</li> * <li>An identifier denoting a prefix should resolve to the element representing the import that * defines the prefix (an {@link ImportElement}).</li> * <li>An identifier denoting a variable should resolve to the element representing the variable (a * {@link VariableElement}).</li> * <li>An identifier denoting a parameter should resolve to the element representing the parameter * (a {@link ParameterElement}).</li> * <li>An identifier denoting a field should resolve to the element representing the getter or * setter being invoked (a {@link PropertyAccessorElement}).</li> * <li>An identifier denoting the name of a method or function being invoked should resolve to the * element representing the method or function (a {@link ExecutableElement}).</li> * <li>An identifier denoting a label should resolve to the element representing the label (a * {@link LabelElement}).</li> * </ul> * The identifiers within directives are exceptions to this rule and are covered below.</li> * <li>Every node containing a token representing an operator that can be overridden ( * {@link BinaryExpression}, {@link PrefixExpression}, {@link PostfixExpression}) should resolve to * the element representing the method invoked by that operator (a {@link MethodElement}).</li> * <li>Every {@link FunctionExpressionInvocation} should resolve to the element representing the * function being invoked (a {@link 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.</li> * <li>Every {@link LibraryDirective} and {@link PartOfDirective} should resolve to the element * representing the library being specified by the directive (a {@link LibraryElement}) unless, in * the case of a part-of directive, the specified library does not exist.</li> * <li>Every {@link ImportDirective} and {@link ExportDirective} should resolve to the element * representing the library being specified by the directive unless the specified library does not * exist (an {@link ImportElement} or {@link ExportElement}).</li> * <li>The identifier representing the prefix in an {@link ImportDirective} should resolve to the * element representing the prefix (a {@link PrefixElement}).</li> * <li>The identifiers in the hide and show combinators in {@link ImportDirective}s and * {@link 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).</li> * <li>Every {@link PartDirective} should resolve to the element representing the compilation unit * being specified by the string unless the specified compilation unit does not exist (a * {@link CompilationUnitElement}).</li> * </ol> * 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). * * @coverage dart.engine.resolver */ public class ElementResolver extends SimpleAstVisitor<Void> { /** * Instances of the class {@code SyntheticIdentifier} implement 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. */ private static class SyntheticIdentifier extends Identifier { /** * The name of the synthetic identifier. */ private final String name; /** * The identifier to be highlighted in case of an error */ private final Identifier targetIdentifier; /** * Initialize a newly created synthetic identifier to have the given name. * * @param name the name of the synthetic identifier * @param targetIdentifier the identifier to be highlighted in case of an error */ private SyntheticIdentifier(String name, Identifier targetIdentifier) { this.name = name; this.targetIdentifier = targetIdentifier; } @Override public <R> R accept(AstVisitor<R> visitor) { return null; } @Override public Token getBeginToken() { return null; } @Override public Element getBestElement() { return null; } @Override public Token getEndToken() { return null; } @Override public int getLength() { return targetIdentifier.getLength(); } @Override public String getName() { return name; } @Override public int getOffset() { return targetIdentifier.getOffset(); } @Override public int getPrecedence() { return 16; } @Override public Element getPropagatedElement() { return null; } @Override public Element getStaticElement() { return null; } @Override public void visitChildren(AstVisitor<?> visitor) { } } /** * Checks whether the given expression is a reference to a class. If it is then the * {@link ClassElement} is returned, otherwise {@code null} is returned. * * @param expression the expression to evaluate * @return the element representing the class */ public static ClassElementImpl getTypeReference(Expression expression) { if (expression instanceof Identifier) { Element staticElement = ((Identifier) expression).getStaticElement(); if (staticElement instanceof ClassElementImpl) { return (ClassElementImpl) staticElement; } } return null; } /** * Helper function for {@code maybeMergeExecutableElements} that does the actual merging. * * @param elementArrayToMerge non-empty array of elements to merge. * @return */ private static ExecutableElement computeMergedExecutableElement( ExecutableElement[] elementArrayToMerge) { // Flatten methods structurally. Based on // [InheritanceManager.computeMergedExecutableElement] and // [InheritanceManager.createSyntheticExecutableElement]. // // However, the approach we take here is much simpler, but expected to work // well in the common case. It degrades gracefully in the uncommon case, // by computing the type [dynamic] for the method, preventing any // hints from being generated (TODO: not done yet). // // The approach is: we require that each [ExecutableElement] has the // same shape: the same number of required, optional positional, and optional named // parameters, in the same positions, and with the named parameters in the // same order. We compute a type by unioning pointwise. ExecutableElement e_0 = elementArrayToMerge[0]; ParameterElement[] ps_0 = e_0.getParameters(); ParameterElementImpl[] ps_out = new ParameterElementImpl[ps_0.length]; for (int j = 0; j < ps_out.length; j++) { ps_out[j] = new ParameterElementImpl(ps_0[j].getName(), 0); ps_out[j].setSynthetic(true); ps_out[j].setType(ps_0[j].getType()); ps_out[j].setParameterKind(ps_0[j].getParameterKind()); } Type r_out = e_0.getReturnType(); for (int i = 1; i < elementArrayToMerge.length; i++) { ExecutableElement e_i = elementArrayToMerge[i]; r_out = UnionTypeImpl.union(r_out, e_i.getReturnType()); ParameterElement[] ps_i = e_i.getParameters(); // Each function must have the same number of params. if (ps_0.length != ps_i.length) { return null; // TODO (collinsn): return an element representing [dynamic] here instead. } else { // Each function must have the same kind of params, with the same names, // in the same order. for (int j = 0; j < ps_i.length; j++) { if (ps_0[j].getParameterKind() != ps_i[j].getParameterKind() || ps_0[j].getName() != ps_i[j].getName()) { return null; } else { // The output parameter type is the union of the input parameter types. ps_out[j].setType(UnionTypeImpl.union(ps_out[j].getType(), ps_i[j].getType())); } } } } // TODO (collinsn): this code should work for functions and methods, // so we may want [FunctionElementImpl] // instead here in some cases? And then there are constructors and property accessors. // Maybe the answer is to create a new subclass of [ExecutableElementImpl] which // is used for merged executable elements, in analogy with [MultiplyInheritedMethodElementImpl] // and [MultiplyInheritedPropertyAcessorElementImpl]. ExecutableElementImpl e_out = new MethodElementImpl(e_0.getName(), 0); e_out.setSynthetic(true); e_out.setReturnType(r_out); e_out.setParameters(ps_out); e_out.setType(new FunctionTypeImpl(e_out)); // Get NPE in [toString()] w/o this. e_out.setEnclosingElement(e_0.getEnclosingElement()); return e_out; } /** * Return {@code true} if the given identifier is the return type of a constructor declaration. * * @return {@code true} if the given identifier is the return type of a constructor declaration. */ private static boolean isConstructorReturnType(SimpleIdentifier identifier) { AstNode parent = identifier.getParent(); if (parent instanceof ConstructorDeclaration) { return ((ConstructorDeclaration) parent).getReturnType() == identifier; } return false; } /** * Return {@code true} if the given identifier is the return type of a factory constructor. * * @return {@code true} if the given identifier is the return type of a factory constructor * declaration. */ private static boolean isFactoryConstructorReturnType(SimpleIdentifier node) { AstNode parent = node.getParent(); if (parent instanceof ConstructorDeclaration) { ConstructorDeclaration constructor = (ConstructorDeclaration) parent; return constructor.getReturnType() == node && constructor.getFactoryKeyword() != null; } return false; } /** * Return {@code true} if the given 'super' expression is used in a valid context. * * @param node the 'super' expression to analyze * @return {@code true} if the 'super' expression is in a valid context */ private static boolean isSuperInValidContext(SuperExpression node) { for (AstNode n = node; n != null; n = n.getParent()) { if (n instanceof CompilationUnit) { return false; } if (n instanceof ConstructorDeclaration) { ConstructorDeclaration constructor = (ConstructorDeclaration) n; return constructor.getFactoryKeyword() == null; } if (n instanceof ConstructorFieldInitializer) { return false; } if (n instanceof MethodDeclaration) { MethodDeclaration method = (MethodDeclaration) n; return !method.isStatic(); } } return false; } /** * Return a method representing the merge of the given elements. The type of the merged element is * the component-wise union of the types of the given elements. If not all input elements have the * same shape then [null] is returned. * * @param elements the {@code ExecutableElement}s to merge * @return an {@code ExecutableElement} representing the merge of {@code elements} */ // TODO (collinsn): somehow return [dynamic] here, or at least in the callers, when not all // given methods have the same shape. private static ExecutableElement maybeMergeExecutableElements(Set<ExecutableElement> elements) { ExecutableElement[] elementArrayToMerge = elements.toArray(new ExecutableElement[elements.size()]); if (elementArrayToMerge.length == 0) { return null; } else if (elementArrayToMerge.length == 1) { // If all methods are equal, don't bother building a new one. return elementArrayToMerge[0]; } else { return computeMergedExecutableElement(elementArrayToMerge); } } /** * The resolver driving this participant. */ private ResolverVisitor resolver; /** * The element for the library containing the compilation unit being visited. */ private LibraryElement definingLibrary; /** * A flag indicating whether we should generate hints. */ private boolean enableHints; /** * The type representing the type 'dynamic'. */ private Type dynamicType; /** * The type representing the type 'type'. */ private Type typeType; /** * A utility class for the resolver to answer the question of "what are my subtypes?". */ private SubtypeManager subtypeManager; /** * The object keeping track of which elements have had their types promoted. */ private TypePromotionManager promoteManager; /** * Initialize a newly created visitor to resolve the nodes in a compilation unit. * * @param resolver the resolver driving this participant */ public ElementResolver(ResolverVisitor resolver) { this.resolver = resolver; this.definingLibrary = resolver.getDefiningLibrary(); AnalysisOptions options = definingLibrary.getContext().getAnalysisOptions(); enableHints = options.getHint(); dynamicType = resolver.getTypeProvider().getDynamicType(); typeType = resolver.getTypeProvider().getTypeType(); subtypeManager = new SubtypeManager(); promoteManager = resolver.getPromoteManager(); } @Override public Void visitAssignmentExpression(AssignmentExpression node) { Token operator = node.getOperator(); TokenType operatorType = operator.getType(); if (operatorType != TokenType.EQ) { operatorType = operatorFromCompoundAssignment(operatorType); Expression leftHandSide = node.getLeftHandSide(); if (leftHandSide != null) { String methodName = operatorType.getLexeme(); Type staticType = getStaticType(leftHandSide); MethodElement staticMethod = lookUpMethod(leftHandSide, staticType, methodName); node.setStaticElement(staticMethod); Type propagatedType = getPropagatedType(leftHandSide); MethodElement propagatedMethod = lookUpMethod(leftHandSide, propagatedType, methodName); node.setPropagatedElement(propagatedMethod); if (shouldReportMissingMember(staticType, staticMethod)) { recordUndefinedToken( staticType.getElement(), StaticTypeWarningCode.UNDEFINED_METHOD, operator, methodName, staticType.getDisplayName()); } else if (enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.getElement(), methodName, true, false)) { recordUndefinedToken( propagatedType.getElement(), HintCode.UNDEFINED_METHOD, operator, methodName, propagatedType.getDisplayName()); } } } return null; } @Override public Void visitBinaryExpression(BinaryExpression node) { Token operator = node.getOperator(); if (operator.isUserDefinableOperator()) { Expression leftOperand = node.getLeftOperand(); if (leftOperand != null) { String methodName = operator.getLexeme(); Type staticType = getStaticType(leftOperand); MethodElement staticMethod = lookUpMethod(leftOperand, staticType, methodName); node.setStaticElement(staticMethod); Type propagatedType = getPropagatedType(leftOperand); MethodElement propagatedMethod = lookUpMethod(leftOperand, propagatedType, methodName); node.setPropagatedElement(propagatedMethod); if (shouldReportMissingMember(staticType, staticMethod)) { recordUndefinedToken( staticType.getElement(), StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, methodName, staticType.getDisplayName()); } else if (enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.getElement(), methodName, true, false)) { recordUndefinedToken( propagatedType.getElement(), HintCode.UNDEFINED_OPERATOR, operator, methodName, propagatedType.getDisplayName()); } } } return null; } @Override public Void visitBreakStatement(BreakStatement node) { lookupLabel(node, node.getLabel()); return null; } @Override public Void visitClassDeclaration(ClassDeclaration node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitClassTypeAlias(ClassTypeAlias node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitCommentReference(CommentReference node) { Identifier identifier = node.getIdentifier(); if (identifier instanceof SimpleIdentifier) { SimpleIdentifier simpleIdentifier = (SimpleIdentifier) identifier; Element element = resolveSimpleIdentifier(simpleIdentifier); if (element == null) { // // This might be a reference to an imported name that is missing the prefix. // element = findImportWithoutPrefix(simpleIdentifier); if (element instanceof MultiplyDefinedElement) { // TODO(brianwilkerson) Report this error? element = null; } } if (element == null) { // TODO(brianwilkerson) Report this error? // resolver.reportError( // StaticWarningCode.UNDEFINED_IDENTIFIER, // simpleIdentifier, // simpleIdentifier.getName()); } else { if (element.getLibrary() == null || !element.getLibrary().equals(definingLibrary)) { // TODO(brianwilkerson) Report this error? } simpleIdentifier.setStaticElement(element); if (node.getNewKeyword() != null) { if (element instanceof ClassElement) { ConstructorElement constructor = ((ClassElement) element).getUnnamedConstructor(); if (constructor == null) { // TODO(brianwilkerson) Report this error. } else { simpleIdentifier.setStaticElement(constructor); } } else { // TODO(brianwilkerson) Report this error. } } } } else if (identifier instanceof PrefixedIdentifier) { PrefixedIdentifier prefixedIdentifier = (PrefixedIdentifier) identifier; SimpleIdentifier prefix = prefixedIdentifier.getPrefix(); SimpleIdentifier name = prefixedIdentifier.getIdentifier(); Element element = resolveSimpleIdentifier(prefix); if (element == null) { // resolver.reportError(StaticWarningCode.UNDEFINED_IDENTIFIER, prefix, prefix.getName()); } else { if (element instanceof PrefixElement) { prefix.setStaticElement(element); // TODO(brianwilkerson) The prefix needs to be resolved to the element for the import that // defines the prefix, not the prefix's element. // TODO(brianwilkerson) Report this error? element = resolver.getNameScope().lookup(identifier, definingLibrary); name.setStaticElement(element); return null; } LibraryElement library = element.getLibrary(); if (library == null) { // TODO(brianwilkerson) We need to understand how the library could ever be null. AnalysisEngine.getInstance().getLogger().logError( "Found element with null library: " + element.getName()); } else if (!library.equals(definingLibrary)) { // TODO(brianwilkerson) Report this error. } name.setStaticElement(element); if (node.getNewKeyword() == null) { if (element instanceof ClassElement) { Element memberElement = lookupGetterOrMethod( ((ClassElement) element).getType(), name.getName()); if (memberElement == null) { memberElement = ((ClassElement) element).getNamedConstructor(name.getName()); if (memberElement == null) { memberElement = lookUpSetter( prefix, ((ClassElement) element).getType(), name.getName()); } } if (memberElement == null) { // reportGetterOrSetterNotFound(prefixedIdentifier, name, element.getDisplayName()); } else { name.setStaticElement(memberElement); } } else { // TODO(brianwilkerson) Report this error. } } else { if (element instanceof ClassElement) { ConstructorElement constructor = ((ClassElement) element).getNamedConstructor(name.getName()); if (constructor == null) { // TODO(brianwilkerson) Report this error. } else { name.setStaticElement(constructor); } } else { // TODO(brianwilkerson) Report this error. } } } } return null; } @Override public Void visitConstructorDeclaration(ConstructorDeclaration node) { super.visitConstructorDeclaration(node); ConstructorElement element = node.getElement(); if (element instanceof ConstructorElementImpl) { ConstructorElementImpl constructorElement = (ConstructorElementImpl) element; ConstructorName redirectedNode = node.getRedirectedConstructor(); if (redirectedNode != null) { // set redirected factory constructor ConstructorElement redirectedElement = redirectedNode.getStaticElement(); constructorElement.setRedirectedConstructor(redirectedElement); } else { // set redirected generative constructor for (ConstructorInitializer initializer : node.getInitializers()) { if (initializer instanceof RedirectingConstructorInvocation) { ConstructorElement redirectedElement = ((RedirectingConstructorInvocation) initializer).getStaticElement(); constructorElement.setRedirectedConstructor(redirectedElement); } } } setMetadata(constructorElement, node); } return null; } @Override public Void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { SimpleIdentifier fieldName = node.getFieldName(); ClassElement enclosingClass = resolver.getEnclosingClass(); FieldElement fieldElement = enclosingClass.getField(fieldName.getName()); fieldName.setStaticElement(fieldElement); return null; } @Override public Void visitConstructorName(ConstructorName node) { Type type = node.getType().getType(); if (type != null && type.isDynamic()) { return null; } else if (!(type instanceof InterfaceType)) { // TODO(brianwilkerson) Report these errors. // ASTNode parent = node.getParent(); // if (parent instanceof InstanceCreationExpression) { // if (((InstanceCreationExpression) parent).isConst()) { // // CompileTimeErrorCode.CONST_WITH_NON_TYPE // } else { // // StaticWarningCode.NEW_WITH_NON_TYPE // } // } else { // // This is part of a redirecting factory constructor; not sure which error code to use // } return null; } // look up ConstructorElement ConstructorElement constructor; SimpleIdentifier name = node.getName(); InterfaceType interfaceType = (InterfaceType) type; if (name == null) { constructor = interfaceType.lookUpConstructor(null, definingLibrary); } else { constructor = interfaceType.lookUpConstructor(name.getName(), definingLibrary); name.setStaticElement(constructor); } node.setStaticElement(constructor); return null; } @Override public Void visitContinueStatement(ContinueStatement node) { lookupLabel(node, node.getLabel()); return null; } @Override public Void visitDeclaredIdentifier(DeclaredIdentifier node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitExportDirective(ExportDirective node) { ExportElement exportElement = node.getElement(); 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.getExportedLibrary(), node.getCombinators()); setMetadata(exportElement, node); } return null; } @Override public Void visitFieldFormalParameter(FieldFormalParameter node) { setMetadataForParameter(node.getElement(), node); return super.visitFieldFormalParameter(node); } @Override public Void visitFunctionDeclaration(FunctionDeclaration node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { // TODO(brianwilkerson) Can we ever resolve the function being invoked? Expression expression = node.getFunction(); if (expression instanceof FunctionExpression) { FunctionExpression functionExpression = (FunctionExpression) expression; ExecutableElement functionElement = functionExpression.getElement(); ArgumentList argumentList = node.getArgumentList(); ParameterElement[] parameters = resolveArgumentsToFunction( false, argumentList, functionElement); if (parameters != null) { argumentList.setCorrespondingStaticParameters(parameters); } } return null; } @Override public Void visitFunctionTypeAlias(FunctionTypeAlias node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { setMetadataForParameter(node.getElement(), node); return null; } @Override public Void visitImportDirective(ImportDirective node) { SimpleIdentifier prefixNode = node.getPrefix(); if (prefixNode != null) { String prefixName = prefixNode.getName(); for (PrefixElement prefixElement : definingLibrary.getPrefixes()) { if (prefixElement.getDisplayName().equals(prefixName)) { prefixNode.setStaticElement(prefixElement); break; } } } ImportElement importElement = node.getElement(); if (importElement != null) { // The element is null when the URI is invalid LibraryElement library = importElement.getImportedLibrary(); if (library != null) { resolveCombinators(library, node.getCombinators()); } setMetadata(importElement, node); } return null; } @Override public Void visitIndexExpression(IndexExpression node) { Expression target = node.getRealTarget(); Type staticType = getStaticType(target); Type propagatedType = getPropagatedType(target); String getterMethodName = TokenType.INDEX.getLexeme(); String setterMethodName = TokenType.INDEX_EQ.getLexeme(); boolean isInGetterContext = node.inGetterContext(); boolean isInSetterContext = node.inSetterContext(); if (isInGetterContext && isInSetterContext) { // lookup setter MethodElement setterStaticMethod = lookUpMethod(target, staticType, setterMethodName); MethodElement setterPropagatedMethod = lookUpMethod(target, propagatedType, setterMethodName); // set setter element node.setStaticElement(setterStaticMethod); node.setPropagatedElement(setterPropagatedMethod); // generate undefined method warning checkForUndefinedIndexOperator( node, target, getterMethodName, setterStaticMethod, setterPropagatedMethod, staticType, propagatedType); // lookup getter method MethodElement getterStaticMethod = lookUpMethod(target, staticType, getterMethodName); MethodElement getterPropagatedMethod = lookUpMethod(target, propagatedType, getterMethodName); // set getter element AuxiliaryElements auxiliaryElements = new AuxiliaryElements( getterStaticMethod, getterPropagatedMethod); node.setAuxiliaryElements(auxiliaryElements); // generate undefined method warning checkForUndefinedIndexOperator( node, target, getterMethodName, getterStaticMethod, getterPropagatedMethod, staticType, propagatedType); } else if (isInGetterContext) { // lookup getter method MethodElement staticMethod = lookUpMethod(target, staticType, getterMethodName); MethodElement propagatedMethod = lookUpMethod(target, propagatedType, getterMethodName); // set getter element node.setStaticElement(staticMethod); node.setPropagatedElement(propagatedMethod); // generate undefined method warning checkForUndefinedIndexOperator( node, target, getterMethodName, staticMethod, propagatedMethod, staticType, propagatedType); } else if (isInSetterContext) { // lookup setter method MethodElement staticMethod = lookUpMethod(target, staticType, setterMethodName); MethodElement propagatedMethod = lookUpMethod(target, propagatedType, setterMethodName); // set setter element node.setStaticElement(staticMethod); node.setPropagatedElement(propagatedMethod); // generate undefined method warning checkForUndefinedIndexOperator( node, target, setterMethodName, staticMethod, propagatedMethod, staticType, propagatedType); } return null; } @Override public Void visitInstanceCreationExpression(InstanceCreationExpression node) { ConstructorElement invokedConstructor = node.getConstructorName().getStaticElement(); node.setStaticElement(invokedConstructor); ArgumentList argumentList = node.getArgumentList(); ParameterElement[] parameters = resolveArgumentsToFunction( node.isConst(), argumentList, invokedConstructor); if (parameters != null) { argumentList.setCorrespondingStaticParameters(parameters); } return null; } @Override public Void visitLibraryDirective(LibraryDirective node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitMethodDeclaration(MethodDeclaration node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitMethodInvocation(MethodInvocation node) { SimpleIdentifier methodName = node.getMethodName(); // // Synthetic identifiers have been already reported during parsing. // if (methodName.isSynthetic()) { return null; } // // We have a method invocation of one of two forms: 'e.m(a1, ..., an)' or 'm(a1, ..., an)'. The // first step is to figure out which executable is being invoked, using both the static and the // propagated type information. // Expression target = node.getRealTarget(); if (target instanceof SuperExpression && !isSuperInValidContext((SuperExpression) target)) { return null; } Element staticElement; Element propagatedElement; Type staticType = null; Type propagatedType = null; if (target == null) { staticElement = resolveInvokedElement(methodName); propagatedElement = null; } else if (methodName.getName().equals(FunctionElement.LOAD_LIBRARY_NAME) && isDeferredPrefix(target)) { LibraryElement importedLibrary = getImportedLibrary(target); methodName.setStaticElement(importedLibrary.getLoadLibraryFunction()); return null; } else { staticType = getStaticType(target); propagatedType = getPropagatedType(target); // // If this method invocation is of the form 'C.m' where 'C' is a class, then we don't call // resolveInvokedElement(..) which walks up the class hierarchy, instead we just look for the // member in the type only. // ClassElementImpl typeReference = getTypeReference(target); if (typeReference != null) { staticElement = propagatedElement = resolveElement(typeReference, methodName); } else { staticElement = resolveInvokedElementWithTarget(target, staticType, methodName); propagatedElement = resolveInvokedElementWithTarget(target, propagatedType, methodName); } } staticElement = convertSetterToGetter(staticElement); propagatedElement = convertSetterToGetter(propagatedElement); // // Record the results. // methodName.setStaticElement(staticElement); methodName.setPropagatedElement(propagatedElement); ArgumentList argumentList = node.getArgumentList(); if (staticElement != null) { ParameterElement[] parameters = computeCorrespondingParameters(argumentList, staticElement); if (parameters != null) { argumentList.setCorrespondingStaticParameters(parameters); } } if (propagatedElement != null) { ParameterElement[] parameters = computeCorrespondingParameters( argumentList, propagatedElement); if (parameters != null) { argumentList.setCorrespondingPropagatedParameters(parameters); } } // // Then check for error conditions. // ErrorCode errorCode = checkForInvocationError(target, true, staticElement); boolean generatedWithTypePropagation = false; if (enableHints && errorCode == null && staticElement == null) { // The method lookup may have failed because there were multiple // incompatible choices. In this case we don't want to generate a hint. if (propagatedElement == null && propagatedType instanceof UnionType) { // TODO(collinsn): an improvement here is to make the propagated type of the method call // the union of the propagated types of all possible calls. if (lookupMethods(target, (UnionType) propagatedType, methodName.getName()).size() > 1) { return null; } } errorCode = checkForInvocationError(target, false, propagatedElement); if (errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) { ClassElement classElementContext = null; if (target == null) { classElementContext = resolver.getEnclosingClass(); } else { Type type = target.getBestType(); if (type != null) { if (type.getElement() instanceof ClassElement) { classElementContext = (ClassElement) type.getElement(); } } } if (classElementContext != null) { subtypeManager.ensureLibraryVisited(definingLibrary); HashSet<ClassElement> subtypeElements = subtypeManager.computeAllSubtypes(classElementContext); for (ClassElement subtypeElement : subtypeElements) { if (subtypeElement.getMethod(methodName.getName()) != null) { errorCode = null; } } } } generatedWithTypePropagation = true; } if (errorCode == null) { return null; } if (errorCode == StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) { resolver.reportErrorForNode( StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName, methodName.getName()); } else if (errorCode == StaticTypeWarningCode.UNDEFINED_FUNCTION) { resolver.reportErrorForNode( StaticTypeWarningCode.UNDEFINED_FUNCTION, methodName, methodName.getName()); } else if (errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) { String targetTypeName; if (target == null) { ClassElement enclosingClass = resolver.getEnclosingClass(); targetTypeName = enclosingClass.getDisplayName(); ErrorCode proxyErrorCode = generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD; recordUndefinedNode( resolver.getEnclosingClass(), proxyErrorCode, methodName, methodName.getName(), targetTypeName); } else { // ignore Function "call" // (if we are about to create a hint using type propagation, then we can use type // propagation here as well) Type targetType = null; if (!generatedWithTypePropagation) { targetType = getStaticType(target); } else { // choose the best type targetType = getPropagatedType(target); if (targetType == null) { targetType = getStaticType(target); } } if (targetType != null && targetType.isDartCoreFunction() && methodName.getName().equals(FunctionElement.CALL_METHOD_NAME)) { // TODO(brianwilkerson) Can we ever resolve the function being invoked? //resolveArgumentsToParameters(node.getArgumentList(), invokedFunction); return null; } targetTypeName = targetType == null ? null : targetType.getDisplayName(); ErrorCode proxyErrorCode = generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD; recordUndefinedNode( targetType.getElement(), proxyErrorCode, methodName, methodName.getName(), targetTypeName); } } else if (errorCode == StaticTypeWarningCode.UNDEFINED_SUPER_METHOD) { // Generate the type name. // The error code will never be generated via type propagation Type targetType = getStaticType(target); if (targetType instanceof InterfaceType && !targetType.isObject()) { targetType = ((InterfaceType) targetType).getSuperclass(); } String targetTypeName = targetType == null ? null : targetType.getName(); resolver.reportErrorForNode( StaticTypeWarningCode.UNDEFINED_SUPER_METHOD, methodName, methodName.getName(), targetTypeName); } return null; } @Override public Void visitPartDirective(PartDirective node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitPartOfDirective(PartOfDirective node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitPostfixExpression(PostfixExpression node) { Expression operand = node.getOperand(); String methodName = getPostfixOperator(node); Type staticType = getStaticType(operand); MethodElement staticMethod = lookUpMethod(operand, staticType, methodName); node.setStaticElement(staticMethod); Type propagatedType = getPropagatedType(operand); MethodElement propagatedMethod = lookUpMethod(operand, propagatedType, methodName); node.setPropagatedElement(propagatedMethod); if (shouldReportMissingMember(staticType, staticMethod)) { recordUndefinedToken( staticType.getElement(), StaticTypeWarningCode.UNDEFINED_OPERATOR, node.getOperator(), methodName, staticType.getDisplayName()); } else if (enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.getElement(), methodName, true, false)) { recordUndefinedToken( propagatedType.getElement(), HintCode.UNDEFINED_OPERATOR, node.getOperator(), methodName, propagatedType.getDisplayName()); } return null; } @Override public Void visitPrefixedIdentifier(PrefixedIdentifier node) { SimpleIdentifier prefix = node.getPrefix(); SimpleIdentifier identifier = node.getIdentifier(); // // First, check the "lib.loadLibrary" case // if (identifier.getName().equals(FunctionElement.LOAD_LIBRARY_NAME) && isDeferredPrefix(prefix)) { LibraryElement importedLibrary = getImportedLibrary(prefix); identifier.setStaticElement(importedLibrary.getLoadLibraryFunction()); return null; } // // Check to see whether the prefix is really a prefix. // Element prefixElement = prefix.getStaticElement(); if (prefixElement instanceof PrefixElement) { Element element = resolver.getNameScope().lookup(node, definingLibrary); if (element == null && identifier.inSetterContext()) { element = resolver.getNameScope().lookup( new SyntheticIdentifier(node.getName() + "=", node), definingLibrary); } if (element == null) { if (identifier.inSetterContext()) { resolver.reportErrorForNode( StaticWarningCode.UNDEFINED_SETTER, identifier, identifier.getName(), prefixElement.getName()); } else if (node.getParent() instanceof Annotation) { Annotation annotation = (Annotation) node.getParent(); resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATION, annotation); return null; } else { resolver.reportErrorForNode( StaticWarningCode.UNDEFINED_GETTER, identifier, identifier.getName(), prefixElement.getName()); } return null; } if (element instanceof PropertyAccessorElement && identifier.inSetterContext()) { PropertyInducingElement variable = ((PropertyAccessorElement) element).getVariable(); if (variable != null) { PropertyAccessorElement setter = variable.getSetter(); if (setter != null) { element = setter; } } } // TODO(brianwilkerson) The prefix needs to be resolved to the element for the import that // defines the prefix, not the prefix's element. identifier.setStaticElement(element); // Validate annotation element. if (node.getParent() instanceof Annotation) { Annotation annotation = (Annotation) node.getParent(); resolveAnnotationElement(annotation); return null; } return null; } // May be annotation, resolve invocation of "const" constructor. if (node.getParent() instanceof Annotation) { Annotation annotation = (Annotation) node.getParent(); resolveAnnotationElement(annotation); } // // Otherwise, the prefix is really an expression that happens to be a simple identifier and this // is really equivalent to a property access node. // resolvePropertyAccess(prefix, identifier); return null; } @Override public Void visitPrefixExpression(PrefixExpression node) { Token operator = node.getOperator(); TokenType operatorType = operator.getType(); if (operatorType.isUserDefinableOperator() || operatorType == TokenType.PLUS_PLUS || operatorType == TokenType.MINUS_MINUS) { Expression operand = node.getOperand(); String methodName = getPrefixOperator(node); Type staticType = getStaticType(operand); MethodElement staticMethod = lookUpMethod(operand, staticType, methodName); node.setStaticElement(staticMethod); Type propagatedType = getPropagatedType(operand); MethodElement propagatedMethod = lookUpMethod(operand, propagatedType, methodName); node.setPropagatedElement(propagatedMethod); if (shouldReportMissingMember(staticType, staticMethod)) { recordUndefinedToken( staticType.getElement(), StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, methodName, staticType.getDisplayName()); } else if (enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.getElement(), methodName, true, false)) { recordUndefinedToken( propagatedType.getElement(), HintCode.UNDEFINED_OPERATOR, operator, methodName, propagatedType.getDisplayName()); } } return null; } @Override public Void visitPropertyAccess(PropertyAccess node) { Expression target = node.getRealTarget(); if (target instanceof SuperExpression && !isSuperInValidContext((SuperExpression) target)) { return null; } SimpleIdentifier propertyName = node.getPropertyName(); resolvePropertyAccess(target, propertyName); return null; } @Override public Void visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) { ClassElement enclosingClass = resolver.getEnclosingClass(); if (enclosingClass == null) { // TODO(brianwilkerson) Report this error. return null; } SimpleIdentifier name = node.getConstructorName(); ConstructorElement element; if (name == null) { element = enclosingClass.getUnnamedConstructor(); } else { element = enclosingClass.getNamedConstructor(name.getName()); } if (element == null) { // TODO(brianwilkerson) Report this error and decide what element to associate with the node. return null; } if (name != null) { name.setStaticElement(element); } node.setStaticElement(element); ArgumentList argumentList = node.getArgumentList(); ParameterElement[] parameters = resolveArgumentsToFunction(false, argumentList, element); if (parameters != null) { argumentList.setCorrespondingStaticParameters(parameters); } return null; } @Override public Void visitSimpleFormalParameter(SimpleFormalParameter node) { setMetadataForParameter(node.getElement(), node); return null; } @Override public Void visitSimpleIdentifier(SimpleIdentifier node) { // // Synthetic identifiers have been already reported during parsing. // if (node.isSynthetic()) { return null; } // // We ignore identifiers that have already been resolved, such as identifiers representing the // name in a declaration. // if (node.getStaticElement() != null) { return null; } // // The name dynamic denotes a Type object even though dynamic is not a class. // if (node.getName().equals(dynamicType.getName())) { node.setStaticElement(dynamicType.getElement()); node.setStaticType(typeType); return null; } // // Otherwise, the node should be resolved. // Element element = resolveSimpleIdentifier(node); ClassElement enclosingClass = resolver.getEnclosingClass(); if (isFactoryConstructorReturnType(node) && element != enclosingClass) { resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_FACTORY_NAME_NOT_A_CLASS, node); } else if (isConstructorReturnType(node) && element != enclosingClass) { resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node); element = null; } else if (element == null || (element instanceof PrefixElement && !isValidAsPrefix(node))) { // TODO(brianwilkerson) Recover from this error. if (isConstructorReturnType(node)) { resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node); } else if (node.getParent() instanceof Annotation) { Annotation annotation = (Annotation) node.getParent(); resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATION, annotation); } else { recordUndefinedNode( resolver.getEnclosingClass(), StaticWarningCode.UNDEFINED_IDENTIFIER, node, node.getName()); } } node.setStaticElement(element); if (node.inSetterContext() && node.inGetterContext() && enclosingClass != null) { InterfaceType enclosingType = enclosingClass.getType(); AuxiliaryElements auxiliaryElements = new AuxiliaryElements(lookUpGetter( null, enclosingType, node.getName()), null); node.setAuxiliaryElements(auxiliaryElements); } // // Validate annotation element. // if (node.getParent() instanceof Annotation) { Annotation annotation = (Annotation) node.getParent(); resolveAnnotationElement(annotation); } return null; } @Override public Void visitSuperConstructorInvocation(SuperConstructorInvocation node) { ClassElement enclosingClass = resolver.getEnclosingClass(); if (enclosingClass == null) { // TODO(brianwilkerson) Report this error. return null; } InterfaceType superType = enclosingClass.getSupertype(); if (superType == null) { // TODO(brianwilkerson) Report this error. return null; } SimpleIdentifier name = node.getConstructorName(); String superName = name != null ? name.getName() : null; ConstructorElement element = superType.lookUpConstructor(superName, definingLibrary); if (element == null) { if (name != null) { resolver.reportErrorForNode( CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER, node, superType.getDisplayName(), name); } else { resolver.reportErrorForNode( CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, node, superType.getDisplayName()); } return null; } else { if (element.isFactory()) { resolver.reportErrorForNode(CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node, element); } } if (name != null) { name.setStaticElement(element); } node.setStaticElement(element); ArgumentList argumentList = node.getArgumentList(); ParameterElement[] parameters = resolveArgumentsToFunction( isInConstConstructor(), argumentList, element); if (parameters != null) { argumentList.setCorrespondingStaticParameters(parameters); } return null; } @Override public Void visitSuperExpression(SuperExpression node) { if (!isSuperInValidContext(node)) { resolver.reportErrorForNode(CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT, node); } return super.visitSuperExpression(node); } @Override public Void visitTypeParameter(TypeParameter node) { setMetadata(node.getElement(), node); return null; } @Override public Void visitVariableDeclaration(VariableDeclaration node) { setMetadata(node.getElement(), node); return null; } /** * Generate annotation elements for each of the annotations in the given node list and add them to * the given list of elements. * * @param annotationList the list of elements to which new elements are to be added * @param annotations the AST nodes used to generate new elements */ private void addAnnotations(ArrayList<ElementAnnotationImpl> annotationList, NodeList<Annotation> annotations) { int annotationCount = annotations.size(); for (int i = 0; i < annotationCount; i++) { Annotation annotation = annotations.get(i); Element resolvedElement = annotation.getElement(); if (resolvedElement != null) { ElementAnnotationImpl elementAnnotation = new ElementAnnotationImpl(resolvedElement); annotation.setElementAnnotation(elementAnnotation); annotationList.add(elementAnnotation); } } } /** * Given that we have found code to invoke the given element, return the error code that should be * reported, or {@code null} if no error should be reported. * * @param target the target of the invocation, or {@code null} if there was no target * @param useStaticContext * @param element the element to be invoked * @return the error code that should be reported */ private ErrorCode checkForInvocationError(Expression target, boolean useStaticContext, Element element) { // Prefix is not declared, instead "prefix.id" are declared. if (element instanceof PrefixElement) { element = null; } if (element instanceof PropertyAccessorElement) { // // This is really a function expression invocation. // // TODO(brianwilkerson) Consider the possibility of re-writing the AST. FunctionType getterType = ((PropertyAccessorElement) element).getType(); if (getterType != null) { Type returnType = getterType.getReturnType(); if (!isExecutableType(returnType)) { return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; } } } else if (element instanceof ExecutableElement) { return null; } else if (element instanceof MultiplyDefinedElement) { // The error has already been reported return null; } else if (element == null && target instanceof SuperExpression) { // TODO(jwren) We should split the UNDEFINED_METHOD into two error codes, this one, and // a code that describes the situation where the method was found, but it was not // accessible from the current library. return StaticTypeWarningCode.UNDEFINED_SUPER_METHOD; } else { // // This is really a function expression invocation. // // TODO(brianwilkerson) Consider the possibility of re-writing the AST. if (element instanceof PropertyInducingElement) { PropertyAccessorElement getter = ((PropertyInducingElement) element).getGetter(); FunctionType getterType = getter.getType(); if (getterType != null) { Type returnType = getterType.getReturnType(); if (!isExecutableType(returnType)) { return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; } } } else if (element instanceof VariableElement) { Type variableType = ((VariableElement) element).getType(); if (!isExecutableType(variableType)) { return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; } } else { if (target == null) { ClassElement enclosingClass = resolver.getEnclosingClass(); if (enclosingClass == null) { return StaticTypeWarningCode.UNDEFINED_FUNCTION; } else if (element == null) { // Proxy-conditional warning, based on state of resolver.getEnclosingClass() return StaticTypeWarningCode.UNDEFINED_METHOD; } else { return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; } } else { Type targetType; if (useStaticContext) { targetType = getStaticType(target); } else { // Compute and use the propagated type, if it is null, then it may be the case that // static type is some type, in which the static type should be used. targetType = target.getBestType(); } if (targetType == null) { return StaticTypeWarningCode.UNDEFINED_FUNCTION; } else if (!targetType.isDynamic() && !targetType.isBottom()) { // Proxy-conditional warning, based on state of targetType.getElement() return StaticTypeWarningCode.UNDEFINED_METHOD; } } } } return null; } /** * Check that the for some index expression that the method element was resolved, otherwise a * {@link StaticWarningCode#UNDEFINED_OPERATOR} is generated. * * @param node the index expression to resolve * @param target the target of the expression * @param methodName the name of the operator associated with the context of using of the given * index expression * @return {@code true} if and only if an error code is generated on the passed node */ private boolean checkForUndefinedIndexOperator(IndexExpression node, Expression target, String methodName, MethodElement staticMethod, MethodElement propagatedMethod, Type staticType, Type propagatedType) { boolean shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod); boolean shouldReportMissingMember_propagated = !shouldReportMissingMember_static && enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.getElement(), methodName, true, false); if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) { Token leftBracket = node.getLeftBracket(); Token rightBracket = node.getRightBracket(); ErrorCode errorCode = shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR; if (leftBracket == null || rightBracket == null) { recordUndefinedNode( shouldReportMissingMember_static ? staticType.getElement() : propagatedType.getElement(), errorCode, node, methodName, shouldReportMissingMember_static ? staticType.getDisplayName() : propagatedType.getDisplayName()); } else { int offset = leftBracket.getOffset(); int length = rightBracket.getOffset() - offset + 1; recordUndefinedOffset( shouldReportMissingMember_static ? staticType.getElement() : propagatedType.getElement(), errorCode, offset, length, methodName, shouldReportMissingMember_static ? staticType.getDisplayName() : propagatedType.getDisplayName()); } return true; } return false; } /** * Given a list of arguments and the element that will be invoked using those argument, compute * the list of parameters that correspond to the list of arguments. Return the parameters that * correspond to the arguments, or {@code null} if no correspondence could be computed. * * @param argumentList the list of arguments being passed to the element * @param executableElement the element that will be invoked with the arguments * @return the parameters that correspond to the arguments */ private ParameterElement[] computeCorrespondingParameters(ArgumentList argumentList, Element element) { if (element instanceof PropertyAccessorElement) { // // This is an invocation of the call method defined on the value returned by the getter. // FunctionType getterType = ((PropertyAccessorElement) element).getType(); if (getterType != null) { Type getterReturnType = getterType.getReturnType(); if (getterReturnType instanceof InterfaceType) { MethodElement callMethod = ((InterfaceType) getterReturnType).lookUpMethod( FunctionElement.CALL_METHOD_NAME, definingLibrary); if (callMethod != null) { return resolveArgumentsToFunction(false, argumentList, callMethod); } } else if (getterReturnType instanceof FunctionType) { ParameterElement[] parameters = ((FunctionType) getterReturnType).getParameters(); return resolveArgumentsToParameters(false, argumentList, parameters); } } } else if (element instanceof ExecutableElement) { return resolveArgumentsToFunction(false, argumentList, (ExecutableElement) element); } else if (element instanceof VariableElement) { VariableElement variable = (VariableElement) element; Type type = promoteManager.getStaticType(variable); if (type instanceof FunctionType) { FunctionType functionType = (FunctionType) type; ParameterElement[] parameters = functionType.getParameters(); return resolveArgumentsToParameters(false, argumentList, parameters); } else if (type instanceof InterfaceType) { // "call" invocation MethodElement callMethod = ((InterfaceType) type).lookUpMethod( FunctionElement.CALL_METHOD_NAME, definingLibrary); if (callMethod != null) { ParameterElement[] parameters = callMethod.getParameters(); return resolveArgumentsToParameters(false, argumentList, parameters); } } } return null; } /** * If the given element is a setter, return the getter associated with it. Otherwise, return the * element unchanged. * * @param element the element to be normalized * @return a non-setter element derived from the given element */ private Element convertSetterToGetter(Element element) { // TODO(brianwilkerson) Determine whether and why the element could ever be a setter. if (element instanceof PropertyAccessorElement) { return ((PropertyAccessorElement) element).getVariable().getGetter(); } return element; } /** * Return {@code true} if the given element is not a proxy. * * @param element the enclosing element. If null, {@code true} will be returned. * @return {@code false} iff the passed {@link Element} is a {@link ClassElement} that is a proxy * or inherits proxy * @see ClassElement#isOrInheritsProxy() */ private boolean doesntHaveProxy(Element element) { return !(element instanceof ClassElement && ((ClassElement) element).isOrInheritsProxy()); } /** * Look for any declarations of the given identifier that are imported using a prefix. Return the * element that was found, or {@code null} if the name is not imported using a prefix. * * @param identifier the identifier that might have been imported using a prefix * @return the element that was found */ private Element findImportWithoutPrefix(SimpleIdentifier identifier) { Element element = null; Scope nameScope = resolver.getNameScope(); for (ImportElement importElement : definingLibrary.getImports()) { PrefixElement prefixElement = importElement.getPrefix(); if (prefixElement != null) { Identifier prefixedIdentifier = new SyntheticIdentifier(prefixElement.getName() + "." + identifier.getName(), identifier); Element importedElement = nameScope.lookup(prefixedIdentifier, definingLibrary); if (importedElement != null) { if (element == null) { element = importedElement; } else { element = MultiplyDefinedElementImpl.fromElements( definingLibrary.getContext(), element, importedElement); } } } } return element; } /** * Assuming that the given expression is a prefix for a deferred import, return the library that * is being imported. * * @param expression the expression representing the deferred import's prefix * @return the library that is being imported by the import associated with the prefix */ private LibraryElement getImportedLibrary(Expression expression) { PrefixElement prefixElement = (PrefixElement) ((SimpleIdentifier) expression).getStaticElement(); ImportElement[] imports = prefixElement.getEnclosingElement().getImportsWithPrefix( prefixElement); return imports[0].getImportedLibrary(); } /** * Return the name of the method invoked by the given postfix expression. * * @param node the postfix expression being invoked * @return the name of the method invoked by the expression */ private String getPostfixOperator(PostfixExpression node) { return (node.getOperator().getType() == TokenType.PLUS_PLUS) ? TokenType.PLUS.getLexeme() : TokenType.MINUS.getLexeme(); } /** * Return the name of the method invoked by the given postfix expression. * * @param node the postfix expression being invoked * @return the name of the method invoked by the expression */ private String getPrefixOperator(PrefixExpression node) { Token operator = node.getOperator(); TokenType operatorType = operator.getType(); if (operatorType == TokenType.PLUS_PLUS) { return TokenType.PLUS.getLexeme(); } else if (operatorType == TokenType.MINUS_MINUS) { return TokenType.MINUS.getLexeme(); } else if (operatorType == TokenType.MINUS) { return "unary-"; } else { return operator.getLexeme(); } } /** * Return the propagated type of the given expression that is to be used for type analysis. * * @param expression the expression whose type is to be returned * @return the type of the given expression */ private Type getPropagatedType(Expression expression) { Type propagatedType = resolveTypeParameter(expression.getPropagatedType()); if (propagatedType instanceof FunctionType) { // // All function types are subtypes of 'Function', which is itself a subclass of 'Object'. // propagatedType = resolver.getTypeProvider().getFunctionType(); } return propagatedType; } /** * Return the static type of the given expression that is to be used for type analysis. * * @param expression the expression whose type is to be returned * @return the type of the given expression */ private Type getStaticType(Expression expression) { if (expression instanceof NullLiteral) { return resolver.getTypeProvider().getBottomType(); } Type staticType = resolveTypeParameter(expression.getStaticType()); if (staticType instanceof FunctionType) { // // All function types are subtypes of 'Function', which is itself a subclass of 'Object'. // staticType = resolver.getTypeProvider().getFunctionType(); } return staticType; } /** * Return {@code true} if the given expression is a prefix for a deferred import. * * @param expression the expression being tested * @return {@code true} if the given expression is a prefix for a deferred import */ private boolean isDeferredPrefix(Expression expression) { if (!(expression instanceof SimpleIdentifier)) { return false; } Element element = ((SimpleIdentifier) expression).getStaticElement(); if (!(element instanceof PrefixElement)) { return false; } PrefixElement prefixElement = (PrefixElement) element; ImportElement[] imports = prefixElement.getEnclosingElement().getImportsWithPrefix( prefixElement); if (imports.length != 1) { return false; } return imports[0].isDeferred(); } /** * Return {@code true} if the given type represents an object that could be invoked using the call * operator '()'. * * @param type the type being tested * @return {@code true} if the given type represents an object that could be invoked */ private boolean isExecutableType(Type type) { if (type.isDynamic() || (type instanceof FunctionType) || type.isDartCoreFunction() || type.isObject()) { return true; } else if (type instanceof InterfaceType) { ClassElement classElement = ((InterfaceType) type).getElement(); // 16078 from Gilad: If the type is a Functor with the @proxy annotation, treat it as an // executable type. // example code: NonErrorResolverTest.test_invocationOfNonFunction_proxyOnFunctionClass() if (classElement.isProxy() && type.isSubtypeOf(resolver.getTypeProvider().getFunctionType())) { return true; } MethodElement methodElement = classElement.lookUpMethod( FunctionElement.CALL_METHOD_NAME, definingLibrary); return methodElement != null; } return false; } /** * @return {@code true} iff current enclosing function is constant constructor declaration. */ private boolean isInConstConstructor() { ExecutableElement function = resolver.getEnclosingFunction(); if (function instanceof ConstructorElement) { return ((ConstructorElement) function).isConst(); } return false; } /** * Return {@code true} if the given element is a static element. * * @param element the element being tested * @return {@code true} if the given element is a static element */ private boolean isStatic(Element element) { if (element instanceof ExecutableElement) { return ((ExecutableElement) element).isStatic(); } else if (element instanceof PropertyInducingElement) { return ((PropertyInducingElement) element).isStatic(); } return false; } /** * Return {@code true} if the given node can validly be resolved to a prefix: * <ul> * <li>it is the prefix in an import directive, or</li> * <li>it is the prefix in a prefixed identifier.</li> * </ul> * * @param node the node being tested * @return {@code true} if the given node is the prefix in an import directive */ private boolean isValidAsPrefix(SimpleIdentifier node) { AstNode parent = node.getParent(); if (parent instanceof ImportDirective) { return ((ImportDirective) parent).getPrefix() == node; } else if (parent instanceof PrefixedIdentifier) { return true; } else if (parent instanceof MethodInvocation) { return ((MethodInvocation) parent).getTarget() == node; } return false; } /** * Look up the getter with the given name in the given type. Return the element representing the * getter that was found, or {@code null} if there is no getter with the given name. * * @param target the target of the invocation, or {@code null} if there is no target * @param type the type in which the getter is defined * @param getterName the name of the getter being looked up * @return the element representing the getter that was found */ private PropertyAccessorElement lookUpGetter(Expression target, Type type, String getterName) { type = resolveTypeParameter(type); if (type instanceof InterfaceType) { InterfaceType interfaceType = (InterfaceType) type; PropertyAccessorElement accessor; if (target instanceof SuperExpression) { accessor = interfaceType.lookUpGetterInSuperclass(getterName, definingLibrary); } else { accessor = interfaceType.lookUpGetter(getterName, definingLibrary); } if (accessor != null) { return accessor; } return lookUpGetterInInterfaces(interfaceType, false, getterName, new HashSet<ClassElement>()); } return null; } /** * Look up the getter with the given name in the interfaces implemented by the given type, either * directly or indirectly. Return the element representing the getter that was found, or * {@code null} if there is no getter with the given name. * * @param targetType the type in which the getter might be defined * @param includeTargetType {@code true} if the search should include the target type * @param getterName the name of the getter being looked up * @param visitedInterfaces a set containing all of the interfaces that have been examined, used * to prevent infinite recursion and to optimize the search * @return the element representing the getter that was found */ private PropertyAccessorElement lookUpGetterInInterfaces(InterfaceType targetType, boolean includeTargetType, String getterName, HashSet<ClassElement> visitedInterfaces) { // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for // finding the inherited member. We need to follow that scheme. The code below should cover the // 80% case. ClassElement targetClass = targetType.getElement(); if (visitedInterfaces.contains(targetClass)) { return null; } visitedInterfaces.add(targetClass); if (includeTargetType) { PropertyAccessorElement getter = targetType.getGetter(getterName); if (getter != null && getter.isAccessibleIn(definingLibrary)) { return getter; } } for (InterfaceType interfaceType : targetType.getInterfaces()) { PropertyAccessorElement getter = lookUpGetterInInterfaces( interfaceType, true, getterName, visitedInterfaces); if (getter != null) { return getter; } } for (InterfaceType mixinType : targetType.getMixins()) { PropertyAccessorElement getter = lookUpGetterInInterfaces( mixinType, true, getterName, visitedInterfaces); if (getter != null) { return getter; } } InterfaceType superclass = targetType.getSuperclass(); if (superclass == null) { return null; } return lookUpGetterInInterfaces(superclass, true, getterName, visitedInterfaces); } /** * Look up the method or getter with the given name in the given type. Return the element * representing the method or getter that was found, or {@code null} if there is no method or * getter with the given name. * * @param type the type in which the method or getter is defined * @param memberName the name of the method or getter being looked up * @return the element representing the method or getter that was found */ private ExecutableElement lookupGetterOrMethod(Type type, String memberName) { type = resolveTypeParameter(type); if (type instanceof InterfaceType) { InterfaceType interfaceType = (InterfaceType) type; ExecutableElement member = interfaceType.lookUpMethod(memberName, definingLibrary); if (member != null) { return member; } member = interfaceType.lookUpGetter(memberName, definingLibrary); if (member != null) { return member; } return lookUpGetterOrMethodInInterfaces( interfaceType, false, memberName, new HashSet<ClassElement>()); } return null; } /** * Look up the method or getter with the given name in the interfaces implemented by the given * type, either directly or indirectly. Return the element representing the method or getter that * was found, or {@code null} if there is no method or getter with the given name. * * @param targetType the type in which the method or getter might be defined * @param includeTargetType {@code true} if the search should include the target type * @param memberName the name of the method or getter being looked up * @param visitedInterfaces a set containing all of the interfaces that have been examined, used * to prevent infinite recursion and to optimize the search * @return the element representing the method or getter that was found */ private ExecutableElement lookUpGetterOrMethodInInterfaces(InterfaceType targetType, boolean includeTargetType, String memberName, HashSet<ClassElement> visitedInterfaces) { // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for // finding the inherited member. We need to follow that scheme. The code below should cover the // 80% case. ClassElement targetClass = targetType.getElement(); if (visitedInterfaces.contains(targetClass)) { return null; } visitedInterfaces.add(targetClass); if (includeTargetType) { ExecutableElement member = targetType.getMethod(memberName); if (member != null) { return member; } member = targetType.getGetter(memberName); if (member != null) { return member; } } for (InterfaceType interfaceType : targetType.getInterfaces()) { ExecutableElement member = lookUpGetterOrMethodInInterfaces( interfaceType, true, memberName, visitedInterfaces); if (member != null) { return member; } } for (InterfaceType mixinType : targetType.getMixins()) { ExecutableElement member = lookUpGetterOrMethodInInterfaces( mixinType, true, memberName, visitedInterfaces); if (member != null) { return member; } } InterfaceType superclass = targetType.getSuperclass(); if (superclass == null) { return null; } return lookUpGetterOrMethodInInterfaces(superclass, true, memberName, visitedInterfaces); } /** * Find the element corresponding to the given label node in the current label scope. * * @param parentNode the node containing the given label * @param labelNode the node representing the label being looked up * @return the element corresponding to the given label node in the current scope */ private LabelElementImpl lookupLabel(AstNode parentNode, SimpleIdentifier labelNode) { LabelScope labelScope = resolver.getLabelScope(); LabelElementImpl labelElement = null; if (labelNode == null) { if (labelScope == null) { // TODO(brianwilkerson) Do we need to report this error, or is this condition always caught in the parser? // reportError(ResolverErrorCode.BREAK_OUTSIDE_LOOP); } else { labelElement = (LabelElementImpl) labelScope.lookup(LabelScope.EMPTY_LABEL); if (labelElement == null) { // TODO(brianwilkerson) Do we need to report this error, or is this condition always caught in the parser? // reportError(ResolverErrorCode.BREAK_OUTSIDE_LOOP); } // // The label element that was returned was a marker for look-up and isn't stored in the // element model. // labelElement = null; } } else { if (labelScope == null) { resolver.reportErrorForNode( CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, labelNode.getName()); } else { labelElement = (LabelElementImpl) labelScope.lookup(labelNode.getName()); if (labelElement == null) { resolver.reportErrorForNode( CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, labelNode.getName()); } else { labelNode.setStaticElement(labelElement); } } } if (labelElement != null) { ExecutableElement labelContainer = labelElement.getAncestor(ExecutableElement.class); if (labelContainer != resolver.getEnclosingFunction()) { resolver.reportErrorForNode( CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE, labelNode, labelNode.getName()); labelElement = null; } } return labelElement; } /** * Look up the method with the given name in the given type. Return the element representing the * method that was found, or {@code null} if there is no method with the given name. * * @param target the target of the invocation, or {@code null} if there is no target * @param type the type in which the method is defined * @param methodName the name of the method being looked up * @return the element representing the method that was found */ private MethodElement lookUpMethod(Expression target, Type type, String methodName) { type = resolveTypeParameter(type); if (type instanceof InterfaceType) { InterfaceType interfaceType = (InterfaceType) type; MethodElement method; if (target instanceof SuperExpression) { method = interfaceType.lookUpMethodInSuperclass(methodName, definingLibrary); } else { method = interfaceType.lookUpMethod(methodName, definingLibrary); } if (method != null) { return method; } return lookUpMethodInInterfaces(interfaceType, false, methodName, new HashSet<ClassElement>()); } else if (type instanceof UnionType) { // TODO (collinsn): I want [computeMergedExecutableElement] to be general // and work with functions, methods, constructors, and property accessors. However, // I won't be able to assume it returns [MethodElement] here then. return (MethodElement) maybeMergeExecutableElements(lookupMethods( target, (UnionType) type, methodName)); } return null; } /** * Look up the method with the given name in the interfaces implemented by the given type, either * directly or indirectly. Return the element representing the method that was found, or * {@code null} if there is no method with the given name. * * @param targetType the type in which the member might be defined * @param includeTargetType {@code true} if the search should include the target type * @param methodName the name of the method being looked up * @param visitedInterfaces a set containing all of the interfaces that have been examined, used * to prevent infinite recursion and to optimize the search * @return the element representing the method that was found */ private MethodElement lookUpMethodInInterfaces(InterfaceType targetType, boolean includeTargetType, String methodName, HashSet<ClassElement> visitedInterfaces) { // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for // finding the inherited member. We need to follow that scheme. The code below should cover the // 80% case. ClassElement targetClass = targetType.getElement(); if (visitedInterfaces.contains(targetClass)) { return null; } visitedInterfaces.add(targetClass); if (includeTargetType) { MethodElement method = targetType.getMethod(methodName); if (method != null && method.isAccessibleIn(definingLibrary)) { return method; } } for (InterfaceType interfaceType : targetType.getInterfaces()) { MethodElement method = lookUpMethodInInterfaces( interfaceType, true, methodName, visitedInterfaces); if (method != null) { return method; } } for (InterfaceType mixinType : targetType.getMixins()) { MethodElement method = lookUpMethodInInterfaces( mixinType, true, methodName, visitedInterfaces); if (method != null) { return method; } } InterfaceType superclass = targetType.getSuperclass(); if (superclass == null) { return null; } return lookUpMethodInInterfaces(superclass, true, methodName, visitedInterfaces); } /** * Look up all methods of a given name defined on a union type. * * @param target * @param type * @param methodName * @return all methods named {@code methodName} defined on the union type {@code type}. */ private Set<ExecutableElement> lookupMethods(Expression target, UnionType type, String methodName) { Set<ExecutableElement> methods = new HashSet<ExecutableElement>(); boolean allElementsHaveMethod = true; for (Type t : type.getElements()) { MethodElement m = lookUpMethod(target, t, methodName); if (m != null) { methods.add(m); } else { allElementsHaveMethod = false; } } // For strict union types we require that all types in the union define the method. if (AnalysisEngine.getInstance().getStrictUnionTypes()) { if (allElementsHaveMethod) { return methods; } else { return Collections.emptySet(); } } else { return methods; } } /** * Look up the setter with the given name in the given type. Return the element representing the * setter that was found, or {@code null} if there is no setter with the given name. * * @param target the target of the invocation, or {@code null} if there is no target * @param type the type in which the setter is defined * @param setterName the name of the setter being looked up * @return the element representing the setter that was found */ private PropertyAccessorElement lookUpSetter(Expression target, Type type, String setterName) { type = resolveTypeParameter(type); if (type instanceof InterfaceType) { InterfaceType interfaceType = (InterfaceType) type; PropertyAccessorElement accessor; if (target instanceof SuperExpression) { accessor = interfaceType.lookUpSetterInSuperclass(setterName, definingLibrary); } else { accessor = interfaceType.lookUpSetter(setterName, definingLibrary); } if (accessor != null) { return accessor; } return lookUpSetterInInterfaces(interfaceType, false, setterName, new HashSet<ClassElement>()); } return null; } /** * Look up the setter with the given name in the interfaces implemented by the given type, either * directly or indirectly. Return the element representing the setter that was found, or * {@code null} if there is no setter with the given name. * * @param targetType the type in which the setter might be defined * @param includeTargetType {@code true} if the search should include the target type * @param setterName the name of the setter being looked up * @param visitedInterfaces a set containing all of the interfaces that have been examined, used * to prevent infinite recursion and to optimize the search * @return the element representing the setter that was found */ private PropertyAccessorElement lookUpSetterInInterfaces(InterfaceType targetType, boolean includeTargetType, String setterName, HashSet<ClassElement> visitedInterfaces) { // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for // finding the inherited member. We need to follow that scheme. The code below should cover the // 80% case. ClassElement targetClass = targetType.getElement(); if (visitedInterfaces.contains(targetClass)) { return null; } visitedInterfaces.add(targetClass); if (includeTargetType) { PropertyAccessorElement setter = targetType.getSetter(setterName); if (setter != null && setter.isAccessibleIn(definingLibrary)) { return setter; } } for (InterfaceType interfaceType : targetType.getInterfaces()) { PropertyAccessorElement setter = lookUpSetterInInterfaces( interfaceType, true, setterName, visitedInterfaces); if (setter != null) { return setter; } } for (InterfaceType mixinType : targetType.getMixins()) { PropertyAccessorElement setter = lookUpSetterInInterfaces( mixinType, true, setterName, visitedInterfaces); if (setter != null) { return setter; } } InterfaceType superclass = targetType.getSuperclass(); if (superclass == null) { return null; } return lookUpSetterInInterfaces(superclass, true, setterName, visitedInterfaces); } /** * Given some class element, this method uses {@link #subtypeManager} to find the set of all * subtypes; the subtypes are then searched for a member (method, getter, or setter), that matches * a passed * * @param element the class element to search the subtypes of, if a non-ClassElement element is * passed, then {@code false} is returned * @param memberName the member name to search for * @param asMethod {@code true} if the methods should be searched for in the subtypes * @param asAccessor {@code true} if the accessors (getters and setters) should be searched for in * the subtypes * @return {@code true} if and only if the passed memberName was found in a subtype */ private boolean memberFoundInSubclass(Element element, String memberName, boolean asMethod, boolean asAccessor) { if (element instanceof ClassElement) { subtypeManager.ensureLibraryVisited(definingLibrary); HashSet<ClassElement> subtypeElements = subtypeManager.computeAllSubtypes((ClassElement) element); for (ClassElement subtypeElement : subtypeElements) { if (asMethod && subtypeElement.getMethod(memberName) != null) { return true; } else if (asAccessor && (subtypeElement.getGetter(memberName) != null || subtypeElement.getSetter(memberName) != null)) { return true; } } } return false; } /** * Return the binary operator that is invoked by the given compound assignment operator. * * @param operator the assignment operator being mapped * @return the binary operator that invoked by the given assignment operator */ private TokenType operatorFromCompoundAssignment(TokenType operator) { switch (operator) { case AMPERSAND_EQ: return TokenType.AMPERSAND; case BAR_EQ: return TokenType.BAR; case CARET_EQ: return TokenType.CARET; case GT_GT_EQ: return TokenType.GT_GT; case LT_LT_EQ: return TokenType.LT_LT; case MINUS_EQ: return TokenType.MINUS; case PERCENT_EQ: return TokenType.PERCENT; case PLUS_EQ: return TokenType.PLUS; case SLASH_EQ: return TokenType.SLASH; case STAR_EQ: return TokenType.STAR; case TILDE_SLASH_EQ: return TokenType.TILDE_SLASH; default: // Internal error: Unmapped assignment operator. AnalysisEngine.getInstance().getLogger().logError( "Failed to map " + operator.getLexeme() + " to it's corresponding operator"); return operator; } } /** * Record that the given node is undefined, causing an error to be reported if appropriate. * * @param declaringElement the element inside which no declaration was found. If this element is a * proxy, no error will be reported. If null, then an error will always be reported. * @param errorCode the error code to report. * @param node the node which is undefined. * @param arguments arguments to the error message. */ private void recordUndefinedNode(Element declaringElement, ErrorCode errorCode, AstNode node, Object... arguments) { if (doesntHaveProxy(declaringElement)) { resolver.reportErrorForNode(errorCode, node, arguments); } } /** * Record that the given offset/length is undefined, causing an error to be reported if * appropriate. * * @param declaringElement the element inside which no declaration was found. If this element is a * proxy, no error will be reported. If null, then an error will always be reported. * @param errorCode the error code to report. * @param offset the offset to the text which is undefined. * @param length the length of the text which is undefined. * @param arguments arguments to the error message. */ private void recordUndefinedOffset(Element declaringElement, ErrorCode errorCode, int offset, int length, Object... arguments) { if (doesntHaveProxy(declaringElement)) { resolver.reportErrorForOffset(errorCode, offset, length, arguments); } } /** * Record that the given token is undefined, causing an error to be reported if appropriate. * * @param declaringElement the element inside which no declaration was found. If this element is a * proxy, no error will be reported. If null, then an error will always be reported. * @param errorCode the error code to report. * @param token the token which is undefined. * @param arguments arguments to the error message. */ private void recordUndefinedToken(Element declaringElement, ErrorCode errorCode, Token token, Object... arguments) { if (doesntHaveProxy(declaringElement)) { resolver.reportErrorForToken(errorCode, token, arguments); } } private void resolveAnnotationConstructorInvocationArguments(Annotation annotation, ConstructorElement constructor) { ArgumentList argumentList = annotation.getArguments(); // error will be reported in ConstantVerifier if (argumentList == null) { return; } // resolve arguments to parameters ParameterElement[] parameters = resolveArgumentsToFunction(true, argumentList, constructor); if (parameters != null) { argumentList.setCorrespondingStaticParameters(parameters); } } /** * Continues resolution of the given {@link Annotation}. * * @param annotation the {@link Annotation} to resolve */ private void resolveAnnotationElement(Annotation annotation) { SimpleIdentifier nameNode1; SimpleIdentifier nameNode2; { Identifier annName = annotation.getName(); if (annName instanceof PrefixedIdentifier) { PrefixedIdentifier prefixed = (PrefixedIdentifier) annName; nameNode1 = prefixed.getPrefix(); nameNode2 = prefixed.getIdentifier(); } else { nameNode1 = (SimpleIdentifier) annName; nameNode2 = null; } } SimpleIdentifier nameNode3 = annotation.getConstructorName(); ConstructorElement constructor = null; // // CONST or Class(args) // if (nameNode1 != null && nameNode2 == null && nameNode3 == null) { Element element1 = nameNode1.getStaticElement(); // CONST if (element1 instanceof PropertyAccessorElement) { resolveAnnotationElementGetter(annotation, (PropertyAccessorElement) element1); return; } // Class(args) if (element1 instanceof ClassElement) { ClassElement classElement = (ClassElement) element1; constructor = new InterfaceTypeImpl(classElement).lookUpConstructor(null, definingLibrary); } } // // prefix.CONST or prefix.Class() or Class.CONST or Class.constructor(args) // if (nameNode1 != null && nameNode2 != null && nameNode3 == null) { Element element1 = nameNode1.getStaticElement(); Element element2 = nameNode2.getStaticElement(); // Class.CONST - not resolved yet if (element1 instanceof ClassElement) { ClassElement classElement = (ClassElement) element1; element2 = classElement.lookUpGetter(nameNode2.getName(), definingLibrary); } // prefix.CONST or Class.CONST if (element2 instanceof PropertyAccessorElement) { nameNode2.setStaticElement(element2); annotation.setElement(element2); resolveAnnotationElementGetter(annotation, (PropertyAccessorElement) element2); return; } // prefix.Class() if (element2 instanceof ClassElement) { ClassElement classElement = (ClassElement) element2; constructor = classElement.getUnnamedConstructor(); } // Class.constructor(args) if (element1 instanceof ClassElement) { ClassElement classElement = (ClassElement) element1; constructor = new InterfaceTypeImpl(classElement).lookUpConstructor( nameNode2.getName(), definingLibrary); nameNode2.setStaticElement(constructor); } } // // prefix.Class.CONST or prefix.Class.constructor(args) // if (nameNode1 != null && nameNode2 != null && nameNode3 != null) { Element element2 = nameNode2.getStaticElement(); // element2 should be ClassElement if (element2 instanceof ClassElement) { ClassElement classElement = (ClassElement) element2; String name3 = nameNode3.getName(); // prefix.Class.CONST PropertyAccessorElement getter = classElement.lookUpGetter(name3, definingLibrary); if (getter != null) { nameNode3.setStaticElement(getter); annotation.setElement(element2); resolveAnnotationElementGetter(annotation, getter); return; } // prefix.Class.constructor(args) constructor = new InterfaceTypeImpl(classElement).lookUpConstructor(name3, definingLibrary); nameNode3.setStaticElement(constructor); } } // we need constructor if (constructor == null) { resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATION, annotation); return; } // record element annotation.setElement(constructor); // resolve arguments resolveAnnotationConstructorInvocationArguments(annotation, constructor); } private void resolveAnnotationElementGetter(Annotation annotation, PropertyAccessorElement accessorElement) { // accessor should be synthetic if (!accessorElement.isSynthetic()) { resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATION, annotation); return; } // variable should be constant VariableElement variableElement = accessorElement.getVariable(); if (!variableElement.isConst()) { resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATION, annotation); } // OK return; } /** * Given a list of arguments and the element that will be invoked using those argument, compute * the list of parameters that correspond to the list of arguments. Return the parameters that * correspond to the arguments, or {@code null} if no correspondence could be computed. * * @param reportError if {@code true} then compile-time error should be reported; if {@code false} * then compile-time warning * @param argumentList the list of arguments being passed to the element * @param executableElement the element that will be invoked with the arguments * @return the parameters that correspond to the arguments */ private ParameterElement[] resolveArgumentsToFunction(boolean reportError, ArgumentList argumentList, ExecutableElement executableElement) { if (executableElement == null) { return null; } ParameterElement[] parameters = executableElement.getParameters(); return resolveArgumentsToParameters(reportError, argumentList, parameters); } /** * Given a list of arguments and the parameters related to the element that will be invoked using * those argument, compute the list of parameters that correspond to the list of arguments. Return * the parameters that correspond to the arguments. * * @param reportError if {@code true} then compile-time error should be reported; if {@code false} * then compile-time warning * @param argumentList the list of arguments being passed to the element * @param parameters the of the function that will be invoked with the arguments * @return the parameters that correspond to the arguments */ private ParameterElement[] resolveArgumentsToParameters(boolean reportError, ArgumentList argumentList, ParameterElement[] parameters) { ArrayList<ParameterElement> requiredParameters = new ArrayList<ParameterElement>(); ArrayList<ParameterElement> positionalParameters = new ArrayList<ParameterElement>(); HashMap<String, ParameterElement> namedParameters = new HashMap<String, ParameterElement>(); for (ParameterElement parameter : parameters) { ParameterKind kind = parameter.getParameterKind(); if (kind == ParameterKind.REQUIRED) { requiredParameters.add(parameter); } else if (kind == ParameterKind.POSITIONAL) { positionalParameters.add(parameter); } else { namedParameters.put(parameter.getName(), parameter); } } ArrayList<ParameterElement> unnamedParameters = new ArrayList<ParameterElement>( requiredParameters); unnamedParameters.addAll(positionalParameters); int unnamedParameterCount = unnamedParameters.size(); int unnamedIndex = 0; NodeList<Expression> arguments = argumentList.getArguments(); int argumentCount = arguments.size(); ParameterElement[] resolvedParameters = new ParameterElement[argumentCount]; int positionalArgumentCount = 0; HashSet<String> usedNames = new HashSet<String>(); boolean noBlankArguments = true; for (int i = 0; i < argumentCount; i++) { Expression argument = arguments.get(i); if (argument instanceof NamedExpression) { SimpleIdentifier nameNode = ((NamedExpression) argument).getName().getLabel(); String name = nameNode.getName(); ParameterElement element = namedParameters.get(name); if (element == null) { ErrorCode errorCode = reportError ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER : StaticWarningCode.UNDEFINED_NAMED_PARAMETER; resolver.reportErrorForNode(errorCode, nameNode, name); } else { resolvedParameters[i] = element; nameNode.setStaticElement(element); } if (!usedNames.add(name)) { resolver.reportErrorForNode(CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode, name); } } else { if (argument instanceof SimpleIdentifier && ((SimpleIdentifier) argument).getName().isEmpty()) { noBlankArguments = false; } positionalArgumentCount++; if (unnamedIndex < unnamedParameterCount) { resolvedParameters[i] = unnamedParameters.get(unnamedIndex++); } } } if (positionalArgumentCount < requiredParameters.size() && noBlankArguments) { ErrorCode errorCode = reportError ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS; resolver.reportErrorForNode( errorCode, argumentList, requiredParameters.size(), positionalArgumentCount); } else if (positionalArgumentCount > unnamedParameterCount && noBlankArguments) { ErrorCode errorCode = reportError ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS; resolver.reportErrorForNode( errorCode, argumentList, unnamedParameterCount, positionalArgumentCount); } return resolvedParameters; } /** * Resolve the names in the given combinators in the scope of the given library. * * @param library the library that defines the names * @param combinators the combinators containing the names to be resolved */ private 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 = new NamespaceBuilder().createExportNamespaceForLibrary(library); for (Combinator combinator : combinators) { NodeList<SimpleIdentifier> names; if (combinator instanceof HideCombinator) { names = ((HideCombinator) combinator).getHiddenNames(); } else { names = ((ShowCombinator) combinator).getShownNames(); } for (SimpleIdentifier name : names) { String nameStr = name.getName(); Element element = namespace.get(nameStr); if (element == null) { element = 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 instanceof PropertyAccessorElement) { element = ((PropertyAccessorElement) element).getVariable(); } name.setStaticElement(element); } } } } /** * Given an invocation of the form 'C.x()' where 'C' is a class, find and return the element 'x' * in 'C'. * * @param classElement the class element * @param nameNode the member name node */ private Element resolveElement(ClassElementImpl classElement, SimpleIdentifier nameNode) { String name = nameNode.getName(); Element element = classElement.getMethod(name); if (element == null && nameNode.inSetterContext()) { element = classElement.getSetter(name); } if (element == null && nameNode.inGetterContext()) { element = classElement.getGetter(name); } if (element != null && element.isAccessibleIn(definingLibrary)) { return element; } return null; } /** * Given an invocation of the form 'm(a1, ..., an)', resolve 'm' to the element being invoked. If * the returned element is a method, then the method will be invoked. If the returned element is a * getter, the getter will be invoked without arguments and the result of that invocation will * then be invoked with the arguments. * * @param methodName the name of the method being invoked ('m') * @return the element being invoked */ private Element resolveInvokedElement(SimpleIdentifier methodName) { // // Look first in the lexical scope. // Element element = resolver.getNameScope().lookup(methodName, definingLibrary); if (element == null) { // // If it isn't defined in the lexical scope, and the invocation is within a class, then look // in the inheritance scope. // ClassElement enclosingClass = resolver.getEnclosingClass(); if (enclosingClass != null) { InterfaceType enclosingType = enclosingClass.getType(); element = lookUpMethod(null, enclosingType, methodName.getName()); if (element == null) { // // If there's no method, then it's possible that 'm' is a getter that returns a function. // element = lookUpGetter(null, enclosingType, methodName.getName()); } } } // TODO(brianwilkerson) Report this error. return element; } /** * Given an invocation of the form 'e.m(a1, ..., an)', resolve 'e.m' to the element being invoked. * If the returned element is a method, then the method will be invoked. If the returned element * is a getter, the getter will be invoked without arguments and the result of that invocation * will then be invoked with the arguments. * * @param target the target of the invocation ('e') * @param targetType the type of the target * @param methodName the name of the method being invoked ('m') * @return the element being invoked */ private Element resolveInvokedElementWithTarget(Expression target, Type targetType, SimpleIdentifier methodName) { if (targetType instanceof InterfaceType || targetType instanceof UnionType) { Element element = lookUpMethod(target, targetType, methodName.getName()); if (element == null) { // // If there's no method, then it's possible that 'm' is a getter that returns a function. // // TODO (collinsn): need to add union type support here too, in the style of [lookUpMethod]. element = lookUpGetter(target, targetType, methodName.getName()); } return element; } else if (target instanceof SimpleIdentifier) { SimpleIdentifier identifier = (SimpleIdentifier) target; Element targetElement = identifier.getStaticElement(); if (targetElement instanceof PrefixElement) { // // Look to see whether the name of the method is really part of a prefixed identifier for an // imported top-level function or top-level getter that returns a function. // final String name = identifier.getName() + "." + methodName; Identifier functionName = new SyntheticIdentifier(name, methodName); Element element = resolver.getNameScope().lookup(functionName, definingLibrary); if (element != null) { // TODO(brianwilkerson) This isn't a method invocation, it's a function invocation where // the function name is a prefixed identifier. Consider re-writing the AST. return element; } } } // TODO(brianwilkerson) Report this error. return null; } /** * Given that we are accessing a property of the given type with the given name, return the * element that represents the property. * * @param target the target of the invocation ('e') * @param targetType the type in which the search for the property should begin * @param propertyName the name of the property being accessed * @return the element that represents the property */ private ExecutableElement resolveProperty(Expression target, Type targetType, SimpleIdentifier propertyName) { ExecutableElement memberElement = null; if (propertyName.inSetterContext()) { memberElement = lookUpSetter(target, targetType, propertyName.getName()); } if (memberElement == null) { memberElement = lookUpGetter(target, targetType, propertyName.getName()); } if (memberElement == null) { memberElement = lookUpMethod(target, targetType, propertyName.getName()); } return memberElement; } private void resolvePropertyAccess(Expression target, SimpleIdentifier propertyName) { Type staticType = getStaticType(target); Type propagatedType = getPropagatedType(target); Element staticElement = null; Element propagatedElement = null; // // If this property access is of the form 'C.m' where 'C' is a class, then we don't call // resolveProperty(..) which walks up the class hierarchy, instead we just look for the // member in the type only. // ClassElementImpl typeReference = getTypeReference(target); if (typeReference != null) { // TODO(brianwilkerson) Why are we setting the propagated element here? It looks wrong. staticElement = propagatedElement = resolveElement(typeReference, propertyName); } else { staticElement = resolveProperty(target, staticType, propertyName); propagatedElement = resolveProperty(target, propagatedType, propertyName); } // May be part of annotation, record property element only if exists. // Error was already reported in validateAnnotationElement(). if (target.getParent().getParent() instanceof Annotation) { if (staticElement != null) { propertyName.setStaticElement(staticElement); } return; } propertyName.setStaticElement(staticElement); propertyName.setPropagatedElement(propagatedElement); boolean shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticElement); boolean shouldReportMissingMember_propagated = !shouldReportMissingMember_static && enableHints && shouldReportMissingMember(propagatedType, propagatedElement) && // If we are about to generate the hint (propagated version of this warning), then check // that the member is not in a subtype of the propagated type. !memberFoundInSubclass(propagatedType.getElement(), propertyName.getName(), false, true); // TODO(collinsn): add support for errors on union types by extending // [lookupGetter] and [lookupSetter] in analogy with the earlier [lookupMethod] extensions. if (propagatedType instanceof UnionType) { shouldReportMissingMember_propagated = false; } if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) { Element staticOrPropagatedEnclosingElt = shouldReportMissingMember_static ? staticType.getElement() : propagatedType.getElement(); boolean isStaticProperty = isStatic(staticOrPropagatedEnclosingElt); String displayName = staticOrPropagatedEnclosingElt != null ? staticOrPropagatedEnclosingElt.getDisplayName() : propagatedType != null ? propagatedType.getDisplayName() : staticType.getDisplayName(); // Special getter cases. if (propertyName.inGetterContext()) { if (!isStaticProperty && staticOrPropagatedEnclosingElt instanceof ClassElement) { ClassElement classElement = (ClassElement) staticOrPropagatedEnclosingElt; InterfaceType targetType = classElement.getType(); if (targetType != null && targetType.isDartCoreFunction() && propertyName.getName().equals(FunctionElement.CALL_METHOD_NAME)) { // TODO(brianwilkerson) Can we ever resolve the function being invoked? //resolveArgumentsToParameters(node.getArgumentList(), invokedFunction); return; } else if (classElement.isEnum() && propertyName.getName().equals("_name")) { resolver.reportErrorForNode( CompileTimeErrorCode.ACCESS_PRIVATE_ENUM_FIELD, propertyName, propertyName.getName()); return; } } } Element declaringElement = staticType.isVoid() ? null : staticOrPropagatedEnclosingElt; if (propertyName.inSetterContext()) { ErrorCode staticErrorCode = isStaticProperty && !staticType.isVoid() ? StaticWarningCode.UNDEFINED_SETTER : StaticTypeWarningCode.UNDEFINED_SETTER; ErrorCode errorCode = shouldReportMissingMember_static ? staticErrorCode : HintCode.UNDEFINED_SETTER; recordUndefinedNode( declaringElement, errorCode, propertyName, propertyName.getName(), displayName); } else if (propertyName.inGetterContext()) { ErrorCode staticErrorCode = isStaticProperty && !staticType.isVoid() ? StaticWarningCode.UNDEFINED_GETTER : StaticTypeWarningCode.UNDEFINED_GETTER; ErrorCode errorCode = shouldReportMissingMember_static ? staticErrorCode : HintCode.UNDEFINED_GETTER; recordUndefinedNode( declaringElement, errorCode, propertyName, propertyName.getName(), displayName); } else { recordUndefinedNode( declaringElement, StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, propertyName.getName()); } } } /** * Resolve the given simple identifier if possible. Return the element to which it could be * resolved, or {@code null} if it could not be resolved. This does not record the results of the * resolution. * * @param node the identifier to be resolved * @return the element to which the identifier could be resolved */ private Element resolveSimpleIdentifier(SimpleIdentifier node) { Element element = resolver.getNameScope().lookup(node, definingLibrary); if (element instanceof PropertyAccessorElement && node.inSetterContext()) { PropertyInducingElement variable = ((PropertyAccessorElement) element).getVariable(); if (variable != null) { PropertyAccessorElement setter = variable.getSetter(); if (setter == null) { // // Check to see whether there might be a locally defined getter and an inherited setter. // ClassElement enclosingClass = resolver.getEnclosingClass(); if (enclosingClass != null) { setter = lookUpSetter(null, enclosingClass.getType(), node.getName()); } } if (setter != null) { element = setter; } } } else if (element == null && (node.inSetterContext() || node.getParent() instanceof CommentReference)) { element = resolver.getNameScope().lookup( new SyntheticIdentifier(node.getName() + "=", node), definingLibrary); } ClassElement enclosingClass = resolver.getEnclosingClass(); if (element == null && enclosingClass != null) { InterfaceType enclosingType = enclosingClass.getType(); if (element == null && (node.inSetterContext() || node.getParent() instanceof CommentReference)) { element = lookUpSetter(null, enclosingType, node.getName()); } if (element == null && node.inGetterContext()) { element = lookUpGetter(null, enclosingType, node.getName()); } if (element == null) { element = lookUpMethod(null, enclosingType, node.getName()); } } 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. * * @param type the type that is to be resolved if it is a type parameter * @return the type that should be used in place of the argument if it is a type parameter, or the * original argument if it isn't a type parameter */ private Type resolveTypeParameter(Type type) { if (type instanceof TypeParameterType) { Type bound = ((TypeParameterType) type).getElement().getBound(); if (bound == null) { return resolver.getTypeProvider().getObjectType(); } return bound; } return type; } /** * Given a node that can have annotations associated with it and the element to which that node * has been resolved, create the annotations in the element model representing the annotations on * the node. * * @param element the element to which the node has been resolved * @param node the node that can have annotations associated with it */ private void setMetadata(Element element, AnnotatedNode node) { if (!(element instanceof ElementImpl)) { return; } ArrayList<ElementAnnotationImpl> annotationList = new ArrayList<ElementAnnotationImpl>(); addAnnotations(annotationList, node.getMetadata()); if (node instanceof VariableDeclaration && node.getParent() instanceof VariableDeclarationList) { VariableDeclarationList list = (VariableDeclarationList) node.getParent(); addAnnotations(annotationList, list.getMetadata()); if (list.getParent() instanceof FieldDeclaration) { FieldDeclaration fieldDeclaration = (FieldDeclaration) list.getParent(); addAnnotations(annotationList, fieldDeclaration.getMetadata()); } else if (list.getParent() instanceof TopLevelVariableDeclaration) { TopLevelVariableDeclaration variableDeclaration = (TopLevelVariableDeclaration) list.getParent(); addAnnotations(annotationList, variableDeclaration.getMetadata()); } } if (!annotationList.isEmpty()) { ((ElementImpl) element).setMetadata(annotationList.toArray(new ElementAnnotationImpl[annotationList.size()])); } } /** * Given a node that can have annotations associated with it and the element to which that node * has been resolved, create the annotations in the element model representing the annotations on * the node. * * @param element the element to which the node has been resolved * @param node the node that can have annotations associated with it */ private void setMetadataForParameter(Element element, NormalFormalParameter node) { if (!(element instanceof ElementImpl)) { return; } ArrayList<ElementAnnotationImpl> annotationList = new ArrayList<ElementAnnotationImpl>(); addAnnotations(annotationList, node.getMetadata()); if (!annotationList.isEmpty()) { ((ElementImpl) element).setMetadata(annotationList.toArray(new ElementAnnotationImpl[annotationList.size()])); } } /** * Return {@code true} if we should report an error as a result of looking up a member in the * given type and not finding any member. * * @param type the type in which we attempted to perform the look-up * @param member the result of the look-up * @return {@code true} if we should report an error */ private boolean shouldReportMissingMember(Type type, Element member) { if (member != null || type == null || type.isDynamic() || type.isBottom()) { return false; } return true; } }