@Override public void recordInterfaces( JSType type, JSType relatedType, DisambiguateProperties<JSType>.Property p) { ObjectType objType = ObjectType.cast(type); if (objType != null) { FunctionType constructor; if (objType instanceof FunctionType) { constructor = (FunctionType) objType; } else if (objType instanceof FunctionPrototypeType) { constructor = ((FunctionPrototypeType) objType).getOwnerFunction(); } else { constructor = objType.getConstructor(); } while (constructor != null) { for (ObjectType itype : constructor.getImplementedInterfaces()) { JSType top = getTypeWithProperty(p.name, itype); if (top != null) { p.addType(itype, top, relatedType); } else { recordInterfaces(itype, relatedType, p); } // If this interface invalidated this property, return now. if (p.skipRenaming) return; } if (constructor.isInterface() || constructor.isConstructor()) { constructor = constructor.getSuperClassConstructor(); } else { constructor = null; } } } }
/** * Infers the type of {@code this}. * * @param info The JSDocInfo for this function. * @param owner The node for the object whose prototype "owns" this function. For example, {@code * A} in the expression {@code A.prototype.foo}. May be null to indicate that this is not a * prototype property. */ FunctionTypeBuilder inferThisType(JSDocInfo info, @Nullable Node owner) { ObjectType maybeThisType = null; if (info != null && info.hasThisType()) { maybeThisType = ObjectType.cast(info.getThisType().evaluate(scope, typeRegistry)); } if (maybeThisType != null) { thisType = maybeThisType; thisType.setValidator(new ThisTypeValidator()); } else if (owner != null && (info == null || !info.hasType())) { // If the function is of the form: // x.prototype.y = function() {} // then we can assume "x" is the @this type. On the other hand, // if it's of the form: // /** @type {Function} */ x.prototype.y; // then we should not give it a @this type. String ownerTypeName = owner.getQualifiedName(); ObjectType ownerType = ObjectType.cast( typeRegistry.getForgivingType( scope, ownerTypeName, sourceName, owner.getLineno(), owner.getCharno())); if (ownerType != null) { thisType = ownerType; } } return this; }
@Override public boolean isInvalidatingType(JSType type) { if (type == null || invalidatingTypes.contains(type) || type.isUnknownType() /* unresolved types */) { return true; } ObjectType objType = ObjectType.cast(type); return objType != null && !objType.hasReferenceName(); }
private Set<JSType> getTypesToSkipForTypeNonUnion(JSType type) { Set<JSType> types = Sets.newHashSet(); JSType skipType = type; while (skipType != null) { types.add(skipType); ObjectType objSkipType = skipType.toObjectType(); if (objSkipType != null) { skipType = objSkipType.getImplicitPrototype(); } else { break; } } return types; }
/** Invalidates the given type, so that no properties on it will be renamed. */ private void addInvalidatingType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type instanceof UnionType) { for (JSType alt : ((UnionType) type).getAlternates()) { addInvalidatingType(alt); } return; } typeSystem.addInvalidatingType(type); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getImplicitPrototype() != null) { typeSystem.addInvalidatingType(objType.getImplicitPrototype()); } }
/** * Infers the type of {@code this}. * * @param type The type of this. */ FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) { ObjectType objType = ObjectType.cast(type); if (objType != null && (info == null || !info.hasType())) { thisType = objType; } return this; }
protected void initTypes() { ALL_TYPE = registry.getNativeType(JSTypeNative.ALL_TYPE); NO_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE); NO_TYPE = registry.getNativeObjectType(JSTypeNative.NO_TYPE); NO_RESOLVED_TYPE = registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE); ARRAY_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE); ARRAY_TYPE = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); BOOLEAN_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE); BOOLEAN_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); BOOLEAN_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); CHECKED_UNKNOWN_TYPE = registry.getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); DATE_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.DATE_FUNCTION_TYPE); DATE_TYPE = registry.getNativeObjectType(JSTypeNative.DATE_TYPE); ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.ERROR_FUNCTION_TYPE); ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.ERROR_TYPE); EVAL_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.EVAL_ERROR_FUNCTION_TYPE); EVAL_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.EVAL_ERROR_TYPE); FUNCTION_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.FUNCTION_FUNCTION_TYPE); FUNCTION_INSTANCE_TYPE = registry.getNativeFunctionType(JSTypeNative.FUNCTION_INSTANCE_TYPE); FUNCTION_PROTOTYPE = registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE); GREATEST_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE); LEAST_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE); NULL_TYPE = registry.getNativeType(JSTypeNative.NULL_TYPE); NUMBER_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE); NUMBER_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE); NUMBER_STRING_BOOLEAN = registry.getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN); NUMBER_TYPE = registry.getNativeType(JSTypeNative.NUMBER_TYPE); OBJECT_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE); OBJECT_NUMBER_STRING = registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING); OBJECT_NUMBER_STRING_BOOLEAN = registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN); OBJECT_PROTOTYPE = registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE); OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); RANGE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.RANGE_ERROR_FUNCTION_TYPE); RANGE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.RANGE_ERROR_TYPE); REFERENCE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE); REFERENCE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.REFERENCE_ERROR_TYPE); REGEXP_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE); REGEXP_TYPE = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); STRING_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE); STRING_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); STRING_TYPE = registry.getNativeType(JSTypeNative.STRING_TYPE); SYNTAX_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE); SYNTAX_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.SYNTAX_ERROR_TYPE); TYPE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.TYPE_ERROR_FUNCTION_TYPE); TYPE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.TYPE_ERROR_TYPE); U2U_CONSTRUCTOR_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE); U2U_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_FUNCTION_TYPE); UNKNOWN_TYPE = registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); URI_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.URI_ERROR_FUNCTION_TYPE); URI_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.URI_ERROR_TYPE); VOID_TYPE = registry.getNativeType(JSTypeNative.VOID_TYPE); addNativeProperties(registry); NATIVE_PROPERTIES_COUNT = OBJECT_TYPE.getPropertiesCount(); }
@Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportWarning(EXTENDS_NON_OBJECT, fnName, type.toString()); } else if (objectType.isUnknownType() && // If this has a supertype that hasn't been resolved yet, // then we can assume this type will be ok once the super // type resolves. (objectType.getImplicitPrototype() == null || objectType.getImplicitPrototype().isResolved())) { reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); } else { return true; } return false; }
@Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportError(BAD_IMPLEMENTED_TYPE, fnName); } else if (objectType.isUnknownType() && // If this has a supertype that hasn't been resolved yet, // then we can assume this type will be ok once the super // type resolves. (objectType.getImplicitPrototype() == null || objectType.getImplicitPrototype().isResolved())) { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); } else { return true; } return false; }
@Override public Iterable<JSType> getTypeAlternatives(JSType type) { if (type.isUnionType()) { return ((UnionType) type).getAlternates(); } else { ObjectType objType = type.toObjectType(); if (objType != null && objType.getConstructor() != null && objType.getConstructor().isInterface()) { List<JSType> list = Lists.newArrayList(); for (FunctionType impl : registry.getDirectImplementors(objType)) { list.add(impl.getInstanceType()); } return list; } else { return null; } } }
/** * Infer the role of the function (whether it's a constructor or interface) and what it inherits * from in JSDocInfo. */ FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) { if (info != null) { isConstructor = info.isConstructor(); isInterface = info.isInterface(); // base type if (info.hasBaseType()) { if (isConstructor || isInterface) { JSType maybeBaseType = info.getBaseType().evaluate(scope, typeRegistry); if (maybeBaseType != null && maybeBaseType.setValidator(new ExtendedTypeValidator())) { baseType = (ObjectType) maybeBaseType; } } else { reportWarning(EXTENDS_WITHOUT_TYPEDEF, fnName); } } // implemented interfaces if (isConstructor || isInterface) { implementedInterfaces = Lists.newArrayList(); for (JSTypeExpression t : info.getImplementedInterfaces()) { JSType maybeInterType = t.evaluate(scope, typeRegistry); if (maybeInterType != null && maybeInterType.setValidator(new ImplementedTypeValidator())) { implementedInterfaces.add((ObjectType) maybeInterType); } } if (baseType != null) { JSType maybeFunctionType = baseType.getConstructor(); if (maybeFunctionType instanceof FunctionType) { FunctionType functionType = baseType.getConstructor(); Iterables.addAll(implementedInterfaces, functionType.getImplementedInterfaces()); } } } else if (info.getImplementedInterfaceCount() > 0) { reportWarning(IMPLEMENTS_WITHOUT_CONSTRUCTOR, fnName); } } return this; }
@Test public void canReferenceExportedTypeReferences() { CompilerUtil compiler = createCompiler(path("foo/bar.js"), path("foo/baz.js")); compiler.compile( createSourceFile(path("foo/bar.js"), "/** @constructor */", "exports.foo = function(){};"), createSourceFile( path("foo/baz.js"), "var bar = require('./bar');", "var foo = require('./bar').foo;", "/** @type {!bar.foo} */", "var a = new bar.foo();", "/** @type {!foo} */", "var b = new foo();")); assertThat(compiler.toSource()) .startsWith( lines( "var $jscomp = {};", "$jscomp.scope = {};", "var module$foo$bar = {};", "module$foo$bar.foo = function() {", "};", "var module$foo$baz = {};", "$jscomp.scope.a = new module$foo$bar.foo;", "$jscomp.scope.b = new module$foo$bar.foo;")); ObjectType scope = compiler .getCompiler() .getTopScope() .getVar("$jscomp") .getType() .findPropertyType("scope") .toObjectType(); assertEquals("module$foo$bar.foo", scope.getPropertyType("a").getDisplayName()); assertEquals("module$foo$bar.foo", scope.getPropertyType("b").getDisplayName()); }
/** Gets the type of {@code this} in the current scope. */ @Override public JSType getTypeOfThis() { if (isGlobal()) { return ObjectType.cast(rootNode.getJSType()); } Preconditions.checkState(rootNode.isFunction()); JSType nodeType = rootNode.getJSType(); if (nodeType != null && nodeType.isFunctionType()) { return nodeType.toMaybeFunctionType().getTypeOfThis(); } else { // Executed when the current scope has not been typechecked. return null; } }
@Override public ObjectType getTypeWithProperty(String field, JSType type) { if (!(type instanceof ObjectType)) { if (type.autoboxesTo() != null) { type = type.autoboxesTo(); } else { return null; } } // Ignore the prototype itself at all times. if ("prototype".equals(field)) { return null; } // We look up the prototype chain to find the highest place (if any) that // this appears. This will make references to overriden properties look // like references to the initial property, so they are renamed alike. ObjectType foundType = null; ObjectType objType = ObjectType.cast(type); while (objType != null && objType.getImplicitPrototype() != objType) { if (objType.hasOwnProperty(field)) { foundType = objType; } objType = objType.getImplicitPrototype(); } // If the property does not exist on the referenced type but the original // type is an object type, see if any subtype has the property. if (foundType == null) { ObjectType maybeType = ObjectType.cast(registry.getGreatestSubtypeWithProperty(type, field)); // getGreatestSubtypeWithProperty does not guarantee that the property // is defined on the returned type, it just indicates that it might be, // so we have to double check. if (maybeType != null && maybeType.hasOwnProperty(field)) { foundType = maybeType; } } return foundType; }
/** Adds a basic set of properties to the native types. */ public static void addNativeProperties(JSTypeRegistry registry) { JSType booleanType = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); JSType numberType = registry.getNativeType(JSTypeNative.NUMBER_TYPE); JSType stringType = registry.getNativeType(JSTypeNative.STRING_TYPE); JSType unknownType = registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); ObjectType objectType = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); ObjectType arrayType = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); ObjectType dateType = registry.getNativeObjectType(JSTypeNative.DATE_TYPE); ObjectType regexpType = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); ObjectType booleanObjectType = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); ObjectType numberObjectType = registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE); ObjectType stringObjectType = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); ObjectType objectPrototype = registry.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE).getPrototype(); addMethod(registry, objectPrototype, "constructor", objectType); addMethod(registry, objectPrototype, "toString", stringType); addMethod(registry, objectPrototype, "toLocaleString", stringType); addMethod(registry, objectPrototype, "valueOf", unknownType); addMethod(registry, objectPrototype, "hasOwnProperty", booleanType); addMethod(registry, objectPrototype, "isPrototypeOf", booleanType); addMethod(registry, objectPrototype, "propertyIsEnumerable", booleanType); ObjectType arrayPrototype = registry.getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE).getPrototype(); addMethod(registry, arrayPrototype, "constructor", arrayType); addMethod(registry, arrayPrototype, "toString", stringType); addMethod(registry, arrayPrototype, "toLocaleString", stringType); addMethod(registry, arrayPrototype, "concat", arrayType); addMethod(registry, arrayPrototype, "join", stringType); addMethod(registry, arrayPrototype, "pop", unknownType); addMethod(registry, arrayPrototype, "push", numberType); addMethod(registry, arrayPrototype, "reverse", arrayType); addMethod(registry, arrayPrototype, "shift", unknownType); addMethod(registry, arrayPrototype, "slice", arrayType); addMethod(registry, arrayPrototype, "sort", arrayType); addMethod(registry, arrayPrototype, "splice", arrayType); addMethod(registry, arrayPrototype, "unshift", numberType); arrayType.defineDeclaredProperty("length", numberType, true, null); ObjectType booleanPrototype = registry.getNativeFunctionType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE).getPrototype(); addMethod(registry, booleanPrototype, "constructor", booleanObjectType); addMethod(registry, booleanPrototype, "toString", stringType); addMethod(registry, booleanPrototype, "valueOf", booleanType); ObjectType datePrototype = registry.getNativeFunctionType(JSTypeNative.DATE_FUNCTION_TYPE).getPrototype(); addMethod(registry, datePrototype, "constructor", dateType); addMethod(registry, datePrototype, "toString", stringType); addMethod(registry, datePrototype, "toDateString", stringType); addMethod(registry, datePrototype, "toTimeString", stringType); addMethod(registry, datePrototype, "toLocaleString", stringType); addMethod(registry, datePrototype, "toLocaleDateString", stringType); addMethod(registry, datePrototype, "toLocaleTimeString", stringType); addMethod(registry, datePrototype, "valueOf", numberType); addMethod(registry, datePrototype, "getTime", numberType); addMethod(registry, datePrototype, "getFullYear", numberType); addMethod(registry, datePrototype, "getUTCFullYear", numberType); addMethod(registry, datePrototype, "getMonth", numberType); addMethod(registry, datePrototype, "getUTCMonth", numberType); addMethod(registry, datePrototype, "getDate", numberType); addMethod(registry, datePrototype, "getUTCDate", numberType); addMethod(registry, datePrototype, "getDay", numberType); addMethod(registry, datePrototype, "getUTCDay", numberType); addMethod(registry, datePrototype, "getHours", numberType); addMethod(registry, datePrototype, "getUTCHours", numberType); addMethod(registry, datePrototype, "getMinutes", numberType); addMethod(registry, datePrototype, "getUTCMinutes", numberType); addMethod(registry, datePrototype, "getSeconds", numberType); addMethod(registry, datePrototype, "getUTCSeconds", numberType); addMethod(registry, datePrototype, "getMilliseconds", numberType); addMethod(registry, datePrototype, "getUTCMilliseconds", numberType); addMethod(registry, datePrototype, "getTimezoneOffset", numberType); addMethod(registry, datePrototype, "setTime", numberType); addMethod(registry, datePrototype, "setMilliseconds", numberType); addMethod(registry, datePrototype, "setUTCMilliseconds", numberType); addMethod(registry, datePrototype, "setSeconds", numberType); addMethod(registry, datePrototype, "setUTCSeconds", numberType); addMethod(registry, datePrototype, "setMinutes", numberType); addMethod(registry, datePrototype, "setUTCMinutes", numberType); addMethod(registry, datePrototype, "setHours", numberType); addMethod(registry, datePrototype, "setUTCHours", numberType); addMethod(registry, datePrototype, "setDate", numberType); addMethod(registry, datePrototype, "setUTCDate", numberType); addMethod(registry, datePrototype, "setMonth", numberType); addMethod(registry, datePrototype, "setUTCMonth", numberType); addMethod(registry, datePrototype, "setFullYear", numberType); addMethod(registry, datePrototype, "setUTCFullYear", numberType); addMethod(registry, datePrototype, "toUTCString", stringType); addMethod(registry, datePrototype, "toGMTString", stringType); ObjectType numberPrototype = registry.getNativeFunctionType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE).getPrototype(); addMethod(registry, numberPrototype, "constructor", numberObjectType); addMethod(registry, numberPrototype, "toString", stringType); addMethod(registry, numberPrototype, "toLocaleString", stringType); addMethod(registry, numberPrototype, "valueOf", numberType); addMethod(registry, numberPrototype, "toFixed", stringType); addMethod(registry, numberPrototype, "toExponential", stringType); addMethod(registry, numberPrototype, "toPrecision", stringType); ObjectType regexpPrototype = registry.getNativeFunctionType(JSTypeNative.REGEXP_FUNCTION_TYPE).getPrototype(); addMethod(registry, regexpPrototype, "constructor", regexpType); addMethod(registry, regexpPrototype, "exec", registry.createNullableType(arrayType)); addMethod(registry, regexpPrototype, "test", booleanType); addMethod(registry, regexpPrototype, "toString", stringType); regexpType.defineDeclaredProperty("source", stringType, true, null); regexpType.defineDeclaredProperty("global", booleanType, true, null); regexpType.defineDeclaredProperty("ignoreCase", booleanType, true, null); regexpType.defineDeclaredProperty("multiline", booleanType, true, null); regexpType.defineDeclaredProperty("lastIndex", numberType, true, null); ObjectType stringPrototype = registry.getNativeFunctionType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE).getPrototype(); addMethod(registry, stringPrototype, "constructor", stringObjectType); addMethod(registry, stringPrototype, "toString", stringType); addMethod(registry, stringPrototype, "valueOf", stringType); addMethod(registry, stringPrototype, "charAt", stringType); addMethod(registry, stringPrototype, "charCodeAt", numberType); addMethod(registry, stringPrototype, "concat", stringType); addMethod(registry, stringPrototype, "indexOf", numberType); addMethod(registry, stringPrototype, "lastIndexOf", numberType); addMethod(registry, stringPrototype, "localeCompare", numberType); addMethod(registry, stringPrototype, "match", registry.createNullableType(arrayType)); addMethod(registry, stringPrototype, "replace", stringType); addMethod(registry, stringPrototype, "search", numberType); addMethod(registry, stringPrototype, "slice", stringType); addMethod(registry, stringPrototype, "split", arrayType); addMethod(registry, stringPrototype, "substring", stringType); addMethod(registry, stringPrototype, "toLowerCase", stringType); addMethod(registry, stringPrototype, "toLocaleLowerCase", stringType); addMethod(registry, stringPrototype, "toUpperCase", stringType); addMethod(registry, stringPrototype, "toLocaleUpperCase", stringType); stringObjectType.defineDeclaredProperty("length", numberType, true, null); }
private static void addMethod( JSTypeRegistry registry, ObjectType receivingType, String methodName, JSType returnType) { receivingType.defineDeclaredProperty( methodName, new FunctionBuilder(registry).withReturnType(returnType).build(), true, null); }
@Override public JSType caseObjectType(ObjectType type) { return type.isSubtype(getNativeType(ARRAY_TYPE)) ? null : type; }
/** @param fnNode A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node fnNode) { Preconditions.checkState(fnNode.isFunction()); StringBuilder sb = new StringBuilder("/**\n"); JSType type = fnNode.getJSType(); if (type == null || type.isUnknownType()) { return ""; } FunctionType funType = type.toMaybeFunctionType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 if (fnNode != null) { Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * "); appendAnnotation(sb, "param", getParameterNodeJSDocType(n)); sb.append(" ").append(paramNode.getString()).append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) { sb.append(" * "); appendAnnotation(sb, "return", retType.toAnnotationString()); sb.append("\n"); } // Constructor/interface if (funType.isConstructor() || funType.isInterface()) { FunctionType superConstructor = funType.getSuperClassConstructor(); if (superConstructor != null) { ObjectType superInstance = funType.getSuperClassConstructor().getInstanceType(); if (!superInstance.toString().equals("Object")) { sb.append(" * "); appendAnnotation(sb, "extends", superInstance.toAnnotationString()); sb.append("\n"); } } if (funType.isInterface()) { for (ObjectType interfaceType : funType.getExtendedInterfaces()) { sb.append(" * "); appendAnnotation(sb, "extends", interfaceType.toAnnotationString()); sb.append("\n"); } } // Avoid duplicates, add implemented type to a set first Set<String> interfaces = Sets.newTreeSet(); for (ObjectType interfaze : funType.getImplementedInterfaces()) { interfaces.add(interfaze.toAnnotationString()); } for (String interfaze : interfaces) { sb.append(" * "); appendAnnotation(sb, "implements", interfaze); sb.append("\n"); } if (funType.isConstructor()) { sb.append(" * @constructor\n"); } else if (funType.isInterface()) { sb.append(" * @interface\n"); } } if (fnNode != null && fnNode.getBooleanProp(Node.IS_DISPATCHER)) { sb.append(" * @javadispatch\n"); } sb.append(" */\n"); return sb.toString(); }