diff --git a/test/com/google/javascript/jscomp/AstAnalyzerTest.java b/test/com/google/javascript/jscomp/AstAnalyzerTest.java
index be34da4fe88..c07355c04ae 100644
--- a/test/com/google/javascript/jscomp/AstAnalyzerTest.java
+++ b/test/com/google/javascript/jscomp/AstAnalyzerTest.java
@@ -68,6 +68,8 @@
import com.google.javascript.jscomp.parsing.parser.util.format.SimpleFormat;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
+import java.util.ArrayDeque;
+import java.util.Optional;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
@@ -84,29 +86,49 @@
* runner.
*/
@RunWith(Enclosed.class)
-public class AstAnalyzerTest {
+public final class AstAnalyzerTest {
- /** Provides methods for parsing and accessing the compiler used for the parsing. */
- private static class ParseHelper {
- private boolean hasRegExpGlobalReferences = false;
- private boolean hasFakeGetterAndSetter = false;
- private Compiler compiler = null;
+ private static final class AnalysisCase {
+ boolean expect;
+ String js;
+ Token token;
+ boolean globalRegExp;
- /**
- * Tell the compiler to behave as if it has (or has not) seen any references to the RegExp
- * global properties, which are modified when matches are performed.
- */
- ParseHelper setHasRegExpGlobalReferences(boolean hasRegExpGlobalReferences) {
- this.hasRegExpGlobalReferences = hasRegExpGlobalReferences;
+ AnalysisCase expect(boolean b) {
+ this.expect = b;
+ return this;
+ }
+
+ AnalysisCase js(String s) {
+ this.js = s;
+ return this;
+ }
+
+ AnalysisCase token(Token t) {
+ this.token = t;
return this;
}
- ParseHelper registerFakeGetterAndSetter() {
- this.hasFakeGetterAndSetter = true;
+ AnalysisCase globalRegExp(boolean b) {
+ this.globalRegExp = b;
return this;
}
- private void createNewCompiler() {
+ @Override
+ public String toString() {
+ return SimpleFormat.format("%s node in `%s` -> %s", token, js, expect);
+ }
+ }
+
+ private static AnalysisCase kase() {
+ return new AnalysisCase();
+ }
+
+ /** Provides methods for parsing and accessing the compiler used for the parsing. */
+ private static final class ParseHelper {
+ private Compiler compiler = null;
+
+ private void resetCompiler() {
CompilerOptions options = new CompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_NEXT);
@@ -117,19 +139,14 @@ private void createNewCompiler() {
compiler = new Compiler();
compiler.initOptions(options);
- compiler.setHasRegExpGlobalReferences(hasRegExpGlobalReferences);
- if (hasFakeGetterAndSetter) {
- compiler.setAccessorSummary(
- AccessorSummary.create(
- ImmutableMap.of(
- "getter", PropertyAccessKind.GETTER_ONLY, //
- "setter", PropertyAccessKind.SETTER_ONLY)));
- }
+ compiler.setAccessorSummary(
+ AccessorSummary.create(
+ ImmutableMap.of(
+ "getter", PropertyAccessKind.GETTER_ONLY, //
+ "setter", PropertyAccessKind.SETTER_ONLY)));
}
- Node parse(String js) {
- createNewCompiler();
-
+ private Node parseInternal(String js) {
Node n = compiler.parseTestCode(js);
assertThat(compiler.getErrors()).isEmpty();
return n;
@@ -140,210 +157,149 @@ Node parse(String js) {
* preorder DFS.
*/
Node parseFirst(Token token, String js) {
- Node rootNode = this.parse(js);
- checkState(rootNode.isScript(), rootNode);
- Node firstNodeWithToken = getFirstNode(rootNode, token);
- if (firstNodeWithToken == null) {
- throw new AssertionError(SimpleFormat.format("No %s node found in:\n %s", token, js));
- } else {
- return firstNodeWithToken;
- }
+ resetCompiler();
+
+ return findFirst(token, parseInternal(js)).get();
}
- /**
- * Returns the parsed expression (e.g. returns a NAME given 'a').
- *
- *
Presumes that the given JavaScript is an expression.
- */
- Node parseExpr(String js) {
- Node script = parse("(" + js + ");"); // Parens force interpretation as an expression.
- return script
- .getFirstChild() // EXPR_RESULT
- .getFirstChild(); // expr
+ Node parseCase(AnalysisCase kase) {
+ resetCompiler();
+ compiler.setHasRegExpGlobalReferences(kase.globalRegExp);
+
+ Node root = parseInternal(kase.js);
+ if (kase.token == null) {
+ return root.getFirstChild();
+ } else {
+ return findFirst(kase.token, root).get();
+ }
}
- private AstAnalyzer getAstAnalyzer() {
+ AstAnalyzer getAstAnalyzer() {
return new AstAnalyzer(compiler, false);
}
}
- /**
- * Does a preorder DFS, returning the first node found that has the given token.
- *
- * @return the first matching node
- * @throws AssertionError if no matching node was found.
- */
- private static Node getFirstNode(Node root, Token token) {
- if (root.getToken() == token) {
- return root;
- } else {
- for (Node child = root.getFirstChild(); child != null; child = child.getNext()) {
- Node matchingNode = getFirstNode(child, token);
- if (matchingNode != null) {
- return matchingNode;
- }
+ /** Does a preorder DFS, returning the first node found that has the given token. */
+ private static Optional findFirst(Token token, Node root) {
+ ArrayDeque stack = new ArrayDeque<>();
+ stack.push(root);
+
+ while (!stack.isEmpty()) {
+ Node cur = stack.pop();
+ if (cur.getToken() == token) {
+ return Optional.of(cur);
+ }
+
+ for (Node child = cur.getLastChild(); child != null; child = child.getPrevious()) {
+ stack.push(child);
}
}
- return null;
+
+ return Optional.empty();
}
@RunWith(Parameterized.class)
public static final class MayEffectMutableStateTest {
- @Parameter(0)
- public String jsExpression;
-
- @Parameter(1)
- public Token token;
-
- @Parameter(2)
- public Boolean expectedResult;
+ @Parameter public AnalysisCase kase;
// Always include the index. If two cases have the same name, only one will be executed.
- @Parameters(name = "#{index} {1} node in ({0}) -> {2}")
- public static Iterable