Skip to content

Commit

Permalink
Fix crash when trying to suggest a fix, when there are two different …
Browse files Browse the repository at this point in the history
…styles of goog.require for the same namespace.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=184353406
  • Loading branch information
tbreisacher authored and blickly committed Feb 5, 2018
1 parent 8f7ac7a commit bd6bb5b
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 16 deletions.
8 changes: 6 additions & 2 deletions src/com/google/javascript/jscomp/ClosureCheckModule.java
Expand Up @@ -160,7 +160,8 @@ public final class ClosureCheckModule extends AbstractModuleCallback
private static class ModuleInfo { private static class ModuleInfo {
// Name of the module in question (i.e. the argument to goog.module) // Name of the module in question (i.e. the argument to goog.module)
private final String name; private final String name;
// Mapping from fully qualified goog.required names to the import LHS node // Mapping from fully qualified goog.required names to the import LHS node.
// For standalone goog.require()s the value is the EXPR_RESULT node.
private final Map<String, Node> importsByLongRequiredName = new HashMap<>(); private final Map<String, Node> importsByLongRequiredName = new HashMap<>();
// Module-local short names for goog.required symbols. // Module-local short names for goog.required symbols.
private final Set<String> shortImportNames = new HashSet<>(); private final Set<String> shortImportNames = new HashSet<>();
Expand Down Expand Up @@ -417,7 +418,10 @@ private void checkRequireCall(NodeTraversal t, Node callNode, Node parent) {
} }
switch (parent.getToken()) { switch (parent.getToken()) {
case EXPR_RESULT: case EXPR_RESULT:
currentModule.importsByLongRequiredName.put(extractFirstArgumentName(callNode), parent); String key = extractFirstArgumentName(callNode);
if (!currentModule.importsByLongRequiredName.containsKey(key)) {
currentModule.importsByLongRequiredName.put(key, parent);
}
return; return;
case NAME: case NAME:
case DESTRUCTURING_LHS: case DESTRUCTURING_LHS:
Expand Down
28 changes: 17 additions & 11 deletions src/com/google/javascript/refactoring/SuggestedFix.java
Expand Up @@ -811,23 +811,29 @@ public Builder removeGoogRequire(Match m, String namespace) {
return this; return this;
} }


/**
* Find the goog.require node for the given namespace (or null if there isn't one).
* If there is more than one, this will return the first standalone goog.require statement.
*/
@Nullable
private static Node findGoogRequireNode(Node n, NodeMetadata metadata, String namespace) { private static Node findGoogRequireNode(Node n, NodeMetadata metadata, String namespace) {
Node script = NodeUtil.getEnclosingScript(n); Node script = NodeUtil.getEnclosingScript(n);
if (script.getFirstChild().isModuleBody()) { if (script.getFirstChild().isModuleBody()) {
script = script.getFirstChild(); script = script.getFirstChild();
} }


if (script != null) { for (Node child : script.children()) {
Node child = script.getFirstChild(); if (NodeUtil.isExprCall(child)
while (child != null) { && Matchers.googRequire(namespace).matches(child.getFirstChild(), metadata)) {
if ((NodeUtil.isExprCall(child) return child;
&& Matchers.googRequire(namespace).matches(child.getFirstChild(), metadata)) }
|| (NodeUtil.isNameDeclaration(child) }
&& child.getFirstFirstChild() != null && Matchers.googRequire(namespace)
.matches(child.getFirstFirstChild(), metadata))) { for (Node child : script.children()) {
return child; if (NodeUtil.isNameDeclaration(child)
} && child.getFirstFirstChild() != null
child = child.getNext(); && Matchers.googRequire(namespace).matches(child.getFirstFirstChild(), metadata)) {
return child;
} }
} }
return null; return null;
Expand Down
30 changes: 27 additions & 3 deletions test/com/google/javascript/refactoring/ErrorToFixMapperTest.java
Expand Up @@ -31,7 +31,6 @@
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
Expand Down Expand Up @@ -798,9 +797,8 @@ public void testMissingRequireInGoogModule_atExtends() {
"function Cat() {}")); "function Cat() {}"));
} }


@Ignore("Currently causes a crash. See b/72864468.")
@Test @Test
public void testBothFormsOfRequire() { public void testBothFormsOfRequire1() {
assertChanges( assertChanges(
LINE_JOINER.join( LINE_JOINER.join(
"goog.module('example');", "goog.module('example');",
Expand All @@ -816,6 +814,32 @@ public void testBothFormsOfRequire() {
"goog.module('example');", "goog.module('example');",
"", "",
"const SoyRenderer = goog.require('foo.bar.SoyRenderer');", "const SoyRenderer = goog.require('foo.bar.SoyRenderer');",
"function setUp() {",
" const soyService = new SoyRenderer();",
"}",
""));
}

@Test
public void testBothFormsOfRequire2() {
// After this change, a second run will remove the duplicate require.
// See testBothFormsOfRequire1
assertChanges(
LINE_JOINER.join(
"goog.module('example');",
"",
"goog.require('foo.bar.SoyRenderer');",
"const SoyRenderer = goog.require('foo.bar.SoyRenderer');",
"",
"function setUp() {",
" const soyService = new foo.bar.SoyRenderer();",
"}",
""),
LINE_JOINER.join(
"goog.module('example');",
"",
"const SoyRenderer = goog.require('foo.bar.SoyRenderer');",
"goog.require('foo.bar.SoyRenderer');",
"", "",
"function setUp() {", "function setUp() {",
" const soyService = new SoyRenderer();", " const soyService = new SoyRenderer();",
Expand Down

0 comments on commit bd6bb5b

Please sign in to comment.