Search in sources :

Example 66 with JSType

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

the class NewTypeInference method analyzeFunctionBindFwd.

private EnvTypePair analyzeFunctionBindFwd(Node call, TypeEnv inEnv) {
    checkArgument(call.isCall());
    Bind bindComponents = this.convention.describeFunctionBind(call, true, false);
    Node boundFunNode = bindComponents.target;
    EnvTypePair pair = analyzeExprFwd(boundFunNode, inEnv);
    TypeEnv env = pair.env;
    FunctionType boundFunType = pair.type.getFunTypeIfSingletonObj();
    if (!pair.type.isSubtypeOf(commonTypes.topFunction())) {
        warnings.add(JSError.make(boundFunNode, GOOG_BIND_EXPECTS_FUNCTION, pair.type.toString()));
    }
    // For some function types, we don't know enough to handle .bind specially.
    if (boundFunType == null || boundFunType.isTopFunction() || boundFunType.isQmarkFunction() || boundFunType.isLoose()) {
        return analyzeInvocationArgsFwdWhenError(call, env);
    }
    if (boundFunType.isSomeConstructorOrInterface()) {
        warnings.add(JSError.make(call, CANNOT_BIND_CTOR));
        return new EnvTypePair(env, UNKNOWN);
    }
    // Check if the receiver argument is there
    int callChildCount = call.getChildCount();
    if ((NodeUtil.isGoogBind(call.getFirstChild()) && callChildCount <= 2) || (!NodeUtil.isGoogPartial(call.getFirstChild()) && callChildCount == 1)) {
        warnings.add(JSError.make(call, WRONG_ARGUMENT_COUNT, getReadableCalleeName(call.getFirstChild()), "0", "1", ""));
    }
    // Check that there are not too many of the other arguments
    int maxArity = boundFunType.hasRestFormals() ? Integer.MAX_VALUE : boundFunType.getMaxArity();
    int numArgs = bindComponents.getBoundParameterCount();
    if (numArgs > maxArity) {
        warnings.add(JSError.make(call, WRONG_ARGUMENT_COUNT, getReadableCalleeName(call.getFirstChild()), Integer.toString(numArgs), "0", " and at most " + maxArity));
        return analyzeInvocationArgsFwdWhenError(call, inEnv);
    }
    // If the bound function is polymorphic, we only support the case where we
    // can completely calculate the type instantiation at the .bind call site.
    // We don't support splitting the instantiation between call sites.
    // 
    Node receiver = bindComponents.thisValue;
    if (boundFunType.isGeneric()) {
        Map<String, JSType> typeMap = calcTypeInstantiationFwd(call, receiver, bindComponents.parameters, boundFunType, env);
        boundFunType = boundFunType.instantiateGenerics(typeMap);
    }
    FunctionTypeBuilder builder = new FunctionTypeBuilder(this.commonTypes);
    if (receiver != null) {
        // receiver is null for goog.partial
        JSType reqThisType = boundFunType.getThisType();
        if (reqThisType == null || boundFunType.isSomeConstructorOrInterface()) {
            reqThisType = JSType.join(NULL, TOP_OBJECT);
        }
        pair = analyzeExprFwd(receiver, env, reqThisType);
        env = pair.env;
        if (!pair.type.isSubtypeOf(reqThisType)) {
            warnings.add(JSError.make(call, INVALID_THIS_TYPE_IN_BIND, errorMsgWithTypeDiff(reqThisType, pair.type)));
        } else {
            instantiateGoogBind(call.getFirstChild(), pair.type);
        }
    }
    Iterable<Node> parametersIterable = bindComponents.parameters == null ? ImmutableList.<Node>of() : bindComponents.parameters.siblings();
    // We are passing an arraylist but don't do deferred checks for bind.
    env = analyzeInvocationArgumentsFwd(call, parametersIterable, boundFunType, new ArrayList<JSType>(), env);
    // For any formal not bound here, add it to the resulting function type.
    for (int j = numArgs; j < boundFunType.getMaxArityWithoutRestFormals(); j++) {
        JSType formalType = boundFunType.getFormalType(j);
        if (boundFunType.isRequiredArg(j)) {
            builder.addReqFormal(formalType);
        } else {
            builder.addOptFormal(formalType);
        }
    }
    if (boundFunType.hasRestFormals()) {
        builder.addRestFormals(boundFunType.getRestFormalsType());
    }
    return new EnvTypePair(env, commonTypes.fromFunctionType(builder.addRetType(boundFunType.getReturnType()).buildFunction()));
}
Also used : Bind(com.google.javascript.jscomp.CodingConvention.Bind) 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) ArrayList(java.util.ArrayList) TypeEnv(com.google.javascript.jscomp.newtypes.TypeEnv) FunctionTypeBuilder(com.google.javascript.jscomp.newtypes.FunctionTypeBuilder)

