private void handleConstructorAnnotation(
     String functionName,
     Node funNode,
     RawNominalType constructorType,
     NominalType parentClass,
     ImmutableSet<NominalType> implementedIntfs,
     DeclaredTypeRegistry registry,
     FunctionTypeBuilder builder) {
   String className = constructorType.toString();
   NominalType builtinObject = registry.getCommonTypes().getObjectType();
   if (parentClass == null && !functionName.equals("Object")) {
     parentClass = builtinObject;
   }
   if (parentClass != null) {
     if (!constructorType.addSuperClass(parentClass)) {
       warnings.add(JSError.make(funNode, INHERITANCE_CYCLE, className));
     } else if (parentClass != builtinObject) {
       if (constructorType.isStruct() && !parentClass.isStruct()) {
         warnings.add(JSError.make(funNode, CONFLICTING_SHAPE_TYPE, "struct", className));
       } else if (constructorType.isDict() && !parentClass.isDict()) {
         warnings.add(JSError.make(funNode, CONFLICTING_SHAPE_TYPE, "dict", className));
       }
     }
   }
   if (constructorType.isDict() && !implementedIntfs.isEmpty()) {
     warnings.add(JSError.make(funNode, DICT_IMPLEMENTS_INTERF, className));
   }
   boolean noCycles = constructorType.addInterfaces(implementedIntfs);
   Preconditions.checkState(noCycles);
   builder.addNominalType(constructorType.getInstanceAsJSType());
 }
 public void resolveEnum(EnumType e, DeclaredTypeRegistry registry) {
   Preconditions.checkState(
       e != null, "getEnum should only be " + "called when we know that the enum is defined");
   if (e.isResolved()) {
     return;
   }
   JSTypeExpression texp = e.getTypeExpr();
   JSType enumeratedType;
   if (texp == null) {
     warnings.add(JSError.make(e.getTypeExprForErrorReporting().getRoot(), CIRCULAR_TYPEDEF_ENUM));
     enumeratedType = JSType.UNKNOWN;
   } else {
     int numTypeVars = howmanyTypeVars;
     enumeratedType = getTypeFromJSTypeExpression(texp, registry, null);
     if (howmanyTypeVars > numTypeVars) {
       warnings.add(JSError.make(texp.getRoot(), ENUM_WITH_TYPEVARS));
       enumeratedType = JSType.UNKNOWN;
       howmanyTypeVars = numTypeVars;
     } else if (enumeratedType.isTop()) {
       warnings.add(JSError.make(texp.getRoot(), ENUM_IS_TOP));
       enumeratedType = JSType.UNKNOWN;
     } else if (enumeratedType.isUnion()) {
       warnings.add(JSError.make(texp.getRoot(), ENUM_IS_UNION));
       enumeratedType = JSType.UNKNOWN;
     }
   }
   e.resolveEnum(enumeratedType);
 }
 private NominalType getMaybeParentClass(
     JSDocInfo jsdoc,
     String functionName,
     Node funNode,
     ImmutableList<String> typeParameters,
     DeclaredTypeRegistry registry) {
   if (!jsdoc.hasBaseType()) {
     return null;
   }
   if (!jsdoc.isConstructor()) {
     warnings.add(JSError.make(funNode, EXTENDS_NOT_ON_CTOR_OR_INTERF, functionName));
     return null;
   }
   Node docNode = jsdoc.getBaseType().getRoot();
   JSType extendedType = getMaybeTypeFromComment(docNode, registry, typeParameters);
   if (extendedType == null) {
     return null;
   }
   NominalType parentClass = extendedType.getNominalTypeIfSingletonObj();
   if (parentClass != null && parentClass.isClass()) {
     return parentClass;
   }
   if (parentClass == null) {
     warnings.add(
         JSError.make(funNode, EXTENDS_NON_OBJECT, functionName, extendedType.toString()));
   } else {
     Preconditions.checkState(parentClass.isInterface());
     warnings.add(JSError.make(funNode, CONFLICTING_EXTENDED_TYPE, "constructor", functionName));
   }
   return null;
 }
