diff --git a/src/com/google/javascript/jscomp/DiagnosticGroups.java b/src/com/google/javascript/jscomp/DiagnosticGroups.java index 5ee060c71c3..04cbf4997bb 100644 --- a/src/com/google/javascript/jscomp/DiagnosticGroups.java +++ b/src/com/google/javascript/jscomp/DiagnosticGroups.java @@ -278,7 +278,7 @@ public DiagnosticGroup forName(String name) { // NewTypeInference.PROPERTY_ACCESS_ON_NONOBJECT, // NewTypeInference.RETURN_NONDECLARED_TYPE, NewTypeInference.UNKNOWN_ASSERTION_TYPE, - CheckGlobalThis.GLOBAL_THIS, +// CheckGlobalThis.GLOBAL_THIS, // CheckMissingReturn.MISSING_RETURN_STATEMENT, TypeCheck.CONSTRUCTOR_NOT_CALLABLE, TypeCheck.ILLEGAL_OBJLIT_KEY, @@ -322,7 +322,8 @@ public DiagnosticGroup forName(String name) { DiagnosticGroups.registerGroup("const", CheckAccessControls.CONST_PROPERTY_DELETED, CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE, - ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + ConstCheck.CONST_REASSIGNED_VALUE_ERROR, + NewTypeInference.CONST_REASSIGNED); public static final DiagnosticGroup CONSTANT_PROPERTY = DiagnosticGroups.registerGroup("constantProperty", @@ -339,7 +340,8 @@ public DiagnosticGroup forName(String name) { VarCheck.VAR_MULTIPLY_DECLARED_ERROR, TypeValidator.DUP_VAR_DECLARATION, TypeValidator.DUP_VAR_DECLARATION_TYPE_MISMATCH, - VariableReferenceCheck.REDECLARED_VARIABLE); + VariableReferenceCheck.REDECLARED_VARIABLE, + GlobalTypeInfo.REDECLARED_PROPERTY); public static final DiagnosticGroup ES3 = DiagnosticGroups.registerGroup("es3", diff --git a/src/com/google/javascript/jscomp/GlobalTypeInfo.java b/src/com/google/javascript/jscomp/GlobalTypeInfo.java index 05f333c17e5..449c9b72fab 100644 --- a/src/com/google/javascript/jscomp/GlobalTypeInfo.java +++ b/src/com/google/javascript/jscomp/GlobalTypeInfo.java @@ -274,9 +274,9 @@ public void process(Node externs, Node root) { // defined in the global scope. CollectNamedTypes rootCnt = new CollectNamedTypes(globalScope); if (externs != null) { - new NodeTraversal(compiler, rootCnt).traverse(externs); + NodeTraversal.traverse(compiler, externs, rootCnt); } - new NodeTraversal(compiler, rootCnt).traverse(root); + NodeTraversal.traverse(compiler, root, rootCnt); // (2) Determine the type represented by each typedef and each enum globalScope.resolveTypedefs(typeParser); globalScope.resolveEnums(typeParser); @@ -284,7 +284,7 @@ public void process(Node externs, Node root) { for (int i = 1; i < scopes.size(); i++) { Scope s = scopes.get(i); CollectNamedTypes cnt = new CollectNamedTypes(s); - new NodeTraversal(compiler, cnt).traverse(s.getBody()); + NodeTraversal.traverse(compiler, s.getBody(), cnt); s.resolveTypedefs(typeParser); s.resolveEnums(typeParser); if (NewTypeInference.measureMem) { @@ -297,9 +297,9 @@ public void process(Node externs, Node root) { // - Declare properties on types ProcessScope rootPs = new ProcessScope(globalScope); if (externs != null) { - new NodeTraversal(compiler, rootPs).traverse(externs); + NodeTraversal.traverse(compiler, externs, rootPs); } - new NodeTraversal(compiler, rootPs).traverse(root); + NodeTraversal.traverse(compiler, root, rootPs); // (5) Things that must happen after the traversal of the scope rootPs.finishProcessingScope(); @@ -307,7 +307,7 @@ public void process(Node externs, Node root) { for (int i = 1; i < scopes.size(); i++) { Scope s = scopes.get(i); ProcessScope ps = new ProcessScope(s); - new NodeTraversal(compiler, ps).traverse(s.getBody()); + NodeTraversal.traverse(compiler, s.getBody(), ps); ps.finishProcessingScope(); if (NewTypeInference.measureMem) { NewTypeInference.updatePeakMem(); @@ -1511,6 +1511,7 @@ private boolean mayWarnAboutExistingProp(RawNominalType classType, // 1) Why is it just specific to "duplicate" and to properties? // 2) The docs say that it's only allowed in the top level, but the code // allows it in all scopes. + // https://github.com/google/closure-compiler/wiki/Warnings#suppress-tags // For now, we implement it b/c it exists in the current type inference. // But I wouldn't mind if we stopped supporting it. private boolean suppressDupPropWarning( @@ -1972,6 +1973,11 @@ String getName() { private void setDeclaredType(DeclaredFunctionType declaredType) { this.declaredType = declaredType; + // In NTI, we set the type of a function node after we create the summary. + // NTI doesn't analyze externs, so we set the type for extern functions here. + if (this.root.isFromExterns()) { + this.root.setTypeI(getCommonTypes().fromFunctionType(declaredType.toFunctionType())); + } } DeclaredFunctionType getDeclaredType() { diff --git a/src/com/google/javascript/jscomp/NewTypeInference.java b/src/com/google/javascript/jscomp/NewTypeInference.java index 3d3d37392e9..0762eb77eb9 100644 --- a/src/com/google/javascript/jscomp/NewTypeInference.java +++ b/src/com/google/javascript/jscomp/NewTypeInference.java @@ -887,6 +887,7 @@ private void createSummary(Scope fn) { TypeEnv entryEnv = getEntryTypeEnv(); TypeEnv exitEnv = getFinalTypeEnv(); FunctionTypeBuilder builder = new FunctionTypeBuilder(); + Node fnRoot = fn.getRoot(); DeclaredFunctionType declType = fn.getDeclaredType(); int reqArity = declType.getRequiredArity(); @@ -946,7 +947,7 @@ private void createSummary(Scope fn) { if (!isAllowedToNotReturn(fn) && !JSType.UNDEFINED.isSubtypeOf(declRetType) && hasPathWithNoReturn(cfg)) { - warnings.add(JSError.make(fn.getRoot(), + warnings.add(JSError.make(fnRoot, CheckMissingReturn.MISSING_RETURN_STATEMENT, declRetType.toString())); } @@ -960,6 +961,11 @@ private void createSummary(Scope fn) { println("Function summary for ", fn.getReadableName()); println("\t", summary); summaries.put(fn, summary); + fnRoot.setTypeI(summary); + Node fnNameNode = NodeUtil.getFunctionNameNode(fnRoot); + if (fnNameNode != null) { + fnNameNode.setTypeI(summary); + } } // TODO(dimvar): To get the adjusted end-of-fwd type for objs, we must be @@ -1018,6 +1024,7 @@ private TypeEnv processVarDeclaration(Node nameNode, TypeEnv inEnv) { return inEnv; } if (NodeUtil.isNamespaceDecl(nameNode)) { + nameNode.setTypeI(declType); return envPutType(inEnv, varName, declType); } @@ -1045,6 +1052,7 @@ private TypeEnv processVarDeclaration(Node nameNode, TypeEnv inEnv) { rhsType = declType.specialize(rhsType); } } + nameNode.setTypeI(rhsType); return envPutType(outEnv, varName, rhsType); } @@ -1784,17 +1792,17 @@ private EnvTypePair analyzeFunctionBindFwd(Node call, TypeEnv inEnv) { env = analyzeCallNodeArgumentsFwd(call, bindComponents.parameters, boundFunType, new ArrayList(), env); // For any formal not bound here, add it to the resulting function type. - for (int j = numArgs; j < boundFunType.getMaxArity(); j++) { + for (int j = numArgs; j < boundFunType.getMaxArityWithoutRestFormals(); j++) { JSType formalType = boundFunType.getFormalType(j); if (boundFunType.isRequiredArg(j)) { builder.addReqFormal(formalType); - } else if (boundFunType.isOptionalArg(j)) { - builder.addOptFormal(formalType); } else { - builder.addRestFormals(formalType); - break; // To avoid iterating to Integer.MAX_VALUE + builder.addOptFormal(formalType); } } + if (boundFunType.hasRestFormals()) { + builder.addRestFormals(boundFunType.getRestFormalsType()); + } return new EnvTypePair(env, commonTypes.fromFunctionType( builder.addRetType(boundFunType.getReturnType()).buildFunction())); } @@ -3335,29 +3343,33 @@ private LValueResultFwd analyzeLValueFwd( private LValueResultFwd analyzeLValueFwd( Node expr, TypeEnv inEnv, JSType type, boolean insideQualifiedName) { + LValueResultFwd lvalResult = null; switch (expr.getType()) { case Token.THIS: { if (currentScope.hasThis()) { - return new LValueResultFwd(inEnv, envGetType(inEnv, "this"), + lvalResult = new LValueResultFwd(inEnv, envGetType(inEnv, "this"), currentScope.getDeclaredTypeOf("this"), new QualifiedName("this")); } else { warnings.add(JSError.make(expr, CheckGlobalThis.GLOBAL_THIS)); - return new LValueResultFwd(inEnv, JSType.UNKNOWN, null, null); + lvalResult = new LValueResultFwd(inEnv, JSType.UNKNOWN, null, null); } + break; } case Token.NAME: { String varName = expr.getString(); JSType varType = analyzeExprFwd(expr, inEnv).type; - return new LValueResultFwd(inEnv, varType, + lvalResult = new LValueResultFwd(inEnv, varType, currentScope.getDeclaredTypeOf(varName), varType.hasNonScalar() ? new QualifiedName(varName) : null); + break; } case Token.GETPROP: { Node obj = expr.getFirstChild(); QualifiedName pname = new QualifiedName(expr.getLastChild().getString()); - return analyzePropLValFwd(obj, pname, inEnv, type, insideQualifiedName); + lvalResult = analyzePropLValFwd(obj, pname, inEnv, type, insideQualifiedName); + break; } case Token.GETELEM: { Node obj = expr.getFirstChild(); @@ -3365,18 +3377,20 @@ private LValueResultFwd analyzeLValueFwd( // (1) A getelem where the prop is a string literal is like a getprop if (prop.isString()) { QualifiedName pname = new QualifiedName(prop.getString()); - return analyzePropLValFwd( - obj, pname, inEnv, type, insideQualifiedName); + lvalResult = analyzePropLValFwd(obj, pname, inEnv, type, insideQualifiedName); + break; } // (2) A getelem where the receiver is an array LValueResultFwd lvalue = analyzeLValueFwd(obj, inEnv, JSType.UNKNOWN, true); if (isArrayType(lvalue.type)) { - return analyzeArrayElmLvalFwd(prop, lvalue); + lvalResult = analyzeArrayElmLvalFwd(prop, lvalue); + break; } // (3) All other getelems EnvTypePair pair = analyzeExprFwd(expr, inEnv, type); - return new LValueResultFwd(pair.env, pair.type, null, null); + lvalResult = new LValueResultFwd(pair.env, pair.type, null, null); + break; } case Token.VAR: { // Can happen iff its parent is a for/in. Preconditions.checkState(NodeUtil.isForIn(expr.getParent())); @@ -3396,6 +3410,8 @@ private LValueResultFwd analyzeLValueFwd( return new LValueResultFwd(pair.env, pair.type, null, null); } } + expr.setTypeI(lvalResult.type); + return lvalResult; } private LValueResultFwd analyzeArrayElmLvalFwd( diff --git a/src/com/google/javascript/jscomp/newtypes/FunctionType.java b/src/com/google/javascript/jscomp/newtypes/FunctionType.java index 95984da6e7f..82d60415569 100644 --- a/src/com/google/javascript/jscomp/newtypes/FunctionType.java +++ b/src/com/google/javascript/jscomp/newtypes/FunctionType.java @@ -209,6 +209,11 @@ public boolean hasRestFormals() { return restFormals != null; } + public JSType getRestFormalsType() { + Preconditions.checkNotNull(restFormals); + return restFormals; + } + // 0-indexed // Returns null if argpos indexes past the arguments public JSType getFormalType(int argpos) { @@ -250,6 +255,10 @@ public int getMaxArity() { } } + public int getMaxArityWithoutRestFormals() { + return requiredFormals.size() + optionalFormals.size(); + } + public boolean isRequiredArg(int i) { return i < requiredFormals.size(); } @@ -370,7 +379,7 @@ public boolean isSubtypeOf(FunctionType other) { } // NOTE(dimvar): This is a bug. The code that triggers this should be rare // and the fix is not trivial, so for now we decided to not fix. - // See unit tests in NewTypeInferenceTestES5OrLower#testGenericsSubtyping + // See unit tests in NewTypeInferenceES5OrLowerTest#testGenericsSubtyping return instantiateGenericsWithUnknown(this).isSubtypeOf(other); } diff --git a/src/com/google/javascript/jscomp/newtypes/JSType.java b/src/com/google/javascript/jscomp/newtypes/JSType.java index 0342802ffa9..b5cb8f39635 100644 --- a/src/com/google/javascript/jscomp/newtypes/JSType.java +++ b/src/com/google/javascript/jscomp/newtypes/JSType.java @@ -884,8 +884,8 @@ public FunctionType getFunType() { return result; } - NominalType getNominalTypeIfUnique() { - if (getObjs().isEmpty() || getObjs().size() > 1) { + public NominalType getNominalTypeIfSingletonObj() { + if (getMask() != NON_SCALAR_MASK || getObjs().size() > 1) { return null; } return Iterables.getOnlyElement(getObjs()).getNominalType(); @@ -1104,7 +1104,7 @@ public boolean isFunctionType() { @Override public boolean isInterface() { - NominalType nt = getNominalTypeIfUnique(); + NominalType nt = getNominalTypeIfSingletonObj(); return nt != null && nt.isInterface(); } @@ -1149,11 +1149,11 @@ public int hashCode() { final class UnionType extends JSType { private final int mask; - // objs is null for scalar types + // objs is empty for scalar types private final ImmutableSet objs; // typeVar is null for non-generic types private final String typeVar; - // enums is null for types that don't have enums + // enums is empty for types that don't have enums private final ImmutableSet enums; UnionType(int mask, ImmutableSet objs, diff --git a/src/com/google/javascript/jscomp/newtypes/JSTypeCreatorFromJSDoc.java b/src/com/google/javascript/jscomp/newtypes/JSTypeCreatorFromJSDoc.java index 6aa425283d6..58bf28e1b47 100644 --- a/src/com/google/javascript/jscomp/newtypes/JSTypeCreatorFromJSDoc.java +++ b/src/com/google/javascript/jscomp/newtypes/JSTypeCreatorFromJSDoc.java @@ -385,7 +385,7 @@ public void resolveEnum(EnumType e, DeclaredTypeRegistry registry) { private JSType getNominalTypeHelper(JSType namedType, Node n, DeclaredTypeRegistry registry, ImmutableList outerTypeParameters) throws UnknownTypeException { - NominalType uninstantiated = namedType.getNominalTypeIfUnique(); + NominalType uninstantiated = namedType.getNominalTypeIfSingletonObj(); RawNominalType rawType = uninstantiated.getRawNominalType(); if (!rawType.isGeneric() && !n.hasChildren()) { return rawType.getInstanceAsNullableJSType(); @@ -499,7 +499,7 @@ private void fillInFunTypeBuilder( private NominalType getNominalType(Node n, DeclaredTypeRegistry registry, ImmutableList typeParameters) { return getTypeFromComment(n, registry, typeParameters) - .getNominalTypeIfUnique(); + .removeType(JSType.NULL).getNominalTypeIfSingletonObj(); } private ImmutableSet getImplementedInterfaces( @@ -525,7 +525,7 @@ private ImmutableSet getInterfacesHelper( JSType interfaceType = getMaybeTypeFromComment(expRoot, registry, typeParameters); if (interfaceType != null) { - NominalType nt = interfaceType.getNominalTypeIfUnique(); + NominalType nt = interfaceType.getNominalTypeIfSingletonObj(); if (nt != null && nt.isInterface()) { builder.add(nt); } else { @@ -815,7 +815,7 @@ private DeclaredFunctionType getFunTypeFromTypicalFunctionJsdoc( // people use other types as well: unions, records, etc. // Decide what to do about those. NominalType thisTypeAsNominal = thisType == null - ? null : thisType.getNominalTypeIfUnique(); + ? null : thisType.getNominalTypeIfSingletonObj(); builder.addReceiverType(thisTypeAsNominal); } @@ -924,7 +924,7 @@ private NominalType getMaybeParentClass( if (extendedType == null) { return null; } - NominalType parentClass = extendedType.getNominalTypeIfUnique(); + NominalType parentClass = extendedType.getNominalTypeIfSingletonObj(); if (parentClass != null && parentClass.isClass()) { return parentClass; } diff --git a/src/com/google/javascript/jscomp/newtypes/ObjectType.java b/src/com/google/javascript/jscomp/newtypes/ObjectType.java index 84b4fa0273c..bd26b78706e 100644 --- a/src/com/google/javascript/jscomp/newtypes/ObjectType.java +++ b/src/com/google/javascript/jscomp/newtypes/ObjectType.java @@ -661,7 +661,7 @@ private static boolean areRelatedClasses(NominalType c1, NominalType c2) { // TODO(dimvar): handle greatest lower bound of interface types. // If we do that, we need to normalize the output, otherwise it could contain // two object types that are in a subtype relation, eg, see - // NewTypeInferenceTestES5OrLower#testDifficultObjectSpecialization. + // NewTypeInferenceES5OrLowerTest#testDifficultObjectSpecialization. static ImmutableSet meetSetsHelper( boolean specializeObjs1, Set objs1, Set objs2) { @@ -699,7 +699,7 @@ FunctionType getFunType() { return fn; } - NominalType getNominalType() { + public NominalType getNominalType() { return nominalType; } @@ -848,7 +848,7 @@ public String toString() { return appendTo(new StringBuilder()).toString(); } - public StringBuilder appendTo(StringBuilder builder) { + StringBuilder appendTo(StringBuilder builder) { if (props.isEmpty() || (props.size() == 1 && props.containsKey("prototype"))) { if (fn != null) { diff --git a/src/com/google/javascript/rhino/Node.java b/src/com/google/javascript/rhino/Node.java index f6bde80b249..1773014f93c 100644 --- a/src/com/google/javascript/rhino/Node.java +++ b/src/com/google/javascript/rhino/Node.java @@ -2058,12 +2058,11 @@ public JSType getJSType() { } public void setJSType(JSType jsType) { - this.typei = jsType; + this.typei = jsType; } public TypeI getTypeI() { - // For the time being, we only want to return the type iff it's an old type. - return getJSType(); + return this.typei; } public void setTypeI(TypeI type) {