Search in sources :

Example 6 with FunctionType

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

the class GlobalTypeInfoCollector method isValidOverride.

private boolean isValidOverride(JSType localPropType, JSType inheritedPropType) {
    FunctionType localFunType = localPropType.getFunType();
    FunctionType inheritedFunType = inheritedPropType.getFunType();
    if (localFunType == null) {
        return localPropType.isSubtypeOf(inheritedPropType);
    } else if (inheritedFunType == null) {
        return false;
    } else {
        return localFunType.isValidOverride(inheritedFunType);
    }
}
Also used : FunctionType(com.google.javascript.jscomp.newtypes.FunctionType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType)

Example 7 with FunctionType

use of com.google.javascript.jscomp.newtypes.FunctionType 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 8 with FunctionType

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

the class NewTypeInference method analyzePropAccessFwd.

private EnvTypePair analyzePropAccessFwd(Node receiver, String pname, TypeEnv inEnv, JSType requiredType, JSType specializedType) {
    QualifiedName propQname = new QualifiedName(pname);
    Node propAccessNode = receiver.getParent();
    EnvTypePair pair;
    JSType reqObjType = pickReqObjType(propAccessNode);
    JSType recvReqType;
    JSType recvSpecType;
    // First, analyze the receiver object.
    if ((NodeUtil.isPropertyTest(compiler, propAccessNode) && !specializedType.isFalseOrFalsy()) || (NodeUtil.isPropertyAbsenceTest(propAccessNode) && !specializedType.isTrueOrTruthy()) || // else branch of "if (!x.prop)" is a property test.
    specializedType.isTrueOrTruthy()) {
        recvReqType = reqObjType;
        pair = analyzeExprFwd(receiver, inEnv, recvReqType);
        JSType subtypeWithProp = pair.type.findSubtypeWithProp(propQname);
        if (subtypeWithProp.isBottom()) {
            recvSpecType = reqObjType;
        } else {
            recvSpecType = subtypeWithProp;
        }
        if (specializedType.isTrueOrTruthy()) {
            // This handles cases like: if (x.prop1 && x.prop1.prop2) { ... }
            // In the THEN branch, the only thing we know about x.prop1 is that it
            // has a truthy property, so x.prop1 should be a loose object to avoid
            // spurious warnings.
            recvSpecType = recvSpecType.withLoose().withProperty(propQname, specializedType);
        } else {
            recvSpecType = recvSpecType.withProperty(propQname, specializedType);
        }
    } else if (specializedType.isFalseOrFalsy()) {
        recvReqType = recvSpecType = reqObjType;
    } else {
        recvReqType = reqObjType.withProperty(propQname, requiredType);
        recvSpecType = reqObjType.withProperty(propQname, specializedType);
    }
    pair = analyzeExprFwd(receiver, inEnv, recvReqType, recvSpecType);
    pair = mayWarnAboutNullableReferenceAndTighten(receiver, pair.type, recvSpecType, pair.env);
    JSType recvType = pair.type.autobox();
    if (recvType.isUnknown() || recvType.isTrueOrTruthy()) {
        mayWarnAboutInexistentProp(propAccessNode, recvType, propQname);
        return new EnvTypePair(pair.env, requiredType);
    }
    if (mayWarnAboutNonObject(receiver, recvType, specializedType)) {
        return new EnvTypePair(pair.env, requiredType);
    }
    FunctionType ft = recvType.getFunTypeIfSingletonObj();
    if (ft != null && (pname.equals("call") || pname.equals("apply"))) {
        if (ft.isAbstract()) {
            // We don't check if the parent of the property access is a call node.
            // This catches calls that are a few nodes away, and also warns on .call/.apply
            // accesses that do not result in calls (these should be very rare).
            String funName = receiver.isQualifiedName() ? receiver.getQualifiedName() : "";
            warnings.add(JSError.make(propAccessNode, ABSTRACT_SUPER_METHOD_NOT_CALLABLE, funName));
        }
        return new EnvTypePair(pair.env, pname.equals("call") ? commonTypes.fromFunctionType(ft.transformByCallProperty()) : commonTypes.fromFunctionType(ft.transformByApplyProperty()));
    }
    if (this.convention.isSuperClassReference(pname)) {
        if (ft != null && ft.isUniqueConstructor()) {
            JSType result = ft.getSuperPrototype();
            pair.type = firstNonNull(result, UNDEFINED);
            return pair;
        }
    }
    if (propAccessNode.isGetProp() && mayWarnAboutDictPropAccess(receiver, recvType)) {
        return new EnvTypePair(pair.env, requiredType);
    }
    if (recvType.isTop()) {
        recvType = TOP_OBJECT;
    }
    if (propAccessNode.getParent().isDelProp() && recvType.hasConstantProp(propQname)) {
        warnings.add(JSError.make(propAccessNode.getParent(), CONST_PROPERTY_DELETED, pname));
    }
    // Then, analyze the property access.
    QualifiedName getterPname = new QualifiedName(commonTypes.createGetterPropName(pname));
    if (recvType.hasProp(getterPname)) {
        return new EnvTypePair(pair.env, recvType.getProp(getterPname));
    }
    JSType resultType = recvType.getProp(propQname);
    if (resultType != null && resultType.isBottom()) {
        warnings.add(JSError.make(propAccessNode, BOTTOM_PROP, pname, recvType.toString()));
        return new EnvTypePair(pair.env, UNKNOWN);
    }
    if (!propAccessNode.getParent().isExprResult() && !specializedType.isTrueOrTruthy() && !specializedType.isFalseOrFalsy() && !recvType.mayBeDict() && !mayWarnAboutInexistentProp(propAccessNode, recvType, propQname) && recvType.hasProp(propQname) && !resultType.isSubtypeOf(requiredType) && tightenPropertyTypeAndDontWarn(receiver.isName() ? receiver.getString() : null, propAccessNode, recvType, recvType.getDeclaredProp(propQname), resultType, requiredType)) {
        // Tighten the inferred type and don't warn.
        // See analyzeNameFwd for explanation about types as lower/upper bounds.
        resultType = resultType.specialize(requiredType);
        LValueResultFwd lvr = analyzeLValueFwd(propAccessNode, inEnv, resultType);
        TypeEnv updatedEnv = updateLvalueTypeInEnv(lvr.env, propAccessNode, lvr.ptr, resultType);
        return new EnvTypePair(updatedEnv, resultType);
    }
    // We've already warned about missing props, and never want to return null.
    if (resultType == null) {
        resultType = UNKNOWN;
    }
    // Any potential type mismatch will be caught by the context
    return new EnvTypePair(pair.env, resultType);
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) QualifiedName(com.google.javascript.jscomp.newtypes.QualifiedName) 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) TypeEnv(com.google.javascript.jscomp.newtypes.TypeEnv)

