Search in sources :

Example 1 with DeclaredFunctionType

use of com.google.javascript.jscomp.newtypes.DeclaredFunctionType in project closure-compiler by google.

the class NewTypeInference method isAllowedToNotReturn.

private static boolean isAllowedToNotReturn(NTIScope methodScope) {
    Node fn = methodScope.getRoot();
    if (fn.isFromExterns()) {
        return true;
    }
    DeclaredFunctionType declFn = methodScope.getDeclaredFunctionType();
    if (declFn != null && declFn.isAbstract() && declFn.getReceiverType() != null) {
        return true;
    }
    if (!NodeUtil.isPrototypeMethod(fn)) {
        return false;
    }
    JSType maybeInterface;
    Node ntQnameNode = NodeUtil.getPrototypeClassName(fn.getParent().getFirstChild());
    if (ntQnameNode.isName()) {
        maybeInterface = methodScope.getDeclaredTypeOf(ntQnameNode.getString());
    } else {
        QualifiedName ntQname = QualifiedName.fromNode(ntQnameNode);
        JSType rootNamespace = methodScope.getDeclaredTypeOf(ntQname.getLeftmostName());
        maybeInterface = rootNamespace == null ? null : rootNamespace.getProp(ntQname.getAllButLeftmost());
    }
    return maybeInterface != null && maybeInterface.isInterfaceDefinition();
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) Node(com.google.javascript.rhino.Node) DiGraphNode(com.google.javascript.jscomp.graph.DiGraph.DiGraphNode) QualifiedName(com.google.javascript.jscomp.newtypes.QualifiedName)

Example 2 with DeclaredFunctionType

use of com.google.javascript.jscomp.newtypes.DeclaredFunctionType in project closure-compiler by google.

the class GlobalTypeInfoCollector method checkAndFreezeNominalType.

/**
 * Reconcile the properties of this nominal type with the properties it inherits from its
 * supertypes, and then freeze the type.
 *
 * NOTE(dimvar): The logic is somewhat convoluted, but I'm not sure how to make it clearer.
 * There is just a lot of case analysis involved.
 *
 * - Collect all super definitions of methods and other properties.
 * - During the collection process, warn when a property on this type is not a subtype of
 *   an inherited property on the supertype.
 * - If there are multiple inherited definitions for a property, meet them to find a single
 *   inherited type. If they meet to bottom, warn.
 * - If this type has its own definition of the property, and not just inherits it, adjust the
 *   type of the local definition based on the inherited property type.
 */