Example 67 with JSType

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

the class NewTypeInference method analyzeLtGtBwd.

private EnvTypePair analyzeLtGtBwd(Node expr, TypeEnv outEnv) {
    Node lhs = expr.getFirstChild();
    Node rhs = expr.getLastChild();
    EnvTypePair rhsPair = analyzeExprBwd(rhs, outEnv);
    EnvTypePair lhsPair = analyzeExprBwd(lhs, rhsPair.env);
    JSType meetType = JSType.meet(lhsPair.type, rhsPair.type);
    if (meetType.isBottom()) {
        // Type mismatch, the fwd direction will warn; don't reanalyze
        lhsPair.type = BOOLEAN;
        return lhsPair;
    }
    rhsPair = analyzeExprBwd(rhs, outEnv, meetType);
    lhsPair = analyzeExprBwd(lhs, rhsPair.env, meetType);
    lhsPair.type = BOOLEAN;
    return lhsPair;
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) Node(com.google.javascript.rhino.Node) DiGraphNode(com.google.javascript.jscomp.graph.DiGraph.DiGraphNode)

Example 68 with JSType

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

the class NewTypeInference method analyzeLogicalOpFwd.

private EnvTypePair analyzeLogicalOpFwd(Node expr, TypeEnv inEnv, JSType requiredType, JSType specializedType) {
    Token logicalOp = expr.getToken();
    Node lhs = expr.getFirstChild();
    Node rhs = expr.getLastChild();
    if ((specializedType.isTrueOrTruthy() && logicalOp == Token.AND) || (specializedType.isFalseOrFalsy() && logicalOp == Token.OR)) {
        EnvTypePair lhsPair = analyzeExprFwd(lhs, inEnv, UNKNOWN, specializedType);
        EnvTypePair rhsPair = analyzeExprFwd(rhs, lhsPair.env, UNKNOWN, specializedType);
        return rhsPair;
    }
    if ((specializedType.isFalseOrFalsy() && logicalOp == Token.AND) || (specializedType.isTrueOrTruthy() && logicalOp == Token.OR)) {
        EnvTypePair shortCircuitPair = analyzeExprFwd(lhs, inEnv, UNKNOWN, specializedType);
        EnvTypePair lhsPair = analyzeExprFwd(lhs, inEnv, UNKNOWN, specializedType.negate());
        EnvTypePair rhsPair = analyzeExprFwd(rhs, lhsPair.env, UNKNOWN, specializedType);
        JSType lhsUnspecializedType = JSType.join(shortCircuitPair.type, lhsPair.type);
        return combineLhsAndRhsForLogicalOps(logicalOp, lhsUnspecializedType, shortCircuitPair, rhsPair);
    }
    // Independently of the specializedType, && rhs is only analyzed when
    // lhs is truthy, and || rhs is only analyzed when lhs is falsy.
    JSType stopAfterLhsType = logicalOp == Token.AND ? FALSY : TRUTHY;
    EnvTypePair shortCircuitPair = analyzeExprFwd(lhs, inEnv, UNKNOWN, stopAfterLhsType);
    EnvTypePair lhsPair = analyzeExprFwd(lhs, inEnv, UNKNOWN, stopAfterLhsType.negate());
    EnvTypePair rhsPair = analyzeExprFwd(rhs, lhsPair.env, requiredType, specializedType);
    JSType lhsType = JSType.join(shortCircuitPair.type, lhsPair.type);
    return combineLhsAndRhsForLogicalOps(logicalOp, lhsType, shortCircuitPair, rhsPair);
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) Node(com.google.javascript.rhino.Node) DiGraphNode(com.google.javascript.jscomp.graph.DiGraph.DiGraphNode) Token(com.google.javascript.rhino.Token)

Example 69 with JSType

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

the class NewTypeInference method calcTypeInstantiation.

