| Index: dart/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
|
| diff --git a/dart/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/dart/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
|
| deleted file mode 100644
|
| index 4a5539b1eb3af39aa2ee0b9650150c0bb2d1aec4..0000000000000000000000000000000000000000
|
| --- a/dart/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
|
| +++ /dev/null
|
| @@ -1,3725 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -package com.google.dart.compiler.type;
|
| -
|
| -import com.google.common.annotations.VisibleForTesting;
|
| -import com.google.common.base.Joiner;
|
| -import com.google.common.base.Objects;
|
| -import com.google.common.collect.ArrayListMultimap;
|
| -import com.google.common.collect.ImmutableList;
|
| -import com.google.common.collect.ImmutableSet;
|
| -import com.google.common.collect.LinkedListMultimap;
|
| -import com.google.common.collect.Lists;
|
| -import com.google.common.collect.Maps;
|
| -import com.google.common.collect.Multimap;
|
| -import com.google.common.collect.Sets;
|
| -import com.google.dart.compiler.CommandLineOptions.CompilerOptions;
|
| -import com.google.dart.compiler.DartCompilationError;
|
| -import com.google.dart.compiler.DartCompilationPhase;
|
| -import com.google.dart.compiler.DartCompilerContext;
|
| -import com.google.dart.compiler.ErrorCode;
|
| -import com.google.dart.compiler.ErrorSeverity;
|
| -import com.google.dart.compiler.PackageLibraryManager;
|
| -import com.google.dart.compiler.Source;
|
| -import com.google.dart.compiler.ast.ASTNodes;
|
| -import com.google.dart.compiler.ast.ASTVisitor;
|
| -import com.google.dart.compiler.ast.DartArrayAccess;
|
| -import com.google.dart.compiler.ast.DartArrayLiteral;
|
| -import com.google.dart.compiler.ast.DartAssertStatement;
|
| -import com.google.dart.compiler.ast.DartBinaryExpression;
|
| -import com.google.dart.compiler.ast.DartBlock;
|
| -import com.google.dart.compiler.ast.DartBooleanLiteral;
|
| -import com.google.dart.compiler.ast.DartBreakStatement;
|
| -import com.google.dart.compiler.ast.DartCascadeExpression;
|
| -import com.google.dart.compiler.ast.DartCase;
|
| -import com.google.dart.compiler.ast.DartCatchBlock;
|
| -import com.google.dart.compiler.ast.DartClass;
|
| -import com.google.dart.compiler.ast.DartComment;
|
| -import com.google.dart.compiler.ast.DartConditional;
|
| -import com.google.dart.compiler.ast.DartContinueStatement;
|
| -import com.google.dart.compiler.ast.DartDeclaration;
|
| -import com.google.dart.compiler.ast.DartDefault;
|
| -import com.google.dart.compiler.ast.DartDoWhileStatement;
|
| -import com.google.dart.compiler.ast.DartDoubleLiteral;
|
| -import com.google.dart.compiler.ast.DartEmptyStatement;
|
| -import com.google.dart.compiler.ast.DartExportDirective;
|
| -import com.google.dart.compiler.ast.DartExprStmt;
|
| -import com.google.dart.compiler.ast.DartExpression;
|
| -import com.google.dart.compiler.ast.DartField;
|
| -import com.google.dart.compiler.ast.DartFieldDefinition;
|
| -import com.google.dart.compiler.ast.DartForInStatement;
|
| -import com.google.dart.compiler.ast.DartForStatement;
|
| -import com.google.dart.compiler.ast.DartFunction;
|
| -import com.google.dart.compiler.ast.DartFunctionExpression;
|
| -import com.google.dart.compiler.ast.DartFunctionObjectInvocation;
|
| -import com.google.dart.compiler.ast.DartFunctionTypeAlias;
|
| -import com.google.dart.compiler.ast.DartIdentifier;
|
| -import com.google.dart.compiler.ast.DartIfStatement;
|
| -import com.google.dart.compiler.ast.DartImportDirective;
|
| -import com.google.dart.compiler.ast.DartInitializer;
|
| -import com.google.dart.compiler.ast.DartIntegerLiteral;
|
| -import com.google.dart.compiler.ast.DartInvocation;
|
| -import com.google.dart.compiler.ast.DartLabel;
|
| -import com.google.dart.compiler.ast.DartLibraryDirective;
|
| -import com.google.dart.compiler.ast.DartLiteral;
|
| -import com.google.dart.compiler.ast.DartMapLiteral;
|
| -import com.google.dart.compiler.ast.DartMapLiteralEntry;
|
| -import com.google.dart.compiler.ast.DartMethodDefinition;
|
| -import com.google.dart.compiler.ast.DartMethodInvocation;
|
| -import com.google.dart.compiler.ast.DartNamedExpression;
|
| -import com.google.dart.compiler.ast.DartNativeBlock;
|
| -import com.google.dart.compiler.ast.DartNativeDirective;
|
| -import com.google.dart.compiler.ast.DartNewExpression;
|
| -import com.google.dart.compiler.ast.DartNode;
|
| -import com.google.dart.compiler.ast.DartNullLiteral;
|
| -import com.google.dart.compiler.ast.DartParameter;
|
| -import com.google.dart.compiler.ast.DartParameterizedTypeNode;
|
| -import com.google.dart.compiler.ast.DartParenthesizedExpression;
|
| -import com.google.dart.compiler.ast.DartPropertyAccess;
|
| -import com.google.dart.compiler.ast.DartRedirectConstructorInvocation;
|
| -import com.google.dart.compiler.ast.DartReturnStatement;
|
| -import com.google.dart.compiler.ast.DartSourceDirective;
|
| -import com.google.dart.compiler.ast.DartStatement;
|
| -import com.google.dart.compiler.ast.DartStringInterpolation;
|
| -import com.google.dart.compiler.ast.DartStringLiteral;
|
| -import com.google.dart.compiler.ast.DartSuperConstructorInvocation;
|
| -import com.google.dart.compiler.ast.DartSuperExpression;
|
| -import com.google.dart.compiler.ast.DartSwitchMember;
|
| -import com.google.dart.compiler.ast.DartSwitchStatement;
|
| -import com.google.dart.compiler.ast.DartSyntheticErrorExpression;
|
| -import com.google.dart.compiler.ast.DartSyntheticErrorIdentifier;
|
| -import com.google.dart.compiler.ast.DartSyntheticErrorStatement;
|
| -import com.google.dart.compiler.ast.DartThisExpression;
|
| -import com.google.dart.compiler.ast.DartThrowExpression;
|
| -import com.google.dart.compiler.ast.DartTryStatement;
|
| -import com.google.dart.compiler.ast.DartTypeExpression;
|
| -import com.google.dart.compiler.ast.DartTypeNode;
|
| -import com.google.dart.compiler.ast.DartTypeParameter;
|
| -import com.google.dart.compiler.ast.DartUnaryExpression;
|
| -import com.google.dart.compiler.ast.DartUnit;
|
| -import com.google.dart.compiler.ast.DartUnqualifiedInvocation;
|
| -import com.google.dart.compiler.ast.DartVariable;
|
| -import com.google.dart.compiler.ast.DartVariableStatement;
|
| -import com.google.dart.compiler.ast.DartWhileStatement;
|
| -import com.google.dart.compiler.ast.Modifiers;
|
| -import com.google.dart.compiler.common.HasSourceInfo;
|
| -import com.google.dart.compiler.common.SourceInfo;
|
| -import com.google.dart.compiler.parser.Token;
|
| -import com.google.dart.compiler.resolver.ClassElement;
|
| -import com.google.dart.compiler.resolver.ClassNodeElement;
|
| -import com.google.dart.compiler.resolver.ConstructorElement;
|
| -import com.google.dart.compiler.resolver.CoreTypeProvider;
|
| -import com.google.dart.compiler.resolver.CyclicDeclarationException;
|
| -import com.google.dart.compiler.resolver.Element;
|
| -import com.google.dart.compiler.resolver.ElementKind;
|
| -import com.google.dart.compiler.resolver.Elements;
|
| -import com.google.dart.compiler.resolver.FieldElement;
|
| -import com.google.dart.compiler.resolver.FunctionAliasElement;
|
| -import com.google.dart.compiler.resolver.MethodElement;
|
| -import com.google.dart.compiler.resolver.ResolverErrorCode;
|
| -import com.google.dart.compiler.resolver.TypeErrorCode;
|
| -import com.google.dart.compiler.resolver.VariableElement;
|
| -import com.google.dart.compiler.type.InterfaceType.Member;
|
| -import com.google.dart.compiler.util.apache.ObjectUtils;
|
| -
|
| -import java.util.ArrayList;
|
| -import java.util.Arrays;
|
| -import java.util.Collection;
|
| -import java.util.Collections;
|
| -import java.util.Iterator;
|
| -import java.util.LinkedHashMap;
|
| -import java.util.LinkedList;
|
| -import java.util.List;
|
| -import java.util.Map;
|
| -import java.util.Map.Entry;
|
| -import java.util.Set;
|
| -
|
| -/**
|
| - * Analyzer of static type information.
|
| - */
|
| -public class TypeAnalyzer implements DartCompilationPhase {
|
| -
|
| - private final Set<ClassElement> diagnosedAbstractClasses = Sets.newHashSet();
|
| -
|
| - /**
|
| - * 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) {
|
| - Set<ClassElement> diagnosed = Sets.newHashSet();
|
| - Analyzer analyzer = new Analyzer(context, typeProvider, diagnosed);
|
| - analyzer.setCurrentClass(currentClass);
|
| - return node.accept(analyzer);
|
| - }
|
| -
|
| - @Override
|
| - public DartUnit exec(DartUnit unit, DartCompilerContext context, CoreTypeProvider typeProvider) {
|
| - unit.accept(new Analyzer(context, typeProvider, diagnosedAbstractClasses));
|
| - return unit;
|
| - }
|
| -
|
| - @VisibleForTesting
|
| - static class Analyzer extends ASTVisitor<Type> {
|
| - private final DynamicType dynamicType;
|
| - private final Type stringType;
|
| - private final InterfaceType defaultLiteralMapType;
|
| - private final Type voidType;
|
| - private final DartCompilerContext context;
|
| - private final Types types;
|
| - private Type expected;
|
| - private MethodElement currentMethod;
|
| - private InterfaceType currentClass;
|
| - private final InterfaceType objectType;
|
| - private final InterfaceType boolType;
|
| - private final InterfaceType numType;
|
| - private final InterfaceType intType;
|
| - private final InterfaceType doubleType;
|
| - private final Type nullType;
|
| - private final InterfaceType functionType;
|
| - private final InterfaceType dynamicIteratorType;
|
| - private final boolean developerModeChecks;
|
| - private final boolean suppressSdkWarnings;
|
| - private final boolean typeChecksForInferredTypes;
|
| - private final boolean reportNoMemberWhenHasInterceptor;
|
| - private final Map<DartBlock, VariableElementsRestorer> restoreOnBlockExit = Maps.newHashMap();
|
| - /**
|
| - * When we see variable assignment, we remember here old {@link Type} (if not done already) and
|
| - * set new {@link Type} into {@link VariableElement}. On the exit from basic block we remove
|
| - * first element and merge new types with old types.
|
| - */
|
| - private final LinkedList<BlockTypeContext> blockOldTypes = Lists.newLinkedList();
|
| - private static class BlockTypeContext {
|
| - final Map<VariableElement, Type> oldTypes = Maps.newHashMap();
|
| - final Map<VariableElement, Type> newTypes = Maps.newHashMap();
|
| - void rememberOldType(VariableElement element, Type oriType) {
|
| - if (!oldTypes.containsKey(element)) {
|
| - oldTypes.put(element, oriType);
|
| - }
|
| - }
|
| - void setType(VariableElement element, Type newType) {
|
| - if (canSetType(element)) {
|
| - rememberOldType(element, element.getType());
|
| - newTypes.put(element, newType);
|
| - Elements.setType(element, newType);
|
| - }
|
| - }
|
| - boolean canSetType(VariableElement element) {
|
| - Type type = element.getType();
|
| - // no type declared, no assignment yet
|
| - if (TypeKind.of(type) == TypeKind.DYNAMIC) {
|
| - return true;
|
| - }
|
| - // was assignment, inferred
|
| - if (type != null && TypeQuality.isInferred(type)) {
|
| - return true;
|
| - }
|
| - // was declared with type, keep it
|
| - return false;
|
| - }
|
| - Map<VariableElement, Type> getNewTypesAndRestoreOld() {
|
| - for (Entry<VariableElement, Type> entry : oldTypes.entrySet()) {
|
| - VariableElement variable = entry.getKey();
|
| - Elements.setType(variable, entry.getValue());
|
| - }
|
| - return newTypes;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Keeps track of the number of nested catches, used to detect re-throws
|
| - * outside of any catch block.
|
| - */
|
| - private int catchDepth = 0;
|
| -
|
| - Analyzer(DartCompilerContext context, CoreTypeProvider typeProvider,
|
| - Set<ClassElement> diagnosedAbstractClasses) {
|
| - this.context = context;
|
| - this.developerModeChecks = context.getCompilerConfiguration().developerModeChecks();
|
| - this.types = Types.getInstance(typeProvider);
|
| - this.dynamicType = typeProvider.getDynamicType();
|
| - this.stringType = typeProvider.getStringType();
|
| - this.defaultLiteralMapType = typeProvider.getMapType(stringType, dynamicType);
|
| - this.voidType = typeProvider.getVoidType();
|
| - this.objectType = typeProvider.getObjectType();
|
| - this.boolType = typeProvider.getBoolType();
|
| - this.numType = typeProvider.getNumType();
|
| - this.intType = typeProvider.getIntType();
|
| - this.doubleType = typeProvider.getDoubleType();
|
| - this.nullType = typeProvider.getNullType();
|
| - this.functionType = typeProvider.getFunctionType();
|
| - this.dynamicIteratorType = typeProvider.getIteratorType(dynamicType);
|
| - CompilerOptions compilerOptions = context.getCompilerConfiguration().getCompilerOptions();
|
| - this.suppressSdkWarnings = compilerOptions.suppressSdkWarnings();
|
| - this.typeChecksForInferredTypes = compilerOptions.typeChecksForInferredTypes();
|
| - this.reportNoMemberWhenHasInterceptor = compilerOptions.reportNoMemberWhenHasInterceptor();
|
| - }
|
| -
|
| - @VisibleForTesting
|
| - void setCurrentClass(InterfaceType type) {
|
| - currentClass = type;
|
| - }
|
| -
|
| - @VisibleForTesting
|
| - void pushBasicBlockContext() {
|
| - blockOldTypes.addFirst(new BlockTypeContext());
|
| - }
|
| -
|
| - private InterfaceType getCurrentClass() {
|
| - return currentClass;
|
| - }
|
| -
|
| - private DynamicType typeError(HasSourceInfo node, ErrorCode code, Object... arguments) {
|
| - onError(node, code, arguments);
|
| - return dynamicType;
|
| - }
|
| -
|
| - private void onError(HasSourceInfo node, ErrorCode errorCode, Object... arguments) {
|
| - onError(node.getSourceInfo(), errorCode, arguments);
|
| - }
|
| -
|
| - private void onError(SourceInfo errorTarget, ErrorCode errorCode, Object... arguments) {
|
| - if (suppressSdkWarnings) {
|
| - ErrorSeverity errorSeverity = errorCode.getErrorSeverity();
|
| - if (errorSeverity == ErrorSeverity.WARNING || errorSeverity == ErrorSeverity.INFO) {
|
| - Source source = errorTarget.getSource();
|
| - if (source != null && PackageLibraryManager.isDartUri(source.getUri())) {
|
| - return;
|
| - }
|
| - }
|
| - }
|
| - context.onError(new DartCompilationError(errorTarget, errorCode, arguments));
|
| - }
|
| -
|
| - AssertionError internalError(HasSourceInfo 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 typeOfLiteral(DartLiteral node) {
|
| - Type type = node.getType();
|
| - return type == null ? voidType : type;
|
| - }
|
| -
|
| - 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_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.getElement());
|
| - }
|
| -
|
| - private String methodNameForUnaryOperator(DartNode diagnosticNode, Token operator) {
|
| - if (operator == Token.SUB) {
|
| - return "operator -";
|
| - } else if (operator == Token.BIT_NOT) {
|
| - return "operator ~";
|
| - }
|
| - return "operator " + getBasicOperator(diagnosticNode, operator).getSyntax();
|
| - }
|
| -
|
| - private String methodNameForBinaryOperator(Token operator) {
|
| - if (operator.getSyntax().equals("-")) {
|
| - return "operator -binary";
|
| - }
|
| - return "operator " + operator.getSyntax();
|
| - }
|
| -
|
| - private Type analyzeBinaryOperator(DartNode node, Type lhsType, Token operator,
|
| - DartNode diagnosticNode, DartExpression rhs) {
|
| - Type rhsType = nonVoidTypeOf(rhs);
|
| - String methodName = methodNameForBinaryOperator(operator);
|
| - HasSourceInfo problemTarget = getOperatorHasSourceInfo(node);
|
| - Member member = lookupMember(lhsType, methodName, problemTarget);
|
| - if (member != null) {
|
| - Element element = member.getElement();
|
| - node.setElement(element);
|
| - FunctionType methodType = getMethodType(lhsType, member, methodName, diagnosticNode);
|
| - checkDeprecated(problemTarget, element);
|
| - Type returnType = checkInvocation(Collections.<DartExpression> singletonList(rhs),
|
| - Collections.<Type> singletonList(rhsType),
|
| - diagnosticNode, methodName, methodType, null);
|
| - // tweak return type for int/int and int/double operators
|
| - {
|
| - boolean lhsInt = intType.equals(lhsType);
|
| - boolean rhsInt = intType.equals(rhsType);
|
| - boolean lhsDouble = doubleType.equals(lhsType);
|
| - boolean rhsDouble = doubleType.equals(rhsType);
|
| - switch (operator) {
|
| - case ADD:
|
| - case SUB:
|
| - case MUL:
|
| - case TRUNC:
|
| - case MOD:
|
| - if (lhsInt && rhsInt) {
|
| - return intType;
|
| - }
|
| - case DIV:
|
| - if (lhsDouble || rhsDouble) {
|
| - return doubleType;
|
| - }
|
| - }
|
| - }
|
| - // done
|
| - return returnType;
|
| - } else {
|
| - return dynamicType;
|
| - }
|
| - }
|
| -
|
| - private Type analyzeTernaryOperator(DartNode node, Type lhsType, Token operator,
|
| - DartNode diagnosticNode, DartExpression arg1, DartExpression arg2) {
|
| - String methodName = methodNameForBinaryOperator(operator);
|
| - HasSourceInfo problemTarget = getOperatorHasSourceInfo(node);
|
| - Member member = lookupMember(lhsType, methodName, problemTarget);
|
| - if (member != null) {
|
| - Element element = member.getElement();
|
| - node.setElement(element);
|
| - FunctionType methodType = getMethodType(lhsType, member, methodName, diagnosticNode);
|
| - checkDeprecated(problemTarget, element);
|
| - return checkInvocation(ImmutableList.of(arg1, arg2), diagnosticNode, methodName,
|
| - methodType, null);
|
| - } 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: {
|
| - // prepare RHS type
|
| - Type rhs = getInvocationArgumentType(rhsNode);
|
| - try {
|
| - if (!hasInferredType(lhsNode)) {
|
| - if (checkAssignable(rhsNode, lhs, rhs)) {
|
| - inferFunctionLiteralParametersTypes(rhsNode, lhs);
|
| - }
|
| - }
|
| - } finally {
|
| - if (rhsNode instanceof DartFunctionExpression) {
|
| - rhsNode.accept(this);
|
| - }
|
| - }
|
| - // may be replace type of variable
|
| - setVariableElementType(lhsNode.getElement(), rhs, getTypeQuality(rhsNode));
|
| - checkAssignableElement(lhsNode);
|
| - // if cascade, then use type of "lhs" qualifier
|
| - if (lhsNode instanceof DartPropertyAccess) {
|
| - DartPropertyAccess lhsAccess = (DartPropertyAccess) lhsNode;
|
| - if (lhsAccess.isCascade()) {
|
| - return lhsAccess.getRealTarget().getType();
|
| - }
|
| - }
|
| - // use type or "rhs"
|
| - return rhs;
|
| - }
|
| -
|
| - 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);
|
| - checkAssignableElement(lhsNode);
|
| - 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.
|
| - checkAssignableElement(lhsNode);
|
| - 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;
|
| - }
|
| - }
|
| -
|
| - 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);
|
| - }
|
| - }
|
| -
|
| - 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: {
|
| - // try to resolve "==" to "operator equals()", but don't complain if can not find it
|
| - String methodName = methodNameForBinaryOperator(operator);
|
| - InterfaceType itype = types.getInterfaceType(lhs);
|
| - if (itype != null) {
|
| - Member member = itype.lookupMember(methodName);
|
| - if (member != null) {
|
| - node.setElement(member.getElement());
|
| - }
|
| - }
|
| - }
|
| - case NE:
|
| - case EQ_STRICT:
|
| - case NE_STRICT:
|
| - nonVoidTypeOf(rhsNode);
|
| - return boolType;
|
| -
|
| - case AS:
|
| - return typeOf(rhsNode);
|
| -
|
| - 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);
|
| - }
|
| - }
|
| -
|
| - private void checkAssignableElement(DartExpression lhsNode) {
|
| - Element lhsElement = lhsNode.getElement();
|
| - switch (ElementKind.of(lhsElement)) {
|
| - case DYNAMIC:
|
| - case VARIABLE:
|
| - case PARAMETER:
|
| - case FIELD:
|
| - case NONE:
|
| - // OK or unknown
|
| - break;
|
| -
|
| - case METHOD:
|
| - if (lhsElement.getModifiers().isSetter()
|
| - || lhsElement.getModifiers().isGetter()
|
| - || lhsElement.getModifiers().isOperator()) {
|
| - // The check for methods with setters is elsewhere.
|
| - break;
|
| - }
|
| - default:
|
| - onError(lhsNode, TypeErrorCode.CANNOT_ASSIGN_TO, ElementKind.of(lhsElement));
|
| - break;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @return the best guess for operator token location in the given {@link DartNode}.
|
| - */
|
| - private static HasSourceInfo getOperatorHasSourceInfo(DartNode node) {
|
| - Token operator = null;
|
| - int offset = 0;
|
| - if (node instanceof DartBinaryExpression) {
|
| - DartBinaryExpression binary = (DartBinaryExpression) node;
|
| - operator = binary.getOperator();
|
| - offset = binary.getOperatorOffset();
|
| - }
|
| - if (node instanceof DartUnaryExpression) {
|
| - DartUnaryExpression binary = (DartUnaryExpression) node;
|
| - operator = binary.getOperator();
|
| - offset = binary.getOperatorOffset();
|
| - }
|
| - if (operator != null) {
|
| - Source source = node.getSourceInfo().getSource();
|
| - int length = operator.getSyntax().length();
|
| - final SourceInfo sourceInfo = new SourceInfo(source, offset, length);
|
| - return new HasSourceInfo() {
|
| - @Override
|
| - public SourceInfo getSourceInfo() {
|
| - return sourceInfo;
|
| - }
|
| - };
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitExprStmt(DartExprStmt node) {
|
| - Type type = typeOf(node.getExpression());
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitVariableStatement(DartVariableStatement node) {
|
| - Type type = typeOf(node.getTypeNode());
|
| - visit(node.getVariables());
|
| - return type;
|
| - }
|
| -
|
| - private Member lookupMember(Type receiver, String methodName, HasSourceInfo problemTarget) {
|
| - InterfaceType itype = types.getInterfaceType(receiver);
|
| - if (itype == null) {
|
| - diagnoseNonInterfaceType(problemTarget, receiver);
|
| - return null;
|
| - }
|
| - Member member = itype.lookupMember(methodName);
|
| - if (member == null) {
|
| - member = itype.lookupMember("setter " + methodName);
|
| - }
|
| - // is "receiver" is inferred, attempt to find member in one of the subtypes
|
| - boolean hasMemberInSubClasses = false;
|
| - if (member == null) {
|
| - if (TypeQuality.of(receiver) == TypeQuality.INFERRED && receiver instanceof InterfaceType) {
|
| - List<Member> subMembers = ((InterfaceType) receiver).lookupSubTypeMembers(methodName);
|
| - hasMemberInSubClasses = !subMembers.isEmpty();
|
| - if (subMembers.size() == 1) {
|
| - member = subMembers.get(0);
|
| - }
|
| - }
|
| - }
|
| - // report problem
|
| - if (member == null && problemTarget != null) {
|
| - if (reportNoMemberWhenHasInterceptor || !Elements.handlesNoSuchMethod(itype)) {
|
| - if (typeChecksForInferredTypes && !hasMemberInSubClasses
|
| - && !isTooGenericInferredType(receiver) || !TypeQuality.isInferred(receiver)) {
|
| - ErrorCode code = TypeQuality.isInferred(receiver)
|
| - ? TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED_INFERRED
|
| - : TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED;
|
| - typeError(problemTarget, code, receiver, methodName);
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| - return member;
|
| - }
|
| -
|
| - /**
|
| - * If left-hand-side is {@link VariableElement} with propagated type, then remember type before
|
| - * current "basic block" and set new type.
|
| - */
|
| - private void setVariableElementType(Element element, Type type, TypeQuality quality) {
|
| - if (ElementKind.of(element) == ElementKind.VARIABLE) {
|
| - VariableElement variableElement = (VariableElement) element;
|
| - Type newType = Types.makeInferred(type, quality);
|
| - blockOldTypes.getFirst().setType(variableElement, newType);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if given {@link DartNode} has inferred {@link Type}.
|
| - */
|
| - private static boolean hasInferredType(DartNode node) {
|
| - return node != null && hasInferredType(node.getElement());
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if given {@link Element} is has inferred {@link Type}.
|
| - */
|
| - private static boolean hasInferredType(Element element) {
|
| - return element != null && element.getType() != null
|
| - && element.getType().getQuality() != TypeQuality.EXACT;
|
| - }
|
| -
|
| - /**
|
| - * Helper for visiting {@link DartNode} which happens only if "condition" is satisfied. Attempts
|
| - * to infer types of {@link VariableElement}s from given "condition".
|
| - */
|
| - private void visitConditionalNode(DartExpression condition, DartNode node) {
|
| - final VariableElementsRestorer variableRestorer = new VariableElementsRestorer();
|
| - try {
|
| - inferVariableTypesFromIsConditions(condition, variableRestorer);
|
| - typeOf(node);
|
| - } finally {
|
| - variableRestorer.restore();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Helper for setting {@link Type}s of {@link VariableElement}s when given "condition" is
|
| - * satisfied.
|
| - */
|
| - private void inferVariableTypesFromIsConditions(DartExpression condition,
|
| - final VariableElementsRestorer variableRestorer) {
|
| - if (condition != null) {
|
| - condition.accept(new ASTVisitor<Void>() {
|
| - boolean negation = false;
|
| - @Override
|
| - public Void visitUnaryExpression(DartUnaryExpression node) {
|
| - boolean negationOld = negation;
|
| - try {
|
| - if (node.getOperator() == Token.NOT) {
|
| - negation = !negation;
|
| - }
|
| - return super.visitUnaryExpression(node);
|
| - } finally {
|
| - negation = negationOld;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Void visitBinaryExpression(DartBinaryExpression node) {
|
| - // apply "as" always
|
| - // apply "is" only if not negated
|
| - if (node.getOperator() == Token.AS || node.getOperator() == Token.IS && !negation) {
|
| - DartExpression arg1 = node.getArg1();
|
| - DartExpression arg2 = node.getArg2();
|
| - if (arg1 instanceof DartIdentifier && arg1.getElement() instanceof VariableElement
|
| - && arg2 instanceof DartTypeExpression) {
|
| - VariableElement variableElement = (VariableElement) arg1.getElement();
|
| - Type rhsType = arg2.getType();
|
| - Type varType = Types.makeInferred(rhsType);
|
| - variableRestorer.setType(variableElement, varType);
|
| - }
|
| - }
|
| - // operator || means that we can not be sure about types
|
| - if (node.getOperator() == Token.OR) {
|
| - return null;
|
| - }
|
| - // continue
|
| - return super.visitBinaryExpression(node);
|
| - }
|
| - });
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Helper to temporarily set {@link Type} of {@link VariableElement} and restore original later.
|
| - */
|
| - private class VariableElementsRestorer {
|
| - private final Map<VariableElement, Type> typesMap = Maps.newHashMap();
|
| - void setType(VariableElement element, Type inferredType) {
|
| - if (element == null) {
|
| - return;
|
| - }
|
| - Type currentType = element.getType();
|
| - // remember original if not yet
|
| - if (!typesMap.containsKey(element)) {
|
| - typesMap.put(element, currentType);
|
| - }
|
| - // apply inferred type
|
| - if (inferredType != null) {
|
| - if (TypeKind.of(currentType) == TypeKind.DYNAMIC && TypeQuality.isInferred(currentType)) {
|
| - // if we fell back to Dynamic, keep it
|
| - } else {
|
| - Type unionType = getUnionType(currentType, inferredType);
|
| - if (unionType != currentType) {
|
| - unionType = Types.makeInferred(unionType);
|
| - }
|
| - blockOldTypes.getFirst().rememberOldType(element, element.getType());
|
| - Elements.setType(element, unionType);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void restore() {
|
| - for (Entry<VariableElement, Type> entry : typesMap.entrySet()) {
|
| - Elements.setType(entry.getKey(), entry.getValue());
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @return the {@link Type} which is both "a" and "b" types. May be "dynamic" if "a" and "b"
|
| - * don't form hierarchy.
|
| - */
|
| - private Type getUnionType(Type curType, Type newType) {
|
| - if (TypeKind.of(curType) == TypeKind.DYNAMIC) {
|
| - return newType;
|
| - }
|
| - if (TypeKind.of(newType) == TypeKind.DYNAMIC) {
|
| - return curType;
|
| - }
|
| - if (types.isSubtype(curType, newType)) {
|
| - return curType;
|
| - }
|
| - if (types.isSubtype(newType, curType)) {
|
| - return newType;
|
| - }
|
| - // if InterfaceType, use union
|
| - if (curType instanceof InterfaceType && newType instanceof InterfaceType) {
|
| - return types.unionTypes(ImmutableList.of((InterfaceType) curType, (InterfaceType) newType));
|
| - }
|
| - // keep type as is
|
| - return curType;
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if we can prove that given {@link DartStatement} always leads to
|
| - * the exit from the enclosing function.
|
| - */
|
| - private static boolean isExitFromFunction(DartStatement statement) {
|
| - return isExitFromFunction(statement, false);
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if we can prove that given {@link DartStatement} always leads to
|
| - * the exit from the enclosing function, or stops execution of the enclosing loop.
|
| - */
|
| - private static boolean isExitFromFunctionOrLoop(DartStatement statement) {
|
| - return isExitFromFunction(statement, true);
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if we can prove that given {@link DartStatement} always leads to
|
| - * the exit from the enclosing function, or stops enclosing loop execution.
|
| - */
|
| - private static boolean isExitFromFunction(DartStatement statement, boolean orLoop) {
|
| - // "return" is always exit
|
| - if (statement instanceof DartReturnStatement) {
|
| - return true;
|
| - }
|
| - // "throw" is exit if no enclosing "try"
|
| - if (statement instanceof DartExprStmt && (((DartExprStmt) statement).getExpression() instanceof DartThrowExpression)) {
|
| - for (DartNode p = statement; p != null && !(p instanceof DartFunction); p = p.getParent()) {
|
| - // TODO(scheglov) Can be enhanced:
|
| - // 1. check if there is "catch" block which can catch this exception;
|
| - // 2. even if there is such "catch", we will not visit the rest of the "try".
|
| - if (p instanceof DartTryStatement) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| - // "block" is exit if its last statement is exit
|
| - if (statement instanceof DartBlock) {
|
| - DartBlock block = (DartBlock) statement;
|
| - List<DartStatement> statements = block.getStatements();
|
| - if (!statements.isEmpty()) {
|
| - return isExitFromFunction(statements.get(statements.size() - 1), orLoop);
|
| - }
|
| - }
|
| - // check also if we stop execution of the loop body
|
| - if (orLoop) {
|
| - if (statement instanceof DartContinueStatement) {
|
| - return true;
|
| - }
|
| - if (statement instanceof DartBreakStatement) {
|
| - return true;
|
| - }
|
| - }
|
| - // can not prove that given statement is always exit
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Helper for setting {@link Type}s of {@link VariableElement}s when given "condition" is NOT
|
| - * satisfied.
|
| - */
|
| - private static void inferVariableTypesFromIsNotConditions(DartExpression condition,
|
| - final VariableElementsRestorer variableRestorer) {
|
| - condition.accept(new ASTVisitor<Void>() {
|
| - boolean negation = false;
|
| -
|
| - @Override
|
| - public Void visitUnaryExpression(DartUnaryExpression node) {
|
| - boolean negationOld = negation;
|
| - try {
|
| - if (node.getOperator() == Token.NOT) {
|
| - negation = !negation;
|
| - }
|
| - return super.visitUnaryExpression(node);
|
| - } finally {
|
| - negation = negationOld;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Void visitBinaryExpression(DartBinaryExpression node) {
|
| - // analyze (v is Type)
|
| - if (node.getOperator() == Token.IS) {
|
| - DartExpression arg1 = node.getArg1();
|
| - DartExpression arg2 = node.getArg2();
|
| - if (arg1 instanceof DartIdentifier && arg1.getElement() instanceof VariableElement) {
|
| - VariableElement variableElement = (VariableElement) arg1.getElement();
|
| - // !(v is Type)
|
| - if (negation && arg2 instanceof DartTypeExpression) {
|
| - Type isType = arg2.getType();
|
| - Type varType = Types.makeInferred(isType);
|
| - variableRestorer.setType(variableElement, varType);
|
| - }
|
| - // (v is! Type)
|
| - if (!negation) {
|
| - if (arg2 instanceof DartUnaryExpression) {
|
| - DartUnaryExpression unary2 = (DartUnaryExpression) arg2;
|
| - if (unary2.getOperator() == Token.NOT
|
| - && unary2.getArg() instanceof DartTypeExpression) {
|
| - Type isType = unary2.getArg().getType();
|
| - Type varType = Types.makeInferred(isType);
|
| - variableRestorer.setType(variableElement, varType);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - // visit || expressions
|
| - if (node.getOperator() == Token.OR) {
|
| - return super.visitBinaryExpression(node);
|
| - }
|
| - // other operators, such as && - don't infer types
|
| - return null;
|
| - }
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * If type of variable-like {@link DartDeclaration} (i.e. variables, parameter, field) is not
|
| - * specified and we know somehow this type, then use it.
|
| - */
|
| - private static void inferVariableDeclarationType(DartDeclaration<?> node, DartExpression value) {
|
| - Type type = value.getType();
|
| - TypeQuality quality = getTypeQuality(value);
|
| - inferVariableDeclarationType(node, type, quality);
|
| - }
|
| -
|
| - /**
|
| - * If type of variable-like {@link DartDeclaration} (i.e. variables, parameter, field) is not
|
| - * specified and we know somehow this type, then use it.
|
| - */
|
| - private static void inferVariableDeclarationType(DartDeclaration<?> node, Type type,
|
| - TypeQuality typeQuality) {
|
| - if (type != null && TypeKind.of(type) != TypeKind.DYNAMIC) {
|
| - Element element = node.getElement();
|
| - if (element != null && TypeKind.of(element.getType()) == TypeKind.DYNAMIC) {
|
| - Type inferredType = Types.makeInferred(type, typeQuality);
|
| - Elements.setType(element, inferredType);
|
| - node.getName().setType(inferredType);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * If given "mayBeLiteral" is {@link DartFunctionExpression} without explicit parameters types
|
| - * and its required type is {@link FunctionAliasType}, then infer parameters types from
|
| - * {@link FunctionAliasType}.
|
| - */
|
| - private static void inferFunctionLiteralParametersTypes(DartExpression mayBeLiteral,
|
| - Type mayBeFunctionType) {
|
| - if (mayBeLiteral instanceof DartFunctionExpression) {
|
| - // prepare required type of function literal
|
| - FunctionType requiredType = null;
|
| - if (TypeKind.of(mayBeFunctionType) == TypeKind.FUNCTION) {
|
| - requiredType = (FunctionType) mayBeFunctionType;
|
| - }
|
| - if (TypeKind.of(mayBeFunctionType) == TypeKind.FUNCTION_ALIAS) {
|
| - FunctionAliasType functionAliasType = (FunctionAliasType) mayBeFunctionType;
|
| - requiredType = Types.asFunctionType(functionAliasType);
|
| - }
|
| - // OK, we can try to infer parameter types
|
| - if (requiredType != null) {
|
| - DartFunctionExpression literal = (DartFunctionExpression) mayBeLiteral;
|
| - List<DartParameter> parameterNodes = literal.getFunction().getParameters();
|
| - // try to infer types of "normal" parameters
|
| - List<Type> requiredNormalParameterTypes = requiredType.getParameterTypes();
|
| - int n = Math.min(requiredNormalParameterTypes.size(), parameterNodes.size());
|
| - for (int i = 0; i < n; i++) {
|
| - Type requiredNormalParameterType = requiredNormalParameterTypes.get(i);
|
| - DartParameter parameterNode = parameterNodes.get(i);
|
| - inferVariableDeclarationType(parameterNode, requiredNormalParameterType,
|
| - TypeQuality.INFERRED);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * When we cannot prove that node was visited, then type is intersection of old/new types.
|
| - */
|
| - private void setMergedVariableTypes(BlockTypeContext blockTypeContext) {
|
| - for (VariableElement variable : blockTypeContext.newTypes.keySet()) {
|
| - Type newType = blockTypeContext.newTypes.get(variable);
|
| - Type oldType = blockTypeContext.oldTypes.get(variable);
|
| - Type mergedType = types.intersection(newType, oldType);
|
| - TypeQuality mergedTypeQuality = Types.getIntersectionQuality(newType, oldType);
|
| - setVariableElementType(variable, mergedType, mergedTypeQuality);
|
| - }
|
| - }
|
| -
|
| - private boolean isAssignable(Type t, Type s) {
|
| - t.getClass(); // Null check.
|
| - s.getClass(); // Null check.
|
| - // ignore inferred types, treat them as Dynamic
|
| - if (!typeChecksForInferredTypes) {
|
| - if (TypeQuality.isInferred(t) || TypeQuality.isInferred(s)) {
|
| - return true;
|
| - }
|
| - }
|
| - // do check
|
| - return types.isAssignable(t, s);
|
| - }
|
| -
|
| - private boolean checkAssignable(DartNode node, Type t, Type s) {
|
| - if (!isAssignable(t, s)) {
|
| - TypeErrorCode errorCode = TypeQuality.isInferred(t) || TypeQuality.isInferred(s)
|
| - ? TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE_INFERRED
|
| - : TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE;
|
| - typeError(node, errorCode, s, t);
|
| - return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - private boolean checkAssignable(Type targetType, DartExpression node) {
|
| - // analyze "node"
|
| - Type nodeType = typeOf(node);
|
| - // target is Dynamic, any source type is good, even "void"
|
| - if (TypeKind.of(targetType) == TypeKind.DYNAMIC) {
|
| - return true;
|
| - }
|
| - // source was Dynamic
|
| - if (hasInferredType(node)) {
|
| - return true;
|
| - }
|
| - // OK, check types
|
| - checkNonVoid(node, nodeType);
|
| - return checkAssignable(node, targetType, nodeType);
|
| - }
|
| -
|
| - private FunctionType getMethodType(Type receiver, Member member, String name,
|
| - DartNode diagnosticNode) {
|
| - FunctionType functionType = getMethodType0(receiver, member, name, diagnosticNode);
|
| - if (TypeQuality.isInferred(receiver)) {
|
| - functionType = (FunctionType) Types.makeInferred(functionType);
|
| - }
|
| - return functionType;
|
| - }
|
| -
|
| - private FunctionType getMethodType0(Type receiver, Member member, String name,
|
| - DartNode diagnosticNode) {
|
| - if (member == null) {
|
| - return dynamicType;
|
| - }
|
| - Element element = member.getElement();
|
| - switch (ElementKind.of(element)) {
|
| - case METHOD: {
|
| - MethodElement method = (MethodElement) element;
|
| - if (method.getModifiers().isStatic()) {
|
| - return typeError(diagnosticNode, TypeErrorCode.IS_STATIC_METHOD_IN,
|
| - name, receiver);
|
| - }
|
| - return (FunctionType) member.getType();
|
| - }
|
| - case FIELD: {
|
| - FieldElement field = (FieldElement) element;
|
| - if (field.getModifiers().isStatic()) {
|
| - return typeError(diagnosticNode, TypeErrorCode.IS_STATIC_FIELD_IN,
|
| - name, receiver);
|
| - }
|
| - switch (TypeKind.of(member.getType())) {
|
| - case FUNCTION:
|
| - return (FunctionType) member.getType();
|
| - case FUNCTION_ALIAS:
|
| - return Types.asFunctionType((FunctionAliasType) member.getType());
|
| - default:
|
| - // target.field() as Function invocation.
|
| - if (Elements.isFieldWithGetter(field)) {
|
| - Type fieldType = field.getType();
|
| - if (!types.isAssignable(functionType, fieldType)) {
|
| - onError(diagnosticNode, TypeErrorCode.NOT_A_FUNCTION_TYPE_FIELD, field.getName(), fieldType);
|
| - }
|
| - }
|
| - return dynamicType;
|
| - }
|
| - }
|
| - default:
|
| - if (typeChecksForInferredTypes || !TypeQuality.isInferred(receiver)) {
|
| - TypeErrorCode errorCode = TypeQuality.isInferred(receiver)
|
| - ? TypeErrorCode.NOT_A_METHOD_IN_INFERRED : TypeErrorCode.NOT_A_METHOD_IN;
|
| - typeError(diagnosticNode, errorCode, name, receiver);
|
| - }
|
| - return dynamicType;
|
| - }
|
| - }
|
| -
|
| - private Type diagnoseNonInterfaceType(HasSourceInfo 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<DartExpression> argumentNodes,
|
| - Iterator<Type> argumentTypes, FunctionType ftype,
|
| - List<VariableElement> parameters) {
|
| - int argumentIndex = 0;
|
| - // Check positional parameters.
|
| - {
|
| - List<Type> parameterTypes = ftype.getParameterTypes();
|
| - for (Type parameterType : parameterTypes) {
|
| - parameterType.getClass(); // quick null check
|
| - if (argumentTypes.hasNext()) {
|
| - Type argumentType = argumentTypes.next();
|
| - argumentType.getClass(); // quick null check
|
| - DartExpression argumentNode = argumentNodes.get(argumentIndex);
|
| - if (argumentNode instanceof DartNamedExpression) {
|
| - onError(argumentNode, TypeErrorCode.EXPECTED_POSITIONAL_ARGUMENT, parameterType);
|
| - return ftype.getReturnType();
|
| - }
|
| - if (parameters != null) {
|
| - argumentNode.setInvocationParameterId(parameters.get(argumentIndex));
|
| - } else {
|
| - argumentNode.setInvocationParameterId(argumentIndex);
|
| - }
|
| - if (checkAssignable(argumentNode, parameterType, argumentType)) {
|
| - inferFunctionLiteralParametersTypes(argumentNode, parameterType);
|
| - }
|
| - argumentIndex++;
|
| - } else {
|
| - onError(diagnosticNode, TypeErrorCode.MISSING_ARGUMENT, parameterType);
|
| - return ftype.getReturnType();
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Check optional parameters.
|
| - {
|
| - Map<String, Type> optionalParameterTypes = ftype.getOptionalParameterTypes();
|
| - Iterator<Entry<String, Type>> optionalParameterTypesIterator =
|
| - optionalParameterTypes.entrySet().iterator();
|
| - while (optionalParameterTypesIterator.hasNext()
|
| - && argumentTypes.hasNext()
|
| - && !(argumentNodes.get(argumentIndex) instanceof DartNamedExpression)) {
|
| - Entry<String, Type> namedEntry = optionalParameterTypesIterator.next();
|
| - Type optionalType = namedEntry.getValue();
|
| - optionalType.getClass(); // quick null check
|
| - Type argumentType = argumentTypes.next();
|
| - argumentType.getClass(); // quick null check
|
| - DartExpression argumentNode = argumentNodes.get(argumentIndex);
|
| - if (parameters != null) {
|
| - argumentNode.setInvocationParameterId(parameters.get(argumentIndex));
|
| - } else {
|
| - argumentNode.setInvocationParameterId(argumentIndex);
|
| - }
|
| - if (checkAssignable(argumentNode, optionalType, argumentType)) {
|
| - inferFunctionLiteralParametersTypes(argumentNode, optionalType);
|
| - }
|
| - argumentIndex++;
|
| - }
|
| - }
|
| -
|
| - // Check named parameters.
|
| - {
|
| - Set<String> usedNamedParametersPositional = Sets.newHashSet();
|
| - Set<String> usedNamedParametersNamed = Sets.newHashSet();
|
| - Map<String, Type> namedParameterTypes = ftype.getNamedParameterTypes();
|
| - while (argumentTypes.hasNext()
|
| - && argumentNodes.get(argumentIndex) instanceof DartNamedExpression) {
|
| - DartNamedExpression namedExpression =
|
| - (DartNamedExpression) argumentNodes.get(argumentIndex);
|
| - DartExpression argumentNode = namedExpression.getExpression();
|
| - // Prepare parameter name.
|
| - String parameterName = namedExpression.getName().getName();
|
| - if (parameters != null) {
|
| - for (VariableElement parameter : parameters) {
|
| - if (Objects.equal(parameter.getName(), parameterName)) {
|
| - namedExpression.setInvocationParameterId(parameter);
|
| - namedExpression.getName().setInvocationParameterId(parameter);
|
| - argumentNode.setInvocationParameterId(parameter);
|
| - break;
|
| - }
|
| - }
|
| - } else {
|
| - namedExpression.setInvocationParameterId(parameterName);
|
| - argumentNode.setInvocationParameterId(parameterName);
|
| - }
|
| - if (usedNamedParametersPositional.contains(parameterName)) {
|
| - onError(namedExpression, TypeErrorCode.DUPLICATE_NAMED_ARGUMENT);
|
| - } else if (usedNamedParametersNamed.contains(parameterName)) {
|
| - onError(namedExpression, ResolverErrorCode.DUPLICATE_NAMED_ARGUMENT);
|
| - } else {
|
| - usedNamedParametersNamed.add(parameterName);
|
| - }
|
| - // Check parameter type.
|
| - Type namedParameterType = namedParameterTypes.get(parameterName);
|
| - Type argumentType = argumentTypes.next();
|
| - if (namedParameterType != null) {
|
| - argumentType.getClass(); // quick null check
|
| - if (checkAssignable(argumentNode, namedParameterType, argumentType)) {
|
| - inferFunctionLiteralParametersTypes(argumentNode, namedParameterType);
|
| - }
|
| - } else {
|
| - onError(namedExpression, TypeErrorCode.NO_SUCH_NAMED_PARAMETER, parameterName);
|
| - }
|
| - argumentIndex++;
|
| - }
|
| - }
|
| - // Check rest (currently removed from specification).
|
| - if (ftype.hasRest()) {
|
| - while (argumentTypes.hasNext()) {
|
| - checkAssignable(argumentNodes.get(argumentIndex), ftype.getRest(), argumentTypes.next());
|
| - argumentIndex++;
|
| - }
|
| - }
|
| - // Report extra arguments.
|
| - while (argumentTypes.hasNext()) {
|
| - argumentTypes.next();
|
| - onError(argumentNodes.get(argumentIndex), TypeErrorCode.EXTRA_ARGUMENT);
|
| - argumentIndex++;
|
| - }
|
| -
|
| - // Return type.
|
| - Type type = ftype.getReturnType();
|
| - type.getClass(); // quick null check
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitTypeNode(DartTypeNode node) {
|
| - return validateTypeNode(node);
|
| - }
|
| -
|
| - private Type validateTypeNode(DartTypeNode node) {
|
| - 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());
|
| - return itype;
|
| - }
|
| - default:
|
| - return type;
|
| - }
|
| - }
|
| -
|
| - private void validateBounds(List<? extends DartNode> diagnosticNodes,
|
| - List<Type> arguments,
|
| - List<Type> parameters) {
|
| - if (arguments.size() == parameters.size() && arguments.size() == diagnosticNodes.size()) {
|
| - List<Type> bounds = Lists.newArrayListWithCapacity(parameters.size());
|
| - for (Type parameter : parameters) {
|
| - TypeVariable variable = (TypeVariable) parameter;
|
| - Type bound = variable.getTypeVariableElement().getBound();
|
| - if (bound == null) {
|
| - internalError(variable.getElement(), "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.isSubtype(s, t)) {
|
| - onError(diagnosticNodes.get(i),
|
| - TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - /* Check for a type variable is repeated in its own bounds:
|
| - * e.g. Foo<T extends T>
|
| - */
|
| - private void checkCyclicBounds(List<? extends Type> arguments) {
|
| - for (Type argument : arguments) {
|
| - if (TypeKind.of(argument).equals(TypeKind.VARIABLE)) {
|
| - TypeVariable typeVar = (TypeVariable) argument;
|
| - checkCyclicBound(typeVar, typeVar.getTypeVariableElement().getBound());
|
| - }
|
| - }
|
| - }
|
| -
|
| - private void checkCyclicBound(TypeVariable variable, Type bound) {
|
| - switch(TypeKind.of(bound)) {
|
| - case VARIABLE: {
|
| - TypeVariable boundType = (TypeVariable)bound;
|
| - if (boundType.equals(variable)) {
|
| - onError(boundType.getElement(),
|
| - TypeErrorCode.CYCLIC_REFERENCE_TO_TYPE_VARIABLE,
|
| - boundType.getElement().getOriginalName());
|
| - }
|
| - break;
|
| - }
|
| - default:
|
| - break;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Returns the type of a node. If a type of an expression can't be resolved,
|
| - * returns the dynamic type.
|
| - *
|
| - * @return a non-null type
|
| - */
|
| - Type typeOf(DartNode node) {
|
| - if (node == null) {
|
| - return dynamicType;
|
| - }
|
| - // prepare new type
|
| - Type result = node.accept(this);
|
| - if (result == null) {
|
| - return dynamicType;
|
| - }
|
| - // set new type, or keep existing
|
| - if (node.getType() == null) {
|
| - node.setType(result);
|
| - } else {
|
| - result = node.getType();
|
| - }
|
| - // done
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Returns the type of a node, registering an error if the type is unresolved or
|
| - * void.
|
| - *
|
| - * @return a non-null type
|
| - */
|
| - private Type nonVoidTypeOf(DartNode node) {
|
| - Type type = typeOf(node);
|
| - return checkNonVoid(node, type);
|
| - }
|
| -
|
| - /**
|
| - * @return the given {@link Type}, registering an error if it is unresolved or void.
|
| - */
|
| - private Type checkNonVoid(HasSourceInfo errorTarget, Type type) {
|
| - switch (TypeKind.of(type)) {
|
| - case VOID:
|
| - case NONE:
|
| - return typeError(errorTarget, TypeErrorCode.VOID);
|
| - default:
|
| - return type;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitArrayAccess(DartArrayAccess node) {
|
| - Type target = typeOf(node.getRealTarget());
|
| - DartExpression argKey = node.getKey();
|
| - // t[k] = v
|
| - if (node.getParent() instanceof DartBinaryExpression) {
|
| - DartBinaryExpression binary = (DartBinaryExpression) node.getParent();
|
| - if (binary.getArg1() == node && binary.getOperator() == Token.ASSIGN) {
|
| - DartExpression argValue = binary.getArg2();
|
| - analyzeTernaryOperator(node, target, Token.ASSIGN_INDEX, node, argKey, argValue);
|
| - binary.setElement(node.getElement());
|
| - return argValue.getType();
|
| - }
|
| - }
|
| - // print( t[k] )
|
| - Type result = analyzeBinaryOperator(node, target, Token.INDEX, node, argKey);
|
| - return Types.makeInferred(result, target.getQuality());
|
| - }
|
| -
|
| - /**
|
| - * Asserts that given {@link DartExpression} is valid for using in "assert" statement.
|
| - */
|
| - private void checkAssertCondition(DartExpression conditionNode) {
|
| - Type condition = nonVoidTypeOf(conditionNode);
|
| - switch (condition.getKind()) {
|
| - case FUNCTION:
|
| - FunctionType ftype = (FunctionType) condition;
|
| - Type returnType = ftype.getReturnType();
|
| - if (!types.isAssignable(boolType, returnType) || !ftype.getParameterTypes().isEmpty()) {
|
| - typeError(conditionNode, TypeErrorCode.ASSERT_BOOL);
|
| - }
|
| - break;
|
| -
|
| - default:
|
| - if (!types.isAssignable(boolType, condition)) {
|
| - typeError(conditionNode, TypeErrorCode.ASSERT_BOOL);
|
| - }
|
| - break;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitBlock(DartBlock node) {
|
| - try {
|
| - return typeAsVoid(node);
|
| - } finally {
|
| - VariableElementsRestorer variableRestorer = restoreOnBlockExit.remove(node);
|
| - if (variableRestorer != null) {
|
| - variableRestorer.restore();
|
| - }
|
| - }
|
| - }
|
| -
|
| - private Type typeAsVoid(DartNode node) {
|
| - node.visitChildren(this);
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitBreakStatement(DartBreakStatement node) {
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitCascadeExpression(DartCascadeExpression node) {
|
| - DartExpression target = node.getTarget();
|
| - Type type = nonVoidTypeOf(target);
|
| - node.setType(type);
|
| - inferCascadeType(node);
|
| - node.visitChildren(this);
|
| - return type;
|
| - }
|
| -
|
| - /**
|
| - * Infers {@link Type} of {@link DartCascadeExpression} from context.
|
| - */
|
| - private void inferCascadeType(DartCascadeExpression node) {
|
| - // field declaration
|
| - if (node.getParent() instanceof DartField) {
|
| - DartField field = (DartField) node.getParent();
|
| - Type varType = field.getElement().getType();
|
| - setCascadeUnionType(node, varType);
|
| - }
|
| - // variable declaration
|
| - if (node.getParent() instanceof DartVariable) {
|
| - DartVariable var = (DartVariable) node.getParent();
|
| - Type varType = var.getElement().getType();
|
| - setCascadeUnionType(node, varType);
|
| - }
|
| - // assignment
|
| - if (node.getParent() instanceof DartBinaryExpression) {
|
| - DartBinaryExpression binary = (DartBinaryExpression) node.getParent();
|
| - if (binary.getOperator() == Token.ASSIGN && binary.getArg2() == node
|
| - && binary.getArg1() != null) {
|
| - Element leftElement = binary.getArg1().getElement();
|
| - if (leftElement != null) {
|
| - Type varType = leftElement.getType();
|
| - setCascadeUnionType(node, varType);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Sets for given {@link DartCascadeExpression} and its target {@link Type} which is union of
|
| - * existing type and "newType".
|
| - */
|
| - private void setCascadeUnionType(DartCascadeExpression node, Type newType) {
|
| - DartExpression target = node.getTarget();
|
| - Type type = node.getType();
|
| - if (isExplicitlySpecifiedType(newType) && types.isAssignable(type, newType)) {
|
| - Type unionType = getUnionType(type, newType);
|
| - unionType = Types.makeInferred(unionType);
|
| - node.setType(unionType);
|
| - target.setType(unionType);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitFunctionObjectInvocation(DartFunctionObjectInvocation node) {
|
| - ClassElement element = functionType.getElement();
|
| - node.setElement(element);
|
| - checkDeprecated(node, element);
|
| - return checkInvocation(node, node, null, typeOf(node.getTarget()));
|
| - }
|
| -
|
| - @Override
|
| - public Type visitMethodInvocation(DartMethodInvocation node) {
|
| - if (node.getFunctionName().isResolutionAlreadyReportedThatTheMethodCouldNotBeFound()) {
|
| - return dynamicType;
|
| - }
|
| - DartNode target = node.getRealTarget();
|
| - DartIdentifier nameNode = node.getFunctionName();
|
| - String name = node.getFunctionNameString();
|
| - Element element = node.getElement();
|
| - if (element != null && (element.getModifiers().isStatic()
|
| - || Elements.isTopLevel(element))) {
|
| - typeOf(target);
|
| - node.setElement(element);
|
| - checkDeprecated(nameNode, element);
|
| - return checkInvocation(node, nameNode, name, element.getType());
|
| - }
|
| - Type receiver = nonVoidTypeOf(target);
|
| - Member member = lookupMember(receiver, name, nameNode);
|
| - if (member != null) {
|
| - element = member.getElement();
|
| - checkIllegalPrivateAccess(node.getFunctionName(), element, name);
|
| - node.setElement(element);
|
| - if (nameNode != null) {
|
| - nameNode.setElement(element);
|
| - }
|
| - }
|
| - if (Elements.isAbstractFieldWithoutGetter(element)) {
|
| - onError(nameNode, TypeErrorCode.USE_ASSIGNMENT_ON_SETTER, name);
|
| - }
|
| - checkDeprecated(nameNode, nameNode.getElement());
|
| - FunctionType methodType = getMethodType(receiver, member, name, nameNode);
|
| - Type returnType = checkInvocation(node, nameNode, name, methodType);
|
| - returnType = ExternalTypeAnalyzers.resolve(types, node, element, returnType);
|
| - warningEffectiveIntegerDivision(node, element);
|
| - return returnType;
|
| - }
|
| -
|
| - /**
|
| - * http://code.google.com/p/dart/issues/detail?id=5652
|
| - */
|
| - private void warningEffectiveIntegerDivision(DartMethodInvocation node, Element element) {
|
| - if (element != null && element.getName().equals("toInt")
|
| - && element.getEnclosingElement() != null
|
| - && element.getEnclosingElement().getName().equals("num")) {
|
| - DartExpression target = node.getTarget();
|
| - while (target instanceof DartParenthesizedExpression) {
|
| - target = ((DartParenthesizedExpression) target).getExpression();
|
| - }
|
| - if (target instanceof DartBinaryExpression) {
|
| - DartBinaryExpression binary = (DartBinaryExpression) target;
|
| - if (binary.getOperator() == Token.DIV && intType.equals(binary.getArg1().getType())
|
| - && intType.equals(binary.getArg2().getType())) {
|
| - typeError(node, TypeErrorCode.USE_INTEGER_DIVISION);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - private void checkIllegalPrivateAccess(DartNode diagnosticNode, Element element, String name) {
|
| - if (DartIdentifier.isPrivateName(name)) {
|
| - if (!Elements.areSameLibrary(currentMethod, element)) {
|
| - onError(diagnosticNode, TypeErrorCode.ILLEGAL_ACCESS_TO_PRIVATE, name);
|
| - }
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitSuperConstructorInvocation(DartSuperConstructorInvocation node) {
|
| - return checkConstructorForwarding(node, node.getElement());
|
| - }
|
| -
|
| - private Type checkConstructorForwarding(DartInvocation node, ConstructorElement element) {
|
| - if (element == null) {
|
| - visit(node.getArguments());
|
| - return voidType;
|
| - } else {
|
| - node.setElement(element);
|
| - checkDeprecated(node, element);
|
| - checkInvocation(node, node, null, typeAsMemberOf(element, currentClass));
|
| - return voidType;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitCase(DartCase node) {
|
| - node.visitChildren(this);
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitClass(DartClass node) {
|
| - ClassNodeElement element = node.getElement();
|
| - InterfaceType type = element.getType();
|
| - checkCyclicBounds(type.getArguments());
|
| - // remember unimplemented members
|
| - {
|
| - List<Element> unimplementedMembers = findUnimplementedMembers(element);
|
| - if (!node.getModifiers().isAbstract() && !unimplementedMembers.isEmpty() &&
|
| - (reportNoMemberWhenHasInterceptor || !Elements.handlesNoSuchMethod(type))) {
|
| - StringBuilder sb = getUnimplementedMembersMessage(element, unimplementedMembers);
|
| - onError(node.getName(), TypeErrorCode.CONCRETE_CLASS_WITH_UNIMPLEMENTED_MEMBERS,
|
| - node.getName(), sb.toString());
|
| - }
|
| - }
|
| - //
|
| - setCurrentClass(type);
|
| - visit(node.getTypeParameters());
|
| - if (node.getSuperclass() != null) {
|
| - validateTypeNode(node.getSuperclass());
|
| - }
|
| - if (node.getInterfaces() != null) {
|
| - for (DartTypeNode interfaceNode : node.getInterfaces()) {
|
| - validateTypeNode(interfaceNode);
|
| - }
|
| - }
|
| - visit(node.getMembers());
|
| - checkInterfaceConstructors(element);
|
| - try {
|
| - checkClassDuplicateInterfaces(node, element, element.getAllSupertypes());
|
| - } catch (CyclicDeclarationException ignored) {
|
| - }
|
| -
|
| - // Finish current class.
|
| - setCurrentClass(null);
|
| - return type;
|
| - }
|
| -
|
| - /**
|
| - * Check for duplicate interfaces that aren't assignable to each other due to parameterization.
|
| - *
|
| - * Issue 3803: This isn't in the spec as of 0.10, adding as a 'value added' check, because there is no
|
| - * way to satisfy the method override rules without either causing errors in checked mode or
|
| - * causing override errors for every method implemented for this interface.
|
| - *
|
| - * @param classElement
|
| - */
|
| - private void checkClassDuplicateInterfaces(DartClass node, ClassElement classElement,
|
| - List<InterfaceType> allSupertypes) {
|
| - Map<Element, InterfaceType> elementMap = Maps.newHashMap();
|
| - for (InterfaceType supertype : allSupertypes) {
|
| - Element e = supertype.getElement();
|
| - if (e != null) {
|
| - InterfaceType foundType = elementMap.get(e);
|
| - if (foundType != null && ! types.isAssignable(supertype, foundType)) {
|
| - typeError(node.getName(), TypeErrorCode.INCOMPATIBLE_TYPES_IN_HIERARCHY, foundType.toString(),
|
| - supertype.toString());
|
| - } else {
|
| - elementMap.put(e, supertype);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - /**
|
| - * Checks that interface constructors have corresponding methods in default class.
|
| - */
|
| - private void checkInterfaceConstructors(ClassElement interfaceElement) {
|
| - // If no default class, do nothing.
|
| - if (interfaceElement.getDefaultClass() == null) {
|
| - return;
|
| - }
|
| - // Analyze all constructors.
|
| - String interfaceClassName = interfaceElement.getName();
|
| - String defaultClassName = interfaceElement.getDefaultClass().getElement().getName();
|
| - for (ConstructorElement interfaceConstructor : interfaceElement.getConstructors()) {
|
| - ConstructorElement defaultConstructor = interfaceConstructor.getDefaultConstructor();
|
| - if (defaultConstructor != null) {
|
| - // TODO(scheglov)
|
| - // It is a compile-time error if kI and kF do not have identical type parameters
|
| - // TODO /end
|
| - // Validate types of required and optional parameters.
|
| - {
|
| - List<String> interfaceTypes = Elements.getParameterTypeNames(interfaceConstructor);
|
| - List<String> defaultTypes = Elements.getParameterTypeNames(defaultConstructor);
|
| - if (interfaceTypes.size() == defaultTypes.size()
|
| - && !interfaceTypes.equals(defaultTypes)) {
|
| - onError(
|
| - interfaceConstructor,
|
| - TypeErrorCode.DEFAULT_CONSTRUCTOR_TYPES,
|
| - Elements.getRawMethodName(interfaceConstructor),
|
| - interfaceClassName,
|
| - Joiner.on(",").join(interfaceTypes),
|
| - Elements.getRawMethodName(defaultConstructor),
|
| - defaultClassName,
|
| - Joiner.on(",").join(defaultTypes));
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - private List<Element> findUnimplementedMembers(ClassElement classElement) {
|
| - // May be has members already (cached or already analyzed ClassNodeElement).
|
| - List<Element> members = classElement.getUnimplementedMembers();
|
| - if (members != null) {
|
| - return members;
|
| - }
|
| - // If no cached result, then should be node based.
|
| - ClassNodeElement classNodeElement = (ClassNodeElement) classElement;
|
| - // Analyze ClassElement node.
|
| - AbstractMethodFinder finder = new AbstractMethodFinder(classNodeElement.getType());
|
| - classNodeElement.getNode().accept(finder);
|
| - // Prepare unimplemented members.
|
| - if (classNodeElement.isInterface()) {
|
| - members = Collections.emptyList();
|
| - } else {
|
| - members = finder.unimplementedElements;
|
| - }
|
| - // Remember unimplemented methods.
|
| - classNodeElement.setUnimplementedMembers(members);
|
| - return members;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitConditional(DartConditional node) {
|
| - checkCondition(node.getCondition());
|
| - Type left = typeOf(node.getThenExpression());
|
| - Type right = typeOf(node.getElseExpression());
|
| - return types.intersection(left, right);
|
| - }
|
| -
|
| - private Type checkCondition(DartExpression condition) {
|
| - Type type = nonVoidTypeOf(condition);
|
| - checkAssignable(condition, boolType, type);
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitContinueStatement(DartContinueStatement node) {
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitDefault(DartDefault node) {
|
| - node.visitChildren(this);
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitDoWhileStatement(DartDoWhileStatement node) {
|
| - checkCondition(node.getCondition());
|
| - typeOf(node.getBody());
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitEmptyStatement(DartEmptyStatement node) {
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitFieldDefinition(DartFieldDefinition node) {
|
| - node.visitChildren(this);
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitForInStatement(DartForInStatement node) {
|
| - Type variableType;
|
| - VariableElement variableElement = null;
|
| - if (node.introducesVariable()) {
|
| - variableType = typeOf(node.getVariableStatement());
|
| - variableElement = node.getVariableStatement().getVariables().get(0).getElement();
|
| - } else {
|
| - variableType = typeOf(node.getIdentifier());
|
| - // in most cases variable, but sometimes field
|
| - Element identifierElement = node.getIdentifier().getElement();
|
| - if (identifierElement instanceof VariableElement) {
|
| - variableElement = (VariableElement) identifierElement;
|
| - }
|
| - }
|
| - // prepare Iterable type
|
| - DartExpression iterableExpression = node.getIterable();
|
| - Type iterableType = typeOf(iterableExpression);
|
| - // analyze compatibility of variable and Iterator elements types
|
| - Member iteratorMember = lookupMember(iterableType, "iterator", iterableExpression);
|
| - Type elementType = null;
|
| - if (iteratorMember != null) {
|
| - Type memberReturnType = null;
|
| - if (TypeKind.of(iteratorMember.getType()) == TypeKind.FUNCTION) {
|
| - FunctionType iteratorMethod = (FunctionType) iteratorMember.getType();
|
| - memberReturnType = iteratorMethod.getReturnType();
|
| - } else if (ElementKind.of(iteratorMember.getElement()) == ElementKind.FIELD) {
|
| - memberReturnType = iteratorMember.getType();
|
| - }
|
| - if (memberReturnType != null) {
|
| - InterfaceType asInstanceOf = types.asInstanceOf(memberReturnType,
|
| - dynamicIteratorType.getElement());
|
| - if (asInstanceOf != null) {
|
| - elementType = asInstanceOf.getArguments().get(0);
|
| - checkAssignable(iterableExpression, variableType, elementType);
|
| - } 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);
|
| - }
|
| - }
|
| - // visit body with inferred variable type
|
| - VariableElementsRestorer variableRestorer = new VariableElementsRestorer();
|
| - try {
|
| - if (variableElement != null && elementType != null) {
|
| - variableRestorer.setType(variableElement, elementType);
|
| - }
|
| - BlockTypeContext blockTypeContext = new BlockTypeContext();
|
| - blockOldTypes.addFirst(blockTypeContext);
|
| - try {
|
| - return typeAsVoid(node.getBody());
|
| - } finally {
|
| - blockOldTypes.removeFirst();
|
| - setMergedVariableTypes(blockTypeContext);
|
| - }
|
| - } finally {
|
| - variableRestorer.restore();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitForStatement(DartForStatement node) {
|
| - typeOf(node.getInit());
|
| - DartExpression condition = node.getCondition();
|
| - checkCondition(condition);
|
| - // visit body
|
| - BlockTypeContext blockTypeContext = new BlockTypeContext();
|
| - blockOldTypes.addFirst(blockTypeContext);
|
| - try {
|
| - visitConditionalNode(condition, node.getBody());
|
| - visitConditionalNode(condition, node.getIncrement());
|
| - } finally {
|
| - blockOldTypes.removeFirst();
|
| - setMergedVariableTypes(blockTypeContext);
|
| - }
|
| - // done
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitFunction(DartFunction node) {
|
| - blockOldTypes.addFirst(new BlockTypeContext());
|
| - try {
|
| - Type previous = expected;
|
| - visit(node.getParameters());
|
| - expected = typeOf(node.getReturnTypeNode());
|
| - typeOf(node.getBody());
|
| - expected = previous;
|
| - } finally {
|
| - blockOldTypes.removeFirst();
|
| - }
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitFunctionExpression(DartFunctionExpression node) {
|
| - node.visitChildren(this);
|
| - Type result = node.getElement().getType();
|
| - result.getClass(); // quick null check
|
| - return result;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitFunctionTypeAlias(DartFunctionTypeAlias node) {
|
| - FunctionAliasElement element = node.getElement();
|
| - FunctionAliasType type = element.getType();
|
| - if (TypeKind.of(type) == TypeKind.FUNCTION_ALIAS) {
|
| - checkCyclicBounds(type.getElement().getTypeParameters());
|
| - if (hasFunctionTypeAliasSelfReference(element)) {
|
| - onError(node, TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF);
|
| - }
|
| - }
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitSyntheticErrorIdentifier(DartSyntheticErrorIdentifier node) {
|
| - return dynamicType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitIdentifier(DartIdentifier node) {
|
| - if (node.getType() != null) {
|
| - return node.getType();
|
| - }
|
| - if (node.getParent() instanceof DartDeclaration<?>
|
| - && ((DartDeclaration<?>) node.getParent()).getName() == node) {
|
| - return node.getType();
|
| - }
|
| - if (node.getParent() instanceof DartComment) {
|
| - return dynamicType;
|
| - }
|
| - Element element = node.getElement();
|
| - Type type;
|
| - switch (ElementKind.of(element)) {
|
| - case VARIABLE:
|
| - case PARAMETER:
|
| - case FUNCTION_OBJECT:
|
| - type = element.getType();
|
| - type.getClass(); // quick null check
|
| -
|
| - break;
|
| -
|
| - case CLASS:
|
| - return element.getType();
|
| -
|
| - case FIELD:
|
| - type = typeAsMemberOf(element, currentClass);
|
| - // try to resolve as getter/setter
|
| - FieldElement fieldElement = (FieldElement) element;
|
| - DartNode properyAccess = ASTNodes.getPropertyAccessNode(node);
|
| - if (ASTNodes.inGetterContext(properyAccess)) {
|
| - MethodElement getter = fieldElement.getGetter();
|
| - if (getter != null) {
|
| - type = ((FunctionType) typeAsMemberOf(getter, currentClass)).getReturnType();
|
| - node.setType(type);
|
| - }
|
| - } else if (ASTNodes.inSetterContext(properyAccess)) {
|
| - MethodElement setter = fieldElement.getSetter();
|
| - if (setter != null) {
|
| - if (setter.getParameters().size() > 0) {
|
| - type = setter.getParameters().get(0).getType();
|
| - node.setType(type);
|
| - }
|
| - }
|
| - }
|
| - type.getClass(); // quick null check
|
| - break;
|
| -
|
| - case METHOD:
|
| - type = typeAsMemberOf(element, currentClass);
|
| - type.getClass(); // quick null check
|
| - break;
|
| -
|
| - case NONE:
|
| - if (!node.isResolutionAlreadyReportedThatTheMethodCouldNotBeFound()) {
|
| - typeError(node, TypeErrorCode.CANNOT_BE_RESOLVED, node.getName());
|
| - }
|
| - return dynamicType;
|
| -
|
| - case DYNAMIC:
|
| - return element.getType();
|
| -
|
| - default:
|
| - return voidType;
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitIfStatement(DartIfStatement node) {
|
| - DartExpression condition = node.getCondition();
|
| - checkCondition(condition);
|
| - // visit "then"
|
| - BlockTypeContext thenTypeContext = new BlockTypeContext();
|
| - blockOldTypes.addFirst(thenTypeContext);
|
| - DartStatement thenStatement = node.getThenStatement();
|
| - visitConditionalNode(condition, thenStatement);
|
| - blockOldTypes.removeFirst();
|
| - Map<VariableElement, Type> thenVariableTypes = thenTypeContext.getNewTypesAndRestoreOld();
|
| - // visit "else"
|
| - DartStatement elseStatement = node.getElseStatement();
|
| - BlockTypeContext elseTypeContext = new BlockTypeContext();
|
| - {
|
| - VariableElementsRestorer variableRestorer = new VariableElementsRestorer();
|
| - // if has "else", then types inferred from "is! Type" applied only to "else"
|
| - if (elseStatement != null) {
|
| - blockOldTypes.addFirst(elseTypeContext);
|
| - inferVariableTypesFromIsNotConditions(condition, variableRestorer);
|
| - typeOf(elseStatement);
|
| - variableRestorer.restore();
|
| - blockOldTypes.removeFirst();
|
| - }
|
| - // if no "else", then inferred types applied to the end of the method/loop
|
| - if (elseStatement == null) {
|
| - if (isExitFromFunction(thenStatement)) {
|
| - inferVariableTypesFromIsNotConditions(condition, variableRestorer);
|
| - } else if (isExitFromFunctionOrLoop(thenStatement)) {
|
| - DartBlock restoreBlock = getBlockForLoopTypesInference(node);
|
| - variableRestorer = restoreOnBlockExit.get(restoreBlock);
|
| - if (variableRestorer == null) {
|
| - variableRestorer = new VariableElementsRestorer();
|
| - restoreOnBlockExit.put(restoreBlock, variableRestorer);
|
| - }
|
| - restoreOnBlockExit.put(restoreBlock, variableRestorer);
|
| - inferVariableTypesFromIsNotConditions(condition, variableRestorer);
|
| - }
|
| - }
|
| - }
|
| - Map<VariableElement, Type> elseVariableTypes = elseTypeContext.getNewTypesAndRestoreOld();
|
| - // merge variable types
|
| - {
|
| - Set<VariableElement> variables = Sets.newHashSet();
|
| - variables.addAll(thenVariableTypes.keySet());
|
| - variables.addAll(elseVariableTypes.keySet());
|
| - for (VariableElement variable : variables) {
|
| - List<Type> possibleTypes = Lists.newArrayList();
|
| - Type thenType = thenVariableTypes.get(variable);
|
| - Type elseType = elseVariableTypes.get(variable);
|
| - if (thenType != null && elseType != null) {
|
| - possibleTypes.add(thenType);
|
| - possibleTypes.add(elseType);
|
| - }
|
| - if (thenType != null && elseType == null) {
|
| - possibleTypes.add(thenType);
|
| - possibleTypes.add(variable.getType());
|
| - }
|
| - if (thenType == null && elseType != null) {
|
| - possibleTypes.add(variable.getType());
|
| - possibleTypes.add(elseType);
|
| - }
|
| - // do merge
|
| - Type mergedType = types.intersection(possibleTypes);
|
| - TypeQuality mergedTypeQuality = Types.getIntersectionQuality(possibleTypes);
|
| - setVariableElementType(variable, mergedType, mergedTypeQuality);
|
| - }
|
| - }
|
| - // done
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - 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 visitLabel(DartLabel node) {
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitMapLiteral(DartMapLiteral node) {
|
| - visit(node.getTypeArguments());
|
| - InterfaceType type = node.getType();
|
| -
|
| - // The Map literal has an implicit key type of String, so only one parameter is
|
| - // specified <V> where V is the type of the value.
|
| - checkAssignable(node, defaultLiteralMapType, type);
|
| -
|
| - // Check the map literal entries against the return type.
|
| - {
|
| - Type valueType = type.getArguments().get(1);
|
| - for (DartMapLiteralEntry literalEntry : node.getEntries()) {
|
| - boolean isValueAssignable = checkAssignable(literalEntry, typeOf(literalEntry), valueType);
|
| - if (developerModeChecks && !isValueAssignable) {
|
| - typeError(literalEntry, ResolverErrorCode.MAP_LITERAL_ELEMENT_TYPE, valueType);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Check that each key literal is unique.
|
| - Set<String> keyValues = Sets.newHashSet();
|
| - for (DartMapLiteralEntry literalEntry : node.getEntries()) {
|
| - if (literalEntry.getKey() instanceof DartStringLiteral) {
|
| - DartStringLiteral keyLiteral = (DartStringLiteral) literalEntry.getKey();
|
| - String keyValue = keyLiteral.getValue();
|
| - if (keyValues.contains(keyValue)) {
|
| - typeError(keyLiteral, TypeErrorCode.MAP_LITERAL_KEY_UNIQUE);
|
| - }
|
| - keyValues.add(keyValue);
|
| - }
|
| - }
|
| -
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitMapLiteralEntry(DartMapLiteralEntry node) {
|
| - nonVoidTypeOf(node.getKey());
|
| - return nonVoidTypeOf(node.getValue());
|
| - }
|
| -
|
| - @Override
|
| - public Type visitMethodDefinition(DartMethodDefinition node) {
|
| - MethodElement methodElement = node.getElement();
|
| - Modifiers modifiers = methodElement.getModifiers();
|
| - DartTypeNode returnTypeNode = node.getFunction().getReturnTypeNode();
|
| - if (modifiers.isFactory()
|
| - && ElementKind.of(methodElement).equals(ElementKind.CONSTRUCTOR)) {
|
| - analyzeFactory(node, node.getName(), (ConstructorElement) methodElement);
|
| - } else if (modifiers.isSetter()) {
|
| - if (returnTypeNode != null && returnTypeNode.getType() != voidType) {
|
| - typeError(returnTypeNode, TypeErrorCode.SETTER_RETURN_TYPE, methodElement.getName());
|
| - }
|
| - if (methodElement.getParameters().size() > 0) {
|
| - Element parameterElement = methodElement.getParameters().get(0);
|
| - Type setterType = parameterElement.getType();
|
| - MethodElement getterElement = Elements.lookupFieldElementGetter(
|
| - methodElement.getEnclosingElement(), methodElement.getName());
|
| -
|
| - if (getterElement != null) {
|
| - // prepare "getter" type
|
| - Type getterType;
|
| -
|
| - // prepare super types between "getter" and "setter" enclosing types
|
| - Type getterDeclarationType = getterElement.getEnclosingElement().getType();
|
| - List<InterfaceType> superTypes;
|
| - if (currentClass != null) {
|
| - superTypes = getIntermediateSuperTypes(currentClass, getterDeclarationType);
|
| - } else {
|
| - superTypes = Lists.newArrayList();
|
| - }
|
| - // convert "getter" function type to use "setter" type parameters
|
| - FunctionType getterFunctionType = (FunctionType) getterElement.getType();
|
| - for (InterfaceType superType : superTypes) {
|
| - List<Type> superArguments = superType.getArguments();
|
| - List<Type> superParameters = superType.getElement().getTypeParameters();
|
| - getterFunctionType = (FunctionType) getterFunctionType.subst(superArguments,
|
| - superParameters);
|
| - }
|
| - // get return type
|
| - getterType = getterFunctionType.getReturnType();
|
| -
|
| - // compare "getter" and "setter" types
|
| - if (!types.isAssignable(setterType, getterType)) {
|
| - typeError(parameterElement, TypeErrorCode.SETTER_TYPE_MUST_BE_ASSIGNABLE,
|
| - setterType.getElement().getName(), getterType.getElement().getName());
|
| - }
|
| -
|
| - // getter and setter should have same "static" flag
|
| - if (modifiers.isStatic() != getterElement.getModifiers().isStatic()) {
|
| - onError(node.getName(), TypeErrorCode.FIELD_GETTER_SETTER_SAME_STATIC);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - // operator == should return "bool"
|
| - if (modifiers.isOperator() && methodElement.getName().equals("==")
|
| - && returnTypeNode != null) {
|
| - Type returnType = node.getElement().getFunctionType().getReturnType();
|
| - if (!Objects.equal(returnType, boolType)) {
|
| - typeError(returnTypeNode, TypeErrorCode.OPERATOR_EQUALS_BOOL_RETURN_TYPE);
|
| -
|
| - }
|
| - }
|
| - // operator "[]=" should return void
|
| - if (modifiers.isOperator() && methodElement.getName().equals("[]=")
|
| - && returnTypeNode != null) {
|
| - Type returnType = node.getElement().getFunctionType().getReturnType();
|
| - if (TypeKind.of(returnType) != TypeKind.VOID) {
|
| - typeError(returnTypeNode, TypeErrorCode.OPERATOR_INDEX_ASSIGN_VOID_RETURN_TYPE);
|
| - }
|
| - }
|
| - // visit children
|
| - MethodElement prevMethod = currentMethod;
|
| - currentMethod = methodElement;
|
| - try {
|
| - return typeAsVoid(node);
|
| - } finally {
|
| - currentMethod = prevMethod;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @return "super" {@link InterfaceType}s used in declarations from "subType" to "superType",
|
| - * first item is given "superType". May be empty, but not <code>null</code>.
|
| - */
|
| - private List<InterfaceType> getIntermediateSuperTypes(InterfaceType subType, Type superType) {
|
| - LinkedList<InterfaceType> superTypes = Lists.newLinkedList();
|
| - InterfaceType t = subType.getElement().getSupertype();
|
| - while (t != null) {
|
| - superTypes.addFirst(t);
|
| - if (Objects.equal(t.getElement().getType(), superType)) {
|
| - break;
|
| - }
|
| - t = t.getElement().getSupertype();
|
| - }
|
| - return superTypes;
|
| - }
|
| -
|
| - private void analyzeFactory(DartMethodDefinition node, DartExpression name,
|
| - final ConstructorElement constructorElement) {
|
| - ASTVisitor<Void> visitor = new ASTVisitor<Void>() {
|
| - @Override
|
| - public Void visitParameterizedTypeNode(DartParameterizedTypeNode node) {
|
| - DartExpression expression = node.getExpression();
|
| - Element e = null;
|
| - if (expression instanceof DartIdentifier) {
|
| - e = ((DartIdentifier) expression).getElement();
|
| - } else if (expression instanceof DartPropertyAccess) {
|
| - e = ((DartPropertyAccess) expression).getElement();
|
| - }
|
| - if (!ElementKind.of(e).equals(ElementKind.CLASS)) {
|
| - return null;
|
| - }
|
| - List<DartTypeParameter> parameterNodes = node.getTypeParameters();
|
| - assert (parameterNodes.size() == 0);
|
| - return null;
|
| - }
|
| - };
|
| - name.accept(visitor);
|
| - // redirecting factory constructor
|
| - {
|
| - ConstructorElement targetElement = constructorElement.getRedirectingFactoryConstructor();
|
| - if (targetElement != null) {
|
| - ClassElement targetEnclosingClass = (ClassElement) targetElement.getEnclosingElement();
|
| - Type sourceMethodType = constructorElement.getType();
|
| - Type targetMethodType = targetElement.getType();
|
| - InterfaceType targetClassType = (InterfaceType) node.getRedirectedTypeName().getType();
|
| - targetMethodType = targetMethodType.subst(targetClassType.getArguments(),
|
| - targetEnclosingClass.getTypeParameters());
|
| - if (!types.isSubtype(targetMethodType, sourceMethodType)) {
|
| - DartNode errorNode = node.getRedirectedConstructorName() != null
|
| - ? node.getRedirectedConstructorName() : node.getRedirectedTypeName();
|
| - typeError(errorNode, TypeErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_SUBTYPE,
|
| - targetMethodType, sourceMethodType);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitNewExpression(DartNewExpression node) {
|
| - ConstructorElement constructorElement = node.getElement();
|
| -
|
| - DartTypeNode typeNode = Types.constructorTypeNode(node);
|
| - Type type = null;
|
| -
|
| - // When using a constructor defined in an interface, the bounds can be tighter
|
| - // in the default class than defined in the interface.
|
| - if (TypeKind.of(typeNode.getType()).equals(TypeKind.INTERFACE)
|
| - && ((InterfaceType)typeNode.getType()).getElement().isInterface()) {
|
| - InterfaceType itype = (InterfaceType)typeNode.getType();
|
| - ClassElement interfaceElement = itype.getElement();
|
| - InterfaceType defaultClassType = interfaceElement.getDefaultClass();
|
| - if (defaultClassType != null && defaultClassType.getElement() != null) {
|
| - validateBounds(typeNode.getTypeArguments(),
|
| - itype.getArguments(),
|
| - defaultClassType.getElement().getTypeParameters());
|
| - type = itype;
|
| - }
|
| - }
|
| - if (type == null) {
|
| - type = validateTypeNode(typeNode);
|
| - }
|
| -
|
| - DartNode typeName = typeNode.getIdentifier();
|
| -
|
| - if (constructorElement == null) {
|
| - visit(node.getArguments());
|
| - } else {
|
| - ClassElement cls = (ClassElement) constructorElement.getEnclosingElement();
|
| - // Add warning for instantiating abstract class.
|
| - if (cls.getModifiers().isAbstract()) {
|
| - if (!constructorElement.getModifiers().isFactory()) {
|
| - typeError(typeName, TypeErrorCode.INSTANTIATION_OF_ABSTRACT_CLASS, cls.getName());
|
| - }
|
| - }
|
| - // Check type arguments.
|
| - FunctionType ftype = (FunctionType) constructorElement.getType();
|
| -
|
| - if (ftype != null && TypeKind.of(type).equals(TypeKind.INTERFACE)) {
|
| - InterfaceType ifaceType = (InterfaceType) type;
|
| -
|
| - List<Type> substParams;
|
| - if (ifaceType.getElement().isInterface()) {
|
| - // The constructor in the interface is resolved to the type parameters declared in
|
| - // the interface, but the constructor body has type parameters resolved to the type
|
| - // parameters in the default class. This substitution patches up the type variable
|
| - // references used in parameters so they match the concrete class.
|
| - substParams = ((ClassElement)constructorElement.getEnclosingElement()).getType().getArguments();
|
| - } else {
|
| - substParams = ifaceType.getElement().getTypeParameters();
|
| - }
|
| - List<Type> arguments = ifaceType.getArguments();
|
| - ftype = (FunctionType) ftype.subst(arguments, substParams);
|
| - checkDeprecated(ASTNodes.getConstructorNameNode(node), constructorElement);
|
| - checkInvocation(node, node, constructorElement.getName(), ftype);
|
| - }
|
| - }
|
| - type.getClass(); // quick null check
|
| - return type;
|
| - }
|
| -
|
| - /**
|
| - * @param cls the {@link ClassElement} which has unimplemented members.
|
| - * @param unimplementedMembers the unimplemented members {@link Element}s.
|
| - * @return the {@link StringBuilder} with message about unimplemented members.
|
| - */
|
| - private StringBuilder getUnimplementedMembersMessage(ClassElement cls,
|
| - List<Element> unimplementedMembers) {
|
| - // Prepare groups of unimplemented members for each type.
|
| - Multimap<String, String> membersByTypes = ArrayListMultimap.create();
|
| - for (Element member : unimplementedMembers) {
|
| - ClassElement enclosingElement = (ClassElement) member.getEnclosingElement();
|
| - InterfaceType instance = types.asInstanceOf(cls.getType(), enclosingElement);
|
| - Type memberType = member.getType().subst(instance.getArguments(),
|
| - enclosingElement.getTypeParameters());
|
| - if (memberType.getKind().equals(TypeKind.FUNCTION)) {
|
| - FunctionType ftype = (FunctionType) memberType;
|
| - StringBuilder sb = new StringBuilder();
|
| - sb.append(ftype.getReturnType());
|
| - sb.append(" ");
|
| - sb.append(member.getName());
|
| - String string = ftype.toString();
|
| - sb.append(string, 0, string.lastIndexOf(" -> "));
|
| - membersByTypes.put(enclosingElement.getName(), sb.toString());
|
| - } else {
|
| - StringBuilder sb = new StringBuilder();
|
| - sb.append(memberType);
|
| - sb.append(" ");
|
| - sb.append(member.getName());
|
| - membersByTypes.put(enclosingElement.getName(), sb.toString());
|
| - }
|
| - }
|
| - // Output unimplemented members with grouping by class.
|
| - StringBuilder sb = new StringBuilder();
|
| - for (String typeName : membersByTypes.keySet()) {
|
| - sb.append("\n # From ");
|
| - sb.append(typeName);
|
| - sb.append(":");
|
| - for (String memberString : membersByTypes.get(typeName)) {
|
| - sb.append("\n ");
|
| - sb.append(memberString);
|
| - }
|
| - }
|
| - return sb;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitNullLiteral(DartNullLiteral node) {
|
| - return nullType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitParameter(DartParameter node) {
|
| - VariableElement parameter = node.getElement();
|
| - FieldElement initializerElement = parameter.getParameterInitializerElement();
|
| - if (initializerElement != null) {
|
| - checkAssignable(node, parameter.getType(), initializerElement.getType());
|
| - }
|
| - return checkInitializedDeclaration(node, node.getDefaultExpr());
|
| - }
|
| -
|
| - @Override
|
| - public Type visitParenthesizedExpression(DartParenthesizedExpression node) {
|
| - Type type = nonVoidTypeOf(node.getExpression());
|
| - type.getClass(); // quick null check
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitPropertyAccess(DartPropertyAccess node) {
|
| - if (node.getType() != null) {
|
| - return node.getType();
|
| - }
|
| - Element element = node.getElement();
|
| - if (element != null) {
|
| - return element.getType();
|
| - }
|
| - if (node.getName().isResolutionAlreadyReportedThatTheMethodCouldNotBeFound()) {
|
| - return dynamicType;
|
| - }
|
| - DartNode qualifier = node.getRealTarget();
|
| - Type receiver = nonVoidTypeOf(qualifier);
|
| - // convert into InterfaceType
|
| - 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;
|
| - if (ASTNodes.inSetterContext(node)) {
|
| - member = cls.lookupMember("setter " + name);
|
| - if (member == null) {
|
| - member = cls.lookupMember(name);
|
| - }
|
| - } else {
|
| - member = cls.lookupMember(name);
|
| - if (member == null) {
|
| - member = cls.lookupMember("setter " + name);
|
| - }
|
| - }
|
| - // is "receiver" is inferred, attempt to find member in one of the subtypes
|
| - boolean hasMemberInSubClasses = false;
|
| - if (member == null) {
|
| - if (TypeQuality.of(receiver) == TypeQuality.INFERRED && receiver instanceof InterfaceType) {
|
| - List<Member> subMembers = ((InterfaceType) receiver).lookupSubTypeMembers(name);
|
| - hasMemberInSubClasses = !subMembers.isEmpty();
|
| - if (subMembers.size() == 1) {
|
| - member = subMembers.get(0);
|
| - }
|
| - }
|
| - }
|
| - // report "not a member"
|
| - if (member == null) {
|
| - if (reportNoMemberWhenHasInterceptor || !Elements.handlesNoSuchMethod(cls)) {
|
| - if (typeChecksForInferredTypes && !hasMemberInSubClasses
|
| - && !isTooGenericInferredType(receiver) || !TypeQuality.isInferred(receiver)) {
|
| - TypeErrorCode errorCode = TypeQuality.isInferred(receiver)
|
| - ? TypeErrorCode.NOT_A_MEMBER_OF_INFERRED : TypeErrorCode.NOT_A_MEMBER_OF;
|
| - typeError(node.getName(), errorCode, name, cls);
|
| - }
|
| - }
|
| - return dynamicType;
|
| - }
|
| - // set resolved element
|
| - element = member.getElement();
|
| - node.setElement(element);
|
| - Modifiers modifiers = element.getModifiers();
|
| - if (modifiers.isStatic()) {
|
| - return typeError(node.getName(),
|
| - TypeErrorCode.STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE,
|
| - name, element.getName());
|
| - }
|
| - // @deprecated
|
| - if (element != null && element.getMetadata().isDeprecated()) {
|
| - onError(node.getName(), TypeErrorCode.DEPRECATED_ELEMENT,
|
| - Elements.getDeprecatedElementTitle(element));
|
| - }
|
| - // analyze Element
|
| - switch (element.getKind()) {
|
| - case DYNAMIC:
|
| - return dynamicType;
|
| - case CONSTRUCTOR:
|
| - return typeError(node.getName(), TypeErrorCode.MEMBER_IS_A_CONSTRUCTOR,
|
| - name, element.getName());
|
| -
|
| - case METHOD:
|
| - return member.getType();
|
| -
|
| - case FIELD:
|
| - FieldElement fieldElement = (FieldElement) element;
|
| - Modifiers fieldModifiers = fieldElement.getModifiers();
|
| -
|
| - // Prepare getter/setter members.
|
| - Member getterMember;
|
| - Member setterMember;
|
| - {
|
| - getterMember = cls.lookupMember(name);
|
| - if (getterMember == null && TypeQuality.of(cls) == TypeQuality.INFERRED) {
|
| - List<Member> members = cls.lookupSubTypeMembers(name);
|
| - if (members.size() == 1) {
|
| - getterMember = members.get(0);
|
| - }
|
| - }
|
| - }
|
| - {
|
| - setterMember = cls.lookupMember("setter " + name);
|
| - if (setterMember == null) {
|
| - setterMember = cls.lookupMember(name);
|
| - }
|
| - }
|
| - boolean inSetterContext = ASTNodes.inSetterContext(node);
|
| - boolean inGetterContext = ASTNodes.inGetterContext(node);
|
| -
|
| - // Implicit field declared as "final".
|
| - if (!fieldModifiers.isAbstractField() && fieldModifiers.isFinal() && inSetterContext) {
|
| - return typeError(node.getName(), TypeErrorCode.FIELD_IS_FINAL, node.getName());
|
| - }
|
| -
|
| - Type result = member.getType();
|
| - if (fieldModifiers.isAbstractField()) {
|
| - if (inSetterContext) {
|
| - result = setterMember != null ? setterMember.getSetterType() : null;
|
| - if (result == null) {
|
| - return typeError(node.getName(), TypeErrorCode.FIELD_HAS_NO_SETTER, node.getName());
|
| - }
|
| - }
|
| - if (inGetterContext) {
|
| - result = getterMember != null ? getterMember.getGetterType() : null;
|
| - if (result == null) {
|
| - return typeError(node.getName(), TypeErrorCode.FIELD_HAS_NO_GETTER, node.getName());
|
| - }
|
| - }
|
| - }
|
| - result = Types.makeInferred(result, receiver.getQuality());
|
| - node.setType(result);
|
| - return result;
|
| -
|
| - default:
|
| - throw internalError(node.getName(), "unexpected kind %s", element.getKind());
|
| - }
|
| - }
|
| -
|
| - @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;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitSuperExpression(DartSuperExpression node) {
|
| - if (currentClass == null) {
|
| - return dynamicType;
|
| - }
|
| - Type type = currentClass.getElement().getSupertype();
|
| - type.getClass(); // quick null check
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitSwitchStatement(DartSwitchStatement node) {
|
| - node.visitChildren(this);
|
| - // analyze "expression"
|
| - DartExpression expression = node.getExpression();
|
| - Type switchType = nonVoidTypeOf(expression);
|
| - // check "case" expressions compatibility
|
| - Type sameCaseType = null;
|
| - for (DartSwitchMember switchMember : node.getMembers()) {
|
| - if (switchMember instanceof DartCase) {
|
| - DartCase caseMember = (DartCase) switchMember;
|
| - DartExpression caseExpr = caseMember.getExpr();
|
| - // no expression, parser already reported about this
|
| - if (caseExpr == null) {
|
| - continue;
|
| - }
|
| - Type caseType = nonVoidTypeOf(caseExpr);
|
| - // all "case expressions" should be same type
|
| - if (sameCaseType == null) {
|
| - sameCaseType = caseType;
|
| - }
|
| - if (!Objects.equal(caseType, sameCaseType)) {
|
| - onError(caseExpr, TypeErrorCode.CASE_EXPRESSIONS_SHOULD_BE_SAME_TYPE, sameCaseType,
|
| - caseType);
|
| - }
|
| - // compatibility of "switch expression" and "case expression" types
|
| - checkAssignable(caseExpr, switchType, caseType);
|
| - // should not have "operator =="
|
| - if (!Objects.equal(caseType, intType) && !Objects.equal(caseType, doubleType)
|
| - && !Objects.equal(caseType, stringType)) {
|
| - Member operator = lookupMember(caseType, methodNameForBinaryOperator(Token.EQ), null);
|
| - if (operator != null && !Objects.equal(operator.getHolder(), objectType)) {
|
| - onError(caseExpr, TypeErrorCode.CASE_EXPRESSION_TYPE_SHOULD_NOT_HAVE_EQUALS, caseType);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitSyntheticErrorExpression(DartSyntheticErrorExpression node) {
|
| - return dynamicType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitSyntheticErrorStatement(DartSyntheticErrorStatement node) {
|
| - return dynamicType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitThisExpression(DartThisExpression node) {
|
| - Type type = getCurrentClass();
|
| - if (type == null) {
|
| - // this was used in a static context, so it should have already generated a fatal error
|
| - return voidType;
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitThrowExpression(DartThrowExpression node) {
|
| - if (catchDepth == 0 && node.getException() == null) {
|
| - context.onError(new DartCompilationError(node,
|
| - ResolverErrorCode.RETHROW_NOT_IN_CATCH));
|
| - }
|
| - 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;
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitTryStatement(DartTryStatement node) {
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @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);
|
| - HasSourceInfo problemTarget = getOperatorHasSourceInfo(node);
|
| - Member member = lookupMember(type, name, problemTarget);
|
| - if (member != null) {
|
| - Element element = member.getElement();
|
| - node.setElement(element);
|
| - FunctionType methodType = getMethodType(type, member, name, node);
|
| - checkDeprecated(problemTarget, element);
|
| - return checkInvocation(Collections.<DartExpression>emptyList(), node, name, methodType, null);
|
| - } 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);
|
| - if (operator == Token.DEC) {
|
| - operatorMethodName = "operator -binary";
|
| - }
|
| - Member member = itype.lookupMember(operatorMethodName);
|
| - if (member == null) {
|
| - HasSourceInfo errorTarget = getOperatorHasSourceInfo(node);
|
| - return typeError(errorTarget, TypeErrorCode.CANNOT_BE_RESOLVED,
|
| - operatorMethodName);
|
| - }
|
| - MethodElement element = ((MethodElement) member.getElement());
|
| - node.setElement(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();
|
| - Type operandType = it.next().getType();
|
| - if (!types.isAssignable(numType, operandType)) {
|
| - typeError(node, TypeErrorCode.OPERATOR_WRONG_OPERAND_TYPE,
|
| - operatorMethodName, numType.toString(), operandType.toString());
|
| - }
|
| - // Check that the return type of the operator is compatible with the receiver.
|
| - checkAssignable(node, type, returnType);
|
| - }
|
| - return node.isPrefix() ? returnType : type;
|
| - }
|
| - case CONDITIONAL:
|
| - return boolType;
|
| - default:
|
| - throw internalError(node, "unknown operator %s", operator.toString());
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitUnit(DartUnit node) {
|
| - blockOldTypes.addFirst(new BlockTypeContext());
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitAssertStatement(DartAssertStatement node) {
|
| - DartExpression condition = node.getCondition();
|
| - checkAssertCondition(condition);
|
| - // infer types, which are valid until the end of the enclosing control block
|
| - if (node.getParent() instanceof DartBlock) {
|
| - DartBlock restoreBlock = getBlockForAssertTypesInference(node);
|
| - VariableElementsRestorer variableRestorer = restoreOnBlockExit.get(restoreBlock);
|
| - if (variableRestorer == null) {
|
| - variableRestorer = new VariableElementsRestorer();
|
| - restoreOnBlockExit.put(restoreBlock, variableRestorer);
|
| - }
|
| - restoreOnBlockExit.put(restoreBlock, variableRestorer);
|
| - inferVariableTypesFromIsConditions(condition, variableRestorer);
|
| - }
|
| - // done for "assert"
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitUnqualifiedInvocation(DartUnqualifiedInvocation node) {
|
| - DartIdentifier target = node.getTarget();
|
| - String name = target.getName();
|
| - Element element = target.getElement();
|
| - node.setElement(element);
|
| - Type type;
|
| - switch (ElementKind.of(element)) {
|
| - case FIELD:
|
| - case METHOD:
|
| - type = typeAsMemberOf(element, currentClass);
|
| - break;
|
| - case NONE:
|
| - if (!target.isResolutionAlreadyReportedThatTheMethodCouldNotBeFound()) {
|
| - onError(target, TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, currentClass, target);
|
| - }
|
| - return dynamicType;
|
| - default:
|
| - type = element.getType();
|
| - // attempt to resolve to "call()" method invocation
|
| - if (type instanceof InterfaceType) {
|
| - InterfaceType interfaceType = (InterfaceType) type;
|
| - Element callElement = interfaceType.getElement().lookupLocalElement("call");
|
| - if (ElementKind.of(callElement) == ElementKind.METHOD) {
|
| - node.setElement(callElement);
|
| - type = typeAsMemberOf(callElement, interfaceType);
|
| - }
|
| - }
|
| - break;
|
| - }
|
| - checkDeprecated(target, element);
|
| - Type returnType = checkInvocation(node, target, name, type);
|
| - returnType = ExternalTypeAnalyzers.resolve(types, node, element, returnType);
|
| - return returnType;
|
| - }
|
| -
|
| - private static DartBlock getBlockForAssertTypesInference(DartNode node) {
|
| - while (node != null) {
|
| - if (node instanceof DartBlock) {
|
| - DartBlock block = (DartBlock) node;
|
| - DartNode p = block.getParent();
|
| - if (p instanceof DartIfStatement || p instanceof DartForStatement
|
| - || p instanceof DartForInStatement || p instanceof DartWhileStatement) {
|
| - return block;
|
| - }
|
| - }
|
| - node = node.getParent();
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - private static DartBlock getBlockForLoopTypesInference(DartNode node) {
|
| - while (node != null) {
|
| - if (node instanceof DartBlock) {
|
| - DartBlock block = (DartBlock) node;
|
| - DartNode p = block.getParent();
|
| - if (p instanceof DartForStatement || p instanceof DartForInStatement
|
| - || p instanceof DartWhileStatement) {
|
| - return block;
|
| - }
|
| - }
|
| - node = node.getParent();
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /**
|
| - * Report warning if given {@link Element} is deprecated.
|
| - */
|
| - private void checkDeprecated(HasSourceInfo nameNode, Element element) {
|
| - if (element != null && element.getMetadata() != null && element.getMetadata().isDeprecated()) {
|
| - onError(nameNode, TypeErrorCode.DEPRECATED_ELEMENT,
|
| - Elements.getDeprecatedElementTitle(element));
|
| - }
|
| - }
|
| -
|
| - private Type checkInvocation(DartInvocation node, DartNode diagnosticNode, String name,
|
| - Type type) {
|
| - List<DartExpression> argumentNodes = node.getArguments();
|
| - List<VariableElement> parameters;
|
| - if (node.getElement() instanceof MethodElement) {
|
| - MethodElement methodElement = (MethodElement) node.getElement();
|
| - parameters = methodElement.getParameters();
|
| - } else {
|
| - parameters = null;
|
| - }
|
| - return checkInvocation(argumentNodes, diagnosticNode, name, type, parameters);
|
| - }
|
| -
|
| - private Type checkInvocation(List<DartExpression> argumentNodes, DartNode diagnosticNode,
|
| - String name, Type type, List<VariableElement> parameters) {
|
| - // Prepare argument types.
|
| - List<Type> argumentTypes = Lists.newArrayListWithCapacity(argumentNodes.size());
|
| - for (DartExpression argumentNode : argumentNodes) {
|
| - Type argumentType = getInvocationArgumentType(argumentNode);
|
| - argumentTypes.add(argumentType);
|
| - }
|
| - return checkInvocation(argumentNodes, argumentTypes, diagnosticNode, name, type, parameters);
|
| - }
|
| -
|
| - private Type checkInvocation(List<DartExpression> argumentNodes, List<Type> argumentTypes,
|
| - DartNode diagnosticNode, String name, Type type, List<VariableElement> parameters) {
|
| - // Check that argument types are compatible with type of invoked object.
|
| - try {
|
| - switch (TypeKind.of(type)) {
|
| - case FUNCTION_ALIAS:
|
| - return checkArguments(diagnosticNode, argumentNodes, argumentTypes.iterator(),
|
| - Types.asFunctionType((FunctionAliasType) type), parameters);
|
| - case FUNCTION:
|
| - return checkArguments(diagnosticNode, argumentNodes, argumentTypes.iterator(),
|
| - (FunctionType) type, parameters);
|
| - case DYNAMIC:
|
| - return type;
|
| - default:
|
| - if (isAssignable(functionType, type)) {
|
| - // A subtype of interface Function.
|
| - return dynamicType;
|
| - } else if (name == null || currentClass == null) {
|
| - if (reportNoMemberWhenHasInterceptor || !(type instanceof InterfaceType)
|
| - || !Elements.handlesNoSuchMethod((InterfaceType) type)) {
|
| - return typeError(diagnosticNode, TypeErrorCode.NOT_A_FUNCTION_TYPE, type);
|
| - }
|
| - return dynamicType;
|
| - } else {
|
| - return typeError(diagnosticNode, TypeErrorCode.NOT_A_METHOD_IN, name, currentClass);
|
| - }
|
| - }
|
| - } finally {
|
| - // In any case visit body of function literals, so use inferred parameter types.
|
| - for (DartExpression argument : argumentNodes) {
|
| - if (argument instanceof DartNamedExpression) {
|
| - argument = ((DartNamedExpression) argument).getExpression();
|
| - }
|
| - if (argument instanceof DartFunctionExpression) {
|
| - argument.accept(this);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Almost same as {@link #nonVoidTypeOf(DartNode)}, but does not visit
|
| - * {@link DartFunctionExpression}, because we know its type already and want to infer types of
|
| - * arguments, and then propagate them into body.
|
| - */
|
| - private Type getInvocationArgumentType(DartExpression argument) {
|
| - // We are interesting in the type of expression, without name.
|
| - if (argument instanceof DartNamedExpression) {
|
| - argument = ((DartNamedExpression) argument).getExpression();
|
| - }
|
| - // Don't visit function literal, we know its "declared" type.
|
| - // But we want to visit it later, to use "inferred" type in body.
|
| - if (argument instanceof DartFunctionExpression) {
|
| - return argument.getElement().getType();
|
| - }
|
| - // General case - visit and prepare type.
|
| - return nonVoidTypeOf(argument);
|
| - }
|
| -
|
| - /**
|
| - * 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());
|
| - type.getClass(); // quick null check
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitVariable(DartVariable node) {
|
| - DartExpression value = node.getValue();
|
| - // if type is declared and right side is closure, infer its parameter types
|
| - if (value != null) {
|
| - Type varType = node.getElement().getType();
|
| - if (isExplicitlySpecifiedType(varType)) {
|
| - inferFunctionLiteralParametersTypes(value, varType);
|
| - }
|
| - }
|
| - // prepare type of value
|
| - Type result = checkInitializedDeclaration(node, value);
|
| - // if no type declared for variables, try to use type of value
|
| - if (value != null) {
|
| - inferVariableDeclarationType(node, value);
|
| - }
|
| - // done
|
| - return result;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitWhileStatement(DartWhileStatement node) {
|
| - DartExpression condition = node.getCondition();
|
| - checkCondition(condition);
|
| - // visit body
|
| - BlockTypeContext blockTypeContxt = new BlockTypeContext();
|
| - blockOldTypes.addFirst(blockTypeContxt);
|
| - try {
|
| - visitConditionalNode(condition, node.getBody());
|
| - } finally {
|
| - blockOldTypes.removeFirst();
|
| - setMergedVariableTypes(blockTypeContxt);
|
| - }
|
| - // done
|
| - 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.
|
| - Type type = nonVoidTypeOf(node.getExpression());
|
| - type.getClass(); // quick null check
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitTypeExpression(DartTypeExpression node) {
|
| - return typeOf(node.getTypeNode());
|
| - }
|
| -
|
| - @Override
|
| - public Type visitTypeParameter(DartTypeParameter node) {
|
| - if (node.getBound() != null) {
|
| - validateTypeNode(node.getBound());
|
| - }
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitNativeBlock(DartNativeBlock node) {
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @Override
|
| - public void visit(List<? extends DartNode> nodes) {
|
| - if (nodes != null) {
|
| - for (DartNode node : nodes) {
|
| - node.accept(this);
|
| - }
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Type visitArrayLiteral(DartArrayLiteral node) {
|
| - visit(node.getTypeArguments());
|
| - InterfaceType interfaceType = node.getType();
|
| - if (interfaceType != null
|
| - && interfaceType.getArguments() != null
|
| - && interfaceType.getArguments().size() > 0) {
|
| - Type elementType = interfaceType.getArguments().get(0);
|
| - for (DartExpression expression : node.getExpressions()) {
|
| - boolean isValueAssignable = checkAssignable(elementType, expression);
|
| - if (developerModeChecks && !isValueAssignable) {
|
| - typeError(expression, ResolverErrorCode.LIST_LITERAL_ELEMENT_TYPE, elementType);
|
| - }
|
| - }
|
| - }
|
| - return interfaceType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitBooleanLiteral(DartBooleanLiteral node) {
|
| - return typeOfLiteral(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitDoubleLiteral(DartDoubleLiteral node) {
|
| - return typeOfLiteral(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitField(DartField node) {
|
| - DartMethodDefinition accessor = node.getAccessor();
|
| - if (accessor != null) {
|
| - return typeOf(accessor);
|
| - } else {
|
| - DartExpression value = node.getValue();
|
| - // if type is declared and right side is closure, infer its parameter types
|
| - if (value != null) {
|
| - Type fieldType = node.getElement().getType();
|
| - if (isExplicitlySpecifiedType(fieldType)) {
|
| - inferFunctionLiteralParametersTypes(value, fieldType);
|
| - }
|
| - }
|
| - // prepare type of value
|
| - Type result = checkInitializedDeclaration(node, value);
|
| - // if no type declared for field, try to use type of value
|
| - // only final fields, because only in this case we can be sure that field is not assigned
|
| - // somewhere, may be even not in this unit
|
| - if (node.getModifiers().isFinal() && value != null) {
|
| - inferVariableDeclarationType(node, value);
|
| - }
|
| - // done
|
| - return result;
|
| - }
|
| - }
|
| -
|
| - private Type checkInitializedDeclaration(DartDeclaration<?> node, DartExpression value) {
|
| - if (value != null && node.getElement() != null) {
|
| - checkAssignable(node.getElement().getType(), value);
|
| - }
|
| - return voidType;
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if given {@link FunctionAliasElement} has direct or indirect
|
| - * reference to itself using other {@link FunctionAliasElement}s.
|
| - */
|
| - private boolean hasFunctionTypeAliasSelfReference(FunctionAliasElement target) {
|
| - Set<FunctionAliasElement> visited = Sets.newHashSet();
|
| - return hasFunctionTypeAliasReference(visited, target, target);
|
| - }
|
| -
|
| - /**
|
| - * Checks if "target" is referenced by "current".
|
| - */
|
| - private boolean hasFunctionTypeAliasReference(Set<FunctionAliasElement> visited,
|
| - FunctionAliasElement target, FunctionAliasElement current) {
|
| - FunctionType type = current.getFunctionType();
|
| - // prepare Types directly referenced by "current"
|
| - Set<Type> referencedTypes = Sets.newHashSet();
|
| - if (type != null) {
|
| - // type parameters
|
| - for (Type typeParameter : current.getTypeParameters()) {
|
| - if (typeParameter instanceof TypeVariable) {
|
| - TypeVariable typeVariable = (TypeVariable) typeParameter;
|
| - Type bound = typeVariable.getTypeVariableElement().getBound();
|
| - referencedTypes.add(bound);
|
| - }
|
| - }
|
| - // return type
|
| - referencedTypes.add(type.getReturnType());
|
| - // parameters
|
| - referencedTypes.addAll(type.getParameterTypes());
|
| - referencedTypes.addAll(type.getOptionalParameterTypes().values());
|
| - referencedTypes.addAll(type.getNamedParameterTypes().values());
|
| - }
|
| - // check that referenced types do not have references on "target"
|
| - for (Type referencedType : referencedTypes) {
|
| - if (referencedType != null
|
| - && hasFunctionTypeAliasReference(visited, target, referencedType.getElement())) {
|
| - return true;
|
| - }
|
| - }
|
| - // no
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Checks if "target" is referenced by "current".
|
| - */
|
| - private boolean hasFunctionTypeAliasReference(Set<FunctionAliasElement> visited,
|
| - FunctionAliasElement target, Element currentElement) {
|
| - // only if "current" in function type alias
|
| - if (!(currentElement instanceof FunctionAliasElement)) {
|
| - return false;
|
| - }
|
| - FunctionAliasElement current = (FunctionAliasElement) currentElement;
|
| - // found "target"
|
| - if (Objects.equal(target, current)) {
|
| - return true;
|
| - }
|
| - // prevent recursion
|
| - if (visited.contains(current)) {
|
| - return false;
|
| - }
|
| - visited.add(current);
|
| - // check type of "current" function type alias
|
| - return hasFunctionTypeAliasReference(visited, target, current);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitIntegerLiteral(DartIntegerLiteral node) {
|
| - return typeOfLiteral(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitStringLiteral(DartStringLiteral node) {
|
| - return typeOfLiteral(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitStringInterpolation(DartStringInterpolation node) {
|
| - visit(node.getExpressions());
|
| - return typeOfLiteral(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitParameterizedTypeNode(DartParameterizedTypeNode node) {
|
| - visit(node.getTypeParameters());
|
| - Type type = node.getType();
|
| - type.getClass(); // quick null check
|
| - return type;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitImportDirective(DartImportDirective node) {
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitExportDirective(DartExportDirective node) {
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitLibraryDirective(DartLibraryDirective node) {
|
| - //return typeAsVoid(node);
|
| - return voidType;
|
| - }
|
| -
|
| - @Override
|
| - public Type visitNativeDirective(DartNativeDirective node) {
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - @Override
|
| - public Type visitSourceDirective(DartSourceDirective node) {
|
| - return typeAsVoid(node);
|
| - }
|
| -
|
| - private class AbstractMethodFinder extends ASTVisitor<Void> {
|
| - private final InterfaceType currentClass;
|
| - private final Multimap<String, Element> superMembers = LinkedListMultimap.create();
|
| - private final List<Element> unimplementedElements = Lists.newArrayList();
|
| -
|
| - private AbstractMethodFinder(InterfaceType currentClass) {
|
| - this.currentClass = currentClass;
|
| - }
|
| -
|
| - @Override
|
| - public Void visitNode(DartNode node) {
|
| - throw new AssertionError();
|
| - }
|
| -
|
| - @Override
|
| - public Void visitClass(DartClass node) {
|
| - assert node.getElement().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;
|
| - }
|
| -
|
| - // Add all super members to resolve.
|
| - Element currentLibrary = currentClass.getElement().getEnclosingElement();
|
| -
|
| - // cull out duplicate elements in the supertype list - inheriting more than one interface
|
| - // of the same type is valid.
|
| - Set<ClassElement> typesForAbstractMembers = Sets.newHashSet();
|
| - for (InterfaceType supertype : supertypes) {
|
| - typesForAbstractMembers.add(supertype.getElement());
|
| - }
|
| - Set<String> artificialNames = Sets.newHashSet();
|
| - for (ClassElement interfaceElement : typesForAbstractMembers) {
|
| - for (Element member : interfaceElement.getMembers()) {
|
| - if (interfaceElement == currentClass.getElement() && !Elements.isAbstractElement(member)) {
|
| - continue;
|
| - }
|
| - String name = member.getName();
|
| - if (DartIdentifier.isPrivateName(name)) {
|
| - if (currentLibrary != member.getEnclosingElement().getEnclosingElement()) {
|
| - continue;
|
| - }
|
| - }
|
| - superMembers.put(name, member);
|
| - if (member instanceof FieldElement) {
|
| - FieldElement field = (FieldElement) member;
|
| - if (!field.getModifiers().isAbstractField()) {
|
| - artificialNames.add("setter " + name);
|
| - superMembers.put("setter " + name, member);
|
| - } else if (field.getSetter() != null && !name.startsWith("setter ")) {
|
| - superMembers.put("setter " + 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.
|
| - {
|
| - List<InterfaceType> superTypes = Lists.newArrayList();
|
| - superTypes.add(currentClass.getElement().getSupertype());
|
| - for (InterfaceType supertype : superTypes) {
|
| - while (supertype != null) {
|
| - ClassElement superclass = supertype.getElement();
|
| - for (Element member : superclass.getMembers()) {
|
| - removeSuperMemberIfNotAbstract(member);
|
| - }
|
| - supertype = supertype.getElement().getSupertype();
|
| - }
|
| - }
|
| - }
|
| -
|
| - // visit mixins
|
| - for (InterfaceType mixType : currentClass.getElement().getMixins()) {
|
| - ClassElement mixElement = mixType.getElement();
|
| - for (Element member : mixElement.getMembers()) {
|
| - removeSuperMemberIfNotAbstract(member);
|
| - }
|
| - }
|
| -
|
| - // Remove artificial "setter " members.
|
| - for (String name : artificialNames) {
|
| - superMembers.removeAll(name);
|
| - }
|
| -
|
| - // All remaining methods are unimplemented.
|
| - List<String> keys = new ArrayList<String>(superMembers.keys());
|
| - for (int i = 0; i < keys.size(); i++) {
|
| - String name = keys.get(i);
|
| - 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.
|
| - }
|
| - }
|
| - }
|
| -
|
| - // add abstract members of current class
|
| - for (Element member : currentClass.getElement().getMembers()) {
|
| - if (ElementKind.of(member) == ElementKind.FIELD && member.getModifiers().isAbstractField()) {
|
| - FieldElement field = (FieldElement) member;
|
| - MethodElement getter = field.getGetter();
|
| - MethodElement setter = field.getSetter();
|
| - if (getter != null && Elements.isAbstractElement(getter)) {
|
| - unimplementedElements.add(getter);
|
| - }
|
| - if (setter != null && Elements.isAbstractElement(setter)) {
|
| - unimplementedElements.add(setter);
|
| - }
|
| - } else if (Elements.isAbstractElement(member)) {
|
| - unimplementedElements.add(member);
|
| - }
|
| - }
|
| -
|
| - return null;
|
| - }
|
| -
|
| - private void removeSuperMemberIfNotAbstract(Element member) {
|
| - String name = member.getName();
|
| - if (ElementKind.of(member) == ElementKind.FIELD && member.getModifiers().isAbstractField()) {
|
| - FieldElement field = (FieldElement) member;
|
| - MethodElement getter = field.getGetter();
|
| - MethodElement setter = field.getSetter();
|
| - if (getter != null && !Elements.isAbstractElement(getter)) {
|
| - superMembers.removeAll(name);
|
| - }
|
| - if (setter != null && !Elements.isAbstractElement(setter)) {
|
| - if (!name.startsWith("setter ")) {
|
| - superMembers.removeAll("setter " + name);
|
| - } else {
|
| - superMembers.removeAll(name);
|
| - }
|
| - }
|
| - } else if (!Elements.isAbstractElement(member)) {
|
| - superMembers.removeAll(name);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public Void visitFieldDefinition(DartFieldDefinition node) {
|
| - this.visit(node.getFields());
|
| - return null;
|
| - }
|
| -
|
| - @Override
|
| - public Void visitField(DartField node) {
|
| - if (superMembers == null) {
|
| - return null;
|
| - }
|
| - FieldElement field = node.getElement();
|
| - // prepare overridden elements
|
| - String name = field.getName();
|
| - Set<Element> overridden = Sets.newHashSet();
|
| - if (node.getAccessor() != null) {
|
| - if (node.getAccessor().getModifiers().isSetter()) {
|
| - if (!name.startsWith("setter ")) {
|
| - name = "setter " + name;
|
| - }
|
| - }
|
| - overridden.addAll(superMembers.removeAll(name));
|
| - } else {
|
| - overridden.addAll(superMembers.removeAll(name));
|
| - overridden.addAll(superMembers.removeAll("setter " + name));
|
| - }
|
| - // check override
|
| - for (Element superElement : overridden) {
|
| - if (!(field.isStatic() && superElement.getModifiers().isStatic())) {
|
| - if (canOverride(node.getName(), field.getModifiers(), superElement)
|
| - && !superElement.getModifiers().isStatic()) {
|
| - switch (superElement.getKind()) {
|
| - case FIELD:
|
| - checkOverride(node.getName(), field, superElement);
|
| - break;
|
| - case METHOD:
|
| - typeError(node.getName(), TypeErrorCode.SUPERTYPE_HAS_METHOD, name,
|
| - superElement.getEnclosingElement().getName());
|
| - break;
|
| -
|
| - default:
|
| - typeError(node, TypeErrorCode.INTERNAL_ERROR, superElement);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - // set super-elements for FieldElement
|
| - Elements.setOverridden(field, ImmutableSet.copyOf(overridden));
|
| - // set super-elements for getter/setter
|
| - if (node.getAccessor() != null) {
|
| - Set<Element> superGetters = Sets.newHashSet();
|
| - Set<Element> superSetters = Sets.newHashSet();
|
| - for (Element superElement : overridden) {
|
| - if (superElement instanceof FieldElement) {
|
| - FieldElement superField = (FieldElement) superElement;
|
| - if (superField.getGetter() != null) {
|
| - superGetters.add(superField.getGetter());
|
| - } else if (superField.getSetter() != null) {
|
| - superSetters.add(superField.getSetter());
|
| - } else {
|
| - superGetters.add(superField);
|
| - superSetters.add(superField);
|
| - }
|
| - }
|
| - }
|
| - if (node.getAccessor().getModifiers().isGetter()) {
|
| - Elements.setOverridden(node.getAccessor().getElement(), superGetters);
|
| - }
|
| - if (node.getAccessor().getModifiers().isSetter()) {
|
| - Elements.setOverridden(node.getAccessor().getElement(), superSetters);
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - @Override
|
| - public Void visitMethodDefinition(DartMethodDefinition node) {
|
| - MethodElement method = node.getElement();
|
| - String name = method.getName();
|
| - if (superMembers != null && !method.isConstructor()) {
|
| - Collection<Element> overridden = superMembers.removeAll(name);
|
| - Elements.setOverridden(method, ImmutableSet.copyOf(overridden));
|
| - // Check for invalid @override metadata.
|
| - if (overridden.isEmpty() && node.getObsoleteMetadata().isOverride()) {
|
| - typeError(node.getName(), ResolverErrorCode.INVALID_OVERRIDE_METADATA);
|
| - }
|
| - // Check that override is valid.
|
| - for (Element superElement : overridden) {
|
| - if (!(method.isStatic() && superElement.getModifiers().isStatic())) {
|
| - if (canOverride(node.getName(), method.getModifiers(), superElement)
|
| - && !superElement.getModifiers().isStatic()) {
|
| - switch (superElement.getKind()) {
|
| - case METHOD:
|
| - checkOverride(node.getName(), method, superElement);
|
| - break;
|
| -
|
| - case FIELD:
|
| - typeError(node.getName(), TypeErrorCode.SUPERTYPE_HAS_FIELD, superElement.getName(),
|
| - superElement.getEnclosingElement().getName());
|
| - break;
|
| -
|
| - default:
|
| - typeError(node, TypeErrorCode.INTERNAL_ERROR, superElement);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /**
|
| - * Report a compile-time error if a static member tries to override an instance member
|
| - * @returns true if no compile-time error was reported
|
| - */
|
| - private boolean canOverride(HasSourceInfo errorTarget, Modifiers modifiers,
|
| - Element superElement) {
|
| - if (!superElement.getModifiers().isStatic() && modifiers.isStatic()) {
|
| - onError(errorTarget, ResolverErrorCode.CANNOT_OVERRIDE_INSTANCE_MEMBER,
|
| - superElement.getName(), superElement.getEnclosingElement().getName());
|
| - return false;
|
| - } else if (superElement.getModifiers().isStatic() && !modifiers.isStatic()) {
|
| - onError(errorTarget, TypeErrorCode.OVERRIDING_STATIC_MEMBER,
|
| - superElement.getName(), superElement.getEnclosingElement().getName());
|
| - // Although a warning, override is allowed anyway
|
| - return true;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - /**
|
| - * Report a static type error if member cannot override superElement, that
|
| - * is, they are not assignable.
|
| - */
|
| - private void checkOverride(HasSourceInfo errorTarget, Element member, Element superElement) {
|
| - String name = member.getName();
|
| - Type superMember = typeAsMemberOf(superElement, currentClass);
|
| - if (member.getKind() == ElementKind.METHOD && superElement.getKind() == ElementKind.METHOD) {
|
| - MethodElement method = (MethodElement) member;
|
| - MethodElement superMethod = (MethodElement) superElement;
|
| - if (hasLegalMethodOverrideSignature(errorTarget, method, superMethod)) {
|
| - if (!types.isSubtype(member.getType(), superMember)) {
|
| - typeError(errorTarget,
|
| - TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE,
|
| - name,
|
| - superElement.getEnclosingElement().getName(),
|
| - member.getType(),
|
| - superMember);
|
| - }
|
| - }
|
| - } else {
|
| - if (ElementKind.of(member) == ElementKind.FIELD
|
| - && ElementKind.of(superElement) == ElementKind.FIELD) {
|
| - FieldElement field = (FieldElement) member;
|
| - FieldElement superField = (FieldElement) superElement;
|
| - //
|
| - MethodElement fGetter = field.getGetter();
|
| - MethodElement sGetter = superField.getGetter();
|
| - if (fGetter != null && sGetter != null) {
|
| - checkOverride(errorTarget, fGetter, sGetter);
|
| - } else if (fGetter != null && sGetter == null || fGetter == null && sGetter != null) {
|
| - } else {
|
| - if (!types.isAssignable(superMember, member.getType())) {
|
| - typeError(errorTarget, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, name,
|
| - superElement.getEnclosingElement().getName(), member.getType(), superMember);
|
| - return;
|
| - }
|
| - }
|
| - //
|
| - MethodElement fSetter = field.getSetter();
|
| - MethodElement sSetter = superField.getSetter();
|
| - if (fSetter != null && sSetter != null) {
|
| - checkOverride(errorTarget, fSetter, sSetter);
|
| - } else if (fSetter != null && sSetter == null || fSetter == null && sSetter != null) {
|
| - } else {
|
| - if (!types.isAssignable(superMember, member.getType())) {
|
| - typeError(errorTarget, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, name,
|
| - superElement.getEnclosingElement().getName(), member.getType(), superMember);
|
| - return;
|
| - }
|
| - }
|
| - return;
|
| - }
|
| - if (!types.isAssignable(superMember, member.getType())) {
|
| - typeError(errorTarget, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, name,
|
| - superElement.getEnclosingElement().getName(), member.getType(), superMember);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if given "method" has signature compatible with "superMethod".
|
| - */
|
| - private boolean hasLegalMethodOverrideSignature(HasSourceInfo errorTarget,
|
| - MethodElement method,
|
| - MethodElement superMethod) {
|
| - // Prepare parameters.
|
| - List<VariableElement> parameters = method.getParameters();
|
| - List<VariableElement> superParameters = superMethod.getParameters();
|
| - // Number of required parameters should be same.
|
| - {
|
| - int numRequired = Elements.getNumberOfRequiredParameters(method);
|
| - int superNumRequired = Elements.getNumberOfRequiredParameters(superMethod);
|
| - if (numRequired != superNumRequired) {
|
| - onError(errorTarget,
|
| - ResolverErrorCode.CANNOT_OVERRIDE_METHOD_NUM_REQUIRED_PARAMS,
|
| - getMethodSignature(superMethod),
|
| - superMethod.getEnclosingElement().getName());
|
| - return false;
|
| - }
|
| - }
|
| - // "method" should have at least same number of optional positional parameters as "superMethod"
|
| - {
|
| - LinkedHashMap<VariableElement, DartExpression> defMethod = getParametersDefaultsOptional(parameters);
|
| - LinkedHashMap<VariableElement, DartExpression> defSuper = getParametersDefaultsOptional(superParameters);
|
| - if (defMethod.size() < defSuper.size()) {
|
| - onError(errorTarget,
|
| - ResolverErrorCode.CANNOT_OVERRIDE_METHOD_OPTIONAL_PARAMS,
|
| - getMethodSignature(superMethod),
|
| - superMethod.getEnclosingElement().getName());
|
| - return false;
|
| - }
|
| - Iterator<Entry<VariableElement, DartExpression>> defMethodIter = defMethod.entrySet().iterator();
|
| - Iterator<Entry<VariableElement, DartExpression>> defSuperIter = defSuper.entrySet().iterator();
|
| - while (defSuperIter.hasNext()) {
|
| - Entry<VariableElement, DartExpression> methodEntry = defMethodIter.next();
|
| - Entry<VariableElement, DartExpression> superEntry = defSuperIter.next();
|
| - DartExpression methodValue = methodEntry.getValue();
|
| - DartExpression superValue = superEntry.getValue();
|
| - if (superValue != null
|
| - && !Objects.equal(ObjectUtils.toString(methodValue),
|
| - ObjectUtils.toString(superValue))) {
|
| - onError(methodEntry.getKey().getSourceInfo(),
|
| - TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, method.getName(),
|
| - superValue);
|
| - }
|
| - }
|
| - }
|
| - // "method" should have at least all named parameters of "superMethod".
|
| - {
|
| - Map<String, VariableElement> methodNames = getParametersNamed(parameters);
|
| - Map<String, VariableElement> superNames = getParametersNamed(superParameters);
|
| - if (!methodNames.keySet().containsAll(superNames.keySet())) {
|
| - onError(errorTarget, ResolverErrorCode.CANNOT_OVERRIDE_METHOD_NAMED_PARAMS, method.getName());
|
| - return false;
|
| - }
|
| - Map<String, DartExpression> methodDefs = getParametersDefaultsNamed(parameters);
|
| - Map<String, DartExpression> superDefs = getParametersDefaultsNamed(superParameters);
|
| - for (Entry<String, DartExpression> entry : superDefs.entrySet()) {
|
| - String name = entry.getKey();
|
| - DartExpression defValue = methodDefs.get(name);
|
| - DartExpression superDefValue = superDefs.get(name);
|
| - if (superDefValue != null
|
| - && !Objects.equal(ObjectUtils.toString(defValue),
|
| - ObjectUtils.toString(superDefValue))) {
|
| - onError(methodNames.get(name).getSourceInfo(),
|
| - TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, method.getName(),
|
| - superDefValue);
|
| - }
|
| - }
|
| - }
|
| -// List<VariableElement> named = getNamedParameters(parameters);
|
| -// List<VariableElement> superNamed = getNamedParameters(superParameters);
|
| -// Iterator<VariableElement> namedIterator = named.iterator();
|
| -// Iterator<VariableElement> superNamedIterator = superNamed.iterator();
|
| -// while (superNamedIterator.hasNext()) {
|
| -// VariableElement superParameter = superNamedIterator.next();
|
| -// if (namedIterator.hasNext()) {
|
| -// VariableElement parameter = namedIterator.next();
|
| -// if (Objects.equal(parameter.getName(), superParameter.getName())) {
|
| -// DartExpression superDefValue = superParameter.getDefaultValue();
|
| -// DartExpression defValue = parameter.getDefaultValue();
|
| -// if (superDefValue != null
|
| -// && !Objects.equal(ObjectUtils.toString(defValue),
|
| -// ObjectUtils.toString(superDefValue))) {
|
| -// onError(parameter.getSourceInfo(),
|
| -// TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, method.getName(),
|
| -// superDefValue);
|
| -// }
|
| -// continue;
|
| -// }
|
| -// }
|
| -// onError(errorTarget, ResolverErrorCode.CANNOT_OVERRIDE_METHOD_NAMED_PARAMS, method.getName());
|
| -// return false;
|
| -// }
|
| - return true;
|
| - }
|
| -
|
| - private String getMethodSignature(MethodElement method) {
|
| - StringBuilder builder = new StringBuilder();
|
| - builder.append(method.getName());
|
| - builder.append("(");
|
| - boolean inOptional = false;
|
| - boolean inNamed = false;
|
| - List<VariableElement> superParameters = method.getParameters();
|
| - int parameterCount = superParameters.size();
|
| - for (int i = 0; i < parameterCount; i++) {
|
| - if (i > 0) {
|
| - builder.append(", ");
|
| - }
|
| - VariableElement parameter = superParameters.get(i);
|
| - if (!inOptional && parameter.isOptional()) {
|
| - builder.append("[");
|
| - inOptional = true;
|
| - }
|
| - if (!inNamed && parameter.isNamed()) {
|
| - builder.append("{");
|
| - inNamed = true;
|
| - }
|
| - builder.append(parameter.getType().toString());
|
| - }
|
| - if (inOptional) {
|
| - builder.append("]");
|
| - }
|
| - if (inNamed) {
|
| - builder.append("}");
|
| - }
|
| - builder.append(")");
|
| - String methodSignature = builder.toString();
|
| - return methodSignature;
|
| - }
|
| -
|
| - private LinkedHashMap<VariableElement, DartExpression> getParametersDefaultsOptional(List<VariableElement> parameters) {
|
| - LinkedHashMap<VariableElement, DartExpression> defaults = Maps.newLinkedHashMap();
|
| - for (VariableElement parameter : parameters) {
|
| - if (parameter.isOptional()) {
|
| - defaults.put(parameter, parameter.getDefaultValue());
|
| - }
|
| - }
|
| - return defaults;
|
| - }
|
| -
|
| - private Map<String, VariableElement> getParametersNamed(List<VariableElement> parameters) {
|
| - Map<String, VariableElement> namedParameters = Maps.newHashMap();
|
| - for (VariableElement parameter : parameters) {
|
| - if (parameter.isNamed()) {
|
| - namedParameters.put(parameter.getName(), parameter);
|
| - }
|
| - }
|
| - return namedParameters;
|
| - }
|
| -
|
| - private Map<String, DartExpression> getParametersDefaultsNamed(List<VariableElement> parameters) {
|
| - Map<String, DartExpression> defaults = Maps.newHashMap();
|
| - for (VariableElement parameter : parameters) {
|
| - if (parameter.isNamed()) {
|
| - defaults.put(parameter.getName(), parameter.getDefaultValue());
|
| - }
|
| - }
|
| - return defaults;
|
| - }
|
| - }
|
| - }
|
| -
|
| - private static boolean isExplicitlySpecifiedType(Type type) {
|
| - return type != null && TypeKind.of(type) != TypeKind.DYNAMIC
|
| - && !TypeQuality.isInferred(type);
|
| - }
|
| -
|
| - /**
|
| - * @return the {@link TypeQuality} of given {@link DartExpression}.
|
| - */
|
| - public static TypeQuality getTypeQuality(DartExpression expr) {
|
| - if (expr != null) {
|
| - if (expr instanceof DartIdentifier) {
|
| - Type varType = expr.getType();
|
| - if (varType != null) {
|
| - TypeQuality varTypeQuality = varType.getQuality();
|
| - if (varTypeQuality == TypeQuality.EXACT) {
|
| - varTypeQuality = TypeQuality.INFERRED_EXACT;
|
| - }
|
| - return varTypeQuality;
|
| - }
|
| - }
|
| - if (expr instanceof DartLiteral) {
|
| - return TypeQuality.INFERRED_EXACT;
|
| - }
|
| - if (expr instanceof DartUnaryExpression) {
|
| - DartUnaryExpression unary = (DartUnaryExpression) expr;
|
| - if (hasTypeBoolIntDouble(unary.getArg())) {
|
| - return TypeQuality.INFERRED_EXACT;
|
| - }
|
| - return TypeQuality.INFERRED;
|
| - }
|
| - if (expr instanceof DartBinaryExpression) {
|
| - DartBinaryExpression binary = (DartBinaryExpression) expr;
|
| - if (hasTypeBoolIntDouble(binary.getArg1()) && hasTypeBoolIntDouble(binary.getArg2())) {
|
| - return TypeQuality.INFERRED_EXACT;
|
| - }
|
| - return TypeQuality.INFERRED;
|
| - }
|
| - if (expr instanceof DartNewExpression) {
|
| - DartNewExpression newExpression = (DartNewExpression) expr;
|
| - ConstructorElement constructorElement = newExpression.getElement();
|
| - if (constructorElement != null && !constructorElement.getModifiers().isFactory()) {
|
| - return TypeQuality.INFERRED_EXACT;
|
| - }
|
| - return TypeQuality.INFERRED;
|
| - }
|
| - }
|
| - return TypeQuality.INFERRED;
|
| - }
|
| -
|
| - private static boolean hasTypeBoolIntDouble(DartExpression expr) {
|
| - Type type = expr.getType();
|
| - return isCoreType(type, "bool") || isCoreType(type, "int") || isCoreType(type, "double");
|
| - }
|
| -
|
| - private static boolean isCoreType(Type type, String name) {
|
| - return type != null
|
| - && Elements.isCoreLibrarySource(type.getElement().getSourceInfo().getSource())
|
| - && type.getElement().getName().equals(name);
|
| - }
|
| -
|
| - /**
|
| - * Sometimes inferred type is too generic - such as "Object" or "Collection", so there are no
|
| - * reason to reports problems.
|
| - */
|
| - private static boolean isTooGenericInferredType(Type type) {
|
| - if (TypeQuality.of(type) == TypeQuality.INFERRED) {
|
| - String typeName = type.getElement().getName();
|
| - if ("Object".equals(typeName) || "Collection".equals(typeName)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -}
|
|
|