Example 9 with FunctionType

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

the class NewTypeInference method analyzeInstanceofFwd.

private EnvTypePair analyzeInstanceofFwd(Node expr, TypeEnv inEnv, JSType specializedType) {
    Node obj = expr.getFirstChild();
    Node ctor = expr.getLastChild();
    EnvTypePair objPair;
    EnvTypePair ctorPair;
    // First, evaluate ignoring the specialized context
    objPair = analyzeExprFwd(obj, inEnv);
    JSType objType = objPair.type;
    if (!objType.isTop() && !objType.isUnknown() && !objType.isTrueOrTruthy() && !objType.hasNonScalar() && !objType.hasTypeVariable()) {
        warnInvalidOperand(obj, Token.INSTANCEOF, "an object or a union type that includes an object", objPair.type);
    }
    ctorPair = analyzeExprFwd(ctor, objPair.env, commonTypes.topFunction());
    JSType ctorType = ctorPair.type;
    FunctionType ctorFunType = ctorType.getFunType();
    boolean mayBeConstructorFunction = ctorFunType != null && (ctorFunType.isLoose() || ctorFunType.isQmarkFunction() || ctorFunType.isSomeConstructorOrInterface());
    if (!(ctorType.isUnknown() || mayBeConstructorFunction)) {
        warnInvalidOperand(ctor, Token.INSTANCEOF, "a constructor function", ctorType);
    }
    if (ctorFunType == null || !ctorFunType.isUniqueConstructor() || (!specializedType.isTrueOrTruthy() && !specializedType.isFalseOrFalsy())) {
        ctorPair.type = BOOLEAN;
        return ctorPair;
    }
    // We are in a specialized context *and* we know the constructor type
    JSType instanceType = ctorFunType.getInstanceTypeOfCtor();
    JSType instanceSpecType;
    if (specializedType.isTrueOrTruthy()) {
        instanceSpecType = objType.specialize(instanceType);
    } else if (objType.isTop()) {
        instanceSpecType = objType;
    } else {
        instanceSpecType = objType.removeType(instanceType);
    }
    if (!instanceSpecType.isBottom()) {
        objPair = analyzeExprFwd(obj, inEnv, UNKNOWN, instanceSpecType);
        ctorPair = analyzeExprFwd(ctor, objPair.env, commonTypes.topFunction());
    }
    ctorPair.type = BOOLEAN;
    return ctorPair;
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) 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)