/**
 * We don't use the requiredType of the context to unify with the return
 * type. There are several difficulties:
 * 1) A polymorphic function is allowed to return ANY subtype of the
 *    requiredType, so we would need to use a heuristic to determine the type
 *    to unify with.
 * 2) It's hard to give good error messages in cases like: id('str') - 5
 *    We want an invalid-operand-type, not a not-unique-instantiation.
 *
 * We don't take the arg evaluation order into account during instantiation.
 *
 * When calculating the instantiation, when do we use the receiver type?
 * See the following snippet:
 * /**
 *  * @constructor
 *  * @template T
 *  * @param {T} x
 *  * /
 * function Foo(x) {}
 * /**
 *  * @template T
 *  * @param {T} x
 *  * /
 * Foo.prototype.f = function(x) {};
 * Foo.prototype.f.bind(new Foo(123), 'asdf');
 *
 * Here, the receiver type of f is Foo<T>, but the T is the class's T,
 * not the T of f's template declaration.
 * OTOH, if f had a @this annotation that contained T, T would refer to
 * f's T. We have no way of knowing whether THIS comes from the class or from @this.
 * However, we just go ahead and unify anyway; it is safe to do because we give a unique ID
 * to each type variable. We end up doing redundant work when THIS comes from the class.
 * In the past, before switching to unique IDs for type variables, we used to have a heuristic
 * to decide whether to use the receiver type for unification.
 */
private ImmutableMap<String, JSType> calcTypeInstantiation(Node callNode, Node receiver, Node firstArg, FunctionType funType, TypeEnv typeEnv, boolean isFwd) {
    checkState(receiver == null || isFwd);
    List<String> typeParameters = funType.getTypeParameters();
    Multimap<String, JSType> typeMultimap = LinkedHashMultimap.create();
    JSType funRecvType = funType.getThisType();
    if (receiver != null && funRecvType != null) {
        JSType recvType = (JSType) receiver.getTypeI();
        if (recvType == null) {
            EnvTypePair pair = analyzeExprFwd(receiver, typeEnv);
            recvType = pair.type;
            typeEnv = pair.env;
        }
        funRecvType.unifyWith(recvType, typeParameters, typeMultimap);
    }
    Node arg = firstArg;
    int i = 0;
    while (arg != null) {
        EnvTypePair pair = isFwd ? analyzeExprFwd(arg, typeEnv) : analyzeExprBwd(arg, typeEnv);
        funType.getFormalType(i).unifyWith(pair.type, typeParameters, typeMultimap);
        arg = arg.getNext();
        typeEnv = pair.env;
        i++;
    }
    ImmutableMap.Builder<String, JSType> builder = ImmutableMap.builder();
    for (String typeParam : typeParameters) {
        Collection<JSType> types = typeMultimap.get(typeParam);
        if (types.size() > 1) {
            if (isFwd) {
                warnings.add(JSError.make(callNode, NOT_UNIQUE_INSTANTIATION, Integer.toString(types.size()), UniqueNameGenerator.getOriginalName(typeParam), types.toString(), funType.toString()));
            }
            if (joinTypesWhenInstantiatingGenerics) {
                JSType joinedType = BOTTOM;
                for (JSType t : types) {
                    joinedType = JSType.join(joinedType, t);
                }
                builder.put(typeParam, joinedType);
            } else {
                builder.put(typeParam, UNKNOWN);
            }
        } else if (types.size() == 1) {
            JSType t = Iterables.getOnlyElement(types);
            builder.put(typeParam, firstNonBottom(t, UNKNOWN));
        } else {
            // Put ? for any uninstantiated type variables
            builder.put(typeParam, UNKNOWN);
        }
    }
    return builder.build();
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) Node(com.google.javascript.rhino.Node) DiGraphNode(com.google.javascript.jscomp.graph.DiGraph.DiGraphNode) ImmutableMap(com.google.common.collect.ImmutableMap)

Example 70 with JSType

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

the class NewTypeInference method analyzeObjLitFwd.

