// Computes a type from a jsdoc that includes a function type, rather than
 // one that includes @param, @return, etc.
 private JSType getFunTypeHelper(
     Node jsdocNode, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters)
     throws UnknownTypeException {
   FunctionTypeBuilder builder = new FunctionTypeBuilder();
   fillInFunTypeBuilder(jsdocNode, null, registry, typeParameters, builder);
   return registry.getCommonTypes().fromFunctionType(builder.buildFunction());
 }
 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 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 JSTypeCreatorFromJSDoc(CodingConvention convention, UniqueNameGenerator nameGen) {
   this.qmarkFunctionDeclared =
       new FunctionAndSlotType(
           null, FunctionTypeBuilder.qmarkFunctionBuilder().buildDeclaration());
   this.convention = convention;
   this.nameGen = nameGen;
 }
 public FunctionType toFunctionType() {
   FunctionTypeBuilder builder = new FunctionTypeBuilder();
   for (JSType formal : requiredFormals) {
     builder.addReqFormal(formal == null ? JSType.UNKNOWN : formal);
   }
   for (JSType formal : optionalFormals) {
     builder.addOptFormal(formal == null ? JSType.UNKNOWN : formal);
   }
   builder.addRestFormals(restFormals);
   builder.addRetType(returnType == null ? JSType.UNKNOWN : returnType);
   builder.addNominalType(nominalType);
   builder.addReceiverType(receiverType);
   builder.addTypeParameters(typeParameters);
   return builder.buildFunction();
 }
 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));
   }
 }
 /**
  * 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;
   }
 }
 public DeclaredFunctionType withTypeInfoFromSuper(DeclaredFunctionType superType) {
   FunctionTypeBuilder builder = new FunctionTypeBuilder();
   int i = 0;
   for (JSType formal : requiredFormals) {
     builder.addReqFormal(formal != null ? formal : superType.getFormalType(i));
     i++;
   }
   for (JSType formal : optionalFormals) {
     builder.addOptFormal(formal != null ? formal : superType.getFormalType(i));
     i++;
   }
   if (restFormals != null) {
     builder.addRestFormals(restFormals);
   } else if (superType.hasRestFormals()) {
     builder.addRestFormals(superType.restFormals);
   }
   builder.addRetType(returnType != null ? returnType : superType.returnType);
   builder.addNominalType(nominalType);
   builder.addReceiverType(receiverType);
   builder.addTypeParameters(typeParameters);
   return builder.buildDeclaration();
 }
 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));
 }
 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 static DeclaredFunctionType meet(DeclaredFunctionType f1, DeclaredFunctionType f2) {
    if (f1.equals(f2)) {
      return f1;
    }

    FunctionTypeBuilder builder = new FunctionTypeBuilder();
    int minRequiredArity = Math.min(f1.requiredFormals.size(), f2.requiredFormals.size());
    for (int i = 0; i < minRequiredArity; i++) {
      builder.addReqFormal(nullAcceptingJoin(f1.getFormalType(i), f2.getFormalType(i)));
    }
    int maxTotalArity =
        Math.max(
            f1.requiredFormals.size() + f1.optionalFormals.size(),
            f2.requiredFormals.size() + f2.optionalFormals.size());
    for (int i = minRequiredArity; i < maxTotalArity; i++) {
      builder.addOptFormal(nullAcceptingJoin(f1.getFormalType(i), f2.getFormalType(i)));
    }
    if (f1.restFormals != null || f2.restFormals != null) {
      builder.addRestFormals(nullAcceptingJoin(f1.restFormals, f2.restFormals));
    }
    builder.addRetType(nullAcceptingMeet(f1.returnType, f2.returnType));
    return builder.buildDeclaration();
  }
 static FunctionTypeBuilder qmarkFunctionBuilder() {
   FunctionTypeBuilder builder = new FunctionTypeBuilder();
   builder.addRestFormals(JSType.UNKNOWN);
   builder.addRetType(JSType.UNKNOWN);
   return builder;
 }
  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();
  }