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