/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.internal.resolver; import com.google.dart.engine.AnalysisEngine; import com.google.dart.engine.ast.Annotation; import com.google.dart.engine.ast.AsExpression; import com.google.dart.engine.ast.AstNode; import com.google.dart.engine.ast.CatchClause; import com.google.dart.engine.ast.ClassDeclaration; import com.google.dart.engine.ast.ClassMember; import com.google.dart.engine.ast.ClassTypeAlias; import com.google.dart.engine.ast.ConstructorDeclaration; import com.google.dart.engine.ast.ConstructorName; import com.google.dart.engine.ast.DeclaredIdentifier; import com.google.dart.engine.ast.Expression; import com.google.dart.engine.ast.ExtendsClause; import com.google.dart.engine.ast.FieldFormalParameter; import com.google.dart.engine.ast.FormalParameter; import com.google.dart.engine.ast.FormalParameterList; import com.google.dart.engine.ast.FunctionDeclaration; import com.google.dart.engine.ast.FunctionTypeAlias; import com.google.dart.engine.ast.FunctionTypedFormalParameter; import com.google.dart.engine.ast.Identifier; import com.google.dart.engine.ast.ImplementsClause; import com.google.dart.engine.ast.InstanceCreationExpression; import com.google.dart.engine.ast.IsExpression; import com.google.dart.engine.ast.MethodDeclaration; import com.google.dart.engine.ast.NodeList; import com.google.dart.engine.ast.PrefixedIdentifier; import com.google.dart.engine.ast.SimpleFormalParameter; import com.google.dart.engine.ast.SimpleIdentifier; import com.google.dart.engine.ast.SuperExpression; import com.google.dart.engine.ast.TypeArgumentList; import com.google.dart.engine.ast.TypeName; import com.google.dart.engine.ast.TypeParameter; import com.google.dart.engine.ast.VariableDeclaration; import com.google.dart.engine.ast.VariableDeclarationList; import com.google.dart.engine.ast.WithClause; import com.google.dart.engine.ast.visitor.UnifyingAstVisitor; import com.google.dart.engine.context.AnalysisException; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.FieldElement; import com.google.dart.engine.element.FieldFormalParameterElement; import com.google.dart.engine.element.FunctionTypeAliasElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.MultiplyDefinedElement; import com.google.dart.engine.element.ParameterElement; import com.google.dart.engine.element.PrefixElement; import com.google.dart.engine.element.PropertyAccessorElement; import com.google.dart.engine.element.PropertyInducingElement; import com.google.dart.engine.element.TypeParameterElement; import com.google.dart.engine.element.VariableElement; import com.google.dart.engine.error.AnalysisErrorListener; import com.google.dart.engine.error.CompileTimeErrorCode; import com.google.dart.engine.error.ErrorCode; import com.google.dart.engine.error.StaticTypeWarningCode; import com.google.dart.engine.error.StaticWarningCode; import com.google.dart.engine.internal.element.ClassElementImpl; import com.google.dart.engine.internal.element.ElementAnnotationImpl; import com.google.dart.engine.internal.element.ExecutableElementImpl; import com.google.dart.engine.internal.element.FunctionTypeAliasElementImpl; import com.google.dart.engine.internal.element.LocalVariableElementImpl; import com.google.dart.engine.internal.element.ParameterElementImpl; import com.google.dart.engine.internal.element.PropertyAccessorElementImpl; import com.google.dart.engine.internal.element.PropertyInducingElementImpl; import com.google.dart.engine.internal.element.TypeParameterElementImpl; import com.google.dart.engine.internal.element.VariableElementImpl; import com.google.dart.engine.internal.scope.Scope; import com.google.dart.engine.internal.type.DynamicTypeImpl; import com.google.dart.engine.internal.type.FunctionTypeImpl; import com.google.dart.engine.internal.type.InterfaceTypeImpl; import com.google.dart.engine.internal.type.TypeImpl; import com.google.dart.engine.internal.type.VoidTypeImpl; import com.google.dart.engine.scanner.Keyword; import com.google.dart.engine.scanner.Token; import com.google.dart.engine.scanner.TokenType; import com.google.dart.engine.source.Source; import com.google.dart.engine.type.FunctionType; import com.google.dart.engine.type.InterfaceType; import com.google.dart.engine.type.Type; import java.util.ArrayList; /** * Instances of the class {@code TypeResolverVisitor} are used to resolve the types associated with * the elements in the element model. This includes the types of superclasses, mixins, interfaces, * fields, methods, parameters, and local variables. As a side-effect, this also finishes building * the type hierarchy. * * @coverage dart.engine.resolver */ public class TypeResolverVisitor extends ScopedVisitor { /** * Kind of the redirecting constructor. */ private static enum RedirectingConstructorKind { CONST, NORMAL } /** * @return {@code true} if the name of the given {@link TypeName} is an built-in identifier. */ private static boolean isBuiltInIdentifier(TypeName node) { Token token = node.getName().getBeginToken(); return token.getType() == TokenType.KEYWORD; } /** * @return {@code true} if given {@link TypeName} is used as a type annotation. */ private static boolean isTypeAnnotation(TypeName node) { AstNode parent = node.getParent(); if (parent instanceof VariableDeclarationList) { return ((VariableDeclarationList) parent).getType() == node; } if (parent instanceof FieldFormalParameter) { return ((FieldFormalParameter) parent).getType() == node; } if (parent instanceof SimpleFormalParameter) { return ((SimpleFormalParameter) parent).getType() == node; } return false; } /** * The type representing the type 'dynamic'. */ private Type dynamicType; /** * The flag specifying if currently visited class references 'super' expression. */ private boolean hasReferenceToSuper; /** * Initialize a newly created visitor to resolve the nodes in a compilation unit. * * @param library the library containing the compilation unit being resolved * @param source the source representing the compilation unit being visited * @param typeProvider the object used to access the types from the core library */ public TypeResolverVisitor(Library library, Source source, TypeProvider typeProvider) { super(library, source, typeProvider); dynamicType = typeProvider.getDynamicType(); } /** * Initialize a newly created visitor to resolve the nodes in a compilation unit. * * @param definingLibrary the element for the library containing the compilation unit being * visited * @param source the source representing the compilation unit being visited * @param typeProvider the object used to access the types from the core library * @param errorListener the error listener that will be informed of any errors that are found * during resolution */ public TypeResolverVisitor(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener) { super(definingLibrary, source, typeProvider, errorListener); dynamicType = typeProvider.getDynamicType(); } /** * Initialize a newly created visitor to resolve the nodes in an AST node. * * @param definingLibrary the element for the library containing the node being visited * @param source the source representing the compilation unit containing the node being visited * @param typeProvider the object used to access the types from the core library * @param nameScope the scope used to resolve identifiers in the node that will first be visited * @param errorListener the error listener that will be informed of any errors that are found * during resolution */ public TypeResolverVisitor(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, Scope nameScope, AnalysisErrorListener errorListener) { super(definingLibrary, source, typeProvider, nameScope, errorListener); dynamicType = typeProvider.getDynamicType(); } /** * Initialize a newly created visitor to resolve the nodes in a compilation unit. * * @param library the library containing the compilation unit being resolved * @param source the source representing the compilation unit being visited * @param typeProvider the object used to access the types from the core library */ public TypeResolverVisitor(ResolvableLibrary library, Source source, TypeProvider typeProvider) { super(library, source, typeProvider); dynamicType = typeProvider.getDynamicType(); } @Override public Void visitAnnotation(Annotation node) { // // Visit annotations, if the annotation is @proxy, on a class, and "proxy" resolves to the proxy // annotation in dart.core, then create create the ElementAnnotationImpl and set it as the // metadata on the enclosing class. // // Element resolution is done in the ElementResolver, and this work will be done in the general // case for all annotations in the ElementResolver. The reason we resolve this particular // element early is so that ClassElement.isProxy() returns the correct information during all // phases of the ElementResolver. // super.visitAnnotation(node); Identifier identifier = node.getName(); if (identifier.getName().endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) && node.getParent() instanceof ClassDeclaration) { Element element = getNameScope().lookup(identifier, getDefiningLibrary()); if (element != null && element.getLibrary().isDartCore() && element instanceof PropertyAccessorElement) { // This is the @proxy from dart.core ClassDeclaration classDeclaration = (ClassDeclaration) node.getParent(); ElementAnnotationImpl elementAnnotation = new ElementAnnotationImpl(element); node.setElementAnnotation(elementAnnotation); ((ClassElementImpl) classDeclaration.getElement()).setMetadata(new ElementAnnotationImpl[] {elementAnnotation}); } } return null; } @Override public Void visitCatchClause(CatchClause node) { super.visitCatchClause(node); SimpleIdentifier exception = node.getExceptionParameter(); if (exception != null) { // If an 'on' clause is provided the type of the exception parameter is the type in the 'on' // clause. Otherwise, the type of the exception parameter is 'Object'. TypeName exceptionTypeName = node.getExceptionType(); Type exceptionType; if (exceptionTypeName == null) { exceptionType = getTypeProvider().getDynamicType(); } else { exceptionType = getType(exceptionTypeName); } recordType(exception, exceptionType); Element element = exception.getStaticElement(); if (element instanceof VariableElementImpl) { ((VariableElementImpl) element).setType(exceptionType); } else { // TODO(brianwilkerson) Report the internal error } } SimpleIdentifier stackTrace = node.getStackTraceParameter(); if (stackTrace != null) { recordType(stackTrace, getTypeProvider().getStackTraceType()); } return null; } @Override public Void visitClassDeclaration(ClassDeclaration node) { ExtendsClause extendsClause = node.getExtendsClause(); WithClause withClause = node.getWithClause(); ImplementsClause implementsClause = node.getImplementsClause(); hasReferenceToSuper = false; super.visitClassDeclaration(node); ClassElementImpl classElement = getClassElement(node.getName()); InterfaceType superclassType = null; if (extendsClause != null) { ErrorCode errorCode = withClause == null ? CompileTimeErrorCode.EXTENDS_NON_CLASS : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS; superclassType = resolveType( extendsClause.getSuperclass(), errorCode, CompileTimeErrorCode.EXTENDS_ENUM, errorCode); if (superclassType != getTypeProvider().getObjectType()) { classElement.setValidMixin(false); } } if (classElement != null) { if (superclassType == null) { InterfaceType objectType = getTypeProvider().getObjectType(); if (classElement.getType() != objectType) { superclassType = objectType; } } classElement.setSupertype(superclassType); classElement.setHasReferenceToSuper(hasReferenceToSuper); } resolve(classElement, withClause, implementsClause); return null; } @Override public Void visitClassTypeAlias(ClassTypeAlias node) { super.visitClassTypeAlias(node); ErrorCode errorCode = CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS; InterfaceType superclassType = resolveType( node.getSuperclass(), errorCode, CompileTimeErrorCode.EXTENDS_ENUM, errorCode); if (superclassType == null) { superclassType = getTypeProvider().getObjectType(); } ClassElementImpl classElement = getClassElement(node.getName()); if (classElement != null) { classElement.setSupertype(superclassType); } resolve(classElement, node.getWithClause(), node.getImplementsClause()); return null; } @Override public Void visitConstructorDeclaration(ConstructorDeclaration node) { super.visitConstructorDeclaration(node); ExecutableElementImpl element = (ExecutableElementImpl) node.getElement(); if (element == null) { ClassDeclaration classNode = node.getAncestor(ClassDeclaration.class); StringBuilder builder = new StringBuilder(); builder.append("The element for the constructor "); builder.append(node.getName() == null ? "<unnamed>" : node.getName().getName()); builder.append(" in "); if (classNode == null) { builder.append("<unknown class>"); } else { builder.append(classNode.getName().getName()); } builder.append(" in "); builder.append(getSource().getFullName()); builder.append(" was not set while trying to resolve types."); AnalysisEngine.getInstance().getLogger().logError(builder.toString(), new AnalysisException()); } else { ClassElement definingClass = (ClassElement) element.getEnclosingElement(); element.setReturnType(definingClass.getType()); FunctionTypeImpl type = new FunctionTypeImpl(element); type.setTypeArguments(definingClass.getType().getTypeArguments()); element.setType(type); } return null; } @Override public Void visitDeclaredIdentifier(DeclaredIdentifier node) { super.visitDeclaredIdentifier(node); Type declaredType; TypeName typeName = node.getType(); if (typeName == null) { declaredType = dynamicType; } else { declaredType = getType(typeName); } LocalVariableElementImpl element = (LocalVariableElementImpl) node.getElement(); element.setType(declaredType); return null; } @Override public Void visitFieldFormalParameter(FieldFormalParameter node) { super.visitFieldFormalParameter(node); Element element = node.getIdentifier().getStaticElement(); if (element instanceof ParameterElementImpl) { ParameterElementImpl parameter = (ParameterElementImpl) element; FormalParameterList parameterList = node.getParameters(); if (parameterList == null) { Type type; TypeName typeName = node.getType(); if (typeName == null) { type = dynamicType; if (parameter instanceof FieldFormalParameterElement) { FieldElement fieldElement = ((FieldFormalParameterElement) parameter).getField(); if (fieldElement != null) { type = fieldElement.getType(); } } } else { type = getType(typeName); } parameter.setType(type); } else { setFunctionTypedParameterType(parameter, node.getType(), node.getParameters()); } } else { // TODO(brianwilkerson) Report this internal error } return null; } @Override public Void visitFunctionDeclaration(FunctionDeclaration node) { super.visitFunctionDeclaration(node); ExecutableElementImpl element = (ExecutableElementImpl) node.getElement(); if (element == null) { StringBuilder builder = new StringBuilder(); builder.append("The element for the top-level function "); builder.append(node.getName()); builder.append(" in "); builder.append(getSource().getFullName()); builder.append(" was not set while trying to resolve types."); AnalysisEngine.getInstance().getLogger().logError(builder.toString(), new AnalysisException()); } element.setReturnType(computeReturnType(node.getReturnType())); FunctionTypeImpl type = new FunctionTypeImpl(element); ClassElement definingClass = element.getAncestor(ClassElement.class); if (definingClass != null) { type.setTypeArguments(definingClass.getType().getTypeArguments()); } element.setType(type); return null; } @Override public Void visitFunctionTypeAlias(FunctionTypeAlias node) { FunctionTypeAliasElementImpl element = (FunctionTypeAliasElementImpl) node.getElement(); if (element.getReturnType() == null) { // Only visit function type aliases once. super.visitFunctionTypeAlias(node); element.setReturnType(computeReturnType(node.getReturnType())); } return null; } @Override public Void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { super.visitFunctionTypedFormalParameter(node); Element element = node.getIdentifier().getStaticElement(); if (element instanceof ParameterElementImpl) { setFunctionTypedParameterType( (ParameterElementImpl) element, node.getReturnType(), node.getParameters()); } else { // TODO(brianwilkerson) Report this internal error } return null; } @Override public Void visitMethodDeclaration(MethodDeclaration node) { super.visitMethodDeclaration(node); ExecutableElementImpl element = (ExecutableElementImpl) node.getElement(); if (element == null) { ClassDeclaration classNode = node.getAncestor(ClassDeclaration.class); StringBuilder builder = new StringBuilder(); builder.append("The element for the method "); builder.append(node.getName().getName()); builder.append(" in "); if (classNode == null) { builder.append("<unknown class>"); } else { builder.append(classNode.getName().getName()); } builder.append(" in "); builder.append(getSource().getFullName()); builder.append(" was not set while trying to resolve types."); AnalysisEngine.getInstance().getLogger().logError(builder.toString(), new AnalysisException()); } element.setReturnType(computeReturnType(node.getReturnType())); FunctionTypeImpl type = new FunctionTypeImpl(element); ClassElement definingClass = element.getAncestor(ClassElement.class); if (definingClass != null) { type.setTypeArguments(definingClass.getType().getTypeArguments()); } element.setType(type); if (element instanceof PropertyAccessorElement) { PropertyAccessorElement accessor = (PropertyAccessorElement) element; PropertyInducingElementImpl variable = (PropertyInducingElementImpl) accessor.getVariable(); if (accessor.isGetter()) { variable.setType(type.getReturnType()); } else if (variable.getType() == null) { Type[] parameterTypes = type.getNormalParameterTypes(); if (parameterTypes != null && parameterTypes.length > 0) { variable.setType(parameterTypes[0]); } } } return null; } @Override public Void visitSimpleFormalParameter(SimpleFormalParameter node) { super.visitSimpleFormalParameter(node); Type declaredType; TypeName typeName = node.getType(); if (typeName == null) { declaredType = dynamicType; } else { declaredType = getType(typeName); } Element element = node.getIdentifier().getStaticElement(); if (element instanceof ParameterElement) { ((ParameterElementImpl) element).setType(declaredType); } else { // TODO(brianwilkerson) Report the internal error. } return null; } @Override public Void visitSuperExpression(SuperExpression node) { hasReferenceToSuper = true; return super.visitSuperExpression(node); } @Override public Void visitTypeName(TypeName node) { super.visitTypeName(node); Identifier typeName = node.getName(); TypeArgumentList argumentList = node.getTypeArguments(); Element element = getNameScope().lookup(typeName, getDefiningLibrary()); if (element == null) { // // Check to see whether the type name is either 'dynamic' or 'void', neither of which are in // the name scope and hence will not be found by normal means. // if (typeName.getName().equals(dynamicType.getName())) { setElement(typeName, dynamicType.getElement()); if (argumentList != null) { // TODO(brianwilkerson) Report this error // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, dynamicType.getName(), 0, argumentList.getArguments().size()); } typeName.setStaticType(dynamicType); node.setType(dynamicType); return null; } VoidTypeImpl voidType = VoidTypeImpl.getInstance(); if (typeName.getName().equals(voidType.getName())) { // There is no element for 'void'. if (argumentList != null) { // TODO(brianwilkerson) Report this error // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, voidType.getName(), 0, argumentList.getArguments().size()); } typeName.setStaticType(voidType); node.setType(voidType); return null; } // // If not, the look to see whether we might have created the wrong AST structure for a // constructor name. If so, fix the AST structure and then proceed. // AstNode parent = node.getParent(); if (typeName instanceof PrefixedIdentifier && parent instanceof ConstructorName && argumentList == null) { ConstructorName name = (ConstructorName) parent; if (name.getName() == null) { PrefixedIdentifier prefixedIdentifier = (PrefixedIdentifier) typeName; SimpleIdentifier prefix = prefixedIdentifier.getPrefix(); element = getNameScope().lookup(prefix, getDefiningLibrary()); if (element instanceof PrefixElement) { if (parent.getParent() instanceof InstanceCreationExpression && ((InstanceCreationExpression) parent.getParent()).isConst()) { // If, if this is a const expression, then generate a // CompileTimeErrorCode.CONST_WITH_NON_TYPE error. reportErrorForNode( CompileTimeErrorCode.CONST_WITH_NON_TYPE, prefixedIdentifier.getIdentifier(), prefixedIdentifier.getIdentifier().getName()); } else { // Else, if this expression is a new expression, report a NEW_WITH_NON_TYPE warning. reportErrorForNode( StaticWarningCode.NEW_WITH_NON_TYPE, prefixedIdentifier.getIdentifier(), prefixedIdentifier.getIdentifier().getName()); } setElement(prefix, element); return null; } else if (element != null) { // // Rewrite the constructor name. The parser, when it sees a constructor named "a.b", // cannot tell whether "a" is a prefix and "b" is a class name, or whether "a" is a // class name and "b" is a constructor name. It arbitrarily chooses the former, but // in this case was wrong. // name.setName(prefixedIdentifier.getIdentifier()); name.setPeriod(prefixedIdentifier.getPeriod()); node.setName(prefix); typeName = prefix; } } } } // check element boolean elementValid = !(element instanceof MultiplyDefinedElement); if (elementValid && !(element instanceof ClassElement) && isTypeNameInInstanceCreationExpression(node)) { SimpleIdentifier typeNameSimple = getTypeSimpleIdentifier(typeName); InstanceCreationExpression creation = (InstanceCreationExpression) node.getParent().getParent(); if (creation.isConst()) { if (element == null) { reportErrorForNode(CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, typeName); } else { reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE, typeNameSimple, typeName); } elementValid = false; } else { if (element != null) { reportErrorForNode(StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, typeName); elementValid = false; } } } if (elementValid && element == null) { // We couldn't resolve the type name. // TODO(jwren) Consider moving the check for CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE // from the ErrorVerifier, so that we don't have two errors on a built in identifier being // used as a class name. See CompileTimeErrorCodeTest.test_builtInIdentifierAsType(). SimpleIdentifier typeNameSimple = getTypeSimpleIdentifier(typeName); RedirectingConstructorKind redirectingConstructorKind; if (isBuiltInIdentifier(node) && isTypeAnnotation(node)) { reportErrorForNode( CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, typeName, typeName.getName()); } else if (typeNameSimple.getName().equals("boolean")) { reportErrorForNode(StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple); } else if (isTypeNameInCatchClause(node)) { reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, typeName.getName()); } else if (isTypeNameInAsExpression(node)) { reportErrorForNode(StaticWarningCode.CAST_TO_NON_TYPE, typeName, typeName.getName()); } else if (isTypeNameInIsExpression(node)) { reportErrorForNode(StaticWarningCode.TYPE_TEST_NON_TYPE, typeName, typeName.getName()); } else if ((redirectingConstructorKind = getRedirectingConstructorKind(node)) != null) { ErrorCode errorCode = redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS; reportErrorForNode(errorCode, typeName, typeName.getName()); } else if (isTypeNameInTypeArgumentList(node)) { reportErrorForNode( StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, typeName, typeName.getName()); } else { reportErrorForNode(StaticWarningCode.UNDEFINED_CLASS, typeName, typeName.getName()); } elementValid = false; } if (!elementValid) { if (element instanceof MultiplyDefinedElement) { setElement(typeName, element); } else { setElement(typeName, dynamicType.getElement()); } typeName.setStaticType(dynamicType); node.setType(dynamicType); return null; } Type type = null; if (element instanceof ClassElement) { setElement(typeName, element); type = ((ClassElement) element).getType(); } else if (element instanceof FunctionTypeAliasElement) { setElement(typeName, element); type = ((FunctionTypeAliasElement) element).getType(); } else if (element instanceof TypeParameterElement) { setElement(typeName, element); type = ((TypeParameterElement) element).getType(); if (argumentList != null) { // Type parameters cannot have type arguments. // TODO(brianwilkerson) Report this error. // resolver.reportError(ResolverErrorCode.?, keyType); } } else if (element instanceof MultiplyDefinedElement) { Element[] elements = ((MultiplyDefinedElement) element).getConflictingElements(); type = getTypeWhenMultiplyDefined(elements); if (type != null) { node.setType(type); } } else { // The name does not represent a type. RedirectingConstructorKind redirectingConstructorKind; if (isTypeNameInCatchClause(node)) { reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, typeName.getName()); } else if (isTypeNameInAsExpression(node)) { reportErrorForNode(StaticWarningCode.CAST_TO_NON_TYPE, typeName, typeName.getName()); } else if (isTypeNameInIsExpression(node)) { reportErrorForNode(StaticWarningCode.TYPE_TEST_NON_TYPE, typeName, typeName.getName()); } else if ((redirectingConstructorKind = getRedirectingConstructorKind(node)) != null) { ErrorCode errorCode = redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS; reportErrorForNode(errorCode, typeName, typeName.getName()); } else if (isTypeNameInTypeArgumentList(node)) { reportErrorForNode( StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, typeName, typeName.getName()); } else { AstNode parent = typeName.getParent(); while (parent instanceof TypeName) { parent = parent.getParent(); } if (parent instanceof ExtendsClause || parent instanceof ImplementsClause || parent instanceof WithClause || parent instanceof ClassTypeAlias) { // Ignored. The error will be reported elsewhere. } else { reportErrorForNode(StaticWarningCode.NOT_A_TYPE, typeName, typeName.getName()); } } setElement(typeName, dynamicType.getElement()); typeName.setStaticType(dynamicType); node.setType(dynamicType); return null; } if (argumentList != null) { NodeList<TypeName> arguments = argumentList.getArguments(); int argumentCount = arguments.size(); Type[] parameters = getTypeArguments(type); int parameterCount = parameters.length; Type[] typeArguments = new Type[parameterCount]; if (argumentCount == parameterCount) { for (int i = 0; i < parameterCount; i++) { TypeName argumentTypeName = arguments.get(i); Type argumentType = getType(argumentTypeName); if (argumentType == null) { argumentType = dynamicType; } typeArguments[i] = argumentType; } } else { reportErrorForNode( getInvalidTypeParametersErrorCode(node), node, typeName.getName(), parameterCount, argumentCount); for (int i = 0; i < parameterCount; i++) { typeArguments[i] = dynamicType; } } if (type instanceof InterfaceTypeImpl) { InterfaceTypeImpl interfaceType = (InterfaceTypeImpl) type; type = interfaceType.substitute(typeArguments); } else if (type instanceof FunctionTypeImpl) { FunctionTypeImpl functionType = (FunctionTypeImpl) type; type = functionType.substitute(typeArguments); } else { // TODO(brianwilkerson) Report this internal error. } } else { // // Check for the case where there are no type arguments given for a parameterized type. // Type[] parameters = getTypeArguments(type); int parameterCount = parameters.length; if (parameterCount > 0) { DynamicTypeImpl dynamicType = DynamicTypeImpl.getInstance(); Type[] arguments = new Type[parameterCount]; for (int i = 0; i < parameterCount; i++) { arguments[i] = dynamicType; } type = type.substitute(arguments, parameters); } } typeName.setStaticType(type); node.setType(type); return null; } @Override public Void visitTypeParameter(TypeParameter node) { super.visitTypeParameter(node); TypeName bound = node.getBound(); if (bound != null) { TypeParameterElementImpl typeParameter = (TypeParameterElementImpl) node.getName().getStaticElement(); if (typeParameter != null) { typeParameter.setBound(bound.getType()); } } return null; } @Override public Void visitVariableDeclaration(VariableDeclaration node) { super.visitVariableDeclaration(node); Type declaredType; TypeName typeName = ((VariableDeclarationList) node.getParent()).getType(); if (typeName == null) { declaredType = dynamicType; } else { declaredType = getType(typeName); } Element element = node.getName().getStaticElement(); if (element instanceof VariableElement) { ((VariableElementImpl) element).setType(declaredType); if (element instanceof PropertyInducingElement) { PropertyInducingElement variableElement = (PropertyInducingElement) element; PropertyAccessorElementImpl getter = (PropertyAccessorElementImpl) variableElement.getGetter(); getter.setReturnType(declaredType); FunctionTypeImpl getterType = new FunctionTypeImpl(getter); ClassElement definingClass = element.getAncestor(ClassElement.class); if (definingClass != null) { getterType.setTypeArguments(definingClass.getType().getTypeArguments()); } getter.setType(getterType); PropertyAccessorElementImpl setter = (PropertyAccessorElementImpl) variableElement.getSetter(); if (setter != null) { ParameterElement[] parameters = setter.getParameters(); if (parameters.length > 0) { ((ParameterElementImpl) parameters[0]).setType(declaredType); } setter.setReturnType(VoidTypeImpl.getInstance()); FunctionTypeImpl setterType = new FunctionTypeImpl(setter); if (definingClass != null) { setterType.setTypeArguments(definingClass.getType().getTypeArguments()); } setter.setType(setterType); } } } else { // TODO(brianwilkerson) Report the internal error. } return null; } @Override protected void visitClassMembersInScope(ClassDeclaration node) { // // Process field declarations before constructors and methods so that the types of field formal // parameters can be correctly resolved. // final ArrayList<ClassMember> nonFields = new ArrayList<ClassMember>(); node.visitChildren(new UnifyingAstVisitor<Void>() { @Override public Void visitConstructorDeclaration(ConstructorDeclaration node) { nonFields.add(node); return null; } @Override public Void visitExtendsClause(ExtendsClause node) { // This node was already visited. return null; } @Override public Void visitImplementsClause(ImplementsClause node) { // This node was already visited. return null; } @Override public Void visitMethodDeclaration(MethodDeclaration node) { nonFields.add(node); return null; } @Override public Void visitNode(AstNode node) { return node.accept(TypeResolverVisitor.this); } @Override public Void visitWithClause(WithClause node) { // This node was already visited. return null; } }); int count = nonFields.size(); for (int i = 0; i < count; i++) { nonFields.get(i).accept(this); } } /** * Given a type name representing the return type of a function, compute the return type of the * function. * * @param returnType the type name representing the return type of the function * @return the return type that was computed */ private Type computeReturnType(TypeName returnType) { if (returnType == null) { return dynamicType; } else { return returnType.getType(); } } /** * Return the class element that represents the class whose name was provided. * * @param identifier the name from the declaration of a class * @return the class element that represents the class */ private ClassElementImpl getClassElement(SimpleIdentifier identifier) { // TODO(brianwilkerson) Seems like we should be using ClassDeclaration.getElement(). if (identifier == null) { // TODO(brianwilkerson) Report this // Internal error: We should never build a class declaration without a name. return null; } Element element = identifier.getStaticElement(); if (!(element instanceof ClassElementImpl)) { // TODO(brianwilkerson) Report this // Internal error: Failed to create an element for a class declaration. return null; } return (ClassElementImpl) element; } /** * Return an array containing all of the elements associated with the parameters in the given * list. * * @param parameterList the list of parameters whose elements are to be returned * @return the elements associated with the parameters */ private ParameterElement[] getElements(FormalParameterList parameterList) { ArrayList<ParameterElement> elements = new ArrayList<ParameterElement>(); for (FormalParameter parameter : parameterList.getParameters()) { ParameterElement element = (ParameterElement) parameter.getIdentifier().getStaticElement(); // TODO(brianwilkerson) Understand why the element would be null. if (element != null) { elements.add(element); } } return elements.toArray(new ParameterElement[elements.size()]); } /** * The number of type arguments in the given type name does not match the number of parameters in * the corresponding class element. Return the error code that should be used to report this * error. * * @param node the type name with the wrong number of type arguments * @return the error code that should be used to report that the wrong number of type arguments * were provided */ private ErrorCode getInvalidTypeParametersErrorCode(TypeName node) { AstNode parent = node.getParent(); if (parent instanceof ConstructorName) { parent = parent.getParent(); if (parent instanceof InstanceCreationExpression) { if (((InstanceCreationExpression) parent).isConst()) { return CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS; } else { return StaticWarningCode.NEW_WITH_INVALID_TYPE_PARAMETERS; } } } return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS; } /** * Checks if the given type name is the target in a redirected constructor. * * @param typeName the type name to analyze * @return some {@link RedirectingConstructorKind} if the given type name is used as the type in a * redirected constructor, or {@code null} otherwise */ private RedirectingConstructorKind getRedirectingConstructorKind(TypeName typeName) { AstNode parent = typeName.getParent(); if (parent instanceof ConstructorName) { ConstructorName constructorName = (ConstructorName) parent; parent = constructorName.getParent(); if (parent instanceof ConstructorDeclaration) { ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) parent; if (constructorDeclaration.getRedirectedConstructor() == constructorName) { if (constructorDeclaration.getConstKeyword() != null) { return RedirectingConstructorKind.CONST; } return RedirectingConstructorKind.NORMAL; } } } return null; } /** * Return the type represented by the given type name. * * @param typeName the type name representing the type to be returned * @return the type represented by the type name */ private Type getType(TypeName typeName) { Type type = typeName.getType(); if (type == null) { return dynamicType; } return type; } /** * Return the type arguments associated with the given type. * * @param type the type whole type arguments are to be returned * @return the type arguments associated with the given type */ private Type[] getTypeArguments(Type type) { if (type instanceof InterfaceType) { return ((InterfaceType) type).getTypeArguments(); } else if (type instanceof FunctionType) { return ((FunctionType) type).getTypeArguments(); } return TypeImpl.EMPTY_ARRAY; } /** * Returns the simple identifier of the given (may be qualified) type name. * * @param typeName the (may be qualified) qualified type name * @return the simple identifier of the given (may be qualified) type name. */ private SimpleIdentifier getTypeSimpleIdentifier(Identifier typeName) { if (typeName instanceof SimpleIdentifier) { return (SimpleIdentifier) typeName; } else { return ((PrefixedIdentifier) typeName).getIdentifier(); } } /** * Given the multiple elements to which a single name could potentially be resolved, return the * single interface type that should be used, or {@code null} if there is no clear choice. * * @param elements the elements to which a single name could potentially be resolved * @return the single interface type that should be used for the type name */ private InterfaceType getTypeWhenMultiplyDefined(Element[] elements) { InterfaceType type = null; for (Element element : elements) { if (element instanceof ClassElement) { if (type != null) { return null; } type = ((ClassElement) element).getType(); } } return type; } /** * Checks if the given type name is used as the type in an as expression. * * @param typeName the type name to analyzer * @return {@code true} if the given type name is used as the type in an as expression */ private boolean isTypeNameInAsExpression(TypeName typeName) { AstNode parent = typeName.getParent(); if (parent instanceof AsExpression) { AsExpression asExpression = (AsExpression) parent; return asExpression.getType() == typeName; } return false; } /** * Checks if the given type name is used as the exception type in a catch clause. * * @param typeName the type name to analyzer * @return {@code true} if the given type name is used as the exception type in a catch clause */ private boolean isTypeNameInCatchClause(TypeName typeName) { AstNode parent = typeName.getParent(); if (parent instanceof CatchClause) { CatchClause catchClause = (CatchClause) parent; return catchClause.getExceptionType() == typeName; } return false; } /** * Checks if the given type name is used as the type in an instance creation expression. * * @param typeName the type name to analyzer * @return {@code true} if the given type name is used as the type in an instance creation * expression */ private boolean isTypeNameInInstanceCreationExpression(TypeName typeName) { AstNode parent = typeName.getParent(); if (parent instanceof ConstructorName && parent.getParent() instanceof InstanceCreationExpression) { ConstructorName constructorName = (ConstructorName) parent; return constructorName != null && constructorName.getType() == typeName; } return false; } /** * Checks if the given type name is used as the type in an is expression. * * @param typeName the type name to analyzer * @return {@code true} if the given type name is used as the type in an is expression */ private boolean isTypeNameInIsExpression(TypeName typeName) { AstNode parent = typeName.getParent(); if (parent instanceof IsExpression) { IsExpression isExpression = (IsExpression) parent; return isExpression.getType() == typeName; } return false; } /** * Checks if the given type name used in a type argument list. * * @param typeName the type name to analyzer * @return {@code true} if the given type name is in a type argument list */ private boolean isTypeNameInTypeArgumentList(TypeName typeName) { return typeName.getParent() instanceof TypeArgumentList; } /** * Record that the static type of the given node is the given type. * * @param expression the node whose type is to be recorded * @param type the static type of the node */ private Void recordType(Expression expression, Type type) { if (type == null) { expression.setStaticType(dynamicType); } else { expression.setStaticType(type); } return null; } /** * Resolve the types in the given with and implements clauses and associate those types with the * given class element. * * @param classElement the class element with which the mixin and interface types are to be * associated * @param withClause the with clause to be resolved * @param implementsClause the implements clause to be resolved */ private void resolve(ClassElementImpl classElement, WithClause withClause, ImplementsClause implementsClause) { if (withClause != null) { InterfaceType[] mixinTypes = resolveTypes( withClause.getMixinTypes(), CompileTimeErrorCode.MIXIN_OF_NON_CLASS, CompileTimeErrorCode.MIXIN_OF_ENUM, CompileTimeErrorCode.MIXIN_OF_NON_CLASS); if (classElement != null) { classElement.setMixins(mixinTypes); } } if (implementsClause != null) { NodeList<TypeName> interfaces = implementsClause.getInterfaces(); InterfaceType[] interfaceTypes = resolveTypes( interfaces, CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, CompileTimeErrorCode.IMPLEMENTS_ENUM, CompileTimeErrorCode.IMPLEMENTS_DYNAMIC); if (classElement != null) { classElement.setInterfaces(interfaceTypes); } // TODO(brianwilkerson) Move the following checks to ErrorVerifier. TypeName[] typeNames = interfaces.toArray(new TypeName[interfaces.size()]); boolean[] detectedRepeatOnIndex = new boolean[typeNames.length]; for (int i = 0; i < detectedRepeatOnIndex.length; i++) { detectedRepeatOnIndex[i] = false; } for (int i = 0; i < typeNames.length; i++) { TypeName typeName = typeNames[i]; if (!detectedRepeatOnIndex[i]) { Element element = typeName.getName().getStaticElement(); for (int j = i + 1; j < typeNames.length; j++) { TypeName typeName2 = typeNames[j]; Identifier identifier2 = typeName2.getName(); String name2 = identifier2.getName(); Element element2 = identifier2.getStaticElement(); if (element != null && element.equals(element2)) { detectedRepeatOnIndex[j] = true; reportErrorForNode(CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, name2); } } } } } } /** * Return the type specified by the given name. * * @param typeName the type name specifying the type to be returned * @param nonTypeError the error to produce if the type name is defined to be something other than * a type * @param enumTypeError the error to produce if the type name is defined to be an enum * @param dynamicTypeError the error to produce if the type name is "dynamic" * @return the type specified by the type name */ private InterfaceType resolveType(TypeName typeName, ErrorCode nonTypeError, ErrorCode enumTypeError, ErrorCode dynamicTypeError) { Type type = typeName.getType(); if (type instanceof InterfaceType) { ClassElement element = ((InterfaceType) type).getElement(); if (element != null && element.isEnum()) { reportErrorForNode(enumTypeError, typeName); return null; } return (InterfaceType) type; } // If the type is not an InterfaceType, then visitTypeName() sets the type to be a DynamicTypeImpl Identifier name = typeName.getName(); if (name.getName().equals(Keyword.DYNAMIC.getSyntax())) { reportErrorForNode(dynamicTypeError, name, name.getName()); } else { reportErrorForNode(nonTypeError, name, name.getName()); } return null; } /** * Resolve the types in the given list of type names. * * @param typeNames the type names to be resolved * @param nonTypeError the error to produce if the type name is defined to be something other than * a type * @param enumTypeError the error to produce if the type name is defined to be an enum * @param dynamicTypeError the error to produce if the type name is "dynamic" * @return an array containing all of the types that were resolved. */ private InterfaceType[] resolveTypes(NodeList<TypeName> typeNames, ErrorCode nonTypeError, ErrorCode enumTypeError, ErrorCode dynamicTypeError) { ArrayList<InterfaceType> types = new ArrayList<InterfaceType>(); for (TypeName typeName : typeNames) { InterfaceType type = resolveType(typeName, nonTypeError, enumTypeError, dynamicTypeError); if (type != null) { types.add(type); } } return types.toArray(new InterfaceType[types.size()]); } private void setElement(Identifier typeName, Element element) { if (element != null) { if (typeName instanceof SimpleIdentifier) { ((SimpleIdentifier) typeName).setStaticElement(element); } else if (typeName instanceof PrefixedIdentifier) { PrefixedIdentifier identifier = (PrefixedIdentifier) typeName; identifier.getIdentifier().setStaticElement(element); SimpleIdentifier prefix = identifier.getPrefix(); Element prefixElement = getNameScope().lookup(prefix, getDefiningLibrary()); if (prefixElement != null) { prefix.setStaticElement(prefixElement); } } } } /** * Given a parameter element, create a function type based on the given return type and parameter * list and associate the created type with the element. * * @param element the parameter element whose type is to be set * @param returnType the (possibly {@code null}) return type of the function * @param parameterList the list of parameters to the function */ private void setFunctionTypedParameterType(ParameterElementImpl element, TypeName returnType, FormalParameterList parameterList) { ParameterElement[] parameters = getElements(parameterList); FunctionTypeAliasElementImpl aliasElement = new FunctionTypeAliasElementImpl(null); aliasElement.setSynthetic(true); aliasElement.shareParameters(parameters); aliasElement.setReturnType(computeReturnType(returnType)); // FunctionTypeAliasElementImpl assumes the enclosing element is a // CompilationUnitElement (because non-synthetic function types can only be declared // at top level), so to avoid breaking things, go find the compilation unit element. aliasElement.setEnclosingElement(element.getAncestor(CompilationUnitElement.class)); FunctionTypeImpl type = new FunctionTypeImpl(aliasElement); ClassElement definingClass = element.getAncestor(ClassElement.class); if (definingClass != null) { aliasElement.shareTypeParameters(definingClass.getTypeParameters()); type.setTypeArguments(definingClass.getType().getTypeArguments()); } else { FunctionTypeAliasElement alias = element.getAncestor(FunctionTypeAliasElement.class); while (alias != null && alias.isSynthetic()) { alias = alias.getAncestor(FunctionTypeAliasElement.class); } if (alias != null) { aliasElement.setTypeParameters(alias.getTypeParameters()); type.setTypeArguments(alias.getType().getTypeArguments()); } else { type.setTypeArguments(TypeImpl.EMPTY_ARRAY); } } element.setType(type); } }