Skip to content

Commit

Permalink
[NTI] The name of a function expression shouldn't leak to the enclosi…
Browse files Browse the repository at this point in the history
…ng scope.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=172368526
  • Loading branch information
dimvar authored and lauraharker committed Oct 16, 2017
1 parent 84502f2 commit 3ec58a4
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 14 deletions.
5 changes: 5 additions & 0 deletions src/com/google/javascript/jscomp/GlobalTypeInfoCollector.java
Expand Up @@ -1181,6 +1181,11 @@ private void visitFunctionEarly(Node fn) {


private String createFunctionInternalName(Node fn, Node nameNode) { private String createFunctionInternalName(Node fn, Node nameNode) {
String internalName = null; String internalName = null;
// The name of a function expression isn't visible in the scope where the function expression
// appears, so don't use it as the internalName.
if (nameNode == fn.getFirstChild() && NodeUtil.isFunctionExpression(fn)) {
nameNode = null;
}
if (nameNode == null || !nameNode.isName() if (nameNode == null || !nameNode.isName()
|| nameNode.getParent().isAssign()) { || nameNode.getParent().isAssign()) {
// Anonymous functions, qualified names, and stray assignments // Anonymous functions, qualified names, and stray assignments
Expand Down
22 changes: 8 additions & 14 deletions src/com/google/javascript/jscomp/NewTypeInference.java
Expand Up @@ -788,7 +788,6 @@ private void analyzeFunction(NTIScope scope) {
// The size is > 1 when multiple files are compiled // The size is > 1 when multiple files are compiled
// Preconditions.checkState(cfg.getEntry().getOutEdges().size() == 1); // Preconditions.checkState(cfg.getEntry().getOutEdges().size() == 1);
NTIWorkset workset = NTIWorkset.create(this.cfg); NTIWorkset workset = NTIWorkset.create(this.cfg);
/* println("Workset: ", workset); */
this.typeEnvFromDeclaredTypes = getTypeEnvFromDeclaredTypes(); this.typeEnvFromDeclaredTypes = getTypeEnvFromDeclaredTypes();
if (scope.isFunction() && scope.hasUndeclaredFormalsOrOuters()) { if (scope.isFunction() && scope.hasUndeclaredFormalsOrOuters()) {
// Ideally, we would like to only set the in-edges of the implicit return // Ideally, we would like to only set the in-edges of the implicit return
Expand Down Expand Up @@ -2164,7 +2163,7 @@ private EnvTypePair analyzeInvocationFwd(
return analyzeInvocationArgsFwdWhenError(expr, envAfterCallee); return analyzeInvocationArgsFwdWhenError(expr, envAfterCallee);
} }


FunctionType origFunType = funType; // save for later FunctionType originalFunType = funType; // save for later
if (funType.isGeneric()) { if (funType.isGeneric()) {
Node receiver = callee.isGetProp() ? callee.getFirstChild() : null; Node receiver = callee.isGetProp() ? callee.getFirstChild() : null;
Node firstArg = expr.getSecondChild(); Node firstArg = expr.getSecondChild();
Expand All @@ -2187,10 +2186,9 @@ private EnvTypePair analyzeInvocationFwd(
// exactly using their summaries, and don't need deferred checks // exactly using their summaries, and don't need deferred checks
if (this.currentScope.isLocalFunDef(calleeName)) { if (this.currentScope.isLocalFunDef(calleeName)) {
tmpEnv = collectTypesForEscapedVarsFwd(callee, tmpEnv); tmpEnv = collectTypesForEscapedVarsFwd(callee, tmpEnv);
} else if (!origFunType.isGeneric()) { } else if (!originalFunType.isGeneric()) {
JSType expectedRetType = requiredType; JSType expectedRetType = requiredType;
println("Updating deferred check with ret: ", expectedRetType, println("Updating deferred check with ret: ", expectedRetType, " and args: ", argTypes);
" and args: ", argTypes);
DeferredCheck dc; DeferredCheck dc;
if (isConstructorCall(expr)) { if (isConstructorCall(expr)) {
dc = new DeferredCheck(expr, null, dc = new DeferredCheck(expr, null,
Expand Down Expand Up @@ -4212,10 +4210,8 @@ private void createDeferredCheckBwd(Node expr, JSType requiredType) {
// No deferred check if the return type is declared // No deferred check if the return type is declared
expectedRetType = null; expectedRetType = null;
} }
println("Putting deferred check of function: ", calleeName, println("Putting deferred check of function: ", calleeName, " with ret: ", expectedRetType);
" with ret: ", expectedRetType); DeferredCheck dc = new DeferredCheck(expr, expectedRetType, currentScope, s);
DeferredCheck dc = new DeferredCheck(
expr, expectedRetType, currentScope, s);
deferredChecks.put(expr, dc); deferredChecks.put(expr, dc);
} }
} }
Expand Down Expand Up @@ -4840,8 +4836,7 @@ private class DeferredCheck {
final Node callSite; final Node callSite;
final NTIScope callerScope; final NTIScope callerScope;
final NTIScope calleeScope; final NTIScope calleeScope;
// Null types means that they were declared // Null types means that they were declared (and should have been checked during inference)
// (and should have been checked during inference)
JSType expectedRetType; JSType expectedRetType;
List<JSType> argTypes; List<JSType> argTypes;


Expand All @@ -4866,8 +4861,7 @@ void updateArgTypes(List<JSType> argTypes) {
this.argTypes = argTypes; this.argTypes = argTypes;
} }


private void runCheck( private void runCheck(Map<NTIScope, JSType> summaries, WarningReporter warnings) {
Map<NTIScope, JSType> summaries, WarningReporter warnings) {
FunctionType fnSummary = summaries.get(this.calleeScope).getFunType(); FunctionType fnSummary = summaries.get(this.calleeScope).getFunType();
println( println(
"Running deferred check of function: ", calleeScope.getReadableName(), "Running deferred check of function: ", calleeScope.getReadableName(),
Expand All @@ -4883,7 +4877,7 @@ private void runCheck(
int i = 0; int i = 0;
Iterable<Node> args = NodeUtil.getInvocationArgsAsIterable(callSite); Iterable<Node> args = NodeUtil.getInvocationArgsAsIterable(callSite);
// this.argTypes can be null if in the fwd direction the analysis of the // this.argTypes can be null if in the fwd direction the analysis of the
// call return prematurely, eg, because of a WRONG_ARGUMENT_COUNT. // call returned prematurely, e.g., because of a WRONG_ARGUMENT_COUNT.
if (this.argTypes == null) { if (this.argTypes == null) {
return; return;
} }
Expand Down
14 changes: 14 additions & 0 deletions test/com/google/javascript/jscomp/NewTypeInferenceTest.java
Expand Up @@ -21952,4 +21952,18 @@ public void testGetDeclaredTypeForUnannotatedFunctionFromCast() {
");"), ");"),
NewTypeInference.INEXISTENT_PROPERTY); NewTypeInference.INEXISTENT_PROPERTY);
} }

public void testDontRegisterFunctionExpressions() {
// The first line is a function expression, not a function statement, so it doesn't define
// a global variable f. Therefore the typechecker assumes the second line refers to a global
// 'f' which is defined externally. So there's no warning for passing the wrong number of args.
typeCheck(LINE_JOINER.join(
"(function f() {})();",
"f(234);"));

// The deferred check for the call should refer to the second f; don't crash.
typeCheck(LINE_JOINER.join(
"(function f() {})();",
"(function f(x) { f(234); })(123);"));
}
} }

0 comments on commit 3ec58a4

Please sign in to comment.