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