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