Skip to content

Commit

Permalink
Fix incorrect scoping of catch variables.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=195687456
  • Loading branch information
brad4d authored and tjgq committed May 8, 2018
1 parent dda0e15 commit da41460
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/com/google/javascript/jscomp/TypedScopeCreator.java
Expand Up @@ -1462,6 +1462,7 @@ private TypedScope getLValueRootScope(Node n) {
case CLASS: case CLASS:
case FUNCTION: case FUNCTION:
case PARAM_LIST: case PARAM_LIST:
case CATCH:
return currentScope; return currentScope;
default: default:
TypedVar var = currentScope.getVar(root.getString()); TypedVar var = currentScope.getVar(root.getString());
Expand Down
10 changes: 10 additions & 0 deletions test/com/google/javascript/jscomp/TypeCheckNoTranspileTest.java
Expand Up @@ -49,6 +49,16 @@ protected CompilerOptions getDefaultOptions() {
return options; return options;
} }


public void testDuplicateCatchVarName() {
// Make sure that catch variables with the same name are considered to be distinct variables
// rather than causing a redeclaration error.
testTypes(
lines(
"try { throw 1; } catch (/** @type {number} */ err) {}",
"try { throw 'error'; } catch (/** @type {string} */ err) {}",
""));
}

public void testTypedefFieldInLoopLocal() { public void testTypedefFieldInLoopLocal() {
testTypes( testTypes(
lines( lines(
Expand Down
7 changes: 7 additions & 0 deletions test/com/google/javascript/jscomp/TypeValidatorTest.java
Expand Up @@ -451,6 +451,13 @@ public void testDuplicateSuppression() {
"ns2.x = 'a';"), "ns2.x = 'a';"),
TypeValidator.DUP_VAR_DECLARATION_TYPE_MISMATCH); TypeValidator.DUP_VAR_DECLARATION_TYPE_MISMATCH);


// catch variables in different catch blocks are not duplicate declarations
testSame(
lines(
"try { throw 1; } catch (/** @type {number} */ err) {}",
"try { throw 1; } catch (/** @type {number} */ err) {}"
));

// duplicates suppressed on 1st declaration. // duplicates suppressed on 1st declaration.
testSame( testSame(
lines( lines(
Expand Down
83 changes: 76 additions & 7 deletions test/com/google/javascript/jscomp/TypedScopeCreatorTest.java
Expand Up @@ -16,7 +16,10 @@


package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.javascript.jscomp.ScopeSubject.assertScope; import static com.google.javascript.jscomp.ScopeSubject.assertScope;
import static com.google.javascript.jscomp.TypedScopeCreator.CTOR_INITIALIZER; import static com.google.javascript.jscomp.TypedScopeCreator.CTOR_INITIALIZER;
import static com.google.javascript.jscomp.TypedScopeCreator.IFACE_INITIALIZER; import static com.google.javascript.jscomp.TypedScopeCreator.IFACE_INITIALIZER;
Expand All @@ -42,7 +45,9 @@
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;


/** /**
* Tests for {@link TypedScopeCreator} and {@link TypeInference}. Admittedly, * Tests for {@link TypedScopeCreator} and {@link TypeInference}. Admittedly,
Expand All @@ -56,6 +61,26 @@ public final class TypedScopeCreatorTest extends CompilerTestCase {
private TypedScope lastLocalScope; private TypedScope lastLocalScope;
private TypedScope lastFunctionScope; private TypedScope lastFunctionScope;


/**
* Maps a label name to information about the labeled statement.
*
* <p>This map is recreated each time parseAndRunTypeInference() is executed.
* TODO(bradfordcsmith): This map and LabeledStatement are also in TypeInferenceTest.
* It would be good to unify them.
*/
private Map<String, LabeledStatement> labeledStatementMap;

/** Stores information about a labeled statement and allows making assertions on it. */
static class LabeledStatement {
final Node statementNode;
final TypedScope enclosingScope;

LabeledStatement(Node statementNode, TypedScope enclosingScope) {
this.statementNode = checkNotNull(statementNode);
this.enclosingScope = checkNotNull(enclosingScope);
}
}

@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
Expand All @@ -70,20 +95,40 @@ protected int getNumRepetitions() {
private class ScopeFinder extends AbstractPostOrderCallback { private class ScopeFinder extends AbstractPostOrderCallback {
@Override @Override
public void visit(NodeTraversal t, Node n, Node parent) { public void visit(NodeTraversal t, Node n, Node parent) {
TypedScope s = t.getTypedScope(); TypedScope scope = t.getTypedScope();
if (s.isGlobal()) { if (scope.isGlobal()) {
globalScope = s; globalScope = scope;
} else if (s.isBlockScope()) { } else if (scope.isBlockScope()) {
lastLocalScope = s; // TODO(bradfordcsmith): use labels to find scopes instead of lastLocalScope
} else if (s.isFunctionScope()) { lastLocalScope = scope;
lastFunctionScope = s; } else if (scope.isFunctionScope()) {
lastFunctionScope = scope;
}
if (parent != null && parent.isLabel() && !n.isLabelName()) {
// First child of a LABEL is a LABEL_NAME, n is the second child.
Node labelNameNode = checkNotNull(n.getPrevious(), n);
checkState(labelNameNode.isLabelName(), labelNameNode);
String labelName = labelNameNode.getString();
assertWithMessage("Duplicate label name: %s", labelName)
.that(labeledStatementMap)
.doesNotContainKey(labelName);
labeledStatementMap.put(labelName, new LabeledStatement(n, scope));
} }
} }
} }


private LabeledStatement getLabeledStatement(String label) {
assertWithMessage("No statement found for label: %s", label)
.that(labeledStatementMap)
.containsKey(label);
return labeledStatementMap.get(label);
}

@Override @Override
protected CompilerPass getProcessor(final Compiler compiler) { protected CompilerPass getProcessor(final Compiler compiler) {
registry = compiler.getTypeRegistry(); registry = compiler.getTypeRegistry();
// Create a fresh statement map for each test case.
labeledStatementMap = new HashMap<>();
return new CompilerPass() { return new CompilerPass() {
@Override @Override
public void process(Node externs, Node root) { public void process(Node externs, Node root) {
Expand Down Expand Up @@ -2294,6 +2339,30 @@ public void testDeclaredCatchExpression2() {
assertEquals("string", lastLocalScope.getVar("e").getType().toString()); assertEquals("string", lastLocalScope.getVar("e").getType().toString());
} }


public void testDuplicateCatchVariableNames() {
testSame(
lines(
"try {}", // preserve newlines
"catch (err) {",
" FIRST_CATCH: err;",
"}",
"try {}",
"catch (err) {",
" SECOND_CATCH: err;",
"}",
""));
TypedScope firstCatchScope = getLabeledStatement("FIRST_CATCH").enclosingScope;
assertScope(firstCatchScope).declares("err").directly();
TypedVar firstErrVar = firstCatchScope.getVar("err");

TypedScope secondCatchScope = getLabeledStatement("SECOND_CATCH").enclosingScope;
assertScope(firstCatchScope).declares("err").directly();
assertThat(firstCatchScope).isNotSameAs(secondCatchScope);

TypedVar secondErrVar = secondCatchScope.getVar("err");
assertThat(firstErrVar).isNotSameAs(secondErrVar);
}

public void testGenerator1() { public void testGenerator1() {
testSame("function *gen() { yield 1; } var g = gen();"); testSame("function *gen() { yield 1; } var g = gen();");
assertEquals("function(): Generator<?>", findNameType("gen", globalScope).toString()); assertEquals("function(): Generator<?>", findNameType("gen", globalScope).toString());
Expand Down

0 comments on commit da41460

Please sign in to comment.