Example 10 with FunctionType

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

the class SimpleInference method inferCallNew.

private JSType inferCallNew(Node n, NTIScope scope) {
    Node callee = n.getFirstChild();
    // compiler for i18n.
    if (callee.matchesQualifiedName("goog.getMsg")) {
        return this.commonTypes.STRING;
    }
    FunctionType funType = inferFunction(callee, scope);
    if (funType == null) {
        return null;
    }
    if (funType.isGeneric()) {
        funType = inferInstantiatedCallee(n, funType, true, scope);
        if (funType == null) {
            return null;
        }
    }
    JSType retType = n.isNew() ? funType.getThisType() : funType.getReturnType();
    return retType;
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) Node(com.google.javascript.rhino.Node) FunctionType(com.google.javascript.jscomp.newtypes.FunctionType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType)

Aggregations

DeclaredFunctionType (com.google.javascript.jscomp.newtypes.DeclaredFunctionType)17 FunctionType (com.google.javascript.jscomp.newtypes.FunctionType)17 JSType (com.google.javascript.jscomp.newtypes.JSType)16 Node (com.google.javascript.rhino.Node)13 DiGraphNode (com.google.javascript.jscomp.graph.DiGraph.DiGraphNode)11 TypeEnv (com.google.javascript.jscomp.newtypes.TypeEnv)7 QualifiedName (com.google.javascript.jscomp.newtypes.QualifiedName)5 FunctionTypeBuilder (com.google.javascript.jscomp.newtypes.FunctionTypeBuilder)2 NominalType (com.google.javascript.jscomp.newtypes.NominalType)2 JSDocInfo (com.google.javascript.rhino.JSDocInfo)2 ArrayList (java.util.ArrayList)2 AssertionFunctionSpec (com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec)1 Bind (com.google.javascript.jscomp.CodingConvention.Bind)1 ObjectLiteralCast (com.google.javascript.jscomp.CodingConvention.ObjectLiteralCast)1 Declaration (com.google.javascript.jscomp.newtypes.Declaration)1 EnumType (com.google.javascript.jscomp.newtypes.EnumType)1 FunctionNamespace (com.google.javascript.jscomp.newtypes.FunctionNamespace)1 Namespace (com.google.javascript.jscomp.newtypes.Namespace)1 RawNominalType (com.google.javascript.jscomp.newtypes.RawNominalType)1