예제 #4
0
 @Override
 public void println(CheckLevel level, JSError error) {
   switch (level) {
     case ERROR:
       Servlet.LOG.debug("error: " + error.toString());
       break;
     case WARNING:
       Servlet.LOG.debug("warning: " + error.toString());
       break;
   }
 }
 private void fillInFunTypeBuilder(
     Node jsdocNode,
     RawNominalType ownerType,
     DeclaredTypeRegistry registry,
     ImmutableList<String> typeParameters,
     FunctionTypeBuilder builder)
     throws UnknownTypeException {
   Node child = jsdocNode.getFirstChild();
   if (child.getType() == Token.THIS) {
     if (ownerType == null) {
       builder.addReceiverType(getThisOrNewType(child.getFirstChild(), registry, typeParameters));
     }
     child = child.getNext();
   } else if (child.getType() == Token.NEW) {
     Node newTypeNode = child.getFirstChild();
     JSType t = getThisOrNewType(newTypeNode, registry, typeParameters);
     if (!t.isSubtypeOf(JSType.TOP_OBJECT) && (!t.hasTypeVariable() || t.hasScalar())) {
       warnings.add(JSError.make(newTypeNode, NEW_EXPECTS_OBJECT_OR_TYPEVAR, t.toString()));
     }
     builder.addNominalType(t);
     child = child.getNext();
   }
   if (child.getType() == Token.PARAM_LIST) {
     for (Node arg = child.getFirstChild(); arg != null; arg = arg.getNext()) {
       try {
         switch (arg.getType()) {
           case Token.EQUALS:
             builder.addOptFormal(
                 getTypeFromCommentHelper(arg.getFirstChild(), registry, typeParameters));
             break;
           case Token.ELLIPSIS:
             Node restNode = arg.getFirstChild();
             builder.addRestFormals(
                 restNode == null
                     ? JSType.UNKNOWN
                     : getTypeFromCommentHelper(restNode, registry, typeParameters));
             break;
           default:
             builder.addReqFormal(getTypeFromCommentHelper(arg, registry, typeParameters));
             break;
         }
       } catch (FunctionTypeBuilder.WrongParameterOrderException e) {
         warnings.add(JSError.make(jsdocNode, WRONG_PARAMETER_ORDER));
         builder.addPlaceholderFormal();
       }
     }
     child = child.getNext();
   }
   builder.addRetType(getTypeFromCommentHelper(child, registry, typeParameters));
 }
 /**
  * Consumes either a "classic" function jsdoc with @param, @return, etc, or a jsdoc with @type
  * {function ...} and finds the types of the formal parameters and the return value. It returns a
  * builder because the callers of this function must separately handle @constructor, @interface,
  * etc.
  *
  * <p>constructorType is non-null iff this function is a constructor or interface declaration.
  */
 public FunctionAndSlotType getFunctionType(
     JSDocInfo jsdoc,
     String functionName,
     Node declNode,
     RawNominalType constructorType,
     RawNominalType ownerType,
     DeclaredTypeRegistry registry) {
   FunctionTypeBuilder builder = new FunctionTypeBuilder();
   if (ownerType != null) {
     builder.addReceiverType(ownerType.getInstanceAsJSType());
   }
   try {
     if (jsdoc != null && jsdoc.getType() != null) {
       JSType simpleType = getDeclaredTypeOfNode(jsdoc, ownerType, registry);
       if (simpleType.isUnknown() || simpleType.isTop()) {
         return qmarkFunctionDeclared;
       }
       FunctionType funType = simpleType.getFunType();
       if (funType != null) {
         JSType slotType = simpleType.isFunctionType() ? null : simpleType;
         DeclaredFunctionType declType = funType.toDeclaredFunctionType();
         if (ownerType != null && funType.getThisType() == null) {
           declType = declType.withReceiverType(ownerType.getInstanceAsJSType());
         }
         return new FunctionAndSlotType(slotType, declType);
       } else {
         warnings.add(JSError.make(declNode, FUNCTION_WITH_NONFUNC_JSDOC));
         jsdoc = null;
       }
     }
     DeclaredFunctionType declType =
         getFunTypeFromTypicalFunctionJsdoc(
             jsdoc, functionName, declNode, constructorType, ownerType, registry, builder);
     return new FunctionAndSlotType(null, declType);
   } catch (FunctionTypeBuilder.WrongParameterOrderException e) {
     warnings.add(JSError.make(declNode, WRONG_PARAMETER_ORDER));
     return qmarkFunctionDeclared;
   }
 }
 private void fillInFormalParameterTypes(
     JSDocInfo jsdoc,
     Node funNode,
     ImmutableList<String> typeParameters,
     DeclaredTypeRegistry registry,
     FunctionTypeBuilder builder,
     boolean ignoreJsdoc /* for when the jsdoc is malformed */) {
   boolean ignoreFunNode = !funNode.isFunction();
   Node params = ignoreFunNode ? null : funNode.getSecondChild();
   ParamIterator iterator = new ParamIterator(params, jsdoc);
   while (iterator.hasNext()) {
     String pname = iterator.nextString();
     Node param = iterator.getNode();
     ParameterKind p = ParameterKind.REQUIRED;
     if (param != null && convention.isOptionalParameter(param)) {
       p = ParameterKind.OPTIONAL;
     } else if (param != null && convention.isVarArgsParameter(param)) {
       p = ParameterKind.REST;
     }
     ParameterType inlineParamType =
         (ignoreJsdoc || ignoreFunNode || param.getJSDocInfo() == null)
             ? null
             : parseParameter(param.getJSDocInfo().getType(), p, registry, typeParameters);
     ParameterType fnParamType = inlineParamType;
     JSTypeExpression jsdocExp = jsdoc == null ? null : jsdoc.getParameterType(pname);
     if (jsdocExp != null) {
       if (inlineParamType == null) {
         fnParamType = parseParameter(jsdocExp, p, registry, typeParameters);
       } else {
         warnings.add(JSError.make(param, TWO_JSDOCS, "formal parameter " + pname));
       }
     }
     JSType t = null;
     if (fnParamType != null) {
       p = fnParamType.kind;
       t = fnParamType.type;
     }
     switch (p) {
       case REQUIRED:
         builder.addReqFormal(t);
         break;
       case OPTIONAL:
         builder.addOptFormal(t);
         break;
       case REST:
         builder.addRestFormals(t != null ? t : JSType.UNKNOWN);
         break;
     }
   }
 }
 private ImmutableSet<NominalType> getInterfacesHelper(
     JSDocInfo jsdoc,
     DeclaredTypeRegistry registry,
     ImmutableList<String> typeParameters,
     boolean implementedIntfs) {
   ImmutableSet.Builder<NominalType> builder = ImmutableSet.builder();
   for (JSTypeExpression texp :
       (implementedIntfs ? jsdoc.getImplementedInterfaces() : jsdoc.getExtendedInterfaces())) {
     Node expRoot = texp.getRoot();
     JSType interfaceType = getMaybeTypeFromComment(expRoot, registry, typeParameters);
     if (interfaceType != null) {
       NominalType nt = interfaceType.getNominalTypeIfSingletonObj();
       if (nt != null && nt.isInterface()) {
         builder.add(nt);
       } else if (implementedIntfs) {
         warnings.add(JSError.make(expRoot, IMPLEMENTS_NON_INTERFACE, interfaceType.toString()));
       } else {
         warnings.add(JSError.make(expRoot, EXTENDS_NON_INTERFACE, interfaceType.toString()));
       }
     }
   }
   return builder.build();
 }
