blob: 9a4cbf1e295641360ee8a560d6cf512e42f8d93b [file] [log] [blame]
// 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;
}
}