View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.ecmascript.ast;
5   
6   import static org.junit.Assert.assertEquals;
7   import static org.junit.Assert.assertFalse;
8   import static org.junit.Assert.assertTrue;
9   
10  import java.io.Reader;
11  import java.io.StringReader;
12  import java.util.ArrayList;
13  import java.util.Arrays;
14  import java.util.List;
15  
16  import net.sourceforge.pmd.PMD;
17  import net.sourceforge.pmd.RuleContext;
18  import net.sourceforge.pmd.lang.ast.Node;
19  import net.sourceforge.pmd.lang.ecmascript.Ecmascript3Parser;
20  import net.sourceforge.pmd.lang.ecmascript.EcmascriptParserOptions;
21  import net.sourceforge.pmd.lang.ecmascript.rule.AbstractEcmascriptRule;
22  
23  import org.junit.Test;
24  import org.mozilla.javascript.ast.AstRoot;
25  
26  public class EcmascriptParserTest extends EcmascriptParserTestBase {
27  
28      /**
29       * https://sourceforge.net/p/pmd/bugs/1043/
30       */
31      @Test
32      public void testLineNumbers() {
33          String SOURCE_CODE =
34                  "function a() {" + PMD.EOL
35                + "  alert('hello');" + PMD.EOL
36                + "}" + PMD.EOL;
37          EcmascriptNode<AstRoot> node = parse(SOURCE_CODE);
38          assertEquals(1, node.getBeginLine());
39          assertEquals(1, node.getBeginColumn());
40          assertEquals(3, node.getEndLine());
41          assertEquals(1, node.getEndColumn());
42  
43          Node child = node.getFirstChildOfType(ASTFunctionNode.class);
44          assertEquals(1, child.getBeginLine());
45          assertEquals(1, child.getBeginColumn());
46          assertEquals(3, child.getEndLine());
47          assertEquals(1, child.getEndColumn());
48  
49          child = node.getFirstDescendantOfType(ASTFunctionCall.class);
50          assertEquals(2, child.getBeginLine());
51          assertEquals(3, child.getBeginColumn());
52          assertEquals(2, child.getEndLine());
53          assertEquals(16, child.getEndColumn());
54      }
55  
56      /**
57       * https://sourceforge.net/p/pmd/bugs/1149/
58       */
59      @Test
60      public void testLineNumbersWithinEcmascriptRules() {
61          String source =
62                  "function f(x){\n" + 
63                  "   if (x) {\n" + 
64                  "       return 1;\n" + 
65                  "   } else {\n" + 
66                  "       return 0;\n" + 
67                  "   }\n" + 
68                  "}";
69          final List<String> output = new ArrayList<String>();
70  
71          class MyEcmascriptRule extends AbstractEcmascriptRule {
72              public Object visit(ASTScope node, Object data) {
73                  output.add("Scope from " + node.getBeginLine() + " to " + node.getEndLine());
74                  return super.visit(node, data);
75              }
76          }
77  
78          MyEcmascriptRule rule = new MyEcmascriptRule();
79          RuleContext ctx = new RuleContext();
80          rule.apply(Arrays.asList(parse(source)), ctx);
81  
82          assertEquals("Scope from 2 to 4", output.get(0));
83          assertEquals("Scope from 4 to 6", output.get(1));
84      }
85  
86      /**
87       * Test bug https://sourceforge.net/p/pmd/bugs/1118/
88       */
89      @Test
90      public void testArrayAccess() {
91          EcmascriptNode<AstRoot> node = parse("function a() { b['a'] = 1; c[1] = 2; }");
92          List<ASTElementGet> arrays = node.findDescendantsOfType(ASTElementGet.class);
93          assertEquals("b", arrays.get(0).getTarget().getImage());
94          assertEquals("a", arrays.get(0).getElement().getImage());
95          assertEquals("c", arrays.get(1).getTarget().getImage());
96          assertEquals("1", arrays.get(1).getElement().getImage());
97      }
98  
99      /**
100      * Test for bug #1136 ECAMScript: NullPointerException in getLeft() and getRight()
101      */
102     @Test
103     public void testArrayMethod() {
104         EcmascriptNode<AstRoot> rootNode = parse("function test(){\n" + 
105                 "  a();      // OK\n" + 
106                 "  b.c();    // OK\n" + 
107                 "  d[0]();   // OK\n" + 
108                 "  e[0].f(); // OK\n" + 
109                 "  y.z[0](); // FAIL ==> java.lang.NullPointerException\n" + 
110                 "}");
111 
112         List<ASTFunctionCall> calls = rootNode.findDescendantsOfType(ASTFunctionCall.class);
113         List<String> results = new ArrayList<String>();
114         for (ASTFunctionCall f : calls) {
115             Node node = f.getTarget();
116             results.add(getName(node));
117         }
118         assertEquals("[a, b.c, d[], e[].f, y.z[]]", results.toString());
119     }
120 
121     private String getName(Node node) {
122         if( node instanceof ASTName ){
123             return ((ASTName)node).getIdentifier();
124         }
125         if( node instanceof ASTPropertyGet ){
126             final ASTPropertyGet pgNode = (ASTPropertyGet)node;
127             final String leftName  = getName(pgNode.getLeft());
128             final String rightName = getName(pgNode.getRight());
129             return leftName + "." + rightName;
130         }
131         if( node instanceof ASTElementGet ){
132             return getName(((ASTElementGet)node).getTarget()) + "[]";
133         }
134         return "????";
135     }
136 
137     /**
138      * https://sourceforge.net/p/pmd/bugs/1150/
139      * #1150 "EmptyExpression" for valid statements!
140      */
141     @Test
142     public void testCaseAsIdentifier() {
143         ASTAstRoot rootNode = parse("function f(a){\n" + 
144                 "    a.case.flag = 1;\n" + 
145                 "    return;\n" + 
146                 "}");
147         ASTBlock block = rootNode.getFirstDescendantOfType(ASTBlock.class);
148         assertFalse(block.jjtGetChild(0) instanceof ASTEmptyExpression);
149         assertTrue(block.jjtGetChild(0) instanceof ASTExpressionStatement);
150         assertTrue(block.jjtGetChild(0).jjtGetChild(0) instanceof ASTAssignment);
151     }
152 
153     /**
154      * https://sourceforge.net/p/pmd/bugs/1045/
155      * #1045 //NOPMD not working (or not implemented) with ECMAscript 
156      */
157     @Test
158     public void testSuppresionComment() {
159         Ecmascript3Parser parser = new Ecmascript3Parser(new EcmascriptParserOptions());
160         Reader sourceCode = new StringReader("function(x) {\n"
161                 + "x = x; //NOPMD I know what I'm doing\n"
162                 + "}\n");
163         parser.parse("foo", sourceCode);
164         assertEquals(" I know what I'm doing", parser.getSuppressMap().get(2));
165         assertEquals(1, parser.getSuppressMap().size());
166 
167         EcmascriptParserOptions parserOptions = new EcmascriptParserOptions();
168         parserOptions.setSuppressMarker("FOOOO");
169         parser = new Ecmascript3Parser(parserOptions);
170         sourceCode = new StringReader("function(x) {\n"
171                 + "y = y; //NOPMD xyz\n"
172                 + "x = x; //FOOOO I know what I'm doing\n"
173                 + "}\n");
174         parser.parse("foo", sourceCode);
175         assertEquals(" I know what I'm doing", parser.getSuppressMap().get(3));
176         assertEquals(1, parser.getSuppressMap().size());
177     }
178 
179     /**
180      * #1191 Ecmascript fails to parse "void(0)"
181      */
182     @Test
183     public void testVoidKeyword() {
184         ASTAstRoot rootNode = parse("function f(matchFn, fieldval, n){\n" +
185                 "    return (matchFn)?(matcharray = eval(matchFn+\"('\"+fieldval+\"','\"+n.id+\"')\")):void(0);\n" +
186                 "}\n");
187         ASTUnaryExpression unary = rootNode.getFirstDescendantOfType(ASTUnaryExpression.class);
188         assertEquals("void", unary.getImage());
189     }
190 
191     /**
192      * #1192 Ecmascript fails to parse this operator " ^= "
193      */
194     @Test
195     public void testXorAssignment() {
196         ASTAstRoot rootNode = parse("function f() { var x = 2; x ^= 2; x &= 2; x |= 2; "
197                 + "x &&= true; x ||= false; x *= 2; x /= 2; x %= 2; x += 2; x -= 2; "
198                 + "x <<= 2; x >>= 2; x >>>= 2; }");
199         ASTAssignment infix = rootNode.getFirstDescendantOfType(ASTAssignment.class);
200         assertEquals("^=", infix.getImage());
201     }
202 }