private void checkAndFreezeNominalType(RawNominalType rawType) {
    if (rawType.isFrozen()) {
        return;
    }
    checkState(inProgressFreezes.add(rawType), "Cycle in freeze order: %s (%s)", rawType, inProgressFreezes);
    NominalType superClass = rawType.getSuperClass();
    Set<String> nonInheritedPropNames = rawType.getAllNonInheritedProps();
    if (superClass != null && !superClass.isFrozen()) {
        checkAndFreezeNominalType(superClass.getRawNominalType());
    }
    for (NominalType superInterf : rawType.getInterfaces()) {
        if (!superInterf.isFrozen()) {
            checkAndFreezeNominalType(superInterf.getRawNominalType());
        }
    }
    // If this type defines a method m, collect all inherited definitions of m here in order
    // to possibly adjust the type of m.
    Multimap<String, DeclaredFunctionType> superMethodTypes = LinkedHashMultimap.create();
    // For each property p on this type, regardless of whether it is defined on the type or just
    // inherited, collect all inherited definitions of p here.
    Multimap<String, JSType> superPropTypes = LinkedHashMultimap.create();
    // Collect inherited types for extended classes
    if (superClass != null) {
        checkState(superClass.isFrozen());
        // TODO(blickly): Can we optimize this to skip unnecessary iterations?
        for (String pname : superClass.getPropertyNames()) {
            if (superClass.isAbstractClass() && superClass.hasAbstractMethod(pname) && !rawType.isAbstractClass() && !rawType.mayHaveOwnNonStrayProp(pname)) {
                warnings.add(JSError.make(rawType.getDefSite(), ABSTRACT_METHOD_NOT_IMPLEMENTED_IN_CONCRETE_CLASS, pname, superClass.getName()));
            }
            nonInheritedPropNames.remove(pname);
            checkSuperProperty(rawType, superClass, pname, superMethodTypes, superPropTypes);
        }
    }
    // Collect inherited types for extended/implemented interfaces
    for (NominalType superInterf : rawType.getInterfaces()) {
        checkState(superInterf.isFrozen());
        for (String pname : superInterf.getPropertyNames()) {
            nonInheritedPropNames.remove(pname);
            checkSuperProperty(rawType, superInterf, pname, superMethodTypes, superPropTypes);
        }
    }
    // Munge inherited types of methods
    for (String pname : superMethodTypes.keySet()) {
        Collection<DeclaredFunctionType> methodTypes = superMethodTypes.get(pname);
        checkState(!methodTypes.isEmpty());
        PropertyDef localPropDef = checkNotNull(propertyDefs.get(rawType, pname));
        // To find the declared type of a method, we must meet declared types
        // from all inherited methods.
        DeclaredFunctionType superMethodType = DeclaredFunctionType.meet(methodTypes);
        DeclaredFunctionType localMethodType = localPropDef.methodType;
        boolean getsTypeFromParent = getsTypeInfoFromParentMethod(localPropDef);
        if (superMethodType == null) {
            // If the inherited types are not compatible, pick one.
            superMethodType = methodTypes.iterator().next();
        } else if (getsTypeFromParent && localMethodType.getMaxArity() > superMethodType.getMaxArity()) {
            // When getsTypeFromParent is true, we miss the invalid override earlier, so we check here.
            warnings.add(JSError.make(localPropDef.defSite, INVALID_PROP_OVERRIDE, pname, superMethodType.toFunctionType().toString(), localMethodType.toFunctionType().toString()));
        }
        DeclaredFunctionType updatedMethodType = localMethodType.withTypeInfoFromSuper(superMethodType, getsTypeFromParent);
        localPropDef.updateMethodType(updatedMethodType);
        superPropTypes.put(pname, getCommonTypes().fromFunctionType(updatedMethodType.toFunctionType()));
    }
    // Check inherited types of all properties
    for (String pname : superPropTypes.keySet()) {
        Collection<JSType> defs = superPropTypes.get(pname);
        checkState(!defs.isEmpty());
        JSType inheritedPropType = getCommonTypes().TOP;
        for (JSType inheritedType : defs) {
            inheritedPropType = JSType.meet(inheritedPropType, inheritedType);
            if (inheritedPropType.isBottom()) {
                warnings.add(JSError.make(rawType.getDefSite(), ANCESTOR_TYPES_HAVE_INCOMPATIBLE_PROPERTIES, rawType.getName(), pname, defs.toString()));
                break;
            }
        }
        // Adjust the type of the local property definition based on the inherited type.
        PropertyDef localPropDef = propertyDefs.get(rawType, pname);
        if (localPropDef != null) {
            JSType updatedPropType;
            if (localPropDef.methodType == null) {
                JSType t = rawType.getInstancePropDeclaredType(pname);
                updatedPropType = t == null ? inheritedPropType : t.specialize(inheritedPropType);
            } else {
                // When the property is a method, the local type already includes the meet of the
                // inherited types, from the earlier loop. We don't use inheritedPropType in this branch,
                // because if the inherited method type has static properties (e.g., framework-specific
                // passes can add such properties), we don't want to inherit these.
                FunctionType ft = localPropDef.methodType.toFunctionType();
                updatedPropType = getCommonTypes().fromFunctionType(ft);
            }
            // TODO(dimvar): check if we can have @const props here
            rawType.addProtoProperty(pname, null, updatedPropType, false);
        }
    }
    // Warn when inheriting from incompatible IObject types
    if (rawType.inheritsFromIObject()) {
        JSType wrapped = rawType.getInstanceAsJSType();
        if (wrapped.getIndexType() == null) {
            warnings.add(JSError.make(rawType.getDefSite(), ANCESTOR_TYPES_HAVE_INCOMPATIBLE_PROPERTIES, rawType.getName(), "IObject<K,V>#index", "the keys K have types that can't be joined."));
        } else if (wrapped.getIndexedType() == null) {
            warnings.add(JSError.make(rawType.getDefSite(), ANCESTOR_TYPES_HAVE_INCOMPATIBLE_PROPERTIES, rawType.getName(), "IObject<K,V>#index", "the values V should have a common subtype."));
        }
    }
    // Warn for a prop declared with @override that isn't overriding anything.
    for (String pname : nonInheritedPropNames) {
        PropertyDef propDef = propertyDefs.get(rawType, pname);
        if (propDef != null) {
            Node propDefsite = propDef.defSite;
            JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(propDefsite);
            if (jsdoc != null && jsdoc.isOverride()) {
                warnings.add(JSError.make(propDefsite, UNKNOWN_OVERRIDE, pname, rawType.getName()));
            }
        }
    }
    // Freeze nominal type once all properties are added.
    rawType.freeze();
    if (rawType.isBuiltinObject()) {
        NominalType literalObj = getCommonTypes().getLiteralObjNominalType();
        if (!literalObj.isFrozen()) {
            literalObj.getRawNominalType().freeze();
        }
    }
    inProgressFreezes.remove(rawType);
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) RawNominalType(com.google.javascript.jscomp.newtypes.RawNominalType) NominalType(com.google.javascript.jscomp.newtypes.NominalType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) FunctionType(com.google.javascript.jscomp.newtypes.FunctionType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) Node(com.google.javascript.rhino.Node) JSDocInfo(com.google.javascript.rhino.JSDocInfo)

