Index: dart/compiler/java/com/google/dart/compiler/resolver/Resolver.java |
diff --git a/dart/compiler/java/com/google/dart/compiler/resolver/Resolver.java b/dart/compiler/java/com/google/dart/compiler/resolver/Resolver.java |
deleted file mode 100644 |
index 9a4cbf1e295641360ee8a560d6cf512e42f8d93b..0000000000000000000000000000000000000000 |
--- a/dart/compiler/java/com/google/dart/compiler/resolver/Resolver.java |
+++ /dev/null |
@@ -1,2523 +0,0 @@ |
-// 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.DartCase; |
-import com.google.dart.compiler.ast.DartCatchBlock; |
-import com.google.dart.compiler.ast.DartClass; |
-import com.google.dart.compiler.ast.DartClassTypeAlias; |
-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 visitClassTypeAlias(DartClassTypeAlias cls) { |
- 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()); |
- } |
- checkMixinObjectIsSupertype(cls.getMixins()); |
- checkMixinNoConstructors(cls.getMixins()); |
- checkMixinNoSuperInvocations(cls.getMixins()); |
- return classElement; |
- } |
- |
- @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); |
- } |
- } |
- |
- // check mixins |
- checkMixinObjectIsSupertype(cls.getMixins()); |
- checkMixinNoConstructors(cls.getMixins()); |
- checkMixinNoSuperInvocations(cls.getMixins()); |
- |
- context = previousContext; |
- currentHolder = previousHolder; |
- enclosingElement = previousEnclosingElement; |
- return classElement; |
- } |
- |
- /** |
- * Checks that the types of the given mixin type node don't have explicit constructors. |
- */ |
- private void checkMixinNoConstructors(List<DartTypeNode> mixins) { |
- for (DartTypeNode mixNode : mixins) { |
- if (mixNode.getType() instanceof InterfaceType) { |
- InterfaceType mixType = (InterfaceType) mixNode.getType(); |
- for (ConstructorElement constructor : mixType.getElement().getConstructors()) { |
- if (!constructor.getModifiers().isFactory()) { |
- topLevelContext.onError(mixNode, ResolverErrorCode.CANNOT_MIXIN_CLASS_WITH_CONSTRUCTOR); |
- break; |
- } |
- } |
- } |
- } |
- } |
- |
- /** |
- * Checks that the types of the given mixin type nodes se subtypes of Object. |
- */ |
- private void checkMixinObjectIsSupertype(List<DartTypeNode> mixins) { |
- for (DartTypeNode mixNode : mixins) { |
- if (mixNode.getType() instanceof InterfaceType) { |
- InterfaceType mixType = (InterfaceType) mixNode.getType(); |
- ClassElement mixElement = mixType.getElement(); |
- if (!mixElement.getMixins().isEmpty()) { |
- topLevelContext.onError(mixNode, ResolverErrorCode.CANNOT_MIXIN_CLASS_WITH_MIXINS); |
- continue; |
- } |
- if (!Objects.equal(mixElement.getSupertype(), typeProvider.getObjectType())) { |
- topLevelContext.onError(mixNode, ResolverErrorCode.ONLY_OBJECT_MIXIN_SUPERCLASS); |
- continue; |
- } |
- } |
- } |
- } |
- |
- /** |
- * Checks that the types of the given mixin type nodes don't have super invocations. |
- */ |
- private void checkMixinNoSuperInvocations(List<DartTypeNode> mixins) { |
- for (DartTypeNode mixNode : mixins) { |
- if (mixNode.getType() instanceof InterfaceType) { |
- InterfaceType mixType = (InterfaceType) mixNode.getType(); |
- if (mixType.getElement() instanceof ClassElement) { |
- ClassElement mixElement = (ClassElement) mixType.getElement(); |
- if (mixElement.hasSuperInvocation()) { |
- topLevelContext.onError(mixNode, ResolverErrorCode.CANNOT_MIXIN_CLASS_WITH_SUPER); |
- break; |
- } |
- } |
- } |
- } |
- } |
- |
- 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) { |
- getContext().pushScope("<switch member>"); |
- x.visitChildren(this); |
- getContext().popScope(); |
- return null; |
- } |
- |
- @Override |
- public Element visitCase(DartCase x) { |
- super.visitCase(x); |
- // 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()); |
- // check if correct place for super() |
- if (ElementKind.of(currentHolder) != ElementKind.CLASS || currentMethod == null |
- || !currentMethod.isConstructor()) { |
- onError(x, ResolverErrorCode.SUPER_OUTSIDE_OF_CONSTRUCTOR); |
- return recordElement(x, null); |
- } |
- InterfaceType supertype = ((ClassElement) currentHolder).getSupertype(); |
- // prepare ConstructorElement |
- String name = x.getName() == null ? "" : x.getName().getName(); |
- 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); |
- } |
- if (x.getName() != null) { |
- recordElement(x.getName(), element); |
- } |
- 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)) { |
- if (!element.getModifiers().isConstant()) { |
- if (inInstanceVariableInitializer) { |
- onError(x, ResolverErrorCode.CANNOT_USE_INSTANCE_FIELD_IN_INSTANCE_FIELD_INITIALIZER); |
- } |
- } |
- if (ASTNodes.isStaticContext(x)) { |
- onError(x, ResolverErrorCode.ILLEGAL_FIELD_ACCESS_FROM_STATIC, name); |
- } |
- if (ASTNodes.isFactoryContext(x)) { |
- onError(x, ResolverErrorCode.ILLEGAL_FIELD_ACCESS_FROM_FACTORY, 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)) { |
- ErrorCode errorCode = node.isConst() |
- ? ResolverErrorCode.NEW_EXPRESSION_NOT_CONST_CONSTRUCTOR |
- : TypeErrorCode.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; |
- } |
-} |