diff --git a/src/com/google/javascript/jscomp/Es6RenameReferences.java b/src/com/google/javascript/jscomp/Es6RenameReferences.java index a6fff88eb92..d671c2d3ac7 100644 --- a/src/com/google/javascript/jscomp/Es6RenameReferences.java +++ b/src/com/google/javascript/jscomp/Es6RenameReferences.java @@ -16,6 +16,7 @@ package com.google.javascript.jscomp; +import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Table; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; @@ -30,18 +31,28 @@ */ final class Es6RenameReferences extends AbstractPostOrderCallback { - private static final Splitter SPLIT_ON_DOT = Splitter.on('.').limit(2); + private static final Splitter SPLIT_ON_DOT = Splitter.on('.'); + private static final Joiner JOIN_ON_DOT = Joiner.on('.'); + private final AbstractCompiler compiler; private final Table renameTable; private final boolean typesOnly; - Es6RenameReferences(Table renameTable, boolean typesOnly) { + /** + * @param renameTable table from (scope root, old name) to new name. Both the old and new name can + * be qualified. + * @param typesOnly true if only type annotations should be renamed, false if type annotations and + * code references should be renamed + */ + Es6RenameReferences( + AbstractCompiler compiler, Table renameTable, boolean typesOnly) { + this.compiler = compiler; this.renameTable = renameTable; this.typesOnly = typesOnly; } - Es6RenameReferences(Table renameTable) { - this(renameTable, /* typesOnly= */ false); + Es6RenameReferences(AbstractCompiler compiler, Table renameTable) { + this(compiler, renameTable, /* typesOnly= */ false); } @Override @@ -67,15 +78,44 @@ private void renameTypeNode(NodeTraversal t, Iterable typeNodes) { private void renameReference(NodeTraversal t, Node n, boolean isType) { String fullName = n.getString(); + List split = SPLIT_ON_DOT.splitToList(fullName); - String oldName = split.get(0); + + // Rename most specific (longest) to least specific (shortest). + for (int i = split.size(); i > 0; i--) { + String name = JOIN_ON_DOT.join(split.subList(0, i)); + if (renameTable.containsColumn(name)) { + String rest = JOIN_ON_DOT.join(split.subList(i, split.size())); + if (!rest.isEmpty()) { + rest = "." + rest; + } + renameReference(t, n, isType, name, rest); + } + } + } + + private void renameReference( + NodeTraversal t, Node n, boolean isType, String oldName, String rest) { Scope current = t.getScope(); while (current != null) { String newName = renameTable.get(current.getRootNode(), oldName); if (newName != null) { - String rest = split.size() == 2 ? "." + split.get(1) : ""; - n.setString(newName + rest); - if (!isType) { + String newFullName = newName + rest; + + if (isType || n.isName()) { + n.setOriginalName(n.getString()); + n.setString(newFullName); + n.setLength(newFullName.length()); + + if (!isType) { + t.reportCodeChange(); + } + } else { + Node newNode = NodeUtil.newQName(compiler, newFullName); + newNode.setOriginalName(n.getString()); + newNode.srcrefTree(n); + newNode.addChildrenToBack(n.removeChildren()); + n.replaceWith(newNode); t.reportCodeChange(); } return; diff --git a/src/com/google/javascript/jscomp/Es6RenameVariablesInParamLists.java b/src/com/google/javascript/jscomp/Es6RenameVariablesInParamLists.java index 3113c059324..4b920da6076 100644 --- a/src/com/google/javascript/jscomp/Es6RenameVariablesInParamLists.java +++ b/src/com/google/javascript/jscomp/Es6RenameVariablesInParamLists.java @@ -76,12 +76,16 @@ public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { String oldName = var.getName(); if (collector.currFuncReferences.contains(oldName) && !renameTable.contains(fBlockScope.getRootNode(), oldName)) { - renameTable.put(fBlockScope.getRootNode(), - oldName, oldName + "$" + compiler.getUniqueNameIdSupplier().get()); + renameTable.put( + fBlockScope.getRootNode(), + oldName, + oldName + "$" + compiler.getUniqueNameIdSupplier().get()); } } new NodeTraversal( - compiler, new Es6RenameReferences(renameTable), new Es6SyntacticScopeCreator(compiler)) + compiler, + new Es6RenameReferences(compiler, renameTable), + new Es6SyntacticScopeCreator(compiler)) .traverseInnerNode(block, block.getParent(), fScope); } diff --git a/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java b/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java index 37c97794dae..2e3372b865f 100644 --- a/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java +++ b/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java @@ -105,7 +105,7 @@ public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); // Needed for let / const declarations in .d.ts externs. TranspilationPasses.processTranspile(compiler, externs, transpiledFeatures, this); - NodeTraversal.traverse(compiler, root, new Es6RenameReferences(renameTable)); + NodeTraversal.traverse(compiler, root, new Es6RenameReferences(compiler, renameTable)); LoopClosureTransformer transformer = new LoopClosureTransformer(); NodeTraversal.traverse(compiler, root, transformer); transformer.transformLoopClosure(); @@ -118,7 +118,7 @@ public void hotSwapScript(Node scriptRoot, Node originalRoot) { shouldAddTypesOnNewAstNodes = getShouldAddTypesOnNewAstNodes(); NodeTraversal.traverse(compiler, scriptRoot, new CollectUndeclaredNames()); NodeTraversal.traverse(compiler, scriptRoot, this); - NodeTraversal.traverse(compiler, scriptRoot, new Es6RenameReferences(renameTable)); + NodeTraversal.traverse(compiler, scriptRoot, new Es6RenameReferences(compiler, renameTable)); LoopClosureTransformer transformer = new LoopClosureTransformer(); NodeTraversal.traverse(compiler, scriptRoot, transformer); transformer.transformLoopClosure(); diff --git a/src/com/google/javascript/jscomp/Es6RewriteModules.java b/src/com/google/javascript/jscomp/Es6RewriteModules.java index 8793ff226e7..6be25e07bcf 100644 --- a/src/com/google/javascript/jscomp/Es6RewriteModules.java +++ b/src/com/google/javascript/jscomp/Es6RewriteModules.java @@ -237,7 +237,9 @@ void rewrite(Node scriptNode) { if (!renameTable.isEmpty()) { NodeTraversal.traverse( - compiler, scriptNode, new Es6RenameReferences(renameTable, /* typesOnly= */ true)); + compiler, + scriptNode, + new Es6RenameReferences(compiler, renameTable, /* typesOnly= */ true)); } }