Example 3 with DeclaredFunctionType

use of com.google.javascript.jscomp.newtypes.DeclaredFunctionType in project closure-compiler by google.

the class NTIScope method getLocalDeclaration.

private Declaration getLocalDeclaration(String name, boolean includeTypes) {
    checkArgument(!name.contains("."));
    if (!isDefinedLocally(name, includeTypes)) {
        return null;
    }
    DeclaredFunctionType declaredType = getDeclaredTypeForOwnBody();
    JSType type = null;
    boolean isTypeVar = false;
    if ("this".equals(name)) {
        type = getDeclaredTypeOf("this");
    } else if (locals.containsKey(name)) {
        type = locals.get(name).getDeclaredType();
    } else if (formals.contains(name)) {
        int formalIndex = formals.indexOf(name);
        if (declaredType != null && formalIndex != -1) {
            JSType formalType = declaredType.getFormalType(formalIndex);
            if (formalType != null && !formalType.isBottom()) {
                type = formalType;
            }
        }
    } else if (localFunDefs.containsKey(name)) {
        // external function namespaces, don't rely on localFunDefs
        if (isFrozen && externs.containsKey(name)) {
            type = externs.get(name);
        }
    } else if (localTypedefs.containsKey(name) || localNamespaces.containsKey(name)) {
    // Any further declarations are shadowed
    } else if (declaredType != null && declaredType.isTypeVariableDefinedLocally(name)) {
        isTypeVar = true;
        type = JSType.fromTypeVar(this.commonTypes, declaredType.getTypeVariableDefinedLocally(name));
    } else if (externs.containsKey(name)) {
        type = externs.get(name);
    }
    Namespace ns = null;
    if (localNamespaces.containsKey(name)) {
        ns = localNamespaces.get(name);
    } else if (preservedNamespaces != null) {
        ns = preservedNamespaces.get(name);
    }
    return new Declaration(type, localTypedefs.get(name), ns, localFunDefs.get(name), isTypeVar, constVars.contains(name));
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) Declaration(com.google.javascript.jscomp.newtypes.Declaration) Namespace(com.google.javascript.jscomp.newtypes.Namespace) FunctionNamespace(com.google.javascript.jscomp.newtypes.FunctionNamespace)

Example 4 with DeclaredFunctionType

use of com.google.javascript.jscomp.newtypes.DeclaredFunctionType in project closure-compiler by google.

the class NewTypeInference method createSummary.

private void createSummary(NTIScope fn) {
    Node fnRoot = fn.getRoot();
    checkArgument(!fnRoot.isFromExterns());
    FunctionTypeBuilder builder = new FunctionTypeBuilder(this.commonTypes);
    TypeEnv entryEnv = getEntryTypeEnv();
    TypeEnv exitEnv = getExitTypeEnv();
    DeclaredFunctionType declType = fn.getDeclaredFunctionType();
    int reqArity = declType.getRequiredArity();
    int optArity = declType.getOptionalArity();
    if (declType.isGeneric()) {
        builder.addTypeParameters(declType.getTypeParameters());
    }
    // Every trailing undeclared formal whose inferred type is ?
    // or contains undefined can be marked as optional.
    List<String> formals = fn.getFormals();
    for (int i = reqArity - 1; i >= 0; i--) {
        JSType formalType = declType.getFormalType(i);
        if (formalType != null) {
            break;
        }
        String formalName = formals.get(i);
        formalType = getTypeAfterFwd(formalName, entryEnv, exitEnv);
        if (formalType.isUnknown() || UNDEFINED.isSubtypeOf(formalType)) {
            reqArity--;
        } else {
            break;
        }
    }
    // Collect types of formals in the builder
    int i = 0;
    for (String formalName : formals) {
        JSType formalType = declType.getFormalType(i);
        if (formalType == null) {
            formalType = getTypeAfterFwd(formalName, entryEnv, exitEnv);
        }
        if (i < reqArity) {
            builder.addReqFormal(formalType);
        } else if (i < optArity) {
            builder.addOptFormal(formalType);
        }
        i++;
    }
    if (declType.hasRestFormals()) {
        builder.addRestFormals(declType.getFormalType(i));
    }
    for (String outer : fn.getOuterVars()) {
        println("Free var ", outer, " going in summary");
        builder.addOuterVarPrecondition(outer, envGetType(entryEnv, outer));
    }
    builder.addNominalType(declType.getNominalType());
    builder.addReceiverType(declType.getReceiverType());
    builder.addAbstract(declType.isAbstract());
    addRetTypeAndWarn(fn, exitEnv, declType, builder);
    JSType summary = commonTypes.fromFunctionType(builder.buildFunction());
    println("Function summary for ", fn.getReadableName());
    println("\t", summary);
    summary = changeTypeIfFunctionNamespace(fn, summary);
    summaries.put(fn, summary);
    maybeSetTypeI(fnRoot, summary);
    Node fnNameNode = NodeUtil.getNameNode(fnRoot);
    if (fnNameNode != null) {
        maybeSetTypeI(fnNameNode, summary);
    }
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) Node(com.google.javascript.rhino.Node) DiGraphNode(com.google.javascript.jscomp.graph.DiGraph.DiGraphNode) TypeEnv(com.google.javascript.jscomp.newtypes.TypeEnv) FunctionTypeBuilder(com.google.javascript.jscomp.newtypes.FunctionTypeBuilder)

