// 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(); }