Index: compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
diff --git a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
index 17f1d83f5f87639ef053ee3d5a2031ba44f32502..d50d7fef35511437fba36cceb6f7830719727fca 100644 |
--- a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
+++ b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
@@ -119,42 +119,189 @@ import java.util.concurrent.ConcurrentHashMap; |
* Analyzer of static type information. |
*/ |
public class TypeAnalyzer implements DartCompilationPhase { |
- private final ConcurrentHashMap<ClassElement, List<Element>> unimplementedElements = |
- new ConcurrentHashMap<ClassElement, List<Element>>(); |
- private final Set<ClassElement> diagnosedAbstractClasses = |
- Collections.newSetFromMap(new ConcurrentHashMap<ClassElement, Boolean>()); |
+ @VisibleForTesting |
+ static class Analyzer implements DartPlainVisitor<Type> { |
+ private class AbstractMethodFinder extends DartNodeTraverser<Void> { |
+ private final InterfaceType currentClass; |
+ private final Multimap<String, Element> superMembers; |
+ private final List<Element> unimplementedElements; |
- /** |
- * Perform type analysis on the given AST rooted at <code>node</code>. |
- * |
- * @param node The root of the tree to analyze |
- * @param typeProvider The source of pre-defined type definitions |
- * @param context The compilation context (DartCompilerContext) |
- * @param currentClass The class that contains <code>node</code>. Will be null |
- * for top-level declarations. |
- * @return The type of <code>node</code>. |
- */ |
- public static Type analyze(DartNode node, CoreTypeProvider typeProvider, |
- DartCompilerContext context, InterfaceType currentClass) { |
- ConcurrentHashMap<ClassElement, List<Element>> unimplementedElements = |
- new ConcurrentHashMap<ClassElement, List<Element>>(); |
- Set<ClassElement> diagnosed = |
- Collections.newSetFromMap(new ConcurrentHashMap<ClassElement, Boolean>()); |
- Analyzer analyzer = new Analyzer(context, typeProvider, unimplementedElements, diagnosed); |
- analyzer.setCurrentClass(currentClass); |
- return node.accept(analyzer); |
- } |
+ private AbstractMethodFinder(InterfaceType currentClass) { |
+ this.currentClass = currentClass; |
+ this.superMembers = LinkedListMultimap.create(); |
+ this.unimplementedElements = new ArrayList<Element>(); |
+ } |
- @Override |
- public DartUnit exec(DartUnit unit, DartCompilerContext context, |
- CoreTypeProvider typeProvider) { |
- unit.accept(new Analyzer(context, typeProvider, unimplementedElements, |
- diagnosedAbstractClasses)); |
- return unit; |
- } |
+ /** |
+ * Report a compile-time error if either modifiers or elements.getModifiers() is static. |
+ * @returns true if no compile-time error was reported |
+ */ |
+ private boolean canOverride(DartExpression node, Modifiers modifiers, Element element) { |
+ if (element.getModifiers().isStatic()) { |
+ onError(node, ResolverErrorCode.CANNOT_OVERRIDE_STATIC_MEMBER, |
+ element.getName(), element.getEnclosingElement().getName()); |
+ return false; |
+ } else if (modifiers.isStatic()) { |
+ onError(node, ResolverErrorCode.CANNOT_OVERRIDE_INSTANCE_MEMBER, |
+ element.getName(), element.getEnclosingElement().getName()); |
+ return false; |
+ } |
+ return true; |
+ } |
- @VisibleForTesting |
- static class Analyzer implements DartPlainVisitor<Type> { |
+ /** |
+ * Report a static type error if member cannot override superElement, that is, they are not |
+ * assignable. |
+ */ |
+ private void checkOverride(DartExpression node, Element member, Element superElement) { |
+ String name = member.getName(); |
+ Type superMember = typeAsMemberOf(superElement, currentClass); |
+ if (member.getKind() == ElementKind.METHOD |
+ && superElement.getKind() == ElementKind.METHOD) { |
+ if (!types.isSubtype(member.getType(), superMember)) { |
+ typeError(node, TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE, |
+ name, superElement.getEnclosingElement().getName(), |
+ member.getType(), superMember); |
+ } |
+ } else if (!types.isAssignable(superMember, member.getType())) { |
+ typeError(node, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, |
+ name, superElement.getEnclosingElement().getName(), |
+ member.getType(), superMember); |
+ } |
+ } |
+ |
+ @Override |
+ public Void visitClass(DartClass node) { |
+ assert node.getSymbol().getType() == currentClass; |
+ |
+ // Prepare supertypes - all superclasses and interfaces. |
+ List<InterfaceType> supertypes = Collections.emptyList(); |
+ boolean hasCyclicDeclaration = false; |
+ try { |
+ supertypes = currentClass.getElement().getAllSupertypes(); |
+ } catch (CyclicDeclarationException e) { |
+ // Already reported by resolver. |
+ hasCyclicDeclaration = true; |
+ } catch (DuplicatedInterfaceException e) { |
+ // Already reported by resolver. |
+ } |
+ |
+ // Add all super members to resolve. |
+ EnclosingElement currentLibrary = currentClass.getElement().getEnclosingElement(); |
+ for (InterfaceType supertype : supertypes) { |
+ for (Element member : supertype.getElement().getMembers()) { |
+ String name = member.getName(); |
+ if (name.startsWith("_")) { |
+ if (currentLibrary != member.getEnclosingElement().getEnclosingElement()) { |
+ continue; |
+ } |
+ } |
+ superMembers.put(name, member); |
+ } |
+ } |
+ |
+ // Visit members, so resolve methods declared in this class. |
+ this.visit(node.getMembers()); |
+ |
+ // If interface, we don't care about unimplemented methods. |
+ if (currentClass.getElement().isInterface()) { |
+ return null; |
+ } |
+ |
+ // If we have cyclic declaration, hierarchy is broken, no reason to report unimplemented. |
+ if (hasCyclicDeclaration) { |
+ return null; |
+ } |
+ |
+ // Visit superclasses (without interfaces) and mark methods as implemented. |
+ InterfaceType supertype = currentClass.getElement().getSupertype(); |
+ while (supertype != null) { |
+ ClassElement superclass = supertype.getElement(); |
+ for (Element member : superclass.getMembers()) { |
+ superMembers.removeAll(member.getName()); |
+ } |
+ supertype = supertype.getElement().getSupertype(); |
+ } |
+ |
+ // All remaining methods are unimplemented. |
+ for (String name : superMembers.keys()) { |
+ Collection<Element> elements = superMembers.removeAll(name); |
+ for (Element element : elements) { |
+ if (!element.getModifiers().isStatic()) { |
+ unimplementedElements.add(element); |
+ break; // Only report the first unimplemented element with this name. |
+ } |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ @Override |
+ public Void visitField(DartField node) { |
+ if (superMembers != null) { |
+ FieldElement field = node.getSymbol(); |
+ String name = field.getName(); |
+ List<Element> overridden = new ArrayList<Element>(superMembers.removeAll(name)); |
+ for (Element element : overridden) { |
+ if (canOverride(node.getName(), field.getModifiers(), element)) { |
+ switch (element.getKind()) { |
+ case FIELD: |
+ checkOverride(node.getName(), field, element); |
+ break; |
+ case METHOD: |
+ typeError(node, TypeErrorCode.SUPERTYPE_HAS_METHOD, name, |
+ element.getEnclosingElement().getName()); |
+ break; |
+ |
+ default: |
+ typeError(node, TypeErrorCode.INTERNAL_ERROR, element); |
+ break; |
+ } |
+ } |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ @Override |
+ public Void visitFieldDefinition(DartFieldDefinition node) { |
+ this.visit(node.getFields()); |
+ return null; |
+ } |
+ |
+ @Override |
+ public Void visitMethodDefinition(DartMethodDefinition node) { |
+ MethodElement method = node.getSymbol(); |
+ String name = method.getName(); |
+ if (superMembers != null && !method.isConstructor()) { |
+ Collection<Element> overridden = superMembers.removeAll(name); |
+ for (Element element : overridden) { |
+ if (canOverride(node.getName(), method.getModifiers(), element)) { |
+ switch (element.getKind()) { |
+ case METHOD: |
+ checkOverride(node.getName(), method, element); |
+ break; |
+ |
+ case FIELD: |
+ typeError(node, TypeErrorCode.SUPERTYPE_HAS_FIELD, element.getName(), |
+ element.getEnclosingElement().getName()); |
+ break; |
+ |
+ default: |
+ typeError(node, TypeErrorCode.INTERNAL_ERROR, element); |
+ break; |
+ } |
+ } |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ @Override |
+ public Void visitNode(DartNode node) { |
+ throw new AssertionError(); |
+ } |
+ } |
private final DynamicType dynamicType; |
private final Type stringType; |
private final InterfaceType defaultLiteralMapType; |
@@ -170,7 +317,9 @@ public class TypeAnalyzer implements DartCompilationPhase { |
private final InterfaceType intType; |
private final Type nullType; |
private final InterfaceType functionType; |
+ |
private final InterfaceType dynamicIteratorType; |
+ private final CoreTypeProvider typeProvider; |
/** |
* Keeps track of the number of nested catches, used to detect re-throws |
@@ -178,10 +327,10 @@ public class TypeAnalyzer implements DartCompilationPhase { |
*/ |
private int catchDepth = 0; |
- |
Analyzer(DartCompilerContext context, CoreTypeProvider typeProvider, |
ConcurrentHashMap<ClassElement, List<Element>> unimplementedElements, |
Set<ClassElement> diagnosedAbstractClasses) { |
+ this.typeProvider = typeProvider; |
this.context = context; |
this.unimplementedElements = unimplementedElements; |
this.diagnosedAbstractClasses = diagnosedAbstractClasses; |
@@ -198,253 +347,57 @@ public class TypeAnalyzer implements DartCompilationPhase { |
this.dynamicIteratorType = typeProvider.getIteratorType(dynamicType); |
} |
- @VisibleForTesting |
- void setCurrentClass(InterfaceType type) { |
- currentClass = type; |
- } |
- |
- private InterfaceType getCurrentClass() { |
- return currentClass; |
- } |
- |
- private DynamicType typeError(DartNode node, ErrorCode code, Object... arguments) { |
- onError(node, code, arguments); |
- return dynamicType; |
- } |
- |
- private void onError(DartNode node, ErrorCode code, Object... arguments) { |
- context.onError(new DartCompilationError(node, code, arguments)); |
+ private List<Type> analyzeArgumentTypes(List<? extends DartExpression> argumentNodes) { |
+ List<Type> argumentTypes = new ArrayList<Type>(argumentNodes.size()); |
+ for (DartExpression argumentNode : argumentNodes) { |
+ argumentTypes.add(nonVoidTypeOf(argumentNode)); |
+ } |
+ return argumentTypes; |
} |
- AssertionError internalError(DartNode node, String message, Object... arguments) { |
- message = String.format(message, arguments); |
- context.onError(new DartCompilationError(node, TypeErrorCode.INTERNAL_ERROR, |
- message)); |
- return new AssertionError("Internal error: " + message); |
+ private Type analyzeBinaryOperator(ElementReference node, Type lhs, Token operator, |
+ DartNode diagnosticNode, DartExpression rhs) { |
+ Type rhsType = nonVoidTypeOf(rhs); |
+ String methodName = methodNameForBinaryOperator(operator); |
+ Member member = lookupMember(lhs, methodName, diagnosticNode); |
+ if (member != null) { |
+ node.setReferencedElement(member.getElement()); |
+ return analyzeMethodInvocation(lhs, member, methodName, diagnosticNode, |
+ Collections.<Type>singletonList(rhsType), |
+ Collections.<DartExpression>singletonList(rhs)); |
+ } else { |
+ return dynamicType; |
+ } |
} |
- private Type typeOfLiteral(DartLiteral node) { |
- return node.getType(); |
- } |
- |
- private Token getBasicOperator(DartNode diagnosticNode, Token op) { |
- switch(op) { |
- case INC: |
- return Token.ADD; |
- case DEC: |
- return Token.SUB; |
- case ASSIGN_BIT_OR: |
- return Token.BIT_OR; |
- case ASSIGN_BIT_XOR: |
- return Token.BIT_XOR; |
- case ASSIGN_BIT_AND: |
- return Token.BIT_AND; |
- case ASSIGN_SHL: |
- return Token.SHL; |
- case ASSIGN_SAR: |
- return Token.SAR; |
- case ASSIGN_SHR: |
- return Token.SHR; |
- case ASSIGN_ADD: |
- return Token.ADD; |
- case ASSIGN_SUB: |
- return Token.SUB; |
- case ASSIGN_MUL: |
- return Token.MUL; |
- case ASSIGN_DIV: |
- return Token.DIV; |
- case ASSIGN_MOD: |
- return Token.MOD; |
- case ASSIGN_TRUNC: |
- return Token.TRUNC; |
- default: |
- internalError(diagnosticNode, "unexpected operator %s", op.name()); |
- return null; |
- } |
- } |
- |
- @Override |
- public Type visitRedirectConstructorInvocation(DartRedirectConstructorInvocation node) { |
- return checkConstructorForwarding(node, node.getSymbol()); |
- } |
- |
- private String methodNameForUnaryOperator(DartNode diagnosticNode, Token operator) { |
- if (operator == Token.SUB) { |
- return "operator negate"; |
- } else if (operator == Token.BIT_NOT) { |
- return "operator ~"; |
- } |
- return "operator " + getBasicOperator(diagnosticNode, operator).getSyntax(); |
- } |
- |
- private String methodNameForBinaryOperator(Token operator) { |
- return "operator " + operator.getSyntax(); |
- } |
- |
- private Type analyzeBinaryOperator(ElementReference node, Type lhs, Token operator, |
- DartNode diagnosticNode, DartExpression rhs) { |
- Type rhsType = nonVoidTypeOf(rhs); |
- String methodName = methodNameForBinaryOperator(operator); |
- Member member = lookupMember(lhs, methodName, diagnosticNode); |
- if (member != null) { |
- node.setReferencedElement(member.getElement()); |
- return analyzeMethodInvocation(lhs, member, methodName, diagnosticNode, |
- Collections.<Type>singletonList(rhsType), |
- Collections.<DartExpression>singletonList(rhs)); |
- } else { |
- return dynamicType; |
- } |
- } |
- |
- @Override |
- public Type visitBinaryExpression(DartBinaryExpression node) { |
- DartExpression lhsNode = node.getArg1(); |
- Type lhs = nonVoidTypeOf(lhsNode); |
- DartExpression rhsNode = node.getArg2(); |
- Token operator = node.getOperator(); |
- switch (operator) { |
- case ASSIGN: { |
- Type rhs = nonVoidTypeOf(rhsNode); |
- checkAssignable(rhsNode, lhs, rhs); |
- return rhs; |
- } |
- |
- case ASSIGN_SHR: |
- case ASSIGN_ADD: |
- case ASSIGN_SUB: |
- case ASSIGN_MUL: |
- case ASSIGN_DIV: |
- case ASSIGN_MOD: |
- case ASSIGN_TRUNC: { |
- Token basicOperator = getBasicOperator(node, operator); |
- Type type = analyzeBinaryOperator(node, lhs, basicOperator, lhsNode, rhsNode); |
- checkAssignable(node, lhs, type); |
- return type; |
- } |
- |
- case OR: |
- case AND: { |
- checkAssignable(lhsNode, boolType, lhs); |
- checkAssignable(boolType, rhsNode); |
- return boolType; |
- } |
- |
- case ASSIGN_BIT_OR: |
- case ASSIGN_BIT_XOR: |
- case ASSIGN_BIT_AND: |
- case ASSIGN_SHL: |
- case ASSIGN_SAR: { |
- // Bit operations are only supported by integers and |
- // thus cannot be looked up on num. To ease usage of |
- // bit operations, we currently allow them to be used |
- // if the left-hand-side is of type num. |
- // TODO(karlklose) find a clean solution, i.e., without a special case for num. |
- if (lhs.equals(numType)) { |
- checkAssignable(rhsNode, numType, typeOf(rhsNode)); |
- return intType; |
- } else { |
- Token basicOperator = getBasicOperator(node, operator); |
- Type type = analyzeBinaryOperator(node, lhs, basicOperator, lhsNode, rhsNode); |
- checkAssignable(node, lhs, type); |
- return type; |
+ private void analyzeFactory(DartExpression name, final ConstructorElement methodElement) { |
+ DartNodeTraverser<Void> visitor = new DartNodeTraverser<Void>() { |
+ @Override |
+ public Void visitParameterizedNode(DartParameterizedNode node) { |
+ DartExpression expression = node.getExpression(); |
+ Element e = null; |
+ if (expression instanceof DartIdentifier) { |
+ e = ((DartIdentifier) expression).getTargetSymbol(); |
+ } else if (expression instanceof DartPropertyAccess) { |
+ e = ((DartPropertyAccess) expression).getTargetSymbol(); |
} |
- } |
- |
- case BIT_OR: |
- case BIT_XOR: |
- case BIT_AND: |
- case SHL: |
- case SAR: { |
- // Bit operations are only supported by integers and |
- // thus cannot be looked up on num. To ease usage of |
- // bit operations, we currently allow them to be used |
- // if the left-hand-side is of type num. |
- // TODO(karlklose) find a clean solution, i.e., without a special case for num. |
- if (lhs.equals(numType)) { |
- checkAssignable(rhsNode, numType, typeOf(rhsNode)); |
- return intType; |
- } else { |
- return analyzeBinaryOperator(node, lhs, operator, lhsNode, rhsNode); |
+ if (!ElementKind.of(e).equals(ElementKind.CLASS)) { |
+ return null; |
+ } |
+ ClassElement cls = (ClassElement) e; |
+ InterfaceType type = cls.getType(); |
+ List<DartTypeParameter> parameterNodes = node.getTypeParameters(); |
+ List<? extends Type> arguments = type.getArguments(); |
+ if (parameterNodes.size() == 0) { |
+ return null; |
} |
+ Analyzer.this.visit(parameterNodes); |
+ List<TypeVariable> typeVariables = methodElement.getFunctionType().getTypeVariables(); |
+ validateBounds(parameterNodes, arguments, typeVariables, true); |
+ return null; |
} |
- |
- case SHR: |
- case ADD: |
- case SUB: |
- case MUL: |
- case DIV: |
- case TRUNC: |
- case MOD: |
- case LT: |
- case GT: |
- case LTE: |
- case GTE: |
- return analyzeBinaryOperator(node, lhs, operator, lhsNode, rhsNode); |
- |
- case EQ: |
- case NE: |
- case EQ_STRICT: |
- case NE_STRICT: |
- nonVoidTypeOf(rhsNode); |
- return boolType; |
- |
- case IS: |
- if (rhsNode instanceof DartUnaryExpression) { |
- assert ((DartUnaryExpression) rhsNode).getOperator() == Token.NOT; |
- nonVoidTypeOf(((DartUnaryExpression) rhsNode).getArg()); |
- } else { |
- nonVoidTypeOf(rhsNode); |
- } |
- return boolType; |
- |
- case COMMA: |
- return typeOf(rhsNode); |
- |
- default: |
- throw new AssertionError("Unknown operator: " + operator); |
- } |
- } |
- |
- @Override |
- public Type visitVariableStatement(DartVariableStatement node) { |
- Type type = typeOf(node.getTypeNode()); |
- visit(node.getVariables()); |
- return type; |
- } |
- |
- private List<Type> analyzeArgumentTypes(List<? extends DartExpression> argumentNodes) { |
- List<Type> argumentTypes = new ArrayList<Type>(argumentNodes.size()); |
- for (DartExpression argumentNode : argumentNodes) { |
- argumentTypes.add(nonVoidTypeOf(argumentNode)); |
- } |
- return argumentTypes; |
- } |
- |
- private Member lookupMember(Type receiver, String methodName, DartNode diagnosticNode) { |
- InterfaceType itype = types.getInterfaceType(receiver); |
- if (itype == null) { |
- diagnoseNonInterfaceType(diagnosticNode, receiver); |
- return null; |
- } |
- Member member = itype.lookupMember(methodName); |
- if (member == null) { |
- typeError(diagnosticNode, TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, |
- receiver, methodName); |
- return null; |
- } |
- return member; |
- } |
- |
- private void checkAssignable(DartNode node, Type t, Type s) { |
- t.getClass(); // Null check. |
- s.getClass(); // Null check. |
- if (!types.isAssignable(t, s)) { |
- typeError(node, TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); |
- } |
- } |
- |
- private void checkAssignable(Type targetType, DartExpression node) { |
- checkAssignable(node, targetType, nonVoidTypeOf(node)); |
+ }; |
+ name.accept(visitor); |
} |
private Type analyzeMethodInvocation(Type receiver, Member member, String name, |
@@ -493,29 +446,6 @@ public class TypeAnalyzer implements DartCompilationPhase { |
return checkArguments(diagnosticNode, argumentNodes, argumentTypes.iterator(), ftype); |
} |
- private Type diagnoseNonInterfaceType(DartNode node, Type type) { |
- switch (TypeKind.of(type)) { |
- case DYNAMIC: |
- return type; |
- |
- case FUNCTION: |
- case FUNCTION_ALIAS: |
- case INTERFACE: |
- case VARIABLE: |
- // Cannot happen. |
- throw internalError(node, type.toString()); |
- |
- case NONE: |
- throw internalError(node, "type is null"); |
- |
- case VOID: |
- return typeError(node, TypeErrorCode.VOID); |
- |
- default: |
- throw internalError(node, type.getKind().name()); |
- } |
- } |
- |
private Type checkArguments(DartNode diagnosticNode, |
List<? extends DartExpression> argumentNodes, |
Iterator<Type> argumentTypes, FunctionType ftype) { |
@@ -547,150 +477,24 @@ public class TypeAnalyzer implements DartCompilationPhase { |
return ftype.getReturnType(); |
} |
- @Override |
- public Type visitTypeNode(DartTypeNode node) { |
- return validateTypeNode(node, false); |
- } |
- |
- private Type validateTypeNode(DartTypeNode node, boolean badBoundIsError) { |
- Type type = node.getType(); // Already calculated by resolver. |
- switch (TypeKind.of(type)) { |
- case NONE: |
- return typeError(node, TypeErrorCode.INTERNAL_ERROR, |
- String.format("type \"%s\" is null", node)); |
- |
- case INTERFACE: { |
- InterfaceType itype = (InterfaceType) type; |
- validateBounds(node.getTypeArguments(), |
- itype.getArguments(), itype.getElement().getTypeParameters(), |
- badBoundIsError); |
- return itype; |
- } |
- |
- default: |
- return type; |
- } |
- } |
- |
- private void validateBounds(List<? extends DartNode> diagnosticNodes, |
- List<? extends Type> arguments, |
- List<? extends Type> parameters, |
- boolean badBoundIsError) { |
- if (arguments.size() == parameters.size() && arguments.size() == diagnosticNodes.size()) { |
- List<Type> bounds = new ArrayList<Type>(parameters.size()); |
- for (Type parameter : parameters) { |
- TypeVariable variable = (TypeVariable) parameter; |
- Type bound = variable.getTypeVariableElement().getBound(); |
- if (bound == null) { |
- internalError(variable.getElement().getNode(), "bound is null"); |
- } |
- bounds.add(bound); |
- } |
- bounds = Types.subst(bounds, arguments, parameters); |
- for (int i = 0; i < arguments.size(); i++) { |
- Type t = bounds.get(i); |
- Type s = arguments.get(i); |
- if (!types.isAssignable(t, s)) { |
- if (badBoundIsError) { |
- onError(diagnosticNodes.get(i), |
- ResolverErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); |
- } else { |
- onError(diagnosticNodes.get(i), |
- TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); |
- } |
- } |
- } |
+ private void checkAssignable(DartNode node, Type targetType, Type sourceType) { |
+ targetType.getClass(); // Null check. |
+ sourceType.getClass(); // Null check. |
+ if (!types.isAssignable(targetType, sourceType)) { |
+ typeError(node, TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, sourceType, targetType); |
} |
} |
- Type typeOf(DartNode node) { |
- if (node == null) { |
- return dynamicType; |
- } |
- return node.accept(this); |
+ private void checkAssignable(Type targetType, DartExpression node) { |
+ checkAssignable(node, targetType, nonVoidTypeOf(node)); |
} |
- private Type nonVoidTypeOf(DartNode node) { |
- Type type = typeOf(node); |
- if (type.getKind().equals(TypeKind.VOID)) { |
- return typeError(node, TypeErrorCode.VOID); |
- } |
+ private Type checkCondition(DartExpression condition) { |
+ Type type = nonVoidTypeOf(condition); |
+ checkAssignable(condition, boolType, type); |
return type; |
} |
- @Override |
- public Type visitArrayAccess(DartArrayAccess node) { |
- Type target = typeOf(node.getTarget()); |
- return analyzeBinaryOperator(node, target, Token.INDEX, node, node.getKey()); |
- } |
- |
- @Override |
- public Type visitAssertion(DartAssertion node) { |
- DartExpression conditionNode = node.getExpression(); |
- Type condition = nonVoidTypeOf(conditionNode); |
- switch (condition.getKind()) { |
- case FUNCTION: |
- FunctionType ftype = (FunctionType) condition; |
- Type returnType = ftype.getReturnType(); |
- if (returnType.getKind().equals(TypeKind.VOID)) { |
- typeError(conditionNode, TypeErrorCode.VOID); |
- } |
- checkAssignable(conditionNode, boolType, returnType); |
- break; |
- |
- default: |
- checkAssignable(conditionNode, boolType, condition); |
- break; |
- } |
- return voidType; |
- } |
- |
- @Override |
- public Type visitBlock(DartBlock node) { |
- return typeAsVoid(node); |
- } |
- |
- private Type typeAsVoid(DartNode node) { |
- node.visitChildren(this); |
- return voidType; |
- } |
- |
- @Override |
- public Type visitBreakStatement(DartBreakStatement node) { |
- return voidType; |
- } |
- |
- @Override |
- public Type visitFunctionObjectInvocation(DartFunctionObjectInvocation node) { |
- node.setReferencedElement(functionType.getElement()); |
- return checkInvocation(node, node, null, typeOf(node.getTarget())); |
- } |
- |
- @Override |
- public Type visitMethodInvocation(DartMethodInvocation node) { |
- String name = node.getFunctionNameString(); |
- Element element = (Element) node.getTargetSymbol(); |
- if (element != null && (element.getModifiers().isStatic() |
- || Elements.isTopLevel(element))) { |
- node.setReferencedElement(element); |
- return checkInvocation(node, node, name, element.getType()); |
- } |
- Type receiver = nonVoidTypeOf(node.getTarget()); |
- List<DartExpression> arguments = node.getArgs(); |
- Member member = lookupMember(receiver, name, node); |
- if (member != null) { |
- node.setReferencedElement(member.getElement()); |
- } |
- return analyzeMethodInvocation(receiver, member, name, |
- node.getFunctionName(), analyzeArgumentTypes(arguments), |
- arguments); |
- } |
- |
- @Override |
- public Type visitSuperConstructorInvocation(DartSuperConstructorInvocation node) { |
- return checkConstructorForwarding(node, node.getSymbol()); |
- } |
- |
private Type checkConstructorForwarding(DartInvocation node, ConstructorElement element) { |
if (element == null) { |
visit(node.getArgs()); |
@@ -702,33 +506,62 @@ public class TypeAnalyzer implements DartCompilationPhase { |
} |
} |
- @Override |
- public Type visitCase(DartCase node) { |
- node.visitChildren(this); |
+ private Type checkInitializedDeclaration(DartDeclaration<?> node, DartExpression value) { |
+ if (value != null) { |
+ checkAssignable(node.getSymbol().getType(), value); |
+ } |
return voidType; |
} |
- @Override |
- public Type visitClass(DartClass node) { |
- ClassElement element = node.getSymbol(); |
- InterfaceType type = element.getType(); |
- findUnimplementedMembers(element); |
- setCurrentClass(type); |
- visit(node.getTypeParameters()); |
- if (node.getSuperclass() != null) { |
- validateTypeNode(node.getSuperclass(), true); |
+ private Type checkInvocation(DartInvocation node, DartNode diagnosticNode, String name, |
+ Type type) { |
+ List<? extends DartExpression> argumentNodes = node.getArgs(); |
+ List<Type> argumentTypes = new ArrayList<Type>(argumentNodes.size()); |
+ for (DartExpression argumentNode : argumentNodes) { |
+ argumentTypes.add(nonVoidTypeOf(argumentNode)); |
} |
- if (node.getInterfaces() != null) { |
- for (DartTypeNode interfaceNode : node.getInterfaces()) { |
- validateTypeNode(interfaceNode, true); |
- } |
+ switch (TypeKind.of(type)) { |
+ case FUNCTION_ALIAS: |
+ return checkArguments(node, argumentNodes, argumentTypes.iterator(), |
+ types.asFunctionType((FunctionAliasType) type)); |
+ case FUNCTION: |
+ return checkArguments(node, argumentNodes, argumentTypes.iterator(), (FunctionType) type); |
+ case DYNAMIC: |
+ return type; |
+ default: |
+ if (types.isAssignable(functionType, type)) { |
+ // A subtype of interface Function. |
+ return dynamicType; |
+ } else if (name == null) { |
+ return typeError(diagnosticNode, TypeErrorCode.NOT_A_FUNCTION, type); |
+ } else { |
+ return typeError(diagnosticNode, TypeErrorCode.NOT_A_METHOD_IN, name, |
+ currentClass); |
+ } |
} |
- if (node.getDefaultClass() != null) { |
- validateTypeNode(node.getDefaultClass(), true); |
+ } |
+ |
+ private Type diagnoseNonInterfaceType(DartNode node, Type type) { |
+ switch (TypeKind.of(type)) { |
+ case DYNAMIC: |
+ return type; |
+ |
+ case FUNCTION: |
+ case FUNCTION_ALIAS: |
+ case INTERFACE: |
+ case VARIABLE: |
+ // Cannot happen. |
+ throw internalError(node, type.toString()); |
+ |
+ case NONE: |
+ throw internalError(node, "type is null"); |
+ |
+ case VOID: |
+ return typeError(node, TypeErrorCode.VOID); |
+ |
+ default: |
+ throw internalError(node, type.getKind().name()); |
} |
- visit(node.getMembers()); |
- setCurrentClass(null); |
- return type; |
} |
private List<Element> findUnimplementedMembers(ClassElement element) { |
@@ -752,885 +585,1056 @@ public class TypeAnalyzer implements DartCompilationPhase { |
} |
} |
- @Override |
- public Type visitConditional(DartConditional node) { |
- checkCondition(node.getCondition()); |
- Type left = typeOf(node.getThenExpression()); |
- Type right = typeOf(node.getElseExpression()); |
- return types.leastUpperBound(left, right); |
+ private Token getBasicOperator(DartNode diagnosticNode, Token op) { |
+ switch(op) { |
+ case INC: |
+ return Token.ADD; |
+ case DEC: |
+ return Token.SUB; |
+ case ASSIGN_BIT_OR: |
+ return Token.BIT_OR; |
+ case ASSIGN_BIT_XOR: |
+ return Token.BIT_XOR; |
+ case ASSIGN_BIT_AND: |
+ return Token.BIT_AND; |
+ case ASSIGN_SHL: |
+ return Token.SHL; |
+ case ASSIGN_SAR: |
+ return Token.SAR; |
+ case ASSIGN_SHR: |
+ return Token.SHR; |
+ case ASSIGN_ADD: |
+ return Token.ADD; |
+ case ASSIGN_SUB: |
+ return Token.SUB; |
+ case ASSIGN_MUL: |
+ return Token.MUL; |
+ case ASSIGN_DIV: |
+ return Token.DIV; |
+ case ASSIGN_MOD: |
+ return Token.MOD; |
+ case ASSIGN_TRUNC: |
+ return Token.TRUNC; |
+ default: |
+ internalError(diagnosticNode, "unexpected operator %s", op.name()); |
+ return null; |
+ } |
} |
- private Type checkCondition(DartExpression condition) { |
- Type type = nonVoidTypeOf(condition); |
- checkAssignable(condition, boolType, type); |
- return type; |
+ private InterfaceType getCurrentClass() { |
+ return currentClass; |
} |
- @Override |
- public Type visitContinueStatement(DartContinueStatement node) { |
- return voidType; |
+ AssertionError internalError(DartNode node, String message, Object... arguments) { |
+ message = String.format(message, arguments); |
+ context.onError(new DartCompilationError(node, TypeErrorCode.INTERNAL_ERROR, |
+ message)); |
+ return new AssertionError("Internal error: " + message); |
} |
- @Override |
- public Type visitDefault(DartDefault node) { |
- node.visitChildren(this); |
- return typeAsVoid(node); |
+ private Member lookupMember(Type receiver, String methodName, DartNode diagnosticNode) { |
+ InterfaceType itype = types.getInterfaceType(receiver); |
+ if (itype == null) { |
+ diagnoseNonInterfaceType(diagnosticNode, receiver); |
+ return null; |
+ } |
+ Member member = itype.lookupMember(methodName); |
+ if (member == null) { |
+ typeError(diagnosticNode, TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, |
+ receiver, methodName); |
+ return null; |
+ } |
+ return member; |
} |
- @Override |
- public Type visitDoWhileStatement(DartDoWhileStatement node) { |
- checkCondition(node.getCondition()); |
- typeOf(node.getBody()); |
- return voidType; |
+ private String methodNameForBinaryOperator(Token operator) { |
+ return "operator " + operator.getSyntax(); |
} |
- @Override |
- public Type visitEmptyStatement(DartEmptyStatement node) { |
- return typeAsVoid(node); |
+ private String methodNameForUnaryOperator(DartNode diagnosticNode, Token operator) { |
+ if (operator == Token.SUB) { |
+ return "operator negate"; |
+ } else if (operator == Token.BIT_NOT) { |
+ return "operator ~"; |
+ } |
+ return "operator " + getBasicOperator(diagnosticNode, operator).getSyntax(); |
} |
- @Override |
- public Type visitExprStmt(DartExprStmt node) { |
- typeOf(node.getExpression()); |
- return voidType; |
+ private Type nonVoidTypeOf(DartNode node) { |
+ Type type = typeOf(node); |
+ if (type.getKind().equals(TypeKind.VOID)) { |
+ return typeError(node, TypeErrorCode.VOID); |
+ } |
+ return type; |
} |
- @Override |
- public Type visitFieldDefinition(DartFieldDefinition node) { |
- node.visitChildren(this); |
- return voidType; |
+ private void onError(DartNode node, ErrorCode code, Object... arguments) { |
+ context.onError(new DartCompilationError(node, code, arguments)); |
} |
- @Override |
- public Type visitForInStatement(DartForInStatement node) { |
- Type variableType; |
- if (node.introducesVariable()) { |
- variableType = typeOf(node.getVariableStatement()); |
- } else { |
- variableType = typeOf(node.getIdentifier()); |
- } |
- DartExpression iterableExpression = node.getIterable(); |
- Type iterableType = typeOf(iterableExpression); |
- Member iteratorMember = lookupMember(iterableType, "iterator", iterableExpression); |
- if (iteratorMember != null) { |
- if (TypeKind.of(iteratorMember.getType()) == TypeKind.FUNCTION) { |
- FunctionType iteratorMethod = (FunctionType) iteratorMember.getType(); |
- InterfaceType asInstanceOf = types.asInstanceOf(iteratorMethod.getReturnType(), |
- dynamicIteratorType.getElement()); |
- if (asInstanceOf != null) { |
- checkAssignable(iterableExpression, variableType, asInstanceOf.getArguments().get(0)); |
- } else { |
- InterfaceType expectedIteratorType = dynamicIteratorType.subst( |
- Arrays.asList(variableType), dynamicIteratorType.getElement().getTypeParameters()); |
- typeError(iterableExpression, |
- TypeErrorCode.FOR_IN_WITH_INVALID_ITERATOR_RETURN_TYPE, |
- expectedIteratorType); |
- } |
- } else { |
- // Not a function |
- typeError(iterableExpression, TypeErrorCode.FOR_IN_WITH_ITERATOR_FIELD); |
- } |
- } |
- |
- return typeAsVoid(node); |
+ @VisibleForTesting |
+ void setCurrentClass(InterfaceType type) { |
+ currentClass = type; |
} |
- @Override |
- public Type visitForStatement(DartForStatement node) { |
- typeOf(node.getInit()); |
- checkCondition(node.getCondition()); |
- typeOf(node.getIncrement()); |
- typeOf(node.getBody()); |
- return voidType; |
+ /** |
+ * Return the type of member as if it was a member of subtype. For example, the type of t in Sub |
+ * should be String, not T: |
+ * |
+ * <pre> |
+ * class Super<T> { |
+ * T t; |
+ * } |
+ * class Sub extends Super<String> { |
+ * } |
+ * </pre> |
+ */ |
+ private Type typeAsMemberOf(Element member, InterfaceType subtype) { |
+ Element holder = member.getEnclosingElement(); |
+ if (!ElementKind.of(holder).equals(ElementKind.CLASS)) { |
+ return member.getType(); |
+ } |
+ ClassElement superclass = (ClassElement) holder; |
+ InterfaceType supertype = types.asInstanceOf(subtype, superclass); |
+ Type type = member.getType().subst(supertype.getArguments(), |
+ supertype.getElement().getTypeParameters()); |
+ return type; |
} |
- @Override |
- public Type visitFunction(DartFunction node) { |
- Type previous = expected; |
- visit(node.getParams()); |
- expected = typeOf(node.getReturnTypeNode()); |
- typeOf(node.getBody()); |
- expected = previous; |
+ private Type typeAsVoid(DartNode node) { |
+ node.visitChildren(this); |
return voidType; |
} |
- @Override |
- public Type visitFunctionExpression(DartFunctionExpression node) { |
- node.visitChildren(this); |
- return ((Element) node.getSymbol()).getType(); |
+ private DynamicType typeError(DartNode node, ErrorCode code, Object... arguments) { |
+ onError(node, code, arguments); |
+ return dynamicType; |
} |
- @Override |
- public Type visitFunctionTypeAlias(DartFunctionTypeAlias node) { |
- return typeAsVoid(node); |
+ Type typeOf(DartNode node) { |
+ if (node == null) { |
+ return dynamicType; |
+ } |
+ return node.accept(this); |
} |
- @Override |
- public Type visitIdentifier(DartIdentifier node) { |
- Element element = node.getTargetSymbol(); |
- Type type; |
- switch (ElementKind.of(element)) { |
- case VARIABLE: |
- case PARAMETER: |
- case FUNCTION_OBJECT: |
- type = element.getType(); |
- break; |
+ private Type typeOfLiteral(DartLiteral node) { |
+ return node.getType(); |
+ } |
- case FIELD: |
- case METHOD: |
- type = typeAsMemberOf(element, currentClass); |
- break; |
+ private void validateBounds(List<? extends DartNode> diagnosticNodes, |
+ List<? extends Type> arguments, |
+ List<? extends Type> parameters, |
+ boolean badBoundIsError) { |
+ if (arguments.size() == parameters.size() && arguments.size() == diagnosticNodes.size()) { |
+ List<Type> bounds = new ArrayList<Type>(parameters.size()); |
+ for (Type parameter : parameters) { |
+ TypeVariable variable = (TypeVariable) parameter; |
+ Type bound = variable.getTypeVariableElement().getBound(); |
+ if (bound == null) { |
+ internalError(variable.getElement().getNode(), "bound is null"); |
+ } |
+ bounds.add(bound); |
+ } |
+ bounds = Types.subst(bounds, arguments, parameters); |
+ for (int i = 0; i < arguments.size(); i++) { |
+ Type t = bounds.get(i); |
+ Type s = arguments.get(i); |
+ if (!types.isAssignable(t, s)) { |
+ if (badBoundIsError) { |
+ onError(diagnosticNodes.get(i), |
+ ResolverErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); |
+ } else { |
+ onError(diagnosticNodes.get(i), |
+ TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ private Type validateTypeNode(DartTypeNode node, boolean badBoundIsError) { |
+ Type type = node.getType(); // Already calculated by resolver. |
+ switch (TypeKind.of(type)) { |
case NONE: |
- return typeError(node, TypeErrorCode.CANNOT_BE_RESOLVED, node.getTargetName()); |
+ return typeError(node, TypeErrorCode.INTERNAL_ERROR, |
+ String.format("type \"%s\" is null", node)); |
- case DYNAMIC: |
- return element.getType(); |
+ case INTERFACE: { |
+ InterfaceType itype = (InterfaceType) type; |
+ validateBounds(node.getTypeArguments(), |
+ itype.getArguments(), itype.getElement().getTypeParameters(), |
+ badBoundIsError); |
+ return itype; |
+ } |
default: |
- return voidType; |
+ return type; |
} |
- node.setReferencedElement(element); |
- return type; |
- } |
- |
- @Override |
- public Type visitIfStatement(DartIfStatement node) { |
- checkCondition(node.getCondition()); |
- typeOf(node.getThenStatement()); |
- typeOf(node.getElseStatement()); |
- return voidType; |
} |
@Override |
- public Type visitInitializer(DartInitializer node) { |
- DartIdentifier name = node.getName(); |
- if (name != null) { |
- checkAssignable(typeOf(name), node.getValue()); |
- } else { |
- typeOf(node.getValue()); |
+ public void visit(List<? extends DartNode> nodes) { |
+ if (nodes != null) { |
+ for (DartNode node : nodes) { |
+ node.accept(this); |
+ } |
} |
- return voidType; |
} |
@Override |
- public Type visitLabel(DartLabel node) { |
- return typeAsVoid(node); |
+ public Type visitArrayAccess(DartArrayAccess node) { |
+ Type target = typeOf(node.getTarget()); |
+ return analyzeBinaryOperator(node, target, Token.INDEX, node, node.getKey()); |
} |
@Override |
- public Type visitMapLiteral(DartMapLiteral node) { |
+ public Type visitArrayLiteral(DartArrayLiteral node) { |
visit(node.getTypeArguments()); |
InterfaceType type = node.getType(); |
- |
- // This ensures that the declared type is assignable to Map<String, dynamic>. |
- // For example, the user should not write Map<int,int>. |
- checkAssignable(node, type, defaultLiteralMapType); |
- |
- Type valueType = type.getArguments().get(1); |
- // Check the map literal entries against the return type. |
- for (DartMapLiteralEntry literalEntry : node.getEntries()) { |
- checkAssignable(literalEntry, typeOf(literalEntry), valueType); |
+ Type elementType = type.getArguments().get(0); |
+ for (DartExpression expression : node.getExpressions()) { |
+ checkAssignable(elementType, expression); |
} |
return type; |
} |
@Override |
- public Type visitMapLiteralEntry(DartMapLiteralEntry node) { |
- nonVoidTypeOf(node.getKey()); |
- return nonVoidTypeOf(node.getValue()); |
+ public Type visitAssertion(DartAssertion node) { |
+ DartExpression conditionNode = node.getExpression(); |
+ Type condition = nonVoidTypeOf(conditionNode); |
+ switch (condition.getKind()) { |
+ case FUNCTION: |
+ FunctionType ftype = (FunctionType) condition; |
+ Type returnType = ftype.getReturnType(); |
+ if (returnType.getKind().equals(TypeKind.VOID)) { |
+ typeError(conditionNode, TypeErrorCode.VOID); |
+ } |
+ checkAssignable(conditionNode, boolType, returnType); |
+ break; |
+ |
+ default: |
+ checkAssignable(conditionNode, boolType, condition); |
+ break; |
+ } |
+ return voidType; |
} |
@Override |
- public Type visitMethodDefinition(DartMethodDefinition node) { |
- MethodElement methodElement = node.getSymbol(); |
- FunctionType type = methodElement.getFunctionType(); |
- if (methodElement.getModifiers().isFactory()) { |
- analyzeFactory(node.getName(), (ConstructorElement) methodElement); |
- } else { |
- if (!type.getTypeVariables().isEmpty()) { |
- internalError(node, "generic methods are not supported"); |
+ public Type visitBinaryExpression(DartBinaryExpression node) { |
+ DartExpression lhsNode = node.getArg1(); |
+ Type lhs = nonVoidTypeOf(lhsNode); |
+ DartExpression rhsNode = node.getArg2(); |
+ Token operator = node.getOperator(); |
+ switch (operator) { |
+ case ASSIGN: { |
+ Type rhs = nonVoidTypeOf(rhsNode); |
+ checkAssignable(rhsNode, lhs, rhs); |
+ return rhs; |
} |
- } |
- return typeAsVoid(node); |
- } |
- private void analyzeFactory(DartExpression name, final ConstructorElement methodElement) { |
- DartNodeTraverser<Void> visitor = new DartNodeTraverser<Void>() { |
- @Override |
- public Void visitParameterizedNode(DartParameterizedNode node) { |
- DartExpression expression = node.getExpression(); |
- Element e = null; |
- if (expression instanceof DartIdentifier) { |
- e = ((DartIdentifier) expression).getTargetSymbol(); |
- } else if (expression instanceof DartPropertyAccess) { |
- e = ((DartPropertyAccess) expression).getTargetSymbol(); |
- } |
- if (!ElementKind.of(e).equals(ElementKind.CLASS)) { |
- return null; |
- } |
- ClassElement cls = (ClassElement) e; |
- InterfaceType type = cls.getType(); |
- List<DartTypeParameter> parameterNodes = node.getTypeParameters(); |
- List<? extends Type> arguments = type.getArguments(); |
- if (parameterNodes.size() == 0) { |
- return null; |
- } |
- Analyzer.this.visit(parameterNodes); |
- List<TypeVariable> typeVariables = methodElement.getFunctionType().getTypeVariables(); |
- validateBounds(parameterNodes, arguments, typeVariables, true); |
- return null; |
+ case ASSIGN_SHR: |
+ case ASSIGN_ADD: |
+ case ASSIGN_SUB: |
+ case ASSIGN_MUL: |
+ case ASSIGN_DIV: |
+ case ASSIGN_MOD: |
+ case ASSIGN_TRUNC: { |
+ Token basicOperator = getBasicOperator(node, operator); |
+ Type type = analyzeBinaryOperator(node, lhs, basicOperator, lhsNode, rhsNode); |
+ checkAssignable(node, lhs, type); |
+ return type; |
} |
- }; |
- name.accept(visitor); |
- } |
- @Override |
- public Type visitNewExpression(DartNewExpression node) { |
- ConstructorElement element = node.getSymbol(); |
- node.setReferencedElement(element); |
- DartTypeNode typeNode = Types.constructorTypeNode(node); |
- DartNode typeName = typeNode.getIdentifier(); |
- Type type = validateTypeNode(typeNode, true); |
- if (element == null) { |
- visit(node.getArgs()); |
- } else { |
- ClassElement cls = (ClassElement) element.getEnclosingElement(); |
+ case OR: |
+ case AND: { |
+ checkAssignable(lhsNode, boolType, lhs); |
+ checkAssignable(boolType, rhsNode); |
+ return boolType; |
+ } |
- List<Element> unimplementedMembers = findUnimplementedMembers(cls); |
- if (unimplementedMembers.size() > 0) { |
- if (diagnosedAbstractClasses.add(cls)) { |
- StringBuilder sb = new StringBuilder(); |
- for (Element member : unimplementedMembers) { |
- sb.append("\n # From "); |
- ClassElement enclosingElement = (ClassElement) member.getEnclosingElement(); |
- InterfaceType instance = types.asInstanceOf(cls.getType(), enclosingElement); |
- Type memberType = member.getType().subst(instance.getArguments(), |
- enclosingElement.getTypeParameters()); |
- sb.append(enclosingElement.getName()); |
- sb.append(":\n "); |
- if (memberType.getKind().equals(TypeKind.FUNCTION)) { |
- FunctionType ftype = (FunctionType) memberType; |
- sb.append(ftype.getReturnType()); |
- sb.append(" "); |
- sb.append(member.getName()); |
- String string = ftype.toString(); |
- sb.append(string, 0, string.lastIndexOf(" -> ")); |
- } else { |
- sb.append(memberType); |
- sb.append(" "); |
- sb.append(member.getName()); |
- } |
- } |
- DartNode clsNode = cls.getNode(); |
- if (clsNode != null) { |
- typeError(typeName, TypeErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, cls.getName()); |
- typeError(clsNode, TypeErrorCode.ABSTRACT_CLASS, cls.getName(), sb); |
- } else { |
- typeError(typeName, TypeErrorCode.ABSTRACT_CLASS, cls.getName(), sb); |
- } |
+ case ASSIGN_BIT_OR: |
+ case ASSIGN_BIT_XOR: |
+ case ASSIGN_BIT_AND: |
+ case ASSIGN_SHL: |
+ case ASSIGN_SAR: { |
+ // Bit operations are only supported by integers and |
+ // thus cannot be looked up on num. To ease usage of |
+ // bit operations, we currently allow them to be used |
+ // if the left-hand-side is of type num. |
+ // TODO(karlklose) find a clean solution, i.e., without a special case for num. |
+ if (lhs.equals(numType)) { |
+ checkAssignable(rhsNode, numType, typeOf(rhsNode)); |
+ return intType; |
} else { |
- typeError(typeName, TypeErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, cls.getName()); |
+ Token basicOperator = getBasicOperator(node, operator); |
+ Type type = analyzeBinaryOperator(node, lhs, basicOperator, lhsNode, rhsNode); |
+ checkAssignable(node, lhs, type); |
+ return type; |
} |
} |
- FunctionType ftype = (FunctionType) element.getType(); |
- if (TypeKind.of(type).equals(TypeKind.INTERFACE)) { |
- InterfaceType ifaceType = (InterfaceType)type; |
- List<? extends Type> arguments = ifaceType.getArguments(); |
- ftype = (FunctionType) ftype.subst(arguments, |
- ifaceType.getElement().getTypeParameters()); |
- List<TypeVariable> typeVariables = ftype.getTypeVariables(); |
- if (arguments.size() == typeVariables.size()) { |
- ftype = (FunctionType) ftype.subst(arguments, typeVariables); |
+ |
+ case BIT_OR: |
+ case BIT_XOR: |
+ case BIT_AND: |
+ case SHL: |
+ case SAR: { |
+ // Bit operations are only supported by integers and |
+ // thus cannot be looked up on num. To ease usage of |
+ // bit operations, we currently allow them to be used |
+ // if the left-hand-side is of type num. |
+ // TODO(karlklose) find a clean solution, i.e., without a special case for num. |
+ if (lhs.equals(numType)) { |
+ checkAssignable(rhsNode, numType, typeOf(rhsNode)); |
+ return intType; |
+ } else { |
+ return analyzeBinaryOperator(node, lhs, operator, lhsNode, rhsNode); |
} |
- checkInvocation(node, node, null, ftype); |
} |
+ |
+ case SHR: |
+ case ADD: |
+ case SUB: |
+ case MUL: |
+ case DIV: |
+ case TRUNC: |
+ case MOD: |
+ case LT: |
+ case GT: |
+ case LTE: |
+ case GTE: |
+ return analyzeBinaryOperator(node, lhs, operator, lhsNode, rhsNode); |
+ |
+ case EQ: |
+ case NE: |
+ case EQ_STRICT: |
+ case NE_STRICT: |
+ nonVoidTypeOf(rhsNode); |
+ return boolType; |
+ |
+ case IS: |
+ if (rhsNode instanceof DartUnaryExpression) { |
+ assert ((DartUnaryExpression) rhsNode).getOperator() == Token.NOT; |
+ nonVoidTypeOf(((DartUnaryExpression) rhsNode).getArg()); |
+ } else { |
+ nonVoidTypeOf(rhsNode); |
+ } |
+ return boolType; |
+ |
+ case COMMA: |
+ return typeOf(rhsNode); |
+ |
+ default: |
+ throw new AssertionError("Unknown operator: " + operator); |
} |
- return type; |
} |
@Override |
- public Type visitNullLiteral(DartNullLiteral node) { |
- return nullType; |
+ public Type visitBlock(DartBlock node) { |
+ return typeAsVoid(node); |
} |
@Override |
- public Type visitParameter(DartParameter node) { |
- VariableElement parameter = node.getSymbol(); |
- FieldElement initializerElement = parameter.getParameterInitializerElement(); |
- if (initializerElement != null) { |
- checkAssignable(node, parameter.getType(), initializerElement.getType()); |
- } |
- return checkInitializedDeclaration(node, node.getDefaultExpr()); |
+ public Type visitBooleanLiteral(DartBooleanLiteral node) { |
+ return typeOfLiteral(node); |
} |
@Override |
- public Type visitParenthesizedExpression(DartParenthesizedExpression node) { |
- return node.getExpression().accept(this); |
+ public Type visitBreakStatement(DartBreakStatement node) { |
+ return voidType; |
} |
@Override |
- public Type visitPropertyAccess(DartPropertyAccess node) { |
- Element element = node.getTargetSymbol(); |
- node.setReferencedElement(element); |
- if (element != null && (element.getModifiers().isStatic() |
- || Elements.isTopLevel(element))) { |
- return element.getType(); |
- } |
- DartNode qualifier = node.getQualifier(); |
- Type receiver = nonVoidTypeOf(qualifier); |
- InterfaceType cls = types.getInterfaceType(receiver); |
- if (cls == null) { |
- return diagnoseNonInterfaceType(qualifier, receiver); |
- } |
- // Do not visit the name, it may not have been resolved. |
- String name = node.getPropertyName(); |
- InterfaceType.Member member = cls.lookupMember(name); |
- if (member == null) { |
- return typeError(node.getName(), TypeErrorCode.NOT_A_MEMBER_OF, name, cls); |
- } |
- element = member.getElement(); |
- node.setReferencedElement(element); |
- Modifiers modifiers = element.getModifiers(); |
- if (modifiers.isStatic()) { |
- return typeError(node.getName(), |
- TypeErrorCode.STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE, |
- name, element.getName()); |
- } |
- switch (element.getKind()) { |
- case CONSTRUCTOR: |
- return typeError(node.getName(), TypeErrorCode.MEMBER_IS_A_CONSTRUCTOR, |
- name, element.getName()); |
- |
- case METHOD: |
- case FIELD: |
- return member.getType(); |
+ public Type visitCase(DartCase node) { |
+ node.visitChildren(this); |
+ return voidType; |
+ } |
- default: |
- throw internalError(node.getName(), "unexpected kind %s", element.getKind()); |
- } |
+ @Override |
+ public Type visitCatchBlock(DartCatchBlock node) { |
+ ++catchDepth; |
+ typeOf(node.getException()); |
+ // TODO(karlklose) Check type of stack trace variable. |
+ typeOf(node.getStackTrace()); |
+ typeOf(node.getBlock()); |
+ --catchDepth; |
+ return voidType; |
} |
@Override |
- public Type visitReturnStatement(DartReturnStatement node) { |
- DartExpression value = node.getValue(); |
- if (value == null) { |
- if (!types.isSubtype(voidType, expected)) { |
- typeError(node, TypeErrorCode.MISSING_RETURN_VALUE, expected); |
+ public Type visitClass(DartClass node) { |
+ ClassElement element = node.getSymbol(); |
+ InterfaceType type = element.getType(); |
+ findUnimplementedMembers(element); |
+ setCurrentClass(type); |
+ visit(node.getTypeParameters()); |
+ if (node.getSuperclass() != null) { |
+ validateTypeNode(node.getSuperclass(), true); |
+ } |
+ if (node.getInterfaces() != null) { |
+ for (DartTypeNode interfaceNode : node.getInterfaces()) { |
+ validateTypeNode(interfaceNode, true); |
} |
- } else { |
- checkAssignable(value == null ? node : value, expected, typeOf(value)); |
} |
- return voidType; |
+ if (node.getDefaultClass() != null) { |
+ validateTypeNode(node.getDefaultClass(), true); |
+ } |
+ visit(node.getMembers()); |
+ setCurrentClass(null); |
+ return type; |
} |
@Override |
- public Type visitSuperExpression(DartSuperExpression node) { |
- if (currentClass == null) { |
- return dynamicType; |
- } else { |
- return currentClass.getElement().getSupertype(); |
- } |
+ public Type visitConditional(DartConditional node) { |
+ checkCondition(node.getCondition()); |
+ Type left = typeOf(node.getThenExpression()); |
+ Type right = typeOf(node.getElseExpression()); |
+ return types.leastUpperBound(left, right); |
} |
@Override |
- public Type visitSwitchStatement(DartSwitchStatement node) { |
- return typeAsVoid(node); |
+ public Type visitContinueStatement(DartContinueStatement node) { |
+ return voidType; |
} |
@Override |
- public Type visitSyntheticErrorExpression(DartSyntheticErrorExpression node) { |
- return dynamicType; |
+ public Type visitDefault(DartDefault node) { |
+ node.visitChildren(this); |
+ return typeAsVoid(node); |
} |
@Override |
- public Type visitSyntheticErrorStatement(DartSyntheticErrorStatement node) { |
- return dynamicType; |
+ public Type visitDoubleLiteral(DartDoubleLiteral node) { |
+ return typeOfLiteral(node); |
} |
@Override |
- public Type visitThisExpression(DartThisExpression node) { |
- return getCurrentClass(); |
+ public Type visitDoWhileStatement(DartDoWhileStatement node) { |
+ checkCondition(node.getCondition()); |
+ typeOf(node.getBody()); |
+ return voidType; |
} |
@Override |
- public Type visitThrowStatement(DartThrowStatement node) { |
- if (catchDepth == 0 && node.getException() == null) { |
- context.onError(new DartCompilationError(node, |
- ResolverErrorCode.RETHROW_NOT_IN_CATCH)); |
- } |
+ public Type visitEmptyStatement(DartEmptyStatement node) { |
return typeAsVoid(node); |
} |
@Override |
- public Type visitCatchBlock(DartCatchBlock node) { |
- ++catchDepth; |
- typeOf(node.getException()); |
- // TODO(karlklose) Check type of stack trace variable. |
- typeOf(node.getStackTrace()); |
- typeOf(node.getBlock()); |
- --catchDepth; |
+ public Type visitExprStmt(DartExprStmt node) { |
+ typeOf(node.getExpression()); |
return voidType; |
} |
@Override |
- public Type visitTryStatement(DartTryStatement node) { |
- return typeAsVoid(node); |
+ public Type visitField(DartField node) { |
+ Type result; |
+ DartMethodDefinition accessor = node.getAccessor(); |
+ if (accessor != null) { |
+ result = typeOf(accessor); |
+ } else { |
+ result = checkInitializedDeclaration(node, node.getValue()); |
+ } |
+ return result; |
} |
@Override |
- public Type visitUnaryExpression(DartUnaryExpression node) { |
- DartExpression expression = node.getArg(); |
- Type type = nonVoidTypeOf(expression); |
- Token operator = node.getOperator(); |
- switch (operator) { |
- case BIT_NOT: |
- // Bit operations are only supported by integers and |
- // thus cannot be looked up on num. To ease usage of |
- // bit operations, we currently allow them to be used |
- // if the left-hand-side is of type num. |
- // TODO(karlklose) find a clean solution, i.e., without a special case for num. |
- if (type.equals(numType)) { |
- return intType; |
+ public Type visitFieldDefinition(DartFieldDefinition node) { |
+ node.visitChildren(this); |
+ return voidType; |
+ } |
+ |
+ @Override |
+ public Type visitForInStatement(DartForInStatement node) { |
+ Type variableType; |
+ if (node.introducesVariable()) { |
+ variableType = typeOf(node.getVariableStatement()); |
+ } else { |
+ variableType = typeOf(node.getIdentifier()); |
+ } |
+ DartExpression iterableExpression = node.getIterable(); |
+ Type iterableType = typeOf(iterableExpression); |
+ Member iteratorMember = lookupMember(iterableType, "iterator", iterableExpression); |
+ if (iteratorMember != null) { |
+ if (TypeKind.of(iteratorMember.getType()) == TypeKind.FUNCTION) { |
+ FunctionType iteratorMethod = (FunctionType) iteratorMember.getType(); |
+ InterfaceType asInstanceOf = types.asInstanceOf(iteratorMethod.getReturnType(), |
+ dynamicIteratorType.getElement()); |
+ if (asInstanceOf != null) { |
+ checkAssignable(iterableExpression, variableType, asInstanceOf.getArguments().get(0)); |
} else { |
- String name = methodNameForUnaryOperator(node, operator); |
- Member member = lookupMember(type, name, node); |
- if (member != null) { |
- node.setReferencedElement(member.getElement()); |
- return analyzeMethodInvocation(type, member, name, node, |
- Collections.<Type>emptyList(), |
- Collections.<DartExpression>emptyList()); |
- } else { |
- return dynamicType; |
- } |
- } |
- case NOT: |
- checkAssignable(boolType, expression); |
- return boolType; |
- case SUB: |
- case INC: |
- case DEC: { |
- if (type.getElement().isDynamic()) { |
- return type; |
- } |
- InterfaceType itype = types.getInterfaceType(type); |
- String operatorMethodName = methodNameForUnaryOperator(node, operator); |
- Member member = itype.lookupMember(operatorMethodName); |
- if (member == null) { |
- return typeError(expression, TypeErrorCode.CANNOT_BE_RESOLVED, |
- operatorMethodName); |
- } |
- MethodElement element = ((MethodElement) member.getElement()); |
- node.setReferencedElement(element); |
- Type returnType = ((FunctionType) member.getType()).getReturnType(); |
- if (operator == Token.INC || operator == Token.DEC) { |
- // For INC and DEC, "operator +" and "operator -" are used to add and subtract one, |
- // respectively. Check that the resolved operator has a compatible parameter type. |
- Iterator<VariableElement> it = element.getParameters().iterator(); |
- if (!types.isAssignable(numType, it.next().getType())) { |
- typeError(node, TypeErrorCode.OPERATOR_WRONG_OPERAND_TYPE, |
- operatorMethodName, numType.toString()); |
- } |
- // Check that the return type of the operator is compatible with the receiver. |
- checkAssignable(node, type, returnType); |
+ InterfaceType expectedIteratorType = dynamicIteratorType.subst( |
+ Arrays.asList(variableType), dynamicIteratorType.getElement().getTypeParameters()); |
+ typeError(iterableExpression, |
+ TypeErrorCode.FOR_IN_WITH_INVALID_ITERATOR_RETURN_TYPE, |
+ expectedIteratorType); |
} |
- return node.isPrefix() ? returnType : type; |
+ } else { |
+ // Not a function |
+ typeError(iterableExpression, TypeErrorCode.FOR_IN_WITH_ITERATOR_FIELD); |
} |
- default: |
- throw internalError(node, "unknown operator %s", operator.toString()); |
} |
+ |
+ return typeAsVoid(node); |
} |
@Override |
- public Type visitUnit(DartUnit node) { |
+ public Type visitForStatement(DartForStatement node) { |
+ typeOf(node.getInit()); |
+ checkCondition(node.getCondition()); |
+ typeOf(node.getIncrement()); |
+ typeOf(node.getBody()); |
+ return voidType; |
+ } |
+ |
+ @Override |
+ public Type visitFunction(DartFunction node) { |
+ Type previous = expected; |
+ visit(node.getParams()); |
+ expected = typeOf(node.getReturnTypeNode()); |
+ typeOf(node.getBody()); |
+ expected = previous; |
+ return voidType; |
+ } |
+ |
+ @Override |
+ public Type visitFunctionExpression(DartFunctionExpression node) { |
+ node.visitChildren(this); |
+ return ((Element) node.getSymbol()).getType(); |
+ } |
+ |
+ @Override |
+ public Type visitFunctionObjectInvocation(DartFunctionObjectInvocation node) { |
+ node.setReferencedElement(functionType.getElement()); |
+ return checkInvocation(node, node, null, typeOf(node.getTarget())); |
+ } |
+ |
+ @Override |
+ public Type visitFunctionTypeAlias(DartFunctionTypeAlias node) { |
return typeAsVoid(node); |
} |
@Override |
- public Type visitUnqualifiedInvocation(DartUnqualifiedInvocation node) { |
- DartIdentifier target = node.getTarget(); |
- String name = target.getTargetName(); |
- Element element = target.getTargetSymbol(); |
- node.setReferencedElement(element); |
+ public Type visitIdentifier(DartIdentifier node) { |
+ Element element = node.getTargetSymbol(); |
Type type; |
switch (ElementKind.of(element)) { |
+ case VARIABLE: |
+ case PARAMETER: |
+ case FUNCTION_OBJECT: |
+ type = element.getType(); |
+ break; |
+ |
case FIELD: |
case METHOD: |
type = typeAsMemberOf(element, currentClass); |
break; |
+ |
case NONE: |
- return typeError(target, TypeErrorCode.NOT_A_METHOD_IN, name, currentClass); |
+ return typeError(node, TypeErrorCode.CANNOT_BE_RESOLVED, node.getTargetName()); |
+ |
+ case DYNAMIC: |
+ return element.getType(); |
+ |
default: |
- type = element.getType(); |
- break; |
+ return voidType; |
} |
- return checkInvocation(node, target, name, type); |
- } |
- |
- private Type checkInvocation(DartInvocation node, DartNode diagnosticNode, String name, |
- Type type) { |
- List<? extends DartExpression> argumentNodes = node.getArgs(); |
- List<Type> argumentTypes = new ArrayList<Type>(argumentNodes.size()); |
- for (DartExpression argumentNode : argumentNodes) { |
- argumentTypes.add(nonVoidTypeOf(argumentNode)); |
- } |
- switch (TypeKind.of(type)) { |
- case FUNCTION_ALIAS: |
- return checkArguments(node, argumentNodes, argumentTypes.iterator(), |
- types.asFunctionType((FunctionAliasType) type)); |
- case FUNCTION: |
- return checkArguments(node, argumentNodes, argumentTypes.iterator(), (FunctionType) type); |
- case DYNAMIC: |
- return type; |
- default: |
- if (types.isAssignable(functionType, type)) { |
- // A subtype of interface Function. |
- return dynamicType; |
- } else if (name == null) { |
- return typeError(diagnosticNode, TypeErrorCode.NOT_A_FUNCTION, type); |
- } else { |
- return typeError(diagnosticNode, TypeErrorCode.NOT_A_METHOD_IN, name, |
- currentClass); |
- } |
- } |
- } |
- |
- /** |
- * Return the type of member as if it was a member of subtype. For example, the type of t in Sub |
- * should be String, not T: |
- * |
- * <pre> |
- * class Super<T> { |
- * T t; |
- * } |
- * class Sub extends Super<String> { |
- * } |
- * </pre> |
- */ |
- private Type typeAsMemberOf(Element member, InterfaceType subtype) { |
- Element holder = member.getEnclosingElement(); |
- if (!ElementKind.of(holder).equals(ElementKind.CLASS)) { |
- return member.getType(); |
- } |
- ClassElement superclass = (ClassElement) holder; |
- InterfaceType supertype = types.asInstanceOf(subtype, superclass); |
- Type type = member.getType().subst(supertype.getArguments(), |
- supertype.getElement().getTypeParameters()); |
- return type; |
- } |
- |
- @Override |
- public Type visitVariable(DartVariable node) { |
- return checkInitializedDeclaration(node, node.getValue()); |
+ node.setReferencedElement(element); |
+ return type; |
} |
@Override |
- public Type visitWhileStatement(DartWhileStatement node) { |
+ public Type visitIfStatement(DartIfStatement node) { |
checkCondition(node.getCondition()); |
- typeOf(node.getBody()); |
+ typeOf(node.getThenStatement()); |
+ typeOf(node.getElseStatement()); |
return voidType; |
} |
@Override |
- public Type visitNamedExpression(DartNamedExpression node) { |
- // TODO(jgw): Checking of named parameters in progress. |
- |
- // Intentionally skip the expression's name -- it's stored as an identifier, but doesn't need |
- // to be resolved or type-checked. |
- return node.getExpression().accept(this); |
+ public Type visitImportDirective(DartImportDirective node) { |
+ return typeAsVoid(node); |
} |
@Override |
- public Type visitTypeExpression(DartTypeExpression node) { |
- return typeOf(node.getTypeNode()); |
+ public Type visitInitializer(DartInitializer node) { |
+ DartIdentifier name = node.getName(); |
+ if (name != null) { |
+ checkAssignable(typeOf(name), node.getValue()); |
+ } else { |
+ typeOf(node.getValue()); |
+ } |
+ return voidType; |
} |
@Override |
- public Type visitTypeParameter(DartTypeParameter node) { |
- if (node.getBound() != null) { |
- validateTypeNode(node.getBound(), true); |
- } |
- return voidType; |
+ public Type visitIntegerLiteral(DartIntegerLiteral node) { |
+ return typeOfLiteral(node); |
} |
@Override |
- public Type visitNativeBlock(DartNativeBlock node) { |
+ public Type visitLabel(DartLabel node) { |
return typeAsVoid(node); |
} |
@Override |
- public void visit(List<? extends DartNode> nodes) { |
- if (nodes != null) { |
- for (DartNode node : nodes) { |
- node.accept(this); |
- } |
- } |
+ public Type visitLibraryDirective(DartLibraryDirective node) { |
+ return typeAsVoid(node); |
} |
@Override |
- public Type visitArrayLiteral(DartArrayLiteral node) { |
+ public Type visitMapLiteral(DartMapLiteral node) { |
visit(node.getTypeArguments()); |
InterfaceType type = node.getType(); |
- Type elementType = type.getArguments().get(0); |
- for (DartExpression expression : node.getExpressions()) { |
- checkAssignable(elementType, expression); |
+ |
+ // This ensures that the declared type is assignable to Map<String, dynamic>. |
+ // For example, the user should not write Map<int,int>. |
+ checkAssignable(node, type, defaultLiteralMapType); |
+ |
+ Type valueType = type.getArguments().get(1); |
+ // Check the map literal entries against the return type. |
+ for (DartMapLiteralEntry literalEntry : node.getEntries()) { |
+ checkAssignable(literalEntry, typeOf(literalEntry), valueType); |
} |
return type; |
} |
@Override |
- public Type visitBooleanLiteral(DartBooleanLiteral node) { |
- return typeOfLiteral(node); |
- } |
- |
- @Override |
- public Type visitDoubleLiteral(DartDoubleLiteral node) { |
- return typeOfLiteral(node); |
+ public Type visitMapLiteralEntry(DartMapLiteralEntry node) { |
+ nonVoidTypeOf(node.getKey()); |
+ return nonVoidTypeOf(node.getValue()); |
} |
@Override |
- public Type visitField(DartField node) { |
- DartMethodDefinition accessor = node.getAccessor(); |
- if (accessor != null) { |
- return typeOf(accessor); |
+ public Type visitMethodDefinition(DartMethodDefinition node) { |
+ MethodElement methodElement = node.getSymbol(); |
+ FunctionType type = methodElement.getFunctionType(); |
+ if (methodElement.getModifiers().isFactory()) { |
+ analyzeFactory(node.getName(), (ConstructorElement) methodElement); |
} else { |
- return checkInitializedDeclaration(node, node.getValue()); |
+ if (!type.getTypeVariables().isEmpty()) { |
+ internalError(node, "generic methods are not supported"); |
+ } |
} |
+ return typeAsVoid(node); |
} |
- private Type checkInitializedDeclaration(DartDeclaration<?> node, DartExpression value) { |
- if (value != null) { |
- checkAssignable(node.getSymbol().getType(), value); |
+ @Override |
+ public Type visitMethodInvocation(DartMethodInvocation node) { |
+ String name = node.getFunctionNameString(); |
+ Element element = (Element) node.getTargetSymbol(); |
+ if (element != null && (element.getModifiers().isStatic() |
+ || Elements.isTopLevel(element))) { |
+ node.setReferencedElement(element); |
+ return checkInvocation(node, node, name, element.getType()); |
} |
- return voidType; |
+ Type receiver = nonVoidTypeOf(node.getTarget()); |
+ List<DartExpression> arguments = node.getArgs(); |
+ Member member = lookupMember(receiver, name, node); |
+ if (member != null) { |
+ node.setReferencedElement(member.getElement()); |
+ } |
+ return analyzeMethodInvocation(receiver, member, name, |
+ node.getFunctionName(), analyzeArgumentTypes(arguments), |
+ arguments); |
} |
@Override |
- public Type visitIntegerLiteral(DartIntegerLiteral node) { |
- return typeOfLiteral(node); |
- } |
+ public Type visitNamedExpression(DartNamedExpression node) { |
+ // TODO(jgw): Checking of named parameters in progress. |
- @Override |
- public Type visitStringLiteral(DartStringLiteral node) { |
- return typeOfLiteral(node); |
+ // Intentionally skip the expression's name -- it's stored as an identifier, but doesn't need |
+ // to be resolved or type-checked. |
+ return node.getExpression().accept(this); |
} |
@Override |
- public Type visitStringInterpolation(DartStringInterpolation node) { |
- visit(node.getExpressions()); |
- return typeOfLiteral(node); |
+ public Type visitNativeBlock(DartNativeBlock node) { |
+ return typeAsVoid(node); |
} |
@Override |
- public Type visitParameterizedNode(DartParameterizedNode node) { |
- throw internalError(node, "unexpected node"); |
+ public Type visitNativeDirective(DartNativeDirective node) { |
+ return typeAsVoid(node); |
} |
@Override |
- public Type visitImportDirective(DartImportDirective node) { |
- return typeAsVoid(node); |
+ public Type visitNewExpression(DartNewExpression node) { |
+ ConstructorElement element = node.getSymbol(); |
+ node.setReferencedElement(element); |
+ DartTypeNode typeNode = Types.constructorTypeNode(node); |
+ DartNode typeName = typeNode.getIdentifier(); |
+ Type type = validateTypeNode(typeNode, true); |
+ if (element == null) { |
+ visit(node.getArgs()); |
+ } else { |
+ ClassElement cls = (ClassElement) element.getEnclosingElement(); |
+ |
+ List<Element> unimplementedMembers = findUnimplementedMembers(cls); |
+ if (unimplementedMembers.size() > 0) { |
+ if (diagnosedAbstractClasses.add(cls)) { |
+ StringBuilder sb = new StringBuilder(); |
+ for (Element member : unimplementedMembers) { |
+ sb.append("\n # From "); |
+ ClassElement enclosingElement = (ClassElement) member.getEnclosingElement(); |
+ InterfaceType instance = types.asInstanceOf(cls.getType(), enclosingElement); |
+ Type memberType = member.getType().subst(instance.getArguments(), |
+ enclosingElement.getTypeParameters()); |
+ sb.append(enclosingElement.getName()); |
+ sb.append(":\n "); |
+ if (memberType.getKind().equals(TypeKind.FUNCTION)) { |
+ FunctionType ftype = (FunctionType) memberType; |
+ sb.append(ftype.getReturnType()); |
+ sb.append(" "); |
+ sb.append(member.getName()); |
+ String string = ftype.toString(); |
+ sb.append(string, 0, string.lastIndexOf(" -> ")); |
+ } else { |
+ sb.append(memberType); |
+ sb.append(" "); |
+ sb.append(member.getName()); |
+ } |
+ } |
+ DartNode clsNode = cls.getNode(); |
+ if (clsNode != null) { |
+ typeError(typeName, TypeErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, cls.getName()); |
+ typeError(clsNode, TypeErrorCode.ABSTRACT_CLASS, cls.getName(), sb); |
+ } else { |
+ typeError(typeName, TypeErrorCode.ABSTRACT_CLASS, cls.getName(), sb); |
+ } |
+ } else { |
+ typeError(typeName, TypeErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, cls.getName()); |
+ } |
+ } |
+ FunctionType ftype = (FunctionType) element.getType(); |
+ if (TypeKind.of(type).equals(TypeKind.INTERFACE)) { |
+ InterfaceType ifaceType = (InterfaceType)type; |
+ List<? extends Type> arguments = ifaceType.getArguments(); |
+ ftype = (FunctionType) ftype.subst(arguments, |
+ ifaceType.getElement().getTypeParameters()); |
+ List<TypeVariable> typeVariables = ftype.getTypeVariables(); |
+ if (arguments.size() == typeVariables.size()) { |
+ ftype = (FunctionType) ftype.subst(arguments, typeVariables); |
+ } |
+ checkInvocation(node, node, null, ftype); |
+ } |
+ } |
+ return type; |
} |
@Override |
- public Type visitLibraryDirective(DartLibraryDirective node) { |
- return typeAsVoid(node); |
+ public Type visitNullLiteral(DartNullLiteral node) { |
+ return nullType; |
} |
@Override |
- public Type visitNativeDirective(DartNativeDirective node) { |
- return typeAsVoid(node); |
+ public Type visitParameter(DartParameter node) { |
+ VariableElement parameter = node.getSymbol(); |
+ FieldElement initializerElement = parameter.getParameterInitializerElement(); |
+ if (initializerElement != null) { |
+ checkAssignable(node, parameter.getType(), initializerElement.getType()); |
+ } |
+ return checkInitializedDeclaration(node, node.getDefaultExpr()); |
} |
@Override |
- public Type visitResourceDirective(DartResourceDirective node) { |
- return typeAsVoid(node); |
+ public Type visitParameterizedNode(DartParameterizedNode node) { |
+ throw internalError(node, "unexpected node"); |
} |
@Override |
- public Type visitSourceDirective(DartSourceDirective node) { |
- return typeAsVoid(node); |
+ public Type visitParenthesizedExpression(DartParenthesizedExpression node) { |
+ return node.getExpression().accept(this); |
} |
- private class AbstractMethodFinder extends DartNodeTraverser<Void> { |
- private final InterfaceType currentClass; |
- private final Multimap<String, Element> superMembers; |
- private final List<Element> unimplementedElements; |
- |
- private AbstractMethodFinder(InterfaceType currentClass) { |
- this.currentClass = currentClass; |
- this.superMembers = LinkedListMultimap.create(); |
- this.unimplementedElements = new ArrayList<Element>(); |
+ @Override |
+ public Type visitPropertyAccess(DartPropertyAccess node) { |
+ Element element = node.getTargetSymbol(); |
+ node.setReferencedElement(element); |
+ if (element != null && (element.getModifiers().isStatic() |
+ || Elements.isTopLevel(element))) { |
+ return element.getType(); |
+ } |
+ DartNode qualifier = node.getQualifier(); |
+ Type receiver = nonVoidTypeOf(qualifier); |
+ InterfaceType cls = types.getInterfaceType(receiver); |
+ if (cls == null) { |
+ return diagnoseNonInterfaceType(qualifier, receiver); |
} |
+ // Do not visit the name, it may not have been resolved. |
+ String name = node.getPropertyName(); |
+ InterfaceType.Member member = cls.lookupMember(name); |
+ if (member == null) { |
+ return typeError(node.getName(), TypeErrorCode.NOT_A_MEMBER_OF, name, cls); |
+ } |
+ element = member.getElement(); |
+ node.setReferencedElement(element); |
+ Modifiers modifiers = element.getModifiers(); |
+ if (modifiers.isStatic()) { |
+ return typeError(node.getName(), |
+ TypeErrorCode.STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE, |
+ name, element.getName()); |
+ } |
+ switch (element.getKind()) { |
+ case CONSTRUCTOR: |
+ return typeError(node.getName(), TypeErrorCode.MEMBER_IS_A_CONSTRUCTOR, |
+ name, element.getName()); |
- @Override |
- public Void visitNode(DartNode node) { |
- throw new AssertionError(); |
+ case METHOD: |
+ case FIELD: |
+ return member.getType(); |
+ |
+ default: |
+ throw internalError(node.getName(), "unexpected kind %s", element.getKind()); |
} |
+ } |
- @Override |
- public Void visitClass(DartClass node) { |
- assert node.getSymbol().getType() == currentClass; |
+ @Override |
+ public Type visitRedirectConstructorInvocation(DartRedirectConstructorInvocation node) { |
+ return checkConstructorForwarding(node, node.getSymbol()); |
+ } |
- // Prepare supertypes - all superclasses and interfaces. |
- List<InterfaceType> supertypes = Collections.emptyList(); |
- boolean hasCyclicDeclaration = false; |
- try { |
- supertypes = currentClass.getElement().getAllSupertypes(); |
- } catch (CyclicDeclarationException e) { |
- // Already reported by resolver. |
- hasCyclicDeclaration = true; |
- } catch (DuplicatedInterfaceException e) { |
- // Already reported by resolver. |
- } |
+ @Override |
+ public Type visitResourceDirective(DartResourceDirective node) { |
+ return typeAsVoid(node); |
+ } |
- // Add all super members to resolve. |
- EnclosingElement currentLibrary = currentClass.getElement().getEnclosingElement(); |
- for (InterfaceType supertype : supertypes) { |
- for (Element member : supertype.getElement().getMembers()) { |
- String name = member.getName(); |
- if (name.startsWith("_")) { |
- if (currentLibrary != member.getEnclosingElement().getEnclosingElement()) { |
- continue; |
- } |
- } |
- superMembers.put(name, member); |
- } |
+ @Override |
+ public Type visitReturnStatement(DartReturnStatement node) { |
+ DartExpression value = node.getValue(); |
+ if (value == null) { |
+ if (!types.isSubtype(voidType, expected)) { |
+ typeError(node, TypeErrorCode.MISSING_RETURN_VALUE, expected); |
} |
+ } else { |
+ checkAssignable(value == null ? node : value, expected, typeOf(value)); |
+ } |
+ return voidType; |
+ } |
- // Visit members, so resolve methods declared in this class. |
- this.visit(node.getMembers()); |
+ @Override |
+ public Type visitSourceDirective(DartSourceDirective node) { |
+ return typeAsVoid(node); |
+ } |
- // If interface, we don't care about unimplemented methods. |
- if (currentClass.getElement().isInterface()) { |
- return null; |
- } |
+ @Override |
+ public Type visitStringInterpolation(DartStringInterpolation node) { |
+ visit(node.getExpressions()); |
+ return typeOfLiteral(node); |
+ } |
- // If we have cyclic declaration, hierarchy is broken, no reason to report unimplemented. |
- if (hasCyclicDeclaration) { |
- return null; |
- } |
+ @Override |
+ public Type visitStringLiteral(DartStringLiteral node) { |
+ return typeOfLiteral(node); |
+ } |
- // Visit superclasses (without interfaces) and mark methods as implemented. |
- InterfaceType supertype = currentClass.getElement().getSupertype(); |
- while (supertype != null) { |
- ClassElement superclass = supertype.getElement(); |
- for (Element member : superclass.getMembers()) { |
- superMembers.removeAll(member.getName()); |
- } |
- supertype = supertype.getElement().getSupertype(); |
- } |
+ @Override |
+ public Type visitSuperConstructorInvocation(DartSuperConstructorInvocation node) { |
+ return checkConstructorForwarding(node, node.getSymbol()); |
+ } |
- // All remaining methods are unimplemented. |
- for (String name : superMembers.keys()) { |
- Collection<Element> elements = superMembers.removeAll(name); |
- for (Element element : elements) { |
- if (!element.getModifiers().isStatic()) { |
- unimplementedElements.add(element); |
- break; // Only report the first unimplemented element with this name. |
- } |
- } |
- } |
- return null; |
+ @Override |
+ public Type visitSuperExpression(DartSuperExpression node) { |
+ if (currentClass == null) { |
+ return dynamicType; |
+ } else { |
+ return currentClass.getElement().getSupertype(); |
} |
+ } |
- @Override |
- public Void visitFieldDefinition(DartFieldDefinition node) { |
- this.visit(node.getFields()); |
- return null; |
- } |
+ @Override |
+ public Type visitSwitchStatement(DartSwitchStatement node) { |
+ return typeAsVoid(node); |
+ } |
- @Override |
- public Void visitField(DartField node) { |
- if (superMembers != null) { |
- FieldElement field = node.getSymbol(); |
- String name = field.getName(); |
- List<Element> overridden = new ArrayList<Element>(superMembers.removeAll(name)); |
- for (Element element : overridden) { |
- if (canOverride(node.getName(), field.getModifiers(), element)) { |
- switch (element.getKind()) { |
- case FIELD: |
- checkOverride(node.getName(), field, element); |
- break; |
- case METHOD: |
- typeError(node, TypeErrorCode.SUPERTYPE_HAS_METHOD, name, |
- element.getEnclosingElement().getName()); |
- break; |
+ @Override |
+ public Type visitSyntheticErrorExpression(DartSyntheticErrorExpression node) { |
+ return dynamicType; |
+ } |
- default: |
- typeError(node, TypeErrorCode.INTERNAL_ERROR, element); |
- break; |
- } |
- } |
- } |
- } |
- return null; |
+ @Override |
+ public Type visitSyntheticErrorStatement(DartSyntheticErrorStatement node) { |
+ return dynamicType; |
+ } |
+ |
+ @Override |
+ public Type visitThisExpression(DartThisExpression node) { |
+ return getCurrentClass(); |
+ } |
+ |
+ @Override |
+ public Type visitThrowStatement(DartThrowStatement node) { |
+ if (catchDepth == 0 && node.getException() == null) { |
+ context.onError(new DartCompilationError(node, |
+ ResolverErrorCode.RETHROW_NOT_IN_CATCH)); |
} |
+ return typeAsVoid(node); |
+ } |
- @Override |
- public Void visitMethodDefinition(DartMethodDefinition node) { |
- MethodElement method = node.getSymbol(); |
- String name = method.getName(); |
- if (superMembers != null && !method.isConstructor()) { |
- Collection<Element> overridden = superMembers.removeAll(name); |
- for (Element element : overridden) { |
- if (canOverride(node.getName(), method.getModifiers(), element)) { |
- switch (element.getKind()) { |
- case METHOD: |
- checkOverride(node.getName(), method, element); |
- break; |
+ @Override |
+ public Type visitTryStatement(DartTryStatement node) { |
+ return typeAsVoid(node); |
+ } |
- case FIELD: |
- typeError(node, TypeErrorCode.SUPERTYPE_HAS_FIELD, element.getName(), |
- element.getEnclosingElement().getName()); |
- break; |
+ @Override |
+ public Type visitTypeExpression(DartTypeExpression node) { |
+ return typeOf(node.getTypeNode()); |
+ } |
- default: |
- typeError(node, TypeErrorCode.INTERNAL_ERROR, element); |
- break; |
- } |
+ @Override |
+ public Type visitTypeNode(DartTypeNode node) { |
+ return validateTypeNode(node, false); |
+ } |
+ |
+ @Override |
+ public Type visitTypeParameter(DartTypeParameter node) { |
+ if (node.getBound() != null) { |
+ validateTypeNode(node.getBound(), true); |
+ } |
+ return voidType; |
+ } |
+ |
+ @Override |
+ public Type visitUnaryExpression(DartUnaryExpression node) { |
+ DartExpression expression = node.getArg(); |
+ Type type = nonVoidTypeOf(expression); |
+ Token operator = node.getOperator(); |
+ switch (operator) { |
+ case BIT_NOT: |
+ // Bit operations are only supported by integers and |
+ // thus cannot be looked up on num. To ease usage of |
+ // bit operations, we currently allow them to be used |
+ // if the left-hand-side is of type num. |
+ // TODO(karlklose) find a clean solution, i.e., without a special case for num. |
+ if (type.equals(numType)) { |
+ return intType; |
+ } else { |
+ String name = methodNameForUnaryOperator(node, operator); |
+ Member member = lookupMember(type, name, node); |
+ if (member != null) { |
+ node.setReferencedElement(member.getElement()); |
+ return analyzeMethodInvocation(type, member, name, node, |
+ Collections.<Type>emptyList(), |
+ Collections.<DartExpression>emptyList()); |
+ } else { |
+ return dynamicType; |
} |
} |
+ case NOT: |
+ checkAssignable(boolType, expression); |
+ return boolType; |
+ case SUB: |
+ case INC: |
+ case DEC: { |
+ if (type.getElement().isDynamic()) { |
+ return type; |
+ } |
+ InterfaceType itype = types.getInterfaceType(type); |
+ String operatorMethodName = methodNameForUnaryOperator(node, operator); |
+ Member member = itype.lookupMember(operatorMethodName); |
+ if (member == null) { |
+ return typeError(expression, TypeErrorCode.CANNOT_BE_RESOLVED, |
+ operatorMethodName); |
+ } |
+ MethodElement element = ((MethodElement) member.getElement()); |
+ node.setReferencedElement(element); |
+ Type returnType = ((FunctionType) member.getType()).getReturnType(); |
+ if (operator == Token.INC || operator == Token.DEC) { |
+ // For INC and DEC, "operator +" and "operator -" are used to add and subtract one, |
+ // respectively. Check that the resolved operator has a compatible parameter type. |
+ Iterator<VariableElement> it = element.getParameters().iterator(); |
+ if (!types.isAssignable(numType, it.next().getType())) { |
+ typeError(node, TypeErrorCode.OPERATOR_WRONG_OPERAND_TYPE, |
+ operatorMethodName, numType.toString()); |
+ } |
+ // Check that the return type of the operator is compatible with the receiver. |
+ checkAssignable(node, type, returnType); |
+ } |
+ return node.isPrefix() ? returnType : type; |
} |
- return null; |
+ default: |
+ throw internalError(node, "unknown operator %s", operator.toString()); |
} |
+ } |
- /** |
- * Report a compile-time error if either modifiers or elements.getModifiers() is static. |
- * @returns true if no compile-time error was reported |
- */ |
- private boolean canOverride(DartExpression node, Modifiers modifiers, Element element) { |
- if (element.getModifiers().isStatic()) { |
- onError(node, ResolverErrorCode.CANNOT_OVERRIDE_STATIC_MEMBER, |
- element.getName(), element.getEnclosingElement().getName()); |
- return false; |
- } else if (modifiers.isStatic()) { |
- onError(node, ResolverErrorCode.CANNOT_OVERRIDE_INSTANCE_MEMBER, |
- element.getName(), element.getEnclosingElement().getName()); |
- return false; |
- } |
- return true; |
- } |
+ @Override |
+ public Type visitUnit(DartUnit node) { |
+ return typeAsVoid(node); |
+ } |
- /** |
- * Report a static type error if member cannot override superElement, that is, they are not |
- * assignable. |
- */ |
- private void checkOverride(DartExpression node, Element member, Element superElement) { |
- String name = member.getName(); |
- Type superMember = typeAsMemberOf(superElement, currentClass); |
- if (member.getKind() == ElementKind.METHOD |
- && superElement.getKind() == ElementKind.METHOD) { |
- if (!types.isSubtype(member.getType(), superMember)) { |
- typeError(node, TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE, |
- name, superElement.getEnclosingElement().getName(), |
- member.getType(), superMember); |
- } |
- } else if (!types.isAssignable(superMember, member.getType())) { |
- typeError(node, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, |
- name, superElement.getEnclosingElement().getName(), |
- member.getType(), superMember); |
- } |
+ @Override |
+ public Type visitUnqualifiedInvocation(DartUnqualifiedInvocation node) { |
+ DartIdentifier target = node.getTarget(); |
+ String name = target.getTargetName(); |
+ Element element = target.getTargetSymbol(); |
+ node.setReferencedElement(element); |
+ Type type; |
+ switch (ElementKind.of(element)) { |
+ case FIELD: |
+ case METHOD: |
+ type = typeAsMemberOf(element, currentClass); |
+ break; |
+ case NONE: |
+ return typeError(target, TypeErrorCode.NOT_A_METHOD_IN, name, currentClass); |
+ default: |
+ type = element.getType(); |
+ break; |
} |
+ return checkInvocation(node, target, name, type); |
} |
+ |
+ @Override |
+ public Type visitVariable(DartVariable node) { |
+ return checkInitializedDeclaration(node, node.getValue()); |
+ } |
+ |
+ @Override |
+ public Type visitVariableStatement(DartVariableStatement node) { |
+ Type type = typeOf(node.getTypeNode()); |
+ visit(node.getVariables()); |
+ return type; |
+ } |
+ |
+ @Override |
+ public Type visitWhileStatement(DartWhileStatement node) { |
+ checkCondition(node.getCondition()); |
+ typeOf(node.getBody()); |
+ return voidType; |
+ } |
+ } |
+ /** |
+ * Perform type analysis on the given AST rooted at <code>node</code>. |
+ * |
+ * @param node The root of the tree to analyze |
+ * @param typeProvider The source of pre-defined type definitions |
+ * @param context The compilation context (DartCompilerContext) |
+ * @param currentClass The class that contains <code>node</code>. Will be null |
+ * for top-level declarations. |
+ * @return The type of <code>node</code>. |
+ */ |
+ public static Type analyze(DartNode node, CoreTypeProvider typeProvider, |
+ DartCompilerContext context, InterfaceType currentClass) { |
+ ConcurrentHashMap<ClassElement, List<Element>> unimplementedElements = |
+ new ConcurrentHashMap<ClassElement, List<Element>>(); |
+ Set<ClassElement> diagnosed = |
+ Collections.newSetFromMap(new ConcurrentHashMap<ClassElement, Boolean>()); |
+ Analyzer analyzer = new Analyzer(context, typeProvider, unimplementedElements, diagnosed); |
+ analyzer.setCurrentClass(currentClass); |
+ return node.accept(analyzer); |
+ } |
+ |
+ private final ConcurrentHashMap<ClassElement, List<Element>> unimplementedElements = |
+ new ConcurrentHashMap<ClassElement, List<Element>>(); |
+ |
+ private final Set<ClassElement> diagnosedAbstractClasses = |
+ Collections.newSetFromMap(new ConcurrentHashMap<ClassElement, Boolean>()); |
+ |
+ @Override |
+ public DartUnit exec(DartUnit unit, DartCompilerContext compilerContext, |
+ CoreTypeProvider typeProvider) { |
+ unit.accept(new Analyzer(compilerContext, typeProvider, unimplementedElements, |
+ diagnosedAbstractClasses)); |
+ |
+ return unit; |
} |
} |