/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.ecmascript.ast;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.mozilla.javascript.ast.AstRoot;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ecmascript.Ecmascript3Parser;
import net.sourceforge.pmd.lang.ecmascript.EcmascriptParserOptions;
import net.sourceforge.pmd.lang.ecmascript.rule.AbstractEcmascriptRule;
public class EcmascriptParserTest extends EcmascriptParserTestBase {
/**
* https://sourceforge.net/p/pmd/bugs/1043/
*/
@Test
public void testLineNumbers() {
final String SOURCE_CODE = "function a() {" + PMD.EOL + " alert('hello');" + PMD.EOL + "}" + PMD.EOL;
EcmascriptNode<AstRoot> node = parse(SOURCE_CODE);
assertEquals(1, node.getBeginLine());
assertEquals(1, node.getBeginColumn());
assertEquals(3, node.getEndLine());
assertEquals(1, node.getEndColumn());
Node child = node.getFirstChildOfType(ASTFunctionNode.class);
assertEquals(1, child.getBeginLine());
assertEquals(1, child.getBeginColumn());
assertEquals(3, child.getEndLine());
assertEquals(1, child.getEndColumn());
child = node.getFirstDescendantOfType(ASTFunctionCall.class);
assertEquals(2, child.getBeginLine());
assertEquals(3, child.getBeginColumn());
assertEquals(2, child.getEndLine());
assertEquals(16, child.getEndColumn());
}
/**
* https://sourceforge.net/p/pmd/bugs/1149/
*/
@Test
public void testLineNumbersWithinEcmascriptRules() {
String source = "function f(x){\n" + " if (x) {\n" + " return 1;\n" + " } else {\n"
+ " return 0;\n" + " }\n" + "}";
final List<String> output = new ArrayList<>();
class MyEcmascriptRule extends AbstractEcmascriptRule {
public Object visit(ASTScope node, Object data) {
output.add("Scope from " + node.getBeginLine() + " to " + node.getEndLine());
return super.visit(node, data);
}
}
MyEcmascriptRule rule = new MyEcmascriptRule();
RuleContext ctx = new RuleContext();
rule.apply(Arrays.asList(parse(source)), ctx);
assertEquals("Scope from 2 to 4", output.get(0));
assertEquals("Scope from 4 to 6", output.get(1));
}
/**
* Test bug https://sourceforge.net/p/pmd/bugs/1118/
*/
@Test
public void testArrayAccess() {
EcmascriptNode<AstRoot> node = parse("function a() { b['a'] = 1; c[1] = 2; }");
List<ASTElementGet> arrays = node.findDescendantsOfType(ASTElementGet.class);
assertEquals("b", arrays.get(0).getTarget().getImage());
assertEquals("a", arrays.get(0).getElement().getImage());
assertEquals("c", arrays.get(1).getTarget().getImage());
assertEquals("1", arrays.get(1).getElement().getImage());
}
/**
* Test for bug #1136 ECAMScript: NullPointerException in getLeft() and
* getRight()
*/
@Test
public void testArrayMethod() {
EcmascriptNode<AstRoot> rootNode = parse(
"function test(){\n" + " a(); // OK\n" + " b.c(); // OK\n" + " d[0](); // OK\n"
+ " e[0].f(); // OK\n" + " y.z[0](); // FAIL ==> java.lang.NullPointerException\n" + "}");
List<ASTFunctionCall> calls = rootNode.findDescendantsOfType(ASTFunctionCall.class);
List<String> results = new ArrayList<>();
for (ASTFunctionCall f : calls) {
Node node = f.getTarget();
results.add(getName(node));
}
assertEquals("[a, b.c, d[], e[].f, y.z[]]", results.toString());
}
private String getName(Node node) {
if (node instanceof ASTName) {
return ((ASTName) node).getIdentifier();
}
if (node instanceof ASTPropertyGet) {
final ASTPropertyGet pgNode = (ASTPropertyGet) node;
final String leftName = getName(pgNode.getLeft());
final String rightName = getName(pgNode.getRight());
return leftName + "." + rightName;
}
if (node instanceof ASTElementGet) {
return getName(((ASTElementGet) node).getTarget()) + "[]";
}
return "????";
}
/**
* https://sourceforge.net/p/pmd/bugs/1150/ #1150 "EmptyExpression" for
* valid statements!
*/
@Test
public void testCaseAsIdentifier() {
ASTAstRoot rootNode = parse("function f(a){\n" + " a.case.flag = 1;\n" + " return;\n" + "}");
ASTBlock block = rootNode.getFirstDescendantOfType(ASTBlock.class);
assertFalse(block.jjtGetChild(0) instanceof ASTEmptyExpression);
assertTrue(block.jjtGetChild(0) instanceof ASTExpressionStatement);
assertTrue(block.jjtGetChild(0).jjtGetChild(0) instanceof ASTAssignment);
}
/**
* https://sourceforge.net/p/pmd/bugs/1045/ #1045 //NOPMD not working (or
* not implemented) with ECMAscript
*/
@Test
public void testSuppresionComment() {
Ecmascript3Parser parser = new Ecmascript3Parser(new EcmascriptParserOptions());
Reader sourceCode = new StringReader("function(x) {\n" + "x = x; //NOPMD I know what I'm doing\n" + "}\n");
parser.parse("foo", sourceCode);
assertEquals(" I know what I'm doing", parser.getSuppressMap().get(2));
assertEquals(1, parser.getSuppressMap().size());
EcmascriptParserOptions parserOptions = new EcmascriptParserOptions();
parserOptions.setSuppressMarker("FOOOO");
parser = new Ecmascript3Parser(parserOptions);
sourceCode = new StringReader(
"function(x) {\n" + "y = y; //NOPMD xyz\n" + "x = x; //FOOOO I know what I'm doing\n" + "}\n");
parser.parse("foo", sourceCode);
assertEquals(" I know what I'm doing", parser.getSuppressMap().get(3));
assertEquals(1, parser.getSuppressMap().size());
}
/**
* #1191 Ecmascript fails to parse "void(0)"
*/
@Test
public void testVoidKeyword() {
ASTAstRoot rootNode = parse("function f(matchFn, fieldval, n){\n"
+ " return (matchFn)?(matcharray = eval(matchFn+\"('\"+fieldval+\"','\"+n.id+\"')\")):void(0);\n"
+ "}\n");
ASTUnaryExpression unary = rootNode.getFirstDescendantOfType(ASTUnaryExpression.class);
assertEquals("void", unary.getImage());
}
/**
* #1192 Ecmascript fails to parse this operator " ^= "
*/
@Test
public void testXorAssignment() {
ASTAstRoot rootNode = parse("function f() { var x = 2; x ^= 2; x &= 2; x |= 2; "
+ "x &&= true; x ||= false; x *= 2; x /= 2; x %= 2; x += 2; x -= 2; "
+ "x <<= 2; x >>= 2; x >>>= 2; }");
ASTAssignment infix = rootNode.getFirstDescendantOfType(ASTAssignment.class);
assertEquals("^=", infix.getImage());
}
}