private EnvTypePair analyzeObjLitFwd(Node objLit, TypeEnv inEnv, JSType requiredType, JSType specializedType) {
    if (NodeUtil.isEnumDecl(objLit.getParent())) {
        return analyzeEnumObjLitFwd(objLit, inEnv, requiredType);
    }
    JSDocInfo jsdoc = objLit.getJSDocInfo();
    boolean isStruct = jsdoc != null && jsdoc.makesStructs();
    boolean isDict = jsdoc != null && jsdoc.makesDicts();
    TypeEnv env = inEnv;
    JSType result = pickReqObjType(objLit);
    for (Node prop : objLit.children()) {
        if (isStruct && prop.isQuotedString()) {
            warnings.add(JSError.make(prop, ILLEGAL_OBJLIT_KEY, "struct"));
        } else if (isDict && !prop.isQuotedString()) {
            warnings.add(JSError.make(prop, ILLEGAL_OBJLIT_KEY, "dict"));
        }
        // an accidental clash.
        if (prop.isGetterDef() || prop.isSetterDef()) {
            String pname = NodeUtil.getObjectLitKeyName(prop);
            EnvTypePair pair = analyzeExprFwd(prop.getFirstChild(), env);
            FunctionType funType = pair.type.getFunType();
            checkNotNull(funType);
            String specialPropName;
            JSType propType;
            if (prop.isGetterDef()) {
                specialPropName = commonTypes.createGetterPropName(pname);
                propType = funType.getReturnType();
            } else {
                specialPropName = commonTypes.createSetterPropName(pname);
                propType = pair.type;
            }
            result = result.withProperty(new QualifiedName(specialPropName), propType);
            env = pair.env;
        } else {
            Node pnameNode = NodeUtil.getObjectLitKeyNode(prop);
            if (pnameNode == null) {
                // pnameNode is null when prop is a computed prop does not have a String node key.
                // Just type-check the prop, then move on to the next property.
                env = analyzeExprFwd(prop, env).env;
                continue;
            }
            QualifiedName qname = new QualifiedName(pnameNode.getString());
            JSType jsdocType = (JSType) prop.getTypeI();
            JSType reqPtype;
            JSType specPtype;
            if (jsdocType != null) {
                reqPtype = specPtype = jsdocType;
            } else if (requiredType.mayHaveProp(qname)) {
                reqPtype = specPtype = requiredType.getProp(qname);
                if (specializedType.mayHaveProp(qname)) {
                    specPtype = specializedType.getProp(qname);
                }
            } else {
                reqPtype = specPtype = UNKNOWN;
            }
            EnvTypePair pair = analyzeExprFwd(prop, env, reqPtype, specPtype);
            if (jsdocType != null) {
                // First declare it; then set the maybe more precise inferred type
                result = result.withDeclaredProperty(qname, jsdocType, false);
                if (!pair.type.isSubtypeOf(jsdocType)) {
                    warnings.add(JSError.make(prop, INVALID_OBJLIT_PROPERTY_TYPE, errorMsgWithTypeDiff(jsdocType, pair.type)));
                    pair.type = jsdocType;
                }
            }
            result = result.withProperty(qname, pair.type);
            env = pair.env;
        }
    }
    result = mayAdjustObjLitType(objLit, jsdoc, inEnv, result);
    return new EnvTypePair(env, result);
}
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) QualifiedName(com.google.javascript.jscomp.newtypes.QualifiedName) JSDocInfo(com.google.javascript.rhino.JSDocInfo) TypeEnv(com.google.javascript.jscomp.newtypes.TypeEnv)

Aggregations

JSType (com.google.javascript.jscomp.newtypes.JSType)86 Node (com.google.javascript.rhino.Node)60 DiGraphNode (com.google.javascript.jscomp.graph.DiGraph.DiGraphNode)54 TypeEnv (com.google.javascript.jscomp.newtypes.TypeEnv)28 DeclaredFunctionType (com.google.javascript.jscomp.newtypes.DeclaredFunctionType)20 QualifiedName (com.google.javascript.jscomp.newtypes.QualifiedName)18 FunctionType (com.google.javascript.jscomp.newtypes.FunctionType)16 Declaration (com.google.javascript.jscomp.newtypes.Declaration)5 FunctionTypeBuilder (com.google.javascript.jscomp.newtypes.FunctionTypeBuilder)5 FunctionNamespace (com.google.javascript.jscomp.newtypes.FunctionNamespace)4 Namespace (com.google.javascript.jscomp.newtypes.Namespace)4 ArrayList (java.util.ArrayList)4 ImmutableMap (com.google.common.collect.ImmutableMap)3 TypeI (com.google.javascript.rhino.TypeI)3 LinkedHashMap (java.util.LinkedHashMap)3 Map (java.util.Map)3 NamespaceLit (com.google.javascript.jscomp.newtypes.NamespaceLit)2 NominalType (com.google.javascript.jscomp.newtypes.NominalType)2 RawNominalType (com.google.javascript.jscomp.newtypes.RawNominalType)2 JSDocInfo (com.google.javascript.rhino.JSDocInfo)2