Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(578)

Unified Diff: compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java

Issue 8523034: Compile Time Constants cycle check (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Reverted DartCompiler.java back to just bare necessities Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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&lt;T> {
+ * T t;
+ * }
+ * class Sub extends Super&lt;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&lt;T> {
- * T t;
- * }
- * class Sub extends Super&lt;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;
}
}

Powered by Google App Engine
This is Rietveld 408576698