diff --git a/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java b/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java index 5c1028486b5..d8394cd1d41 100644 --- a/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java +++ b/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java @@ -163,7 +163,12 @@ public void visit(NodeTraversal t, Node n, Node parent) { n.removeProp(Node.IS_CONSTANT_NAME); } n.setString(newName); - t.getCompiler().reportChangeToEnclosingScope(n); + t.reportCodeChange(); + // If we are renaming a function declaration, make sure the containing scope + // has the opporunity to act on the change. + if (parent.isFunction() && NodeUtil.isFunctionDeclaration(parent)) { + t.getCompiler().reportChangeToEnclosingScope(parent); + } } break; diff --git a/src/com/google/javascript/jscomp/NodeUtil.java b/src/com/google/javascript/jscomp/NodeUtil.java index 38545f9a0c2..d969142eb1e 100644 --- a/src/com/google/javascript/jscomp/NodeUtil.java +++ b/src/com/google/javascript/jscomp/NodeUtil.java @@ -4832,10 +4832,20 @@ private static boolean isEquivalentToExcludingFunctions( Node thatChild = thatNode.getFirstChild(); while (thisChild != null && thatChild != null) { if (thisChild.isFunction() || thisChild.isScript()) { - // don't compare function name, parameters or bodies. + // Don't compare function expression name, parameters or bodies. + // But do check that that the node is there. if (thatChild.getToken() != thisChild.getToken()) { return false; } + // Only compare function names for function declarations (not function expressions) + // as they change the outer scope definition. + if (thisChild.isFunction() && NodeUtil.isFunctionDeclaration(thisChild)) { + String thisName = thisChild.getFirstChild().getString(); + String thatName = thatChild.getFirstChild().getString(); + if (!thisName.equals(thatName)) { + return false; + } + } } else if (!isEquivalentToExcludingFunctions(thisChild, thatChild)) { return false; } diff --git a/src/com/google/javascript/jscomp/RenameVars.java b/src/com/google/javascript/jscomp/RenameVars.java index eb583e4e164..035bdc7c121 100644 --- a/src/com/google/javascript/jscomp/RenameVars.java +++ b/src/com/google/javascript/jscomp/RenameVars.java @@ -364,21 +364,12 @@ public void process(Node externs, Node root) { // Rename the globals! for (Node n : globalNameNodes) { - String newName = getNewGlobalName(n); - // Note: if newName is null, then oldName is an extern. - if (newName != null) { - n.setString(newName); - compiler.reportChangeToEnclosingScope(n); - } + setNameAndReport(n, getNewGlobalName(n)); } // Rename the locals! for (Node n : localNameNodes) { - String newName = getNewLocalName(n); - if (newName != null) { - n.setString(newName); - compiler.reportChangeToEnclosingScope(n); - } + setNameAndReport(n, getNewLocalName(n)); } // Lastly, write the name assignments to the debug log. @@ -386,6 +377,20 @@ public void process(Node externs, Node root) { assignmentLog = null; } + private void setNameAndReport(Node n, String newName) { + // A null newName, indicates it should not be renamed. + if (newName != null) { + n.setString(newName); + compiler.reportChangeToEnclosingScope(n); + Node parent = n.getParent(); + if (parent.isFunction() && NodeUtil.isFunctionDeclaration(parent)) { + // If we are renaming a function declaration, make sure the containing scope + // has the opporunity to act on the change. + compiler.reportChangeToEnclosingScope(parent); + } + } + } + private String getNewGlobalName(Node n) { String oldName = n.getString(); Assignment a = assignments.get(oldName); diff --git a/src/com/google/javascript/jscomp/ShadowVariables.java b/src/com/google/javascript/jscomp/ShadowVariables.java index 877def2a2b6..7799cd147c2 100644 --- a/src/com/google/javascript/jscomp/ShadowVariables.java +++ b/src/com/google/javascript/jscomp/ShadowVariables.java @@ -179,10 +179,11 @@ public void enterScope(NodeTraversal t) { // Since we don't shadow global, there is nothing to be done in the // first immediate local scope as well. - if ((t.getScopeRoot().isFunction() - && NodeUtil.getEnclosingFunction(t.getScopeRoot().getParent()) == null) - || (NodeUtil.isFunctionBlock(t.getScopeRoot()) - && NodeUtil.getEnclosingFunction(t.getScopeRoot().getGrandparent()) == null)) { + Node scopeRoot = t.getScopeRoot(); + if ((scopeRoot.isFunction() + && NodeUtil.getEnclosingFunction(scopeRoot.getParent()) == null) + || (NodeUtil.isFunctionBlock(scopeRoot) + && NodeUtil.getEnclosingFunction(scopeRoot.getGrandparent()) == null)) { return; } diff --git a/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java b/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java index d621a2ff2d8..dfd55a70ce4 100644 --- a/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java +++ b/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java @@ -497,7 +497,7 @@ public void testConstRemovingRename2() { "var CONST$jscomp$unique_0 = 3; var b$jscomp$unique_1 = CONST$jscomp$unique_0;"); } - public void testVarParamSameName() { + public void testVarParamSameName0() { test( LINE_JOINER.join( "function f(x) {", @@ -507,7 +507,9 @@ public void testVarParamSameName() { "function f$jscomp$unique_0(x$jscomp$unique_1) {", " if (!x$jscomp$unique_1) var x$jscomp$unique_1 = 6;", "}")); + } + public void testVarParamSameName1() { test( LINE_JOINER.join( "function f(x) {", @@ -517,7 +519,9 @@ public void testVarParamSameName() { "function f$jscomp$unique_0(x$jscomp$unique_1) {", " if (!x$jscomp$unique_1) x$jscomp$unique_1 = 6; ", "}")); + } + public void testVarParamSameAsLet0() { testEs6( LINE_JOINER.join( "function f(x) {", diff --git a/test/com/google/javascript/jscomp/NodeUtilTest.java b/test/com/google/javascript/jscomp/NodeUtilTest.java index b3a36465250..80d56a81edf 100644 --- a/test/com/google/javascript/jscomp/NodeUtilTest.java +++ b/test/com/google/javascript/jscomp/NodeUtilTest.java @@ -2540,6 +2540,23 @@ static void testFunctionName(String js, String expected) { NodeUtil.getNearestFunctionName(getFunctionNode(js))); } + public static void testChangeVerification() { + Node mainScript = IR.script(); + Node main = IR.root(mainScript); + + Node clone = main.cloneTree(); + + Map map = NodeUtil.mapMainToClone(main, clone); + + NodeUtil.verifyScopeChanges("", map, main); + + mainScript.addChildToFront( + IR.function(IR.name("A"), IR.paramList(), IR.block())); + mainScript.setChangeTime(100); + + NodeUtil.verifyScopeChanges("", map, main); + } + static Node getClassNode(String js) { Node root = parse(js); return getClassNode(root);