예제 #9
0
 @Override
 public CompilationError createCompilationError() {
   final int lineno = -1;
   final int charno = -1;
   JSError jsError =
       JSError.make(
           getInput().getName(),
           lineno,
           charno,
           CheckLevel.ERROR,
           MISSING_PROVIDE,
           getMissingProvide(),
           getInput().getName());
   return new CompilationError(jsError);
 }
 private void handleInterfaceAnnotation(
     JSDocInfo jsdoc,
     String functionName,
     Node funNode,
     RawNominalType constructorType,
     ImmutableSet<NominalType> implementedIntfs,
     ImmutableList<String> typeParameters,
     DeclaredTypeRegistry registry,
     FunctionTypeBuilder builder) {
   if (!implementedIntfs.isEmpty()) {
     warnings.add(JSError.make(funNode, CONFLICTING_IMPLEMENTED_TYPE, functionName));
   }
   ImmutableSet<NominalType> extendedInterfaces =
       getExtendedInterfaces(jsdoc, registry, typeParameters);
   boolean noCycles =
       constructorType.addInterfaces(
           extendedInterfaces.isEmpty()
               ? ImmutableSet.of(registry.getCommonTypes().getObjectType())
               : extendedInterfaces);
   if (!noCycles) {
     warnings.add(JSError.make(funNode, INHERITANCE_CYCLE, constructorType.toString()));
   }
   builder.addNominalType(constructorType.getInstanceAsJSType());
 }
 private JSType getNominalTypeHelper(
     RawNominalType rawType,
     Node n,
     DeclaredTypeRegistry registry,
     ImmutableList<String> outerTypeParameters)
     throws UnknownTypeException {
   NominalType uninstantiated = rawType.getAsNominalType();
   if (!rawType.isGeneric() && !n.hasChildren()) {
     return rawType.getInstanceWithNullability(NULLABLE_TYPES_BY_DEFAULT);
   }
   ImmutableList.Builder<JSType> typeList = ImmutableList.builder();
   if (n.hasChildren()) {
     // Compute instantiation of polymorphic class/interface.
     Preconditions.checkState(n.getFirstChild().isBlock(), n);
     for (Node child : n.getFirstChild().children()) {
       typeList.add(getTypeFromCommentHelper(child, registry, outerTypeParameters));
     }
   }
   ImmutableList<JSType> typeArguments = typeList.build();
   ImmutableList<String> typeParameters = rawType.getTypeParameters();
   int typeArgsSize = typeArguments.size();
   int typeParamsSize = typeParameters.size();
   if (typeArgsSize != typeParamsSize) {
     // We used to also warn when (typeArgsSize < typeParamsSize), but it
     // happens so often that we stopped. Array, Object and goog.Promise are
     // common culprits, but many other types as well.
     if (typeArgsSize > typeParamsSize) {
       warnings.add(
           JSError.make(
               n,
               INVALID_GENERICS_INSTANTIATION,
               uninstantiated.getName(),
               String.valueOf(typeParamsSize),
               String.valueOf(typeArgsSize)));
     }
     return maybeMakeNullable(
         JSType.fromObjectType(
             ObjectType.fromNominalType(
                 uninstantiated.instantiateGenerics(
                     fixLengthOfTypeList(typeParameters.size(), typeArguments)))));
   }
   return maybeMakeNullable(
       JSType.fromObjectType(
           ObjectType.fromNominalType(uninstantiated.instantiateGenerics(typeArguments))));
 }
 public void resolveTypedef(Typedef td, DeclaredTypeRegistry registry) {
   Preconditions.checkState(
       td != null,
       "getTypedef should only be " + "called when we know that the typedef is defined");
   if (td.isResolved()) {
     return;
   }
   JSTypeExpression texp = td.getTypeExpr();
   JSType tdType;
   if (texp == null) {
     warnings.add(
         JSError.make(td.getTypeExprForErrorReporting().getRoot(), CIRCULAR_TYPEDEF_ENUM));
     tdType = JSType.UNKNOWN;
   } else {
     tdType = getTypeFromJSTypeExpression(texp, registry, null);
   }
   td.resolveTypedef(tdType);
 }
 private void fillInReturnType(
     JSDocInfo jsdoc,
     Node funNode,
     Node parent,
     ImmutableList<String> typeParameters,
     DeclaredTypeRegistry registry,
     FunctionTypeBuilder builder,
     boolean ignoreJsdoc /* for when the jsdoc is malformed */) {
   JSDocInfo inlineRetJsdoc = ignoreJsdoc ? null : funNode.getFirstChild().getJSDocInfo();
   JSTypeExpression retTypeExp = jsdoc == null ? null : jsdoc.getReturnType();
   if (parent.isSetterDef() && retTypeExp == null) {
     // inline returns for getters/setters are not parsed
     builder.addRetType(JSType.UNDEFINED);
   } else if (inlineRetJsdoc != null) {
     builder.addRetType(getDeclaredTypeOfNode(inlineRetJsdoc, registry, typeParameters));
     if (retTypeExp != null) {
       warnings.add(JSError.make(funNode, TWO_JSDOCS, "the return type"));
     }
   } else {
     builder.addRetType(getTypeFromJSTypeExpression(retTypeExp, registry, typeParameters));
   }
 }
  private DeclaredFunctionType getFunTypeFromTypicalFunctionJsdoc(
      JSDocInfo jsdoc,
      String functionName,
      Node funNode,
      RawNominalType constructorType,
      RawNominalType ownerType,
      DeclaredTypeRegistry registry,
      FunctionTypeBuilder builder) {
    ImmutableList.Builder<String> typeParamsBuilder = ImmutableList.builder();
    ImmutableList<String> typeParameters = ImmutableList.of();
    Node parent = funNode.getParent();

    // TODO(dimvar): need more @template warnings
    // - warn for multiple @template annotations
    // - warn for @template annotation w/out usage

    boolean ignoreJsdoc = false;
    if (jsdoc != null) {
      if (constructorType != null) {
        // We have created new names for these type variables in GTI, don't
        // create new ones here.
        typeParamsBuilder.addAll(constructorType.getTypeParameters());
      } else {
        for (String typeParam : jsdoc.getTemplateTypeNames()) {
          typeParamsBuilder.add(this.nameGen.getNextName(typeParam));
        }
      }
      // We don't properly support the type transformation language; we treat
      // its type variables as ordinary type variables.
      for (String typeParam : jsdoc.getTypeTransformations().keySet()) {
        typeParamsBuilder.add(this.nameGen.getNextName(typeParam));
      }
      typeParameters = typeParamsBuilder.build();
      if (!typeParameters.isEmpty()) {
        if (parent.isSetterDef() || parent.isGetterDef()) {
          ignoreJsdoc = true;
          jsdoc = null;
          warnings.add(JSError.make(funNode, TEMPLATED_GETTER_SETTER));
        } else {
          builder.addTypeParameters(typeParameters);
        }
      }
    }
    if (ownerType != null) {
      typeParamsBuilder.addAll(ownerType.getTypeParameters());
      typeParameters = typeParamsBuilder.build();
    }

    fillInFormalParameterTypes(jsdoc, funNode, typeParameters, registry, builder, ignoreJsdoc);
    fillInReturnType(jsdoc, funNode, parent, typeParameters, registry, builder, ignoreJsdoc);
    if (jsdoc == null) {
      return builder.buildDeclaration();
    }

    // Look at other annotations, eg, @constructor
    NominalType parentClass =
        getMaybeParentClass(jsdoc, functionName, funNode, typeParameters, registry);
    ImmutableSet<NominalType> implementedIntfs =
        getImplementedInterfaces(jsdoc, registry, typeParameters);
    if (constructorType == null && jsdoc.isConstructorOrInterface()) {
      // Anonymous type, don't register it.
      return builder.buildDeclaration();
    } else if (jsdoc.isConstructor()) {
      handleConstructorAnnotation(
          functionName, funNode, constructorType, parentClass, implementedIntfs, registry, builder);
    } else if (jsdoc.isInterface()) {
      handleInterfaceAnnotation(
          jsdoc,
          functionName,
          funNode,
          constructorType,
          implementedIntfs,
          typeParameters,
          registry,
          builder);
    } else if (!implementedIntfs.isEmpty()) {
      warnings.add(JSError.make(funNode, IMPLEMENTS_WITHOUT_CONSTRUCTOR, functionName));
    }

    if (jsdoc.hasThisType()) {
      Node thisRoot = jsdoc.getThisType().getRoot();
      Preconditions.checkState(thisRoot.getType() == Token.BANG);
      builder.addReceiverType(getThisOrNewType(thisRoot.getFirstChild(), registry, typeParameters));
    }

    return builder.buildDeclaration();
  }
 private JSType getTypeFromCommentHelper(
     Node n, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters)
     throws UnknownTypeException {
   Preconditions.checkNotNull(n);
   if (typeParameters == null) {
     typeParameters = ImmutableList.of();
   }
   switch (n.getType()) {
     case Token.LC:
       return getRecordTypeHelper(n, registry, typeParameters);
     case Token.EMPTY: // for function types that don't declare a return type
       return JSType.UNKNOWN;
     case Token.VOID:
       // TODO(dimvar): void can be represented in 2 ways: Token.VOID and a
       // Token.STRING whose getString() is "void".
       // Change jsdoc parsing to only have one representation.
       return JSType.UNDEFINED;
     case Token.LB:
       warnings.add(JSError.make(n, BAD_ARRAY_TYPE_SYNTAX));
       return JSType.UNKNOWN;
     case Token.STRING:
       return getNamedTypeHelper(n, registry, typeParameters);
     case Token.PIPE:
       {
         // The way JSType.join works, Subtype|Supertype is equal to Supertype,
         // so when programmers write un-normalized unions, we normalize them
         // silently. We may also want to warn.
         JSType union = JSType.BOTTOM;
         for (Node child = n.getFirstChild(); child != null; child = child.getNext()) {
           // TODO(dimvar): When the union has many things, we join and throw
           // away types, except the result of the last join. Very inefficient.
           // Consider optimizing.
           JSType nextType = getTypeFromCommentHelper(child, registry, typeParameters);
           if (nextType.isUnknown()) {
             return JSType.UNKNOWN;
           }
           JSType nextUnion = JSType.join(union, nextType);
           if (nextUnion.isBottom()) {
             warnings.add(
                 JSError.make(n, UNION_IS_UNINHABITABLE, nextType.toString(), union.toString()));
             return JSType.UNKNOWN;
           }
           union = nextUnion;
         }
         return union;
       }
     case Token.BANG:
       {
         JSType nullableType =
             getTypeFromCommentHelper(n.getFirstChild(), registry, typeParameters);
         if (nullableType.isTypeVariable()) {
           warnings.add(JSError.make(n, CANNOT_MAKE_TYPEVAR_NON_NULL));
         }
         return nullableType.removeType(JSType.NULL);
       }
     case Token.QMARK:
       {
         Node child = n.getFirstChild();
         if (child == null) {
           return JSType.UNKNOWN;
         } else {
           return JSType.join(
               JSType.NULL, getTypeFromCommentHelper(child, registry, typeParameters));
         }
       }
     case Token.STAR:
       return JSType.TOP;
     case Token.FUNCTION:
       return getFunTypeHelper(n, registry, typeParameters);
     default:
       throw new IllegalArgumentException(
           "Unsupported type exp: " + Token.name(n.getType()) + " " + n.toStringTree());
   }
 }