Example 5 with DeclaredFunctionType

use of com.google.javascript.jscomp.newtypes.DeclaredFunctionType in project closure-compiler by google.

the class NewTypeInference method computeFnDeclaredTypeForCallback.

/**
 * Given a scope whose root is an unannotated callback, finds a declared type for the callback
 * using the types in the callback's context.
 * Similar to GlobalTypeInfoCollector#computeFnDeclaredTypeFromCallee, but not similar enough
 * to use the same code for both.
 */
private void computeFnDeclaredTypeForCallback(NTIScope scope) {
    Node callback = scope.getRoot();
    checkState(NodeUtil.isUnannotatedCallback(callback));
    Node call = callback.getParent();
    JSType calleeType = (JSType) call.getFirstChild().getTypeI();
    if (calleeType == null) {
        return;
    }
    FunctionType calleeFunType = calleeType.getFunType();
    if (calleeFunType == null) {
        return;
    }
    int argIndex = call.getIndexOfChild(callback) - 1;
    JSType formalType = calleeFunType.getFormalType(argIndex);
    if (formalType == null) {
        return;
    }
    FunctionType ft = formalType.getFunType();
    if (ft == null || ft.isLoose()) {
        return;
    }
    DeclaredFunctionType callbackDft = scope.getDeclaredFunctionType();
    JSType scopeType = this.commonTypes.fromFunctionType(callbackDft.toFunctionType());
    if (ft.isUniqueConstructor() || ft.isInterfaceDefinition()) {
        warnAboutInvalidArgument(call, callback, argIndex, formalType, scopeType);
        return;
    }
    DeclaredFunctionType declaredDft = checkNotNull(ft.toDeclaredFunctionType());
    // the arity of the callback.
    if (ft.acceptsAnyArguments() || callbackDft.getRequiredArity() <= declaredDft.getMaxArity()) {
        scope.setDeclaredType(declaredDft);
    } else {
        warnAboutInvalidArgument(call, callback, argIndex, formalType, scopeType);
    }
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) Node(com.google.javascript.rhino.Node) DiGraphNode(com.google.javascript.jscomp.graph.DiGraph.DiGraphNode) FunctionType(com.google.javascript.jscomp.newtypes.FunctionType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType)

Aggregations

DeclaredFunctionType (com.google.javascript.jscomp.newtypes.DeclaredFunctionType)6 JSType (com.google.javascript.jscomp.newtypes.JSType)6 Node (com.google.javascript.rhino.Node)5 DiGraphNode (com.google.javascript.jscomp.graph.DiGraph.DiGraphNode)4 FunctionType (com.google.javascript.jscomp.newtypes.FunctionType)2 TypeEnv (com.google.javascript.jscomp.newtypes.TypeEnv)2 Declaration (com.google.javascript.jscomp.newtypes.Declaration)1 FunctionNamespace (com.google.javascript.jscomp.newtypes.FunctionNamespace)1 FunctionTypeBuilder (com.google.javascript.jscomp.newtypes.FunctionTypeBuilder)1 Namespace (com.google.javascript.jscomp.newtypes.Namespace)1 NominalType (com.google.javascript.jscomp.newtypes.NominalType)1 QualifiedName (com.google.javascript.jscomp.newtypes.QualifiedName)1 RawNominalType (com.google.javascript.jscomp.newtypes.RawNominalType)1 JSDocInfo (com.google.javascript.rhino.JSDocInfo)1