| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.google.dart.compiler.resolver; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Objects; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import com.google.dart.compiler.DartCompilationPhase; |
| import com.google.dart.compiler.DartCompilerContext; |
| import com.google.dart.compiler.ErrorCode; |
| import com.google.dart.compiler.Source; |
| import com.google.dart.compiler.ast.ASTNodes; |
| import com.google.dart.compiler.ast.ASTVisitor; |
| import com.google.dart.compiler.ast.DartArrayLiteral; |
| import com.google.dart.compiler.ast.DartBinaryExpression; |
| import com.google.dart.compiler.ast.DartBlock; |
| import com.google.dart.compiler.ast.DartBooleanLiteral; |
| import com.google.dart.compiler.ast.DartBreakStatement; |
| import com.google.dart.compiler.ast.DartCatchBlock; |
| import com.google.dart.compiler.ast.DartClass; |
| import com.google.dart.compiler.ast.DartComment; |
| import com.google.dart.compiler.ast.DartCommentNewName; |
| import com.google.dart.compiler.ast.DartCommentRefName; |
| import com.google.dart.compiler.ast.DartContinueStatement; |
| import com.google.dart.compiler.ast.DartDirective; |
| import com.google.dart.compiler.ast.DartDoWhileStatement; |
| import com.google.dart.compiler.ast.DartDoubleLiteral; |
| import com.google.dart.compiler.ast.DartExprStmt; |
| import com.google.dart.compiler.ast.DartExpression; |
| import com.google.dart.compiler.ast.DartField; |
| import com.google.dart.compiler.ast.DartFieldDefinition; |
| import com.google.dart.compiler.ast.DartForInStatement; |
| import com.google.dart.compiler.ast.DartForStatement; |
| import com.google.dart.compiler.ast.DartFunction; |
| import com.google.dart.compiler.ast.DartFunctionExpression; |
| import com.google.dart.compiler.ast.DartFunctionObjectInvocation; |
| import com.google.dart.compiler.ast.DartFunctionTypeAlias; |
| import com.google.dart.compiler.ast.DartGotoStatement; |
| import com.google.dart.compiler.ast.DartIdentifier; |
| import com.google.dart.compiler.ast.DartIfStatement; |
| import com.google.dart.compiler.ast.DartImportDirective; |
| import com.google.dart.compiler.ast.DartInitializer; |
| import com.google.dart.compiler.ast.DartIntegerLiteral; |
| import com.google.dart.compiler.ast.DartInvocation; |
| import com.google.dart.compiler.ast.DartLabel; |
| import com.google.dart.compiler.ast.DartMapLiteral; |
| import com.google.dart.compiler.ast.DartMethodDefinition; |
| import com.google.dart.compiler.ast.DartMethodInvocation; |
| import com.google.dart.compiler.ast.DartNamedExpression; |
| import com.google.dart.compiler.ast.DartNativeBlock; |
| import com.google.dart.compiler.ast.DartNewExpression; |
| import com.google.dart.compiler.ast.DartNode; |
| import com.google.dart.compiler.ast.DartParameter; |
| import com.google.dart.compiler.ast.DartParameterizedTypeNode; |
| import com.google.dart.compiler.ast.DartPartOfDirective; |
| import com.google.dart.compiler.ast.DartPropertyAccess; |
| import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; |
| import com.google.dart.compiler.ast.DartReturnStatement; |
| import com.google.dart.compiler.ast.DartStatement; |
| import com.google.dart.compiler.ast.DartStringInterpolation; |
| import com.google.dart.compiler.ast.DartStringLiteral; |
| import com.google.dart.compiler.ast.DartSuperConstructorInvocation; |
| import com.google.dart.compiler.ast.DartSuperExpression; |
| import com.google.dart.compiler.ast.DartSwitchMember; |
| import com.google.dart.compiler.ast.DartSwitchStatement; |
| import com.google.dart.compiler.ast.DartThisExpression; |
| import com.google.dart.compiler.ast.DartThrowExpression; |
| import com.google.dart.compiler.ast.DartTryStatement; |
| import com.google.dart.compiler.ast.DartTypeExpression; |
| import com.google.dart.compiler.ast.DartTypeNode; |
| import com.google.dart.compiler.ast.DartTypeParameter; |
| import com.google.dart.compiler.ast.DartUnaryExpression; |
| import com.google.dart.compiler.ast.DartUnit; |
| import com.google.dart.compiler.ast.DartUnqualifiedInvocation; |
| import com.google.dart.compiler.ast.DartVariable; |
| import com.google.dart.compiler.ast.DartVariableStatement; |
| import com.google.dart.compiler.ast.DartWhileStatement; |
| import com.google.dart.compiler.ast.LibraryImport; |
| import com.google.dart.compiler.ast.LibraryUnit; |
| import com.google.dart.compiler.ast.Modifiers; |
| import com.google.dart.compiler.common.HasSourceInfo; |
| import com.google.dart.compiler.common.SourceInfo; |
| import com.google.dart.compiler.parser.Token; |
| import com.google.dart.compiler.resolver.LabelElement.LabeledStatementType; |
| import com.google.dart.compiler.type.InterfaceType; |
| import com.google.dart.compiler.type.InterfaceType.Member; |
| import com.google.dart.compiler.type.Type; |
| import com.google.dart.compiler.type.TypeAnalyzer; |
| import com.google.dart.compiler.type.TypeKind; |
| import com.google.dart.compiler.type.TypeQuality; |
| import com.google.dart.compiler.type.TypeVariable; |
| import com.google.dart.compiler.type.Types; |
| |
| import java.util.EnumSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| /** |
| * Resolves unqualified elements in a compilation unit. |
| */ |
| public class Resolver { |
| |
| private final ResolutionContext topLevelContext; |
| private final CoreTypeProvider typeProvider; |
| private final InterfaceType rawArrayType; |
| private final InterfaceType defaultLiteralMapType; |
| |
| |
| private static final EnumSet<ElementKind> INVOKABLE_ELEMENTS = EnumSet.<ElementKind>of( |
| ElementKind.FIELD, |
| ElementKind.PARAMETER, |
| ElementKind.VARIABLE, |
| ElementKind.FUNCTION_OBJECT, |
| ElementKind.METHOD); |
| |
| @VisibleForTesting |
| public Resolver(DartCompilerContext compilerContext, Scope libraryScope, |
| CoreTypeProvider typeProvider) { |
| compilerContext.getClass(); // Fast null-check. |
| libraryScope.getClass(); // Fast null-check. |
| typeProvider.getClass(); // Fast null-check. |
| this.topLevelContext = new ResolutionContext(libraryScope, compilerContext, typeProvider); |
| this.typeProvider = typeProvider; |
| Type dynamicType = typeProvider.getDynamicType(); |
| Type stringType = typeProvider.getStringType(); |
| this.defaultLiteralMapType = typeProvider.getMapType(stringType, dynamicType); |
| this.rawArrayType = typeProvider.getArrayType(dynamicType); |
| } |
| |
| @VisibleForTesting |
| public DartUnit exec(DartUnit unit) { |
| // Visits all top level elements of a compilation unit and resolves names used in method |
| // bodies. |
| LibraryElement library = unit.getLibrary() != null ? unit.getLibrary().getElement() : null; |
| unit.accept(new ResolveElementsVisitor(topLevelContext, library)); |
| return unit; |
| } |
| |
| /** |
| * Main entry point for IDE. Resolves a member (method or field) |
| * incrementally in the given context. |
| * |
| * @param classElement the class enclosing the member. |
| * @param member the member to resolve. |
| * @param context a resolution context corresponding to classElement. |
| */ |
| public void resolveMember(ClassNodeElement classElement, NodeElement member, ResolutionContext context) { |
| ResolveElementsVisitor visitor; |
| if(member == null) { |
| return; |
| } |
| switch (member.getKind()) { |
| case CONSTRUCTOR: |
| case METHOD: |
| ResolutionContext methodContext = context.extend(member.getName()); |
| visitor = new ResolveElementsVisitor(methodContext, classElement, |
| (MethodElement) member); |
| break; |
| |
| case FIELD: |
| ResolutionContext fieldContext = context; |
| if (member.getModifiers().isAbstractField()) { |
| fieldContext = context.extend(member.getName()); |
| } |
| visitor = new ResolveElementsVisitor(fieldContext, classElement); |
| break; |
| |
| default: |
| throw topLevelContext.internalError(member, |
| "unexpected element kind: %s", member.getKind()); |
| } |
| member.getNode().accept(visitor); |
| } |
| |
| /** |
| * Resolves names in a method body. |
| * |
| * TODO(ngeoffray): Errors reported: |
| * - A default implementation not providing the default methods. |
| * - An interface with default methods but without a default implementation. |
| * - A member method shadowing a super property. |
| * - A member property shadowing a super method. |
| * - A formal parameter in a non-constructor shadowing a member. |
| * - A local variable shadowing another variable. |
| * - A local variable shadowing a formal parameter. |
| * - A local variable shadowing a class member. |
| * - Using 'this' or 'super' in a static or factory method, or in an initializer. |
| * - Using 'super' in a class without a super class. |
| * - Incorrectly using a resolved element. |
| */ |
| @VisibleForTesting |
| public class ResolveElementsVisitor extends ResolveVisitor { |
| private EnclosingElement currentHolder; |
| private EnclosingElement enclosingElement; |
| private MethodElement currentMethod; |
| private boolean inInstanceVariableInitializer; |
| private boolean inInitializer; |
| private MethodElement innermostFunction; |
| private ResolutionContext context; |
| private Set<LabelElement> referencedLabels = Sets.newHashSet(); |
| private Set<LabelElement> labelsInScopes = Sets.newHashSet(); |
| private Set<FieldElement> finalsNeedingInitializing = Sets.newHashSet(); |
| private Set<FieldElement> resolvedFields = Sets.newHashSet(); |
| |
| @VisibleForTesting |
| public ResolveElementsVisitor(ResolutionContext context, |
| EnclosingElement currentHolder, |
| MethodElement currentMethod) { |
| super(typeProvider); |
| this.context = context; |
| this.currentMethod = currentMethod; |
| this.innermostFunction = currentMethod; |
| this.currentHolder = currentHolder; |
| this.enclosingElement = currentHolder; |
| this.inInitializer = false; |
| } |
| |
| private ResolveElementsVisitor(ResolutionContext context, EnclosingElement currentHolder) { |
| this(context, currentHolder, null); |
| } |
| |
| @Override |
| ResolutionContext getContext() { |
| return context; |
| } |
| |
| @Override |
| protected EnclosingElement getEnclosingElement() { |
| return enclosingElement; |
| } |
| |
| @Override |
| public Element visitUnit(DartUnit unit) { |
| List<DartImportDirective> importDirectives = Lists.newArrayList(); |
| for (DartDirective directive : unit.getDirectives()) { |
| if (directive instanceof DartImportDirective) { |
| importDirectives.add((DartImportDirective) directive); |
| } |
| if (directive instanceof DartPartOfDirective) { |
| directive.accept(this); |
| } |
| } |
| // set LibraryElement for "import" directives |
| { |
| LibraryUnit library = unit.getLibrary(); |
| if (library != null) { |
| Iterator<LibraryImport> importIterator = library.getImports().iterator(); |
| Iterator<DartImportDirective> directiveIterator = importDirectives.iterator(); |
| while (importIterator.hasNext() && directiveIterator.hasNext()) { |
| LibraryImport imp = importIterator.next(); |
| DartImportDirective dir = directiveIterator.next(); |
| DartStringLiteral uri = dir.getLibraryUri(); |
| LibraryUnit impLibrary = imp.getLibrary(); |
| if (uri != null && impLibrary != null) { |
| uri.setElement(impLibrary.getElement()); |
| } |
| } |
| } |
| } |
| // visit top-level nodes |
| for (DartNode node : unit.getTopLevelNodes()) { |
| node.accept(this); |
| } |
| checkRedirectingFactoryConstructorsCycle(unit); |
| return null; |
| } |
| |
| private void checkRedirectingFactoryConstructorsCycle(DartUnit unit) { |
| unit.accept(new ASTVisitor<Void>() { |
| @Override |
| public Void visitMethodDefinition(DartMethodDefinition node) { |
| MethodNodeElement element = node.getElement(); |
| if (ElementKind.of(element) == ElementKind.CONSTRUCTOR) { |
| ConstructorElement constructor = (ConstructorElement) element; |
| if (hasRedirectingFactoryConstructorCycle(constructor)) { |
| onError(constructor.getNameLocation(), |
| ResolverErrorCode.REDIRECTION_CONSTRUCTOR_CYCLE); |
| } |
| } |
| return super.visitMethodDefinition(node); |
| } |
| }); |
| } |
| |
| private boolean hasRedirectingFactoryConstructorCycle(ConstructorElement element) { |
| Set<ConstructorElement> constructors = Sets.newHashSet(); |
| while (element != null) { |
| if (constructors.contains(element)) { |
| return true; |
| } |
| constructors.add(element); |
| element = element.getRedirectingFactoryConstructor(); |
| } |
| return false; |
| } |
| |
| @Override |
| public Element visitFunctionTypeAlias(DartFunctionTypeAlias alias) { |
| alias.getMetadata().accept(this); |
| getContext().pushFunctionAliasScope(alias); |
| resolveFunctionAlias(alias); |
| |
| getContext().pushScope("<parameters>"); |
| try { |
| List<DartParameter> parameters = alias.getParameters(); |
| for (DartParameter parameter : parameters) { |
| assert parameter.getElement() != null; |
| if (parameter.getQualifier() instanceof DartThisExpression) { |
| onError(parameter.getName(), ResolverErrorCode.PARAMETER_INIT_OUTSIDE_CONSTRUCTOR); |
| } else { |
| if (DartIdentifier.isPrivateName(parameter.getElement().getName())) { |
| if (parameter.getModifiers().isOptional()) { |
| onError(parameter.getName(), |
| ResolverErrorCode.OPTIONAL_PARAMETERS_CANNOT_START_WITH_UNDER); |
| } |
| if (parameter.getModifiers().isNamed()) { |
| onError(parameter.getName(), |
| ResolverErrorCode.NAMED_PARAMETERS_CANNOT_START_WITH_UNDER); |
| } |
| } |
| getContext().declare(parameter.getElement(), ResolverErrorCode.DUPLICATE_PARAMETER); |
| } |
| } |
| } finally { |
| getContext().popScope(); |
| } |
| |
| getContext().popScope(); |
| return null; |
| } |
| |
| @Override |
| public Element visitCommentRefName(DartCommentRefName node) { |
| Scope scope = getContext().getScope(); |
| String name = node.getName(); |
| Element element = scope.findElement(scope.getLibrary(), name); |
| return recordElement(node, element); |
| } |
| |
| @Override |
| public Element visitCommentNewName(DartCommentNewName node) { |
| String className = node.getClassName(); |
| String constructorName = node.getConstructorName(); |
| Scope scope = getContext().getScope(); |
| Element element = scope.findElement(scope.getLibrary(), className); |
| if (ElementKind.of(element) == ElementKind.CLASS) { |
| ClassElement classElement = (ClassElement) element; |
| for (ConstructorElement constructor : classElement.getConstructors()) { |
| if (constructor.getName().equals(constructorName)) { |
| node.setElements(classElement, constructor); |
| return constructor; |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitClass(DartClass cls) { |
| assert currentMethod == null : "nested class?"; |
| ClassNodeElement classElement = cls.getElement(); |
| try { |
| classElement.getAllSupertypes(); |
| } catch (CyclicDeclarationException e) { |
| HasSourceInfo errorTarget = e.getElement(); |
| if (errorTarget == null) { |
| errorTarget = cls; |
| } |
| onError(errorTarget, ResolverErrorCode.CYCLIC_CLASS, e.getElement().getName()); |
| } |
| checkClassTypeVariables(classElement); |
| cls.getMetadata().accept(this); |
| |
| // Push new resolution context. |
| ResolutionContext previousContext = context; |
| EnclosingElement previousHolder = currentHolder; |
| EnclosingElement previousEnclosingElement = enclosingElement; |
| currentHolder = classElement; |
| enclosingElement = classElement; |
| context = topLevelContext.extend(classElement); |
| |
| // members |
| this.finalsNeedingInitializing.clear(); |
| for (DartNode member : cls.getMembers()) { |
| if (ElementKind.of(member.getElement()) == ElementKind.CONSTRUCTOR) { |
| continue; |
| } |
| member.accept(this); |
| } |
| |
| // constructors |
| boolean testForAllConstantFields = false; |
| for (DartNode member : cls.getMembers()) { |
| if (member instanceof DartMethodDefinition) { |
| DartMethodDefinition method = (DartMethodDefinition) member; |
| if (method.getElement().isConstructor()) { |
| method.accept(this); |
| if (method.getModifiers().isConstant()) { |
| testForAllConstantFields = true; |
| } |
| } |
| } |
| } |
| |
| if (testForAllConstantFields) { |
| InterfaceType interfaceType = classElement.getType(); |
| while (interfaceType != null && interfaceType != typeProvider.getObjectType()) { |
| ClassElement interfaceElement = interfaceType.getElement(); |
| constVerifyMembers(interfaceElement.getMembers(), classElement, interfaceElement); |
| interfaceType = interfaceElement.getSupertype(); |
| } |
| } |
| |
| checkRedirectConstructorCycle(classElement.getConstructors(), context); |
| if (Elements.needsImplicitDefaultConstructor(classElement)) { |
| checkImplicitDefaultDefaultSuperInvocation(cls, classElement); |
| } |
| |
| if (cls.getDefaultClass() != null && classElement.getDefaultClass() == null) { |
| onError(cls.getDefaultClass(), ResolverErrorCode.NO_SUCH_TYPE, cls.getDefaultClass()); |
| } else if (classElement.getDefaultClass() != null) { |
| recordElement(cls.getDefaultClass().getExpression(), |
| classElement.getDefaultClass().getElement()); |
| bindDefaultTypeParameters(classElement.getDefaultClass().getElement().getTypeParameters(), |
| cls.getDefaultClass().getTypeParameters(), |
| context); |
| |
| // Make sure the 'default' clause matches the referenced class type parameters |
| checkDefaultClassTypeParamsToDefaultDecl(classElement.getDefaultClass(), |
| cls.getDefaultClass()); |
| |
| ClassElement defaultClass = classElement.getDefaultClass().getElement(); |
| if (defaultClass.isInterface()) { |
| onError(cls.getDefaultClass().getExpression(), |
| ResolverErrorCode.DEFAULT_MUST_SPECIFY_CLASS); |
| } |
| |
| // Make sure the default class matches the interface type parameters |
| checkInterfaceTypeParamsToDefault(classElement, defaultClass); |
| } |
| |
| if (!classElement.isInterface() && Elements.needsImplicitDefaultConstructor(classElement)) { |
| // Check to see that all final fields are initialized when no explicit |
| // generative constructor is declared |
| cls.accept(new ASTVisitor<DartNode>() { |
| @Override |
| public DartNode visitField(DartField node) { |
| FieldElement fieldElement = node.getElement(); |
| if (fieldElement != null && fieldElement.getModifiers().isFinal() |
| && !fieldElement.isStatic() |
| && !fieldElement.getModifiers().isConstant() |
| && !fieldElement.getModifiers().isGetter() |
| && !fieldElement.getModifiers().isSetter() |
| && !fieldElement.getModifiers().isInitialized()) { |
| onError(node, ResolverErrorCode.FINAL_FIELD_MUST_BE_INITIALIZED, |
| fieldElement.getName()); |
| } |
| return null; |
| } |
| }); |
| } |
| |
| { |
| DartComment comment = cls.getDartDoc(); |
| if (comment != null) { |
| comment.accept(this); |
| } |
| } |
| |
| context = previousContext; |
| currentHolder = previousHolder; |
| enclosingElement = previousEnclosingElement; |
| return classElement; |
| } |
| |
| private void constVerifyMembers(Iterable<? extends Element> members, ClassElement originalClass, |
| ClassElement currentClass) { |
| for (Element element : members) { |
| Modifiers modifiers = element.getModifiers(); |
| if (ElementKind.of(element).equals(ElementKind.FIELD) && !modifiers.isFinal() |
| && !modifiers.isStatic() && !modifiers.isAbstractField()) { |
| FieldElement field = (FieldElement) element; |
| HasSourceInfo errorNode = field.getSetter() == null ? element : field.getSetter(); |
| onError(errorNode, currentClass == originalClass |
| ? ResolverErrorCode.CONST_CLASS_WITH_NONFINAL_FIELDS |
| : ResolverErrorCode.CONST_CLASS_WITH_INHERITED_NONFINAL_FIELDS, |
| originalClass.getName(), field.getName(), currentClass.getName()); |
| } |
| } |
| } |
| |
| /** |
| * Sets the type in the AST of the default clause of an interface so that the type |
| * parameters to resolve back to the default class. |
| */ |
| private void bindDefaultTypeParameters(List<Type> parameterTypes, |
| List<DartTypeParameter> parameterNodes, |
| ResolutionContext classContext) { |
| Iterator<? extends Type> typeIterator = parameterTypes.iterator(); |
| Iterator<DartTypeParameter> nodeIterator = parameterNodes.iterator(); |
| |
| while(typeIterator.hasNext() && nodeIterator.hasNext()) { |
| |
| Type type = typeIterator.next(); |
| DartTypeParameter node = nodeIterator.next(); |
| |
| if (type.getElement().getName().equals(node.getName().getName())) { |
| node.setType(type); |
| recordElement(node.getName(), type.getElement()); |
| } else { |
| node.setType(typeProvider.getDynamicType()); |
| } |
| |
| DartTypeNode boundNode = node.getBound(); |
| if (boundNode != null) { |
| Type bound = |
| classContext.resolveType( |
| boundNode, |
| false, |
| false, |
| true, |
| ResolverErrorCode.NO_SUCH_TYPE, |
| ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); |
| boundNode.setType(bound); |
| } |
| } |
| |
| while (nodeIterator.hasNext()) { |
| DartTypeParameter node = nodeIterator.next(); |
| node.setType(typeProvider.getDynamicType()); |
| } |
| } |
| /** |
| * If type parameters are present, the type parameters of the default statement |
| * must exactly match those of those declared in the class it references. |
| * |
| */ |
| private void checkDefaultClassTypeParamsToDefaultDecl(InterfaceType defaultClassType, |
| DartParameterizedTypeNode defaultClassRef) { |
| if (defaultClassRef.getTypeParameters().isEmpty()) { |
| return; |
| } |
| ClassElement defaultClassElement = defaultClassType.getElement(); |
| boolean match = true; |
| if (defaultClassElement.getTypeParameters().isEmpty()) { |
| match = false; |
| } else { |
| // TODO(zundel): This is effective in catching mistakes, but highlights the entire type |
| // expression - A more specific indication of where the error started might be appreciated. |
| String defaultClassSource = defaultClassElement.getDeclarationNameWithTypeParameters(); |
| String refSource = defaultClassRef.toSource(); |
| if (!refSource.equals(defaultClassSource)) { |
| match = false; |
| } |
| } |
| if (!match) { |
| // TODO(zundel): work harder to point out where the type param match failure starts. |
| onError(defaultClassRef, ResolverErrorCode.TYPE_PARAMETERS_MUST_MATCH_EXACTLY); |
| } |
| } |
| |
| private void checkInterfaceTypeParamsToDefault(ClassElement interfaceElement, |
| ClassElement defaultClassElement) { |
| |
| List<Type> interfaceTypeParams = interfaceElement.getTypeParameters(); |
| |
| List<Type> defaultTypeParams = defaultClassElement.getTypeParameters(); |
| |
| |
| if (defaultTypeParams.size() != interfaceTypeParams.size()) { |
| |
| onError(interfaceElement.getNameLocation(), |
| ResolverErrorCode.DEFAULT_CLASS_MUST_HAVE_SAME_TYPE_PARAMS); |
| } else { |
| Iterator<? extends Type> interfaceIterator = interfaceTypeParams.iterator(); |
| Iterator<? extends Type> defaultIterator = defaultTypeParams.iterator(); |
| while (interfaceIterator.hasNext()) { |
| Type iVar = interfaceIterator.next(); |
| Type dVar = defaultIterator.next(); |
| String iVarName = iVar.getElement().getName(); |
| String dVarName = dVar.getElement().getName(); |
| if (!iVarName.equals(dVarName)) { |
| onError(iVar.getElement(), ResolverErrorCode.TYPE_VARIABLE_DOES_NOT_MATCH, |
| iVarName, dVarName, defaultClassElement.getName()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Check that used type variables are unique and don't shadow and existing elements. |
| */ |
| private void checkClassTypeVariables(ClassElement classElement) { |
| Set<String> declaredVariableNames = Sets.newHashSet(); |
| for (Type type : classElement.getTypeParameters()) { |
| if (type instanceof TypeVariable) { |
| Element typeVariableElement = type.getElement(); |
| String name = typeVariableElement.getName(); |
| // Check that type variables are unique in this Class declaration. |
| if (declaredVariableNames.contains(name)) { |
| onError(typeVariableElement, ResolverErrorCode.DUPLICATE_TYPE_VARIABLE, name); |
| } else { |
| declaredVariableNames.add(name); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns <code>true</code> if the {@link ClassElement} has an implicit or a declared |
| * default constructor. |
| */ |
| boolean hasDefaultConstructor(ClassElement classElement) { |
| if (Elements.needsImplicitDefaultConstructor(classElement)) { |
| return true; |
| } |
| |
| ConstructorElement defaultCtor = Elements.lookupConstructor(classElement, ""); |
| if (defaultCtor != null) { |
| return defaultCtor.getParameters().isEmpty(); |
| } |
| |
| return false; |
| } |
| |
| private void checkImplicitDefaultDefaultSuperInvocation(DartClass cls, |
| ClassElement classElement) { |
| assert (Elements.needsImplicitDefaultConstructor(classElement)); |
| |
| InterfaceType supertype = classElement.getSupertype(); |
| if (supertype != null) { |
| ClassElement superElement = supertype.getElement(); |
| if (!superElement.isDynamic()) { |
| ConstructorElement superCtor = Elements.lookupConstructor(superElement, ""); |
| boolean superHasDefaultCtor = |
| (superCtor != null && superCtor.getParameters().isEmpty()) |
| || (superCtor == null && Elements.needsImplicitDefaultConstructor(superElement)); |
| if (!superHasDefaultCtor) { |
| onError(cls.getName(), |
| ResolverErrorCode.CANNOT_RESOLVE_IMPLICIT_CALL_TO_SUPER_CONSTRUCTOR, |
| cls.getSuperclass()); |
| } |
| if (superCtor != null && superCtor.getModifiers().isFactory()) { |
| onError(cls.getName(), ResolverErrorCode.NOT_GENERATIVE_SUPER_CONSTRUCTOR, "<default>", |
| supertype); |
| } |
| } |
| } |
| } |
| |
| private Element resolve(DartNode node) { |
| if (node == null) { |
| return null; |
| } else { |
| return node.accept(this); |
| } |
| } |
| |
| @Override |
| public Element visitTypeParameter(DartTypeParameter node) { |
| node.getMetadata().accept(this); |
| return super.visitTypeParameter(node); |
| } |
| |
| @Override |
| public MethodElement visitMethodDefinition(DartMethodDefinition node) { |
| node.getMetadata().accept(this); |
| MethodElement member = node.getElement(); |
| ResolutionContext previousContext = context; |
| context = context.extend(member.getName()); |
| assert currentMethod == null : "Nested methods?"; |
| innermostFunction = currentMethod = member; |
| EnclosingElement previousEnclosingElement = enclosingElement; |
| enclosingElement = member; |
| |
| DartFunction functionNode = node.getFunction(); |
| List<DartParameter> parameters = functionNode.getParameters(); |
| Set<FieldElement> initializedFields = Sets.newHashSet(); |
| |
| // remember field with initializers |
| if (previousEnclosingElement instanceof ClassElement) { |
| ClassElement classElement = (ClassElement) previousEnclosingElement; |
| for (Element classMember : classElement.getMembers()) { |
| if (ElementKind.of(classMember) == ElementKind.FIELD) { |
| FieldElement fieldMember = (FieldElement) classMember; |
| if (fieldMember.getModifiers().isFinal() && fieldMember.getModifiers().isInitialized()) { |
| initializedFields.add(fieldMember); |
| } |
| } |
| } |
| } |
| |
| // First declare all normal parameters in the scope, putting them in the |
| // scope of the default expressions so we can report better errors. |
| for (DartParameter parameter : parameters) { |
| assert parameter.getElement() != null; |
| parameter.getMetadata().accept(this); |
| |
| if (!(parameter.getQualifier() instanceof DartThisExpression)) { |
| getContext().declare( |
| parameter.getElement(), |
| ResolverErrorCode.DUPLICATE_PARAMETER); |
| } |
| } |
| for (DartParameter parameter : parameters) { |
| // Then resolve the default values. |
| resolve(parameter.getDefaultExpr()); |
| if (parameter.getQualifier() instanceof DartThisExpression && parameter.getElement() != null |
| && !initializedFields.add(parameter.getElement().getParameterInitializerElement())) { |
| onError(parameter, ResolverErrorCode.DUPLICATE_INITIALIZATION, parameter.getName()); |
| } |
| } |
| |
| { |
| DartComment comment = node.getDartDoc(); |
| if (comment != null) { |
| comment.accept(this); |
| } |
| } |
| |
| DartBlock body = functionNode.getBody(); |
| if (body == null) { |
| if (member.getModifiers().isStatic() && !member.getModifiers().isExternal()) { |
| onError(functionNode, ResolverErrorCode.STATIC_METHOD_MUST_HAVE_BODY); |
| } |
| } |
| resolve(functionNode.getBody()); |
| |
| if (Elements.isNonFactoryConstructor(member) && !(body instanceof DartNativeBlock)) { |
| resolveInitializers(node, initializedFields); |
| } |
| |
| // only generative constructor can have initializers, so resolve them, but report error |
| if (!member.isConstructor() || member.getModifiers().isFactory()) { |
| for (DartInitializer initializer : node.getInitializers()) { |
| resolve(initializer); |
| if (initializer.getName() != null) { |
| onError(initializer, ResolverErrorCode.INITIALIZER_ONLY_IN_GENERATIVE_CONSTRUCTOR); |
| } |
| } |
| } |
| |
| // resolve redirecting factory constructor |
| { |
| DartTypeNode rcTypeName = node.getRedirectedTypeName(); |
| if (rcTypeName != null) { |
| Type rcType = resolveType(rcTypeName, true, true, false, |
| TypeErrorCode.NO_SUCH_TYPE, ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); |
| switch (TypeKind.of(rcType)) { |
| case INTERFACE: |
| ConstructorElement targetConstructor = null; |
| Element element = recordType(rcTypeName, rcType); |
| DartIdentifier rcName = node.getRedirectedConstructorName(); |
| if (rcName != null) { |
| element = ((ClassElement) element).lookupConstructor(rcName.getName()); |
| switch (ElementKind.of(element)) { |
| case CONSTRUCTOR: |
| targetConstructor = (ConstructorElement) element; |
| recordElement(rcName, element); |
| if (member.getModifiers().isConstant() && !element.getModifiers().isConstant()) { |
| onError(rcName, |
| ResolverErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_CONST); |
| } |
| break; |
| } |
| } else { |
| targetConstructor = ((ClassElement) element).lookupConstructor(""); |
| } |
| Elements.setRedirectingFactoryConstructor(((ConstructorElement) member), |
| targetConstructor); |
| break; |
| default: |
| onError(rcTypeName, ResolverErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_TYPE); |
| } |
| } |
| } |
| |
| context = previousContext; |
| innermostFunction = currentMethod = null; |
| enclosingElement = previousEnclosingElement; |
| return member; |
| } |
| |
| @Override |
| public Element visitField(DartField node) { |
| DartExpression expression = node.getValue(); |
| Modifiers modifiers = node.getModifiers(); |
| boolean isFinal = modifiers.isFinal(); |
| boolean isTopLevel = ElementKind.of(currentHolder).equals(ElementKind.LIBRARY); |
| boolean isStatic = modifiers.isStatic(); |
| |
| if (expression != null) { |
| inInstanceVariableInitializer = !isTopLevel; |
| try { |
| resolve(expression); |
| } finally { |
| inInstanceVariableInitializer = false; |
| } |
| // Now, this constant has a type. Save it for future reference. |
| Element element = node.getElement(); |
| Type expressionType = expression.getType(); |
| if (isFinal && expressionType != null && TypeKind.of(element.getType()) == TypeKind.DYNAMIC) { |
| TypeQuality typeQuality = TypeAnalyzer.getTypeQuality(expression); |
| Type fieldType = Types.makeInferred(expressionType, typeQuality); |
| Elements.setType(element, fieldType); |
| } |
| } else if (isFinal) { |
| if (modifiers.isConstant()) { |
| onError(node, ResolverErrorCode.CONST_REQUIRES_VALUE); |
| } else if (isStatic) { |
| onError(node, ResolverErrorCode.STATIC_FINAL_REQUIRES_VALUE); |
| } else if (isTopLevel) { |
| onError(node, ResolverErrorCode.TOPLEVEL_FINAL_REQUIRES_VALUE); |
| } else { |
| // If a final instance field wasn't initialized at declaration, we must check |
| // at construction time. |
| this.finalsNeedingInitializing.add(node.getElement()); |
| } |
| } |
| |
| // If field is an accessor, both getter and setter need to be visited (if present). |
| // We check for duplicates because top-level fields are visited twice - for each accessor. |
| FieldNodeElement field = node.getElement(); |
| if (!resolvedFields.contains(field)) { |
| resolvedFields.add(field); |
| if (field.getGetter() != null) { |
| resolve(field.getGetter().getNode()); |
| } |
| if (field.getSetter() != null) { |
| resolve(field.getSetter().getNode()); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitFieldDefinition(DartFieldDefinition node) { |
| node.getMetadata().accept(this); |
| visit(node.getFields()); |
| return null; |
| } |
| |
| @Override |
| public Element visitFunction(DartFunction node) { |
| throw context.internalError(node, "should not be called."); |
| } |
| |
| @Override |
| public Element visitParameter(DartParameter x) { |
| x.getMetadata().accept(this); |
| Element element = super.visitParameter(x); |
| resolve(x.getDefaultExpr()); |
| getContext().declare( |
| element, |
| ResolverErrorCode.DUPLICATE_PARAMETER); |
| return element; |
| } |
| |
| @Override |
| public Element visitVariable(DartVariable node) { |
| node.getMetadata().accept(this); |
| return super.visitVariable(node); |
| } |
| |
| public VariableElement resolveVariable(DartVariable x, Modifiers modifiers) { |
| final DartIdentifier nameNode = x.getName(); |
| final String name = nameNode.getName(); |
| // Visit the initializer first. |
| DartExpression value = x.getValue(); |
| if (value != null) { |
| // It is a compile-time error if e refers to the name v or the name v=. |
| value.accept(new ASTVisitor<Void>() { |
| @Override |
| public Void visitIdentifier(DartIdentifier node) { |
| // ignore cases when name is used with some qualifier |
| if (node.getParent() instanceof DartPropertyAccess) { |
| DartPropertyAccess x = (DartPropertyAccess) node.getParent(); |
| if (x.getName() == node) { |
| return null; |
| } |
| } |
| if (node.getParent() instanceof DartMethodInvocation) { |
| DartMethodInvocation x = (DartMethodInvocation) node.getParent(); |
| if (x.getFunctionName() == node) { |
| return null; |
| } |
| } |
| // TODO(scheglov) remove this after http://code.google.com/p/dart/issues/detail?id=6869 |
| { |
| Source source = node.getSourceInfo().getSource(); |
| if (Elements.isSourceName(source, "dart://json/json.dart/json.dart")) { |
| return null; |
| } |
| } |
| if (Objects.equal(node.getName(), name)) { |
| onError(node, ResolverErrorCode.VARIABLE_REFERENCES_SAME_NAME_IN_INITIALIZER, name, |
| name); |
| node.markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| } |
| return null; |
| } |
| }); |
| // do resolve |
| resolve(value); |
| } |
| // declare variable |
| VariableElement element = Elements.variableElement(enclosingElement, x, name, modifiers); |
| getContext().declare(recordElement(x, element), |
| ResolverErrorCode.DUPLICATE_LOCAL_VARIABLE_ERROR); |
| recordElement(nameNode, element); |
| return element; |
| } |
| |
| @Override |
| public Element visitVariableStatement(DartVariableStatement node) { |
| resolveVariableStatement(node, false); |
| return null; |
| } |
| |
| private void resolveVariableStatement(DartVariableStatement node, |
| boolean isImplicitlyInitialized) { |
| Type type = |
| resolveType( |
| node.getTypeNode(), |
| ASTNodes.isStaticContext(node), |
| ASTNodes.isFactoryContext(node), |
| true, |
| TypeErrorCode.NO_SUCH_TYPE, |
| TypeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); |
| for (DartVariable variable : node.getVariables()) { |
| String name = variable.getVariableName(); |
| getContext().getScope().removeDeclaredButNotReachedVariable(name); |
| Elements.setType(resolveVariable(variable, node.getModifiers()), type); |
| checkVariableStatement(node, variable, isImplicitlyInitialized); |
| } |
| } |
| |
| @Override |
| public Element visitLabel(DartLabel x) { |
| DartNode parent = x.getParent(); |
| if (!(parent instanceof DartSwitchMember && ((DartSwitchMember) parent).getLabels().contains( |
| x))) { |
| LabelElement labelElement; |
| DartStatement childStatement = x.getStatement(); |
| while (childStatement instanceof DartLabel) { |
| childStatement = ((DartLabel) childStatement).getStatement(); |
| } |
| if (childStatement instanceof DartSwitchStatement) { |
| labelElement = Elements.switchLabelElement(x, x.getName(), innermostFunction); |
| } else { |
| labelElement = Elements.statementLabelElement(x, x.getName(), innermostFunction); |
| } |
| recordElement(x.getLabel(), labelElement); |
| recordElement(x, labelElement); |
| } |
| x.visitChildren(this); |
| return null; |
| } |
| |
| @Override |
| public Element visitFunctionExpression(DartFunctionExpression x) { |
| MethodElement element; |
| if (x.isStatement()) { |
| // Function statement names live in the outer scope. |
| element = getContext().declareFunction(x); |
| getContext().pushFunctionScope(x); |
| } else { |
| // Function expression names live in their own scope. |
| getContext().pushFunctionScope(x); |
| element = getContext().declareFunction(x); |
| } |
| // record element |
| if (x.getName() != null) { |
| recordElement(x.getName(), element); |
| } |
| recordElement(x, element); |
| // visit function |
| MethodElement previousFunction = innermostFunction; |
| innermostFunction = element; |
| { |
| DartFunction functionNode = x.getFunction(); |
| EnclosingElement previousEnclosingElement = enclosingElement; |
| enclosingElement = element; |
| getContext().pushFunctionScope(x); |
| try { |
| resolveFunction(functionNode, element); |
| resolve(functionNode.getBody()); |
| } finally { |
| getContext().popScope(); |
| enclosingElement = previousEnclosingElement; |
| } |
| } |
| innermostFunction = previousFunction; |
| getContext().popScope(); |
| return element; |
| } |
| |
| @Override |
| public Element visitBlock(DartBlock x) { |
| getContext().pushScope("<block>"); |
| addLabelToStatement(x); |
| // Remember names of Block variables. |
| for (DartStatement statement : x.getStatements()) { |
| if (statement instanceof DartVariableStatement) { |
| DartVariableStatement node = (DartVariableStatement) statement; |
| List<DartVariable> variables = node.getVariables(); |
| for (DartVariable variable : variables) { |
| String name = variable.getVariableName(); |
| getContext().getScope().addDeclaredButNotReachedVariable(name); |
| } |
| } |
| } |
| // Visit statements. |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| @Override |
| public Element visitBreakStatement(DartBreakStatement x) { |
| // Handle corner case of L: break L; |
| DartNode parent = x.getParent(); |
| if (parent instanceof DartLabel && x.getLabel() != null) { |
| if (((DartLabel) parent).getLabel().getName().equals(x.getLabel().getName())) { |
| getContext().pushScope("<break>"); |
| addLabelToStatement(x); |
| visitGotoStatement(x); |
| getContext().popScope(); |
| return null; |
| } |
| } |
| return visitGotoStatement(x); |
| } |
| |
| @Override |
| public Element visitTryStatement(DartTryStatement x) { |
| getContext().pushScope("<try>"); |
| addLabelToStatement(x); |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| @Override |
| public Element visitCatchBlock(DartCatchBlock x) { |
| getContext().pushScope("<block>"); |
| addLabelToStatement(x); |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| @Override |
| public Element visitDoWhileStatement(DartDoWhileStatement x) { |
| getContext().pushScope("<do>"); |
| addLabelToStatement(x); |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| @Override |
| public Element visitWhileStatement(DartWhileStatement x) { |
| getContext().pushScope("<while>"); |
| addLabelToStatement(x); |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| @Override |
| public Element visitIfStatement(DartIfStatement x) { |
| getContext().pushScope("<if>"); |
| addLabelToStatement(x); |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| @Override |
| public Element visitForInStatement(DartForInStatement x) { |
| getContext().pushScope("<for in>"); |
| addLabelToStatement(x); |
| |
| x.getIterable().accept(this); |
| if (x.introducesVariable()) { |
| resolveVariableStatement(x.getVariableStatement(), true); |
| } else { |
| x.getIdentifier().accept(this); |
| } |
| x.getBody().accept(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| private void addLabelToStatement(DartNode x) { |
| DartNode parent = x.getParent(); |
| while (parent instanceof DartLabel) { |
| DartLabel label = (DartLabel) parent; |
| LabelElement currentLabel = label.getElement(); |
| getContext().getScope().addLabel(currentLabel); |
| labelsInScopes.add(currentLabel); |
| parent = parent.getParent(); |
| } |
| } |
| |
| @Override |
| public Element visitForStatement(DartForStatement x) { |
| getContext().pushScope("<for>"); |
| addLabelToStatement(x); |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| |
| @Override |
| public Element visitSwitchStatement(DartSwitchStatement x) { |
| getContext().pushScope("<switch>"); |
| addLabelToStatement(x); |
| // The scope of a label on the case statement is the case statement itself. These labels |
| // need to be resolved before the continue <label>; statements can be resolved. |
| for (DartSwitchMember member : x.getMembers()) { |
| recordSwitchMemberLabel(member); |
| } |
| x.visitChildren(this); |
| getContext().popScope(); |
| return null; |
| } |
| |
| private boolean isValidLastSwitchCaseStatement(DartStatement statement) { |
| if (statement instanceof DartExprStmt) { |
| DartExprStmt exprStmt = (DartExprStmt) statement; |
| if (exprStmt.getExpression() instanceof DartThrowExpression) { |
| return true; |
| } |
| } |
| return statement instanceof DartBreakStatement || statement instanceof DartContinueStatement |
| || statement instanceof DartReturnStatement; |
| } |
| |
| @Override |
| public Element visitSwitchMember(DartSwitchMember x) { |
| // visit children |
| { |
| getContext().pushScope("<switch member>"); |
| x.visitChildren(this); |
| getContext().popScope(); |
| } |
| // check fall-through |
| { |
| List<DartStatement> statements = x.getStatements(); |
| // the last statement should be: break, continue, return, throw |
| if (!statements.isEmpty()) { |
| DartStatement lastStatement = statements.get(statements.size() - 1); |
| if (!isValidLastSwitchCaseStatement(lastStatement)) { |
| onError(lastStatement, ResolverErrorCode.SWITCH_CASE_FALL_THROUGH); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private void recordSwitchMemberLabel(DartSwitchMember x) { |
| List<DartLabel> labels = x.getLabels(); |
| for (DartLabel label : labels) { |
| LabelElement labelElement = Elements.switchMemberLabelElement(label, label.getName(), |
| innermostFunction); |
| recordElement(label.getLabel(), labelElement); |
| recordElement(label, labelElement); |
| if (getContext().getScope().hasLocalLabel(label.getName())) { |
| onError(label, ResolverErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT); |
| } |
| getContext().getScope().addLabel(labelElement); |
| labelsInScopes.add(labelElement); |
| } |
| } |
| |
| @Override |
| public Element visitThisExpression(DartThisExpression x) { |
| if (ElementKind.of(currentHolder).equals(ElementKind.LIBRARY)) { |
| onError(x, ResolverErrorCode.THIS_ON_TOP_LEVEL); |
| } else if (currentMethod == null) { |
| onError(x, ResolverErrorCode.THIS_OUTSIDE_OF_METHOD); |
| } else if (currentMethod.getModifiers().isStatic()) { |
| onError(x, ResolverErrorCode.THIS_IN_STATIC_METHOD); |
| } else if (currentMethod.getModifiers().isFactory()) { |
| onError(x, ResolverErrorCode.THIS_IN_FACTORY_CONSTRUCTOR); |
| } else if (inInitializer) { |
| onError(x, ResolverErrorCode.THIS_IN_INITIALIZER_AS_EXPRESSION); |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitDirective(DartDirective node) { |
| node.getMetadata().accept(this); |
| return super.visitDirective(node); |
| } |
| |
| @Override |
| public Element visitPartOfDirective(DartPartOfDirective node) { |
| node.getMetadata().accept(this); |
| String elementName = "__library_" + node.getLibraryName(); |
| Element element = context.getScope().findElement(null, elementName); |
| if (ElementKind.of(element) == ElementKind.LIBRARY) { |
| node.getName().setElement(element); |
| return element; |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitSuperExpression(DartSuperExpression x) { |
| if (ElementKind.of(currentHolder).equals(ElementKind.LIBRARY)) { |
| onError(x, ResolverErrorCode.SUPER_ON_TOP_LEVEL); |
| } else if (currentMethod == null) { |
| onError(x, ResolverErrorCode.SUPER_OUTSIDE_OF_METHOD); |
| } else if (currentMethod.getModifiers().isStatic()) { |
| onError(x, ResolverErrorCode.SUPER_IN_STATIC_METHOD); |
| } else if (currentMethod.getModifiers().isFactory()) { |
| onError(x, ResolverErrorCode.SUPER_IN_FACTORY_CONSTRUCTOR); |
| } else { |
| return recordElement(x, Elements.superElement( |
| x, ((ClassElement) currentHolder).getSupertype().getElement())); |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitSuperConstructorInvocation(DartSuperConstructorInvocation x) { |
| visit(x.getArguments()); |
| String name = x.getName() == null ? "" : x.getName().getName(); |
| InterfaceType supertype = ((ClassElement) currentHolder).getSupertype(); |
| ConstructorElement element; |
| if (supertype == null) { |
| element = null; |
| } else { |
| ClassElement classElement = supertype.getElement(); |
| element = Elements.lookupConstructor(classElement, name); |
| if (element != null && element.getModifiers().isFactory()) { |
| onError(x, ResolverErrorCode.NOT_GENERATIVE_SUPER_CONSTRUCTOR, name, supertype); |
| } |
| if (element == null && "".equals(name) && x.getArguments().isEmpty() |
| && Elements.needsImplicitDefaultConstructor(classElement)) { |
| element = new SyntheticDefaultConstructorElement(null, classElement, typeProvider); |
| } |
| } |
| if (element == null) { |
| onError(x, ResolverErrorCode.CANNOT_RESOLVE_SUPER_CONSTRUCTOR, name); |
| } |
| return recordElement(x, element); |
| } |
| |
| @Override |
| public Element visitNamedExpression(DartNamedExpression node) { |
| // Intentionally skip the expression's name -- it's stored as an identifier, but doesn't need |
| // to be resolved. |
| return node.getExpression().accept(this); |
| } |
| |
| @Override |
| public Element visitIdentifier(DartIdentifier x) { |
| return resolveIdentifier(x, false); |
| } |
| |
| private Element resolveIdentifier(DartIdentifier x, boolean isQualifier) { |
| if (x.getParent() instanceof DartLabel) { |
| return x.getElement(); |
| } |
| Scope scope = getContext().getScope(); |
| String name = x.getName(); |
| Element element = scope.findElement(scope.getLibrary(), name); |
| if (element == null) { |
| element = scope.findElement(scope.getLibrary(), "setter " + name); |
| } |
| if (element == null) { |
| // A private identifier could refer to a field in a different library. In this case |
| // we want to provide a more useful error message in the type analyzer. |
| if (DartIdentifier.isPrivateName(name)) { |
| Element found = scope.findElement(null, name); |
| if (found != null) { |
| Element enclosingElement = found.getEnclosingElement(); |
| String referencedElementName = enclosingElement == null |
| ? name : String.format("%s.%s", enclosingElement.getName(), name); |
| onError(x, ResolverErrorCode.ILLEGAL_ACCESS_TO_PRIVATE_MEMBER, |
| name, referencedElementName); |
| } |
| } |
| if (isStaticOrFactoryContextOrInitializer(x) && !isQualifier) { |
| if (!x.isResolutionAlreadyReportedThatTheMethodCouldNotBeFound()) { |
| onError(x, TypeErrorCode.CANNOT_BE_RESOLVED, name); |
| x.markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| } |
| } |
| } else if (x.getParent() instanceof DartComment) { |
| } else { |
| element = checkResolvedIdentifier(x, isQualifier, scope, name, element); |
| } |
| |
| if (ElementKind.of(element) == ElementKind.DUPLICATE) { |
| DuplicateElement duplicateElement = (DuplicateElement) element; |
| List<String> locations = duplicateElement.getLocations(); |
| onError(x, ResolverErrorCode.DUPLICATE_IMPORTED_NAME, element.getName(), locations.size(), |
| locations); |
| return null; |
| } |
| |
| if (inInitializer && ElementKind.of(element) == ElementKind.FIELD) { |
| if (!element.getModifiers().isStatic() && !Elements.isTopLevel(element)) { |
| onError(x, ResolverErrorCode.CANNOT_ACCESS_FIELD_IN_INIT); |
| } |
| } |
| |
| if (ElementKind.of(element) == ElementKind.FIELD) { |
| FieldElement fieldElement = (FieldElement) element; |
| if (fieldElement.getModifiers().isAbstractField()) { |
| if (fieldElement.getGetter() == null && ASTNodes.inGetterContext(x)) { |
| topLevelContext.onError(x, ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_GETTER); |
| x.markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| } |
| if (fieldElement.getSetter() == null && ASTNodes.inSetterContext(x)) { |
| topLevelContext.onError(x, ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_SETTER); |
| x.markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| } |
| } |
| } |
| |
| // May be local variable declared in lexical scope, but its declaration is not visited yet. |
| if (getContext().getScope().isDeclaredButNotReachedVariable(name)) { |
| onError(x, ResolverErrorCode.USING_LOCAL_VARIABLE_BEFORE_DECLARATION, x); |
| } |
| |
| if (!isQualifier && !(x.getParent() instanceof DartComment)) { |
| switch (ElementKind.of(element)) { |
| case FUNCTION_TYPE_ALIAS: |
| onError(x, ResolverErrorCode.CANNOT_USE_TYPE, name); |
| break; |
| case TYPE_VARIABLE: |
| onError(x, ResolverErrorCode.CANNOT_USE_TYPE_VARIABLE, name); |
| break; |
| case DUPLICATE: |
| DuplicateElement duplicateElement = (DuplicateElement) element; |
| List<String> locations = duplicateElement.getLocations(); |
| onError(x, ResolverErrorCode.DUPLICATE_IMPORTED_NAME, element.getName(), |
| locations.size(), locations); |
| return null; |
| } |
| } |
| |
| // If we we haven't resolved the identifier, it will be normalized to |
| // this.<identifier>. |
| |
| checkDeprecated(x, element); |
| return recordElement(x, element); |
| } |
| |
| /** |
| * Possibly recursive check on the resolved identifier. |
| */ |
| private Element checkResolvedIdentifier(DartIdentifier x, boolean isQualifier, Scope scope, |
| String name, Element element) { |
| switch (element.getKind()) { |
| case FIELD: |
| if (!Elements.isStaticContext(element) && !element.getModifiers().isConstant()) { |
| if (inInstanceVariableInitializer) { |
| onError(x, ResolverErrorCode.CANNOT_USE_INSTANCE_FIELD_IN_INSTANCE_FIELD_INITIALIZER); |
| } |
| } |
| if (ASTNodes.isStaticContext(x) && !Elements.isStaticContext(element)) { |
| onError(x, ResolverErrorCode.ILLEGAL_FIELD_ACCESS_FROM_STATIC, name); |
| } |
| if (isIllegalPrivateAccess(x, enclosingElement, element, x.getName())) { |
| return null; |
| } |
| break; |
| case METHOD: |
| if (ASTNodes.isStaticContext(x) && !Elements.isStaticContext(element)) { |
| onError(x, ResolverErrorCode.ILLEGAL_METHOD_ACCESS_FROM_STATIC, |
| name); |
| } |
| if (isIllegalPrivateAccess(x, enclosingElement, element, x.getName())) { |
| return null; |
| } |
| if (!element.getModifiers().isStatic() && !Elements.isTopLevel(element)) { |
| if (referencedFromInitializer(x)) { |
| onError(x, ResolverErrorCode.INSTANCE_METHOD_FROM_INITIALIZER); |
| } |
| } |
| break; |
| case CLASS: |
| if (!isQualifier) { |
| return typeProvider.getTypeType().getElement(); |
| } |
| break; |
| case FUNCTION_TYPE_ALIAS: |
| case TYPE_VARIABLE: |
| return typeProvider.getTypeType().getElement(); |
| default: |
| break; |
| } |
| return element; |
| } |
| |
| @Override |
| public Element visitTypeNode(DartTypeNode x) { |
| // prepare ErrorCode, depends on the context |
| ErrorCode errorCode = ResolverErrorCode.NO_SUCH_TYPE; |
| ErrorCode wrongNumberErrorCode = ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS; |
| { |
| DartNode p = x.getParent(); |
| if (p instanceof DartTypeExpression) { |
| DartTypeExpression typeExpression = (DartTypeExpression) p; |
| if (typeExpression.getTypeNode() == x) { |
| DartNode pp = p.getParent(); |
| if (pp instanceof DartBinaryExpression) { |
| Token operator = ((DartBinaryExpression) pp).getOperator(); |
| if (operator == Token.AS || operator == Token.IS) { |
| errorCode = TypeErrorCode.NO_SUCH_TYPE; |
| } |
| } |
| } |
| } |
| } |
| // do Type resolve |
| return resolveType(x, ASTNodes.isStaticContext(x), ASTNodes.isFactoryContext(x), false, |
| errorCode, wrongNumberErrorCode).getElement(); |
| } |
| |
| @Override |
| public Element visitPropertyAccess(DartPropertyAccess x) { |
| Element qualifier = resolveQualifier(x.getRealTarget()); |
| Element element = null; |
| switch (ElementKind.of(qualifier)) { |
| case CLASS: |
| // Must be a static field. |
| element = Elements.findElement(((ClassElement) qualifier), x.getPropertyName()); |
| if (element == null) { |
| element = Elements.findElement(((ClassElement) qualifier), "setter " + x.getPropertyName()); |
| } |
| if (isIllegalPrivateAccess(x.getName(), qualifier, element, x.getPropertyName())) { |
| // break; |
| return null; |
| } |
| switch (ElementKind.of(element)) { |
| case FIELD: |
| FieldElement field = (FieldElement) element; |
| x.setType(field.getType()); |
| if (!field.getModifiers().isStatic()) { |
| onError(x.getName(), ResolverErrorCode.NOT_A_STATIC_FIELD, |
| x.getPropertyName()); |
| } |
| if (ASTNodes.inSetterContext(x)) { |
| if (field.getGetter() != null) { |
| if (field.getSetter() == null) { |
| onError(x.getName(), ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_SETTER); |
| } |
| } |
| } |
| if (ASTNodes.inGetterContext(x)) { |
| if (field.getSetter() != null) { |
| if (field.getGetter() == null) { |
| onError(x.getName(), ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_GETTER); |
| } |
| } |
| } |
| break; |
| |
| case NONE: |
| x.getName().markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| onError(x.getName(), TypeErrorCode.CANNOT_BE_RESOLVED, |
| x.getPropertyName()); |
| break; |
| |
| case METHOD: |
| MethodElement method = (MethodElement) element; |
| if (!method.getModifiers().isStatic()) { |
| onError(x.getName(), ResolverErrorCode.NOT_A_STATIC_METHOD, |
| x.getPropertyName()); |
| } |
| break; |
| |
| default: |
| onError(x.getName(), ResolverErrorCode.EXPECTED_STATIC_FIELD, |
| element.getKind()); |
| break; |
| } |
| break; |
| |
| case SUPER: |
| if (isIllegalPrivateAccess(x.getName(), qualifier, element, x.getPropertyName())) { |
| return null; |
| } |
| ClassElement cls = ((SuperElement) qualifier).getClassElement(); |
| Member member = cls.getType().lookupMember(x.getPropertyName()); |
| if (member != null) { |
| element = member.getElement(); |
| } |
| switch (ElementKind.of(element)) { |
| case FIELD: |
| FieldElement field = (FieldElement) element; |
| if (field.getModifiers().isStatic()) { |
| onError(x.getName(), ResolverErrorCode.NOT_AN_INSTANCE_FIELD, |
| x.getPropertyName()); |
| } |
| break; |
| case METHOD: |
| MethodElement method = (MethodElement) element; |
| if (method.isStatic()) { |
| onError(x.getName(), ResolverErrorCode.NOT_AN_INSTANCE_FIELD, |
| x.getPropertyName()); |
| } |
| break; |
| |
| case NONE: |
| onError(x.getName(), TypeErrorCode.CANNOT_BE_RESOLVED, |
| x.getPropertyName()); |
| break; |
| |
| default: |
| onError(x.getName(), |
| ResolverErrorCode.EXPECTED_AN_INSTANCE_FIELD_IN_SUPER_CLASS, |
| element.getKind()); |
| break; |
| } |
| break; |
| |
| case LIBRARY_PREFIX: |
| // Library prefix, lookup the element in the referenced library. |
| Scope scope = ((LibraryPrefixElement) qualifier).getScope(); |
| element = scope.findElement(scope.getLibrary(), x.getPropertyName()); |
| if (element != null) { |
| recordElement(x.getQualifier(), element.getEnclosingElement()); |
| } else { |
| onError(x, ResolverErrorCode.CANNOT_BE_RESOLVED_LIBRARY, |
| x.getPropertyName(), qualifier.getName()); |
| } |
| break; |
| |
| case NONE: { |
| // TODO(zundel): This is a bit awkward. Maybe it would be better to have an |
| // ElementKind of THIS just like we have for SUPER? |
| if (x.getRealTarget() instanceof DartThisExpression) { |
| Element foundElement = Elements.findElement(currentHolder, x.getPropertyName()); |
| if (foundElement != null && !foundElement.getModifiers().isStatic()) { |
| if (ElementKind.of(foundElement) == ElementKind.TYPE_VARIABLE) { |
| onError(x.getRealTarget(), ResolverErrorCode.TYPE_VARIABLE_NOT_ALLOWED_IN_IDENTIFIER); |
| break; |
| } |
| element = foundElement; |
| } |
| } |
| } |
| |
| default: |
| break; |
| } |
| if (ElementKind.of(element) == ElementKind.DUPLICATE) { |
| DuplicateElement duplicateElement = (DuplicateElement) element; |
| List<String> locations = duplicateElement.getLocations(); |
| onError(x.getName(), ResolverErrorCode.DUPLICATE_IMPORTED_NAME, duplicateElement.getName(), |
| locations.size(), locations); |
| return null; |
| } |
| return recordElement(x, element); |
| } |
| |
| private boolean isIllegalPrivateAccess(DartNode diagnosticNode, Element qualifier, |
| Element element, String name) { |
| if (DartIdentifier.isPrivateName(name)) { |
| if (element == null) { |
| element = getContext().getScope().findElement(null, name); |
| } |
| if (!Elements.areSameLibrary(enclosingElement, element)) { |
| onError(diagnosticNode, ResolverErrorCode.ILLEGAL_ACCESS_TO_PRIVATE, name); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Element resolveQualifier(DartNode qualifier) { |
| if (qualifier == null) { |
| return null; |
| } |
| return (qualifier instanceof DartIdentifier) |
| ? resolveIdentifier((DartIdentifier) qualifier, true) |
| : qualifier.accept(this); |
| } |
| |
| @Override |
| public Element visitMethodInvocation(DartMethodInvocation x) { |
| DartIdentifier name = x.getFunctionName(); |
| Element target = resolveQualifier(x.getRealTarget()); |
| Element element = null; |
| |
| switch (ElementKind.of(target)) { |
| case CLASS: { |
| // Must be a static method or field. |
| ClassElement classElement = (ClassElement) target; |
| element = Elements.lookupLocalMethod(classElement, x.getFunctionNameString()); |
| if (element == null) { |
| element = Elements.lookupLocalField(classElement, x.getFunctionNameString()); |
| } |
| if (element == null || !element.getModifiers().isStatic()) { |
| diagnoseErrorInMethodInvocation(x, classElement, element); |
| } else { |
| if (isIllegalPrivateAccess(x.getFunctionName(), target, element, |
| x.getFunctionNameString())) { |
| break; |
| } |
| } |
| break; |
| } |
| |
| case SUPER: { |
| if (x.getParent() instanceof DartInitializer) { |
| onError(x, ResolverErrorCode.SUPER_METHOD_INVOCATION_IN_CONSTRUCTOR_INITIALIZER); |
| } |
| // Must be a superclass' method or field. |
| ClassElement classElement = ((SuperElement) target).getClassElement(); |
| InterfaceType type = classElement.getType(); |
| Member member = type.lookupMember(x.getFunctionNameString()); |
| if (member != null) { |
| if (!member.getElement().getModifiers().isStatic()) { |
| element = member.getElement(); |
| // Must be accessible. |
| if (!Elements.isAccessible(context.getScope().getLibrary(), element)) { |
| name.markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| onError(name, ResolverErrorCode.CANNOT_ACCESS_METHOD, x.getFunctionNameString()); |
| } |
| } |
| } |
| break; |
| } |
| |
| case LIBRARY_PREFIX: |
| // Library prefix, lookup the element in the reference library. |
| LibraryPrefixElement library = ((LibraryPrefixElement) target); |
| element = library.getScope().findElement(context.getScope().getLibrary(), |
| x.getFunctionNameString()); |
| if (element == null) { |
| diagnoseErrorInMethodInvocation(x, library, null); |
| } else { |
| recordElement(x.getTarget(), element.getEnclosingElement()); |
| name.setElement(element); |
| } |
| break; |
| } |
| |
| checkInvocationTarget(x, currentMethod, target); |
| visit(x.getArguments()); |
| if (name != null) { |
| recordElement(name, element); |
| } |
| return recordElement(x, element); |
| } |
| |
| @Override |
| public Element visitUnqualifiedInvocation(DartUnqualifiedInvocation x) { |
| Scope scope = getContext().getScope(); |
| Element element = scope.findElement(scope.getLibrary(), x.getTarget().getName()); |
| if (element == null) { |
| element = scope.findElement(scope.getLibrary(), "setter " + x.getTarget().getName()); |
| } |
| ElementKind kind = ElementKind.of(element); |
| if (kind == ElementKind.DUPLICATE) { |
| DuplicateElement duplicateElement = (DuplicateElement) element; |
| List<String> locations = duplicateElement.getLocations(); |
| onError(x.getTarget(), ResolverErrorCode.DUPLICATE_IMPORTED_NAME, element.getName(), |
| locations.size(), locations); |
| return null; |
| } else if (!INVOKABLE_ELEMENTS.contains(kind)) { |
| diagnoseErrorInUnqualifiedInvocation(x); |
| } else { |
| checkInvocationTarget(x, currentMethod, element); |
| } |
| if (Elements.isAbstractFieldWithoutGetter(element)) { |
| String name = element.getName(); |
| if (isStaticOrFactoryContextOrInitializer(x)) { |
| onError(x.getTarget(), ResolverErrorCode.USE_ASSIGNMENT_ON_SETTER, name); |
| } else { |
| onError(x.getTarget(), TypeErrorCode.USE_ASSIGNMENT_ON_SETTER, name); |
| } |
| } |
| recordElement(x, element); |
| recordElement(x.getTarget(), element); |
| visit(x.getArguments()); |
| return null; |
| } |
| |
| @Override |
| public Element visitFunctionObjectInvocation(DartFunctionObjectInvocation x) { |
| x.getTarget().accept(this); |
| visit(x.getArguments()); |
| return null; |
| } |
| |
| @Override |
| public Element visitNewExpression(final DartNewExpression x) { |
| this.visit(x.getArguments()); |
| |
| Element element = x.getConstructor().accept(getContext().new Selector() { |
| // Only 'new' expressions can have a type in a property access. |
| @Override |
| public Element visitTypeNode(DartTypeNode type) { |
| ErrorCode errorCode = x.isConst() ? ResolverErrorCode.NO_SUCH_TYPE_CONST : TypeErrorCode.NO_SUCH_TYPE; |
| return recordType(type, resolveType(type, ASTNodes.isStaticContext(x), |
| ASTNodes.isFactoryContext(x), |
| false, |
| errorCode, |
| ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS)); |
| } |
| |
| @Override public Element visitPropertyAccess(DartPropertyAccess node) { |
| Element element = node.getQualifier().accept(this); |
| if (ElementKind.of(element).equals(ElementKind.CLASS)) { |
| assert node.getQualifier() instanceof DartTypeNode; |
| recordType(node, node.getQualifier().getType()); |
| return Elements.lookupConstructor(((ClassElement) element), node.getPropertyName()); |
| } else { |
| return null; |
| } |
| } |
| }); |
| |
| |
| switch (ElementKind.of(element)) { |
| case DYNAMIC: |
| return null; |
| case CLASS: |
| // Check for default constructor. |
| ClassElement classElement = (ClassElement) element; |
| element = Elements.lookupConstructor(classElement, ""); |
| // If no default constructor, may be use implicit default constructor. |
| if (element == null |
| && x.getArguments().isEmpty() |
| && Elements.needsImplicitDefaultConstructor(classElement)) { |
| element = new SyntheticDefaultConstructorElement(null, classElement, typeProvider); |
| } |
| break; |
| case CONSTRUCTOR: |
| if (enclosingElement != null) { |
| if (element != null && DartIdentifier.isPrivateName(element.getName()) |
| && !Elements.areSameLibrary(enclosingElement, element)) { |
| onError(x.getConstructor(), ResolverErrorCode.ILLEGAL_ACCESS_TO_PRIVATE, |
| element.getName()); |
| return null; |
| } |
| } |
| break; |
| case TYPE_VARIABLE: |
| if (x.isConst() ) { |
| onError(x.getConstructor(), ResolverErrorCode.CONST_EXPRESSION_CANT_USE_TYPE_VAR); |
| } else { |
| onError(x.getConstructor(), ResolverErrorCode.NEW_EXPRESSION_CANT_USE_TYPE_VAR); |
| } |
| return null; |
| default: |
| break; |
| } |
| |
| // Will check that element is not null. |
| ConstructorElement constructor = checkIsConstructor(x, element); |
| |
| // Check constructor. |
| if (constructor != null) { |
| boolean constConstructor = constructor.getModifiers().isConstant(); |
| // Check for using "const" to non-const constructor. |
| if (x.isConst() && !constConstructor) { |
| onError(x, ResolverErrorCode.CONST_AND_NONCONST_CONSTRUCTOR); |
| } |
| // Check for using "const" with type variables as type arguments. |
| if (x.isConst() && constConstructor) { |
| DartTypeNode typeNode = Types.constructorTypeNode(x); |
| List<DartTypeNode> typeArguments = typeNode.getTypeArguments(); |
| for (DartTypeNode typeArgument : typeArguments) { |
| if (typeArgument.getType() instanceof TypeVariable) { |
| onError(typeArgument, ResolverErrorCode.CONST_WITH_TYPE_VARIABLE); |
| } |
| } |
| } |
| } |
| |
| return recordElement(x, constructor); |
| } |
| |
| @Override |
| public Element visitGotoStatement(DartGotoStatement x) { |
| // Don't bother unless there's a target. |
| if (x.getTargetName() != null) { |
| Element element = getContext().getScope().findLabel(x.getTargetName(), innermostFunction); |
| if (ElementKind.of(element).equals(ElementKind.LABEL)) { |
| LabelElement labelElement = (LabelElement) element; |
| if (x instanceof DartBreakStatement |
| && labelElement.getStatementType() == LabeledStatementType.SWITCH_MEMBER_STATEMENT) { |
| onError(x.getLabel(), ResolverErrorCode.BREAK_LABEL_RESOLVES_TO_CASE_OR_DEFAULT); |
| return null; |
| } |
| if (x instanceof DartContinueStatement |
| && labelElement.getStatementType() == LabeledStatementType.SWITCH_STATEMENT) { |
| onError(x.getLabel(), ResolverErrorCode.CONTINUE_LABEL_RESOLVES_TO_SWITCH); |
| return null; |
| } |
| MethodElement enclosingFunction = (labelElement).getEnclosingFunction(); |
| if (enclosingFunction == innermostFunction) { |
| referencedLabels.add(labelElement); |
| return recordElement(x, element); |
| } |
| } |
| diagnoseErrorInGotoStatement(x, element); |
| } |
| return null; |
| } |
| |
| public void diagnoseErrorInGotoStatement(DartGotoStatement x, Element element) { |
| if (element == null) { |
| onError(x.getLabel(), ResolverErrorCode.CANNOT_RESOLVE_LABEL, |
| x.getTargetName()); |
| } else if (ElementKind.of(element).equals(ElementKind.LABEL)) { |
| onError(x.getLabel(), ResolverErrorCode.CANNOT_ACCESS_OUTER_LABEL, |
| x.getTargetName()); |
| } else { |
| onError(x.getLabel(), ResolverErrorCode.NOT_A_LABEL, x.getTargetName()); |
| } |
| } |
| |
| private void diagnoseErrorInMethodInvocation(DartMethodInvocation node, Element classOrLibrary, |
| Element element) { |
| String name = node.getFunctionNameString(); |
| ElementKind kind = ElementKind.of(element); |
| DartNode errorNode = node.getFunctionName(); |
| switch (kind) { |
| case NONE: |
| switch (ElementKind.of(classOrLibrary)) { |
| case CLASS: |
| onError(errorNode, ResolverErrorCode.CANNOT_RESOLVE_METHOD_IN_CLASS, name, |
| classOrLibrary.getName()); |
| node.getFunctionName().markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| break; |
| case LIBRARY: |
| onError(errorNode, ResolverErrorCode.CANNOT_RESOLVE_METHOD_IN_LIBRARY, name, |
| classOrLibrary.getName()); |
| break; |
| default: |
| onError(errorNode, ResolverErrorCode.CANNOT_RESOLVE_METHOD, name); |
| } |
| |
| break; |
| |
| case CONSTRUCTOR: |
| onError(errorNode, ResolverErrorCode.IS_A_CONSTRUCTOR, classOrLibrary.getName(), |
| name); |
| break; |
| |
| case METHOD: { |
| assert !((MethodElement) element).getModifiers().isStatic(); |
| onError(errorNode, ResolverErrorCode.IS_AN_INSTANCE_METHOD, |
| classOrLibrary.getName(), name); |
| break; |
| } |
| |
| case FIELD: { |
| onError(errorNode, ResolverErrorCode.IS_AN_INSTANCE_FIELD, |
| classOrLibrary.getName(), name); |
| break; |
| } |
| |
| default: |
| throw context.internalError(errorNode, "Unexpected kind of element: %s", kind); |
| } |
| } |
| |
| private void diagnoseErrorInUnqualifiedInvocation(DartUnqualifiedInvocation node) { |
| String name = node.getTarget().getName(); |
| Scope scope = getContext().getScope(); |
| Element element = scope.findElement(scope.getLibrary(), name); |
| ElementKind kind = ElementKind.of(element); |
| switch (kind) { |
| case NONE: |
| if (isStaticOrFactoryContextOrInitializer(node) || ASTNodes.isFactoryContext(node)) { |
| node.getTarget().markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| onError(node.getTarget(), ResolverErrorCode.CANNOT_RESOLVE_METHOD, name); |
| } |
| if (scope.findElement(null, name) != null) { |
| node.getTarget().markResolutionAlreadyReportedThatTheMethodCouldNotBeFound(); |
| onError(node.getTarget(), ResolverErrorCode.CANNOT_ACCESS_METHOD, name); |
| } |
| break; |
| |
| case CONSTRUCTOR: |
| onError(node, ResolverErrorCode.DID_YOU_MEAN_NEW, name, "constructor"); |
| break; |
| |
| case CLASS: |
| onError(node, ResolverErrorCode.DID_YOU_MEAN_NEW, name, "class"); |
| break; |
| |
| case TYPE_VARIABLE: |
| onError(node, ResolverErrorCode.DID_YOU_MEAN_NEW, name, "type variable"); |
| break; |
| |
| case FUNCTION_TYPE_ALIAS: |
| onError(node, ResolverErrorCode.CANNOT_CALL_FUNCTION_TYPE_ALIAS); |
| break; |
| |
| case LIBRARY_PREFIX: |
| onError(node, ResolverErrorCode.CANNOT_CALL_LIBRARY_PREFIX); |
| break; |
| |
| default: |
| throw context.internalError(node, "Unexpected kind of element: %s", kind); |
| } |
| } |
| |
| private void diagnoseErrorInInitializer(DartIdentifier x) { |
| String name = x.getName(); |
| Scope scope = getContext().getScope(); |
| Element element = scope.findElement(scope.getLibrary(), name); |
| ElementKind kind = ElementKind.of(element); |
| switch (kind) { |
| case NONE: |
| onError(x, ResolverErrorCode.CANNOT_RESOLVE_FIELD, name); |
| break; |
| |
| case FIELD: |
| FieldElement field = (FieldElement) element; |
| recordElement(x, field); |
| if (field.isStatic()) { |
| onError(x, ResolverErrorCode.CANNOT_INIT_STATIC_FIELD_IN_INITIALIZER); |
| } else if (field.getModifiers().isAbstractField()) { |
| /* |
| * If we get here then we know that this is a property accessor and not a true field. |
| * If there was a field and property accessor with the same name a name collision error |
| * would keep us from reaching this point. |
| */ |
| onError(x, ResolverErrorCode.CANNOT_INIT_STATIC_FIELD_IN_INITIALIZER); |
| } else { |
| onError(x, ResolverErrorCode.INIT_FIELD_ONLY_IMMEDIATELY_SURROUNDING_CLASS); |
| } |
| break; |
| |
| case METHOD: |
| onError(x, ResolverErrorCode.EXPECTED_FIELD_NOT_METHOD, name); |
| break; |
| |
| case CLASS: |
| onError(x, ResolverErrorCode.EXPECTED_FIELD_NOT_CLASS, name); |
| break; |
| |
| case PARAMETER: |
| onError(x, ResolverErrorCode.EXPECTED_FIELD_NOT_PARAMETER, name); |
| break; |
| |
| case TYPE_VARIABLE: |
| onError(x, ResolverErrorCode.EXPECTED_FIELD_NOT_TYPE_VAR, name); |
| break; |
| |
| case VARIABLE: |
| case LABEL: |
| default: |
| throw context.internalError(x, "Unexpected kind of element: %s", kind); |
| } |
| } |
| |
| @Override |
| public Element visitInitializer(DartInitializer x) { |
| if (x.getName() != null) { |
| // Make sure the identifier is a local instance field. |
| FieldElement element = Elements.lookupLocalField( |
| (ClassElement) currentHolder, x.getName().getName()); |
| if (element == null || element.isStatic() || element.getModifiers().isAbstractField()) { |
| diagnoseErrorInInitializer(x.getName()); |
| } |
| recordElement(x.getName(), element); |
| } |
| |
| assert !inInitializer; |
| DartExpression value = x.getValue(); |
| if (value == null) { |
| return null; |
| } |
| inInitializer = true; |
| Element element = value.accept(this); |
| inInitializer = false; |
| return element; |
| } |
| |
| @Override |
| public Element visitRedirectConstructorInvocation(DartRedirectConstructorInvocation x) { |
| |
| visit(x.getArguments()); |
| String name = x.getName() != null ? x.getName().getName() : ""; |
| ConstructorElement element = Elements.lookupConstructor((ClassElement) currentHolder, name); |
| if (element == null) { |
| onError(x, ResolverErrorCode.CANNOT_RESOLVE_CONSTRUCTOR, name); |
| } |
| return recordElement(x, element); |
| } |
| |
| @Override |
| public Element visitReturnStatement(DartReturnStatement x) { |
| if (x.getValue() != null) { |
| // Dart Spec v0.03, section 11.10. |
| // Generative constructors cannot return arbitrary expressions in the form: 'return e;' |
| // they can though have return statement in the form: 'return;' |
| if ((currentMethod == innermostFunction) |
| && Elements.isNonFactoryConstructor(currentMethod)) { |
| onError(x, ResolverErrorCode.INVALID_RETURN_IN_CONSTRUCTOR); |
| } |
| return x.getValue().accept(this); |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitIntegerLiteral(DartIntegerLiteral node) { |
| recordType(node, typeProvider.getIntType()); |
| return null; |
| } |
| |
| @Override |
| public Element visitDoubleLiteral(DartDoubleLiteral node) { |
| recordType(node, typeProvider.getDoubleType()); |
| return null; |
| } |
| |
| @Override |
| public Element visitBooleanLiteral(DartBooleanLiteral node) { |
| recordType(node, typeProvider.getBoolType()); |
| return null; |
| } |
| |
| @Override |
| public Element visitStringLiteral(DartStringLiteral node) { |
| recordType(node, typeProvider.getStringType()); |
| return null; |
| } |
| |
| @Override |
| public Element visitStringInterpolation(DartStringInterpolation node) { |
| node.visitChildren(this); |
| recordType(node, typeProvider.getStringType()); |
| return null; |
| } |
| |
| Element recordType(DartNode node, Type type) { |
| node.setType(type); |
| return type.getElement(); |
| } |
| |
| @Override |
| public Element visitBinaryExpression(DartBinaryExpression node) { |
| Element lhs = resolve(node.getArg1()); |
| resolve(node.getArg2()); |
| if (node.getOperator().isAssignmentOperator()) { |
| switch (ElementKind.of(lhs)) { |
| case FIELD: |
| case PARAMETER: |
| case VARIABLE: |
| if (lhs.getModifiers().isFinal()) { |
| if (Elements.isFieldOfSameClassAsEnclosingConstructor(lhs, enclosingElement)) { |
| topLevelContext.onError(node.getArg1(), |
| ResolverErrorCode.CANNOT_ASSIGN_TO_FINAL_ERROR, lhs.getName()); |
| } else { |
| topLevelContext.onError(node.getArg1(), ResolverErrorCode.CANNOT_ASSIGN_TO_FINAL, |
| lhs.getName()); |
| } |
| } |
| break; |
| case METHOD: |
| if (!lhs.getModifiers().isSetter() && !lhs.getModifiers().isGetter()) { |
| topLevelContext.onError(node.getArg1(), ResolverErrorCode.CANNOT_ASSIGN_TO_METHOD, |
| lhs.getName()); |
| } |
| if (lhs.getModifiers().isSetter()) { |
| node.setElement(lhs); |
| } |
| break; |
| } |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public Element visitUnaryExpression(DartUnaryExpression node) { |
| DartExpression arg = node.getArg(); |
| Element argElement = resolve(arg); |
| if (node.getOperator().isCountOperator()) { |
| switch (ElementKind.of(argElement)) { |
| case FIELD: |
| case PARAMETER: |
| case VARIABLE: |
| if (argElement.getModifiers().isFinal()) { |
| topLevelContext.onError(arg, ResolverErrorCode.CANNOT_ASSIGN_TO_FINAL, |
| argElement.getName()); |
| } |
| break; |
| } |
| } |
| if (node.getOperator() == Token.CONDITIONAL) { |
| if (ElementKind.of(argElement) != ElementKind.PARAMETER) { |
| onError(arg, ResolverErrorCode.FORMAL_PARAMETER_NAME_EXPECTED); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitMapLiteral(DartMapLiteral node) { |
| List<DartTypeNode> originalTypeArgs = node.getTypeArguments(); |
| List<DartTypeNode> typeArgs = Lists.newArrayList(); |
| DartTypeNode implicitKey = new DartTypeNode( |
| new DartIdentifier("String")); |
| switch (originalTypeArgs.size()) { |
| case 1: |
| // Old (pre spec 0.11) map specification |
| typeArgs.add(implicitKey); |
| typeArgs.add(originalTypeArgs.get(0)); |
| // TODO(scheglov) enable this warning |
| // topLevelContext.onError(originalTypeArgs.get(0), ResolverErrorCode.DEPRECATED_MAP_LITERAL_SYNTAX); |
| break; |
| case 2: |
| typeArgs.add(originalTypeArgs.get(0)); |
| typeArgs.add(originalTypeArgs.get(1)); |
| break; |
| default: |
| topLevelContext.onError(node, ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, |
| defaultLiteralMapType, |
| originalTypeArgs.size(), 1); |
| // fall through |
| case 0: |
| typeArgs.add(implicitKey); |
| DartTypeNode implicitValue = new DartTypeNode(new DartIdentifier("dynamic")); |
| typeArgs.add(implicitValue); |
| break; |
| } |
| |
| InterfaceType type = |
| context.instantiateParameterizedType( |
| defaultLiteralMapType.getElement(), |
| node, |
| typeArgs, |
| ASTNodes.isStaticContext(node), |
| ASTNodes.isFactoryContext(node), |
| ResolverErrorCode.NO_SUCH_TYPE, |
| ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); |
| // instantiateParametersType() will complain for wrong number of parameters (!=2) |
| if (node.isConst()) { |
| checkTypeArgumentsInConstLiteral(typeArgs, ResolverErrorCode.CONST_MAP_WITH_TYPE_VARIABLE); |
| } |
| recordType(node, type); |
| visit(node.getEntries()); |
| return null; |
| } |
| |
| @Override |
| public Element visitArrayLiteral(DartArrayLiteral node) { |
| List<DartTypeNode> typeArgs = node.getTypeArguments(); |
| InterfaceType type = |
| context.instantiateParameterizedType( |
| rawArrayType.getElement(), |
| node, |
| typeArgs, |
| ASTNodes.isStaticContext(node), |
| ASTNodes.isFactoryContext(node), |
| ResolverErrorCode.NO_SUCH_TYPE, |
| ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); |
| // instantiateParametersType() will complain for wrong number of parameters (!=1) |
| if (node.isConst()) { |
| checkTypeArgumentsInConstLiteral(typeArgs, ResolverErrorCode.CONST_ARRAY_WITH_TYPE_VARIABLE); |
| } |
| recordType(node, type); |
| visit(node.getExpressions()); |
| return null; |
| } |
| |
| private void checkTypeArgumentsInConstLiteral(List<DartTypeNode> typeArgs, ErrorCode errorCode) { |
| for (DartTypeNode typeNode : typeArgs) { |
| Type type = typeNode.getType(); |
| if (type != null && type.getKind() == TypeKind.VARIABLE) { |
| onError(typeNode, errorCode); |
| } |
| } |
| } |
| |
| private ConstructorElement checkIsConstructor(DartNewExpression node, Element element) { |
| if (!ElementKind.of(element).equals(ElementKind.CONSTRUCTOR)) { |
| ResolverErrorCode errorCode = node.isConst() |
| ? ResolverErrorCode.NEW_EXPRESSION_NOT_CONST_CONSTRUCTOR |
| : ResolverErrorCode.NEW_EXPRESSION_NOT_CONSTRUCTOR; |
| onError(ASTNodes.getConstructorNameNode(node), errorCode); |
| return null; |
| } |
| return (ConstructorElement) element; |
| } |
| |
| private void checkConstructor(DartMethodDefinition node, |
| ConstructorElement superCall) { |
| ClassElement currentClass = (ClassElement) currentHolder; |
| if (superCall == null) { |
| // Look for a default constructor in our super type |
| InterfaceType supertype = currentClass.getSupertype(); |
| if (supertype != null) { |
| superCall = Elements.lookupConstructor(supertype.getElement(), ""); |
| } |
| if (superCall != null) { |
| |
| // Do positional parameters match? |
| int superPositionalCount = Elements.getNumberOfRequiredParameters(superCall); |
| if (superPositionalCount > 0) { |
| onError(node, ResolverErrorCode.TOO_FEW_ARGUMENTS_IN_IMPLICIT_SUPER, |
| superCall.getType().toString()); |
| } |
| } |
| } |
| |
| if (superCall == null |
| && !currentClass.isObject() |
| && !currentClass.isObjectChild()) { |
| InterfaceType supertype = currentClass.getSupertype(); |
| if (supertype != null) { |
| ClassElement superElement = supertype.getElement(); |
| if (superElement != null) { |
| if (!hasDefaultConstructor(superElement)) { |
| onError(node, |
| ResolverErrorCode.CANNOT_RESOLVE_IMPLICIT_CALL_TO_SUPER_CONSTRUCTOR, |
| superElement.getName()); |
| } |
| } |
| } |
| } else if (superCall != null |
| && node.getModifiers().isConstant() |
| && !superCall.getModifiers().isConstant()) { |
| onError(node.getName(), |
| ResolverErrorCode.CONST_CONSTRUCTOR_MUST_CALL_CONST_SUPER); |
| } |
| } |
| |
| private void checkInvocationTarget(DartInvocation node, |
| MethodElement callSite, |
| Element target) { |
| |
| if (ElementKind.of(target).equals(ElementKind.METHOD)) { |
| if (callSite != null && callSite.isStatic()) |
| if (!target.getModifiers().isStatic() && !Elements.isTopLevel(target)) { |
| onError(node, ResolverErrorCode.INSTANCE_METHOD_FROM_STATIC); |
| } |
| if (!target.getModifiers().isStatic() && !Elements.isTopLevel(target)) { |
| if (referencedFromRedirectConstructor(node)) { |
| onError(node, ResolverErrorCode.INSTANCE_METHOD_FROM_REDIRECT); |
| } else if (referencedFromInitializer(node)) { |
| onError(node, ResolverErrorCode.INSTANCE_METHOD_FROM_INITIALIZER); |
| } |
| } |
| } |
| } |
| |
| private boolean referencedFromInitializer(DartNode node) { |
| do { |
| if (node instanceof DartInitializer) { |
| return true; |
| } |
| node = node.getParent(); |
| } while (node != null); |
| return false; |
| } |
| |
| private boolean referencedFromRedirectConstructor(DartNode node) { |
| do { |
| if (node instanceof DartRedirectConstructorInvocation) { |
| return true; |
| } |
| node = node.getParent(); |
| } while (node != null); |
| return false; |
| } |
| |
| private void checkVariableStatement(DartVariableStatement node, |
| DartVariable variable, |
| boolean isImplicitlyInitialized) { |
| Modifiers modifiers = node.getModifiers(); |
| if (modifiers.isFinal()) { |
| if (!isImplicitlyInitialized && (variable.getValue() == null)) { |
| onError(variable.getName(), ResolverErrorCode.CONSTANTS_MUST_BE_INITIALIZED); |
| } else if (modifiers.isStatic() && variable.getValue() != null) { |
| resolve(variable.getValue()); |
| node.setType(variable.getValue().getType()); |
| } |
| } |
| } |
| |
| private void resolveInitializers(DartMethodDefinition node, Set<FieldElement> initializedFields) { |
| ClassElement classElement = (ClassElement) enclosingElement.getEnclosingElement(); |
| |
| ConstructorElement constructorElement = null; |
| boolean hasSuperInvocation = false; |
| for (DartInitializer initializer : node.getInitializers()) { |
| hasSuperInvocation |= initializer.getValue() instanceof DartSuperConstructorInvocation; |
| Element element = resolve(initializer); |
| if ((ElementKind.of(element) == ElementKind.CONSTRUCTOR) && initializer.isInvocation()) { |
| constructorElement = (ConstructorElement) element; |
| } else if (initializer.getName() != null && initializer.getName().getElement() != null |
| && initializer.getName().getElement().getModifiers() != null |
| && !initializedFields.add((FieldElement)initializer.getName().getElement())) { |
| onError(initializer, ResolverErrorCode.DUPLICATE_INITIALIZATION, initializer.getName()); |
| } |
| } |
| |
| // If no explicit super() invocation, then implicit call of default super-type constructor. |
| // Check that it is not factory, i.e. generative. |
| if (!hasSuperInvocation && currentHolder instanceof ClassElement) { |
| InterfaceType superType = classElement.getSupertype(); |
| if (superType != null) { |
| ClassElement superElement = superType.getElement(); |
| ConstructorElement superConstructor = Elements.lookupConstructor(superElement, ""); |
| if (superConstructor != null && superConstructor.getModifiers().isFactory()) { |
| onError(node.getName(), ResolverErrorCode.NOT_GENERATIVE_SUPER_CONSTRUCTOR, |
| "<default>", superType); |
| } |
| } |
| } |
| |
| // Look for final fields that are not initialized |
| Element methodElement = node.getElement(); |
| if (classElement != null && methodElement != null |
| && !classElement.isInterface() |
| && !classElement.getModifiers().isNative() |
| && !methodElement.getModifiers().isExternal() |
| && !methodElement.getModifiers().isRedirectedConstructor()) { |
| for (Element member : classElement.getMembers()) { |
| switch (ElementKind.of(member)) { |
| case FIELD: |
| FieldElement fieldMember = (FieldElement)member; |
| if (fieldMember.getModifiers().isFinal() |
| && !fieldMember.getModifiers().isInitialized() |
| && !initializedFields.contains(fieldMember)) { |
| FieldNodeElement n = (FieldNodeElement)fieldMember; |
| onError(n.getNode(), ResolverErrorCode.FINAL_FIELD_MUST_BE_INITIALIZED, |
| fieldMember.getName()); |
| } |
| } |
| } |
| } |
| |
| checkConstructor(node, constructorElement); |
| } |
| |
| private void onError(HasSourceInfo target, ErrorCode errorCode, Object... arguments) { |
| context.onError(target, errorCode, arguments); |
| } |
| |
| private void onError(SourceInfo target, ErrorCode errorCode, Object... arguments) { |
| context.onError(target, errorCode, arguments); |
| } |
| |
| boolean isStaticOrFactoryContextOrInitializer(DartNode x) { |
| return ASTNodes.isStaticOrFactoryContext(x) || inInitializer; |
| } |
| } |
| |
| public static class Phase implements DartCompilationPhase { |
| /** |
| * Executes element resolution on the given compilation unit. |
| * |
| * @param context The listener through which compilation errors are reported |
| * (not <code>null</code>) |
| */ |
| @Override |
| public DartUnit exec(DartUnit unit, DartCompilerContext context, |
| CoreTypeProvider typeProvider) { |
| Scope unitScope = unit.getLibrary().getElement().getScope(); |
| return new Resolver(context, unitScope, typeProvider).exec(unit); |
| } |
| } |
| |
| private void checkRedirectConstructorCycle(List<ConstructorNodeElement> constructors, |
| ResolutionContext context) { |
| for (ConstructorNodeElement element : constructors) { |
| if (hasRedirectedConstructorCycle(element)) { |
| context.onError(element, ResolverErrorCode.REDIRECTED_CONSTRUCTOR_CYCLE); |
| } |
| } |
| } |
| |
| private boolean hasRedirectedConstructorCycle(ConstructorNodeElement constructorElement) { |
| Set<ConstructorNodeElement> visited = Sets.newHashSet(); |
| ConstructorNodeElement next = getNextConstructorInvocation(constructorElement); |
| while (next != null) { |
| if (visited.contains(next)) { |
| return true; |
| } |
| if (constructorElement.getName().equals(next.getName())) { |
| return true; |
| } |
| visited.add(next); |
| next = getNextConstructorInvocation(next); |
| } |
| return false; |
| } |
| |
| private ConstructorNodeElement getNextConstructorInvocation(ConstructorNodeElement constructor) { |
| List<DartInitializer> inits = ((DartMethodDefinition) constructor.getNode()).getInitializers(); |
| // Parser ensures that redirected constructors can be the only item in the initialization list. |
| if (inits.size() == 1) { |
| DartExpression value = inits.get(0).getValue(); |
| if (value != null) { |
| Element element = value.getElement(); |
| if (ElementKind.of(element).equals(ElementKind.CONSTRUCTOR)) { |
| ConstructorElement nextConstructorElement = (ConstructorElement) element; |
| ClassElement nextClass = (ClassElement) nextConstructorElement.getEnclosingElement(); |
| ClassElement currentClass = (ClassElement) constructor.getEnclosingElement(); |
| if (nextClass == currentClass) { |
| return (ConstructorNodeElement) nextConstructorElement; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| } |