View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocContentLocationCheck.MSG_JAVADOC_CONTENT_SECOND_LINE;
24  
25  import java.io.File;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.List;
29  import java.util.Optional;
30  
31  import org.junit.jupiter.api.Test;
32  
33  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
34  import com.puppycrawl.tools.checkstyle.api.DetailAST;
35  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
36  import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocContentLocationCheck;
37  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
38  
39  public class JavaParserTest extends AbstractModuleTestSupport {
40  
41      @Override
42      protected String getPackageLocation() {
43          return "com/puppycrawl/tools/checkstyle/javaparser";
44      }
45  
46      @Test
47      public void testIsProperUtilsClass() throws ReflectiveOperationException {
48          assertWithMessage("Constructor is not private")
49                  .that(TestUtil.isUtilsClassHasPrivateConstructor(JavaParser.class))
50                  .isTrue();
51      }
52  
53      @Test
54      public void testNullRootWithComments() {
55          assertWithMessage("Invalid return root")
56              .that(JavaParser.appendHiddenCommentNodes(null))
57              .isNull();
58      }
59  
60      @Test
61      public void testAppendHiddenBlockCommentNodes() throws Exception {
62          final DetailAST root =
63              JavaParser.parseFile(new File(getPath("InputJavaParserHiddenComments.java")),
64                  JavaParser.Options.WITH_COMMENTS);
65  
66          final Optional<DetailAST> blockComment = TestUtil.findTokenInAstByPredicate(root,
67              ast -> ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN);
68  
69          assertWithMessage("Block comment should be present")
70                  .that(blockComment.isPresent())
71                  .isTrue();
72  
73          final DetailAST comment = blockComment.orElseThrow();
74  
75          assertWithMessage("Unexpected line number")
76              .that(comment.getLineNo())
77              .isEqualTo(3);
78          assertWithMessage("Unexpected column number")
79              .that(comment.getColumnNo())
80              .isEqualTo(0);
81          assertWithMessage("Unexpected comment content")
82              .that(comment.getText())
83              .isEqualTo("/*");
84  
85          final DetailAST commentContent = comment.getFirstChild();
86          final DetailAST commentEnd = comment.getLastChild();
87  
88          assertWithMessage("Unexpected line number")
89              .that(commentContent.getLineNo())
90              .isEqualTo(3);
91          assertWithMessage("Unexpected column number")
92              .that(commentContent.getColumnNo())
93              .isEqualTo(2);
94          assertWithMessage("Unexpected line number")
95              .that(commentEnd.getLineNo())
96              .isEqualTo(9);
97          assertWithMessage("Unexpected column number")
98              .that(commentEnd.getColumnNo())
99              .isEqualTo(1);
100     }
101 
102     @Test
103     public void testAppendHiddenSingleLineCommentNodes() throws Exception {
104         final DetailAST root =
105             JavaParser.parseFile(new File(getPath("InputJavaParserHiddenComments.java")),
106                 JavaParser.Options.WITH_COMMENTS);
107 
108         final Optional<DetailAST> singleLineComment = TestUtil.findTokenInAstByPredicate(root,
109             ast -> ast.getType() == TokenTypes.SINGLE_LINE_COMMENT);
110         assertWithMessage("Single line comment should be present")
111             .that(singleLineComment.isPresent())
112             .isTrue();
113 
114         final DetailAST comment = singleLineComment.orElseThrow();
115 
116         assertWithMessage("Unexpected line number")
117             .that(comment.getLineNo())
118             .isEqualTo(13);
119         assertWithMessage("Unexpected column number")
120             .that(comment.getColumnNo())
121             .isEqualTo(0);
122         assertWithMessage("Unexpected comment content")
123             .that(comment.getText())
124             .isEqualTo("//");
125 
126         final DetailAST commentContent = comment.getFirstChild();
127 
128         assertWithMessage("Unexpected token type")
129             .that(commentContent.getType())
130             .isEqualTo(TokenTypes.COMMENT_CONTENT);
131         assertWithMessage("Unexpected line number")
132             .that(commentContent.getLineNo())
133             .isEqualTo(13);
134         assertWithMessage("Unexpected column number")
135             .that(commentContent.getColumnNo())
136             .isEqualTo(2);
137         assertWithMessage("Unexpected comment content")
138                 .that(commentContent.getText())
139                 .startsWith(" inline comment");
140     }
141 
142     @Test
143     public void testAppendHiddenSingleLineCommentNodes2() throws Exception {
144         final DetailAST root =
145             JavaParser.parseFile(new File(getPath("InputJavaParserHiddenComments2.java")),
146                 JavaParser.Options.WITH_COMMENTS);
147 
148         final Optional<DetailAST> singleLineComment = TestUtil.findTokenInAstByPredicate(root,
149             ast -> ast.getType() == TokenTypes.SINGLE_LINE_COMMENT);
150         assertWithMessage("Single line comment should be present")
151                 .that(singleLineComment.isPresent())
152                 .isTrue();
153 
154         final DetailAST comment = singleLineComment.orElseThrow();
155 
156         assertWithMessage("Unexpected line number")
157             .that(comment.getLineNo())
158             .isEqualTo(1);
159         assertWithMessage("Unexpected column number")
160             .that(comment.getColumnNo())
161             .isEqualTo(4);
162         assertWithMessage("Unexpected comment content")
163             .that(comment.getText())
164             .isEqualTo("//");
165 
166         final DetailAST commentContent = comment.getFirstChild();
167 
168         assertWithMessage("Unexpected token type")
169             .that(commentContent.getType())
170             .isEqualTo(TokenTypes.COMMENT_CONTENT);
171         assertWithMessage("Unexpected line number")
172             .that(commentContent.getLineNo())
173             .isEqualTo(1);
174         assertWithMessage("Unexpected column number")
175             .that(commentContent.getColumnNo())
176             .isEqualTo(6);
177         assertWithMessage("Unexpected comment content")
178                 .that(commentContent.getText())
179                 .startsWith(" indented comment");
180     }
181 
182     @Test
183     public void testDontAppendCommentNodes() throws Exception {
184         final DetailAST root =
185             JavaParser.parseFile(new File(getPath("InputJavaParserHiddenComments.java")),
186                 JavaParser.Options.WITHOUT_COMMENTS);
187 
188         final Optional<DetailAST> singleLineComment = TestUtil.findTokenInAstByPredicate(root,
189             ast -> ast.getType() == TokenTypes.SINGLE_LINE_COMMENT);
190         assertWithMessage("Single line comment should be present")
191                 .that(singleLineComment.isPresent())
192                 .isFalse();
193     }
194 
195     @Test
196     public void testParseException() throws Exception {
197         final File input = new File(getNonCompilablePath("InputJavaParser.java"));
198         try {
199             JavaParser.parseFile(input, JavaParser.Options.WITH_COMMENTS);
200             assertWithMessage("exception expected").fail();
201         }
202         catch (CheckstyleException ex) {
203             assertWithMessage("Invalid exception message")
204                 .that(ex.toString())
205                 .isEqualTo(CheckstyleException.class.getName()
206                             + ": IllegalStateException occurred while parsing file "
207                             + input.getAbsolutePath() + ".");
208             assertWithMessage("Invalid class")
209                 .that(ex.getCause())
210                 .isInstanceOf(IllegalStateException.class);
211             assertWithMessage("Invalid exception message")
212                 .that(ex.getCause().toString())
213                 .isEqualTo(IllegalStateException.class.getName()
214                             + ": 2:0: no viable alternative at input 'classD'");
215         }
216     }
217 
218     @Test
219     public void testComments() throws Exception {
220         final DetailAST root =
221             JavaParser.parseFile(new File(getPath("InputJavaParserHiddenComments3.java")),
222                 JavaParser.Options.WITH_COMMENTS);
223         final CountComments counter = new CountComments(root);
224 
225         assertWithMessage("Invalid line comments")
226             .that(counter.lineComments)
227             .isEqualTo(Arrays.asList("1,4", "6,4", "9,0"));
228         assertWithMessage("Invalid block comments")
229             .that(counter.blockComments)
230             .isEqualTo(Arrays.asList("5,4", "8,0"));
231     }
232 
233     @Test
234     public void testJava14TextBlocks() throws Exception {
235         final DetailAST root =
236             JavaParser.parseFile(new File(
237                     getNonCompilablePath("InputJavaParserTextBlocks.java")),
238                 JavaParser.Options.WITHOUT_COMMENTS);
239 
240         final Optional<DetailAST> textBlockContent = TestUtil.findTokenInAstByPredicate(root,
241             ast -> ast.getType() == TokenTypes.TEXT_BLOCK_CONTENT);
242 
243         assertWithMessage("Text block content should be present")
244                 .that(textBlockContent.isPresent())
245                 .isTrue();
246 
247         final DetailAST content = textBlockContent.orElseThrow();
248         final String expectedContents = "\n                 string";
249 
250         assertWithMessage("Unexpected line number")
251             .that(content.getLineNo())
252             .isEqualTo(5);
253         assertWithMessage("Unexpected column number")
254             .that(content.getColumnNo())
255             .isEqualTo(32);
256         assertWithMessage("Unexpected text block content")
257             .that(content.getText())
258             .isEqualTo(expectedContents);
259     }
260 
261     @Test
262     public void testNoFreezeOnDeeplyNestedLambdas() throws Exception {
263         final File file =
264                 new File(getNonCompilablePath("InputJavaParserNoFreezeOnDeeplyNestedLambdas.java"));
265         assertWithMessage("File parsing should complete successfully.")
266                 .that(JavaParser.parseFile(file, JavaParser.Options.WITH_COMMENTS))
267                 .isNotNull();
268     }
269 
270     @Test
271     public void testFullJavaIdentifierSupport() throws Exception {
272         final File file =
273                 new File(getNonCompilablePath("InputJavaParserFullJavaIdentifierSupport.java"));
274         assertWithMessage("File parsing should complete successfully.")
275                 .that(JavaParser.parseFile(file, JavaParser.Options.WITH_COMMENTS))
276                 .isNotNull();
277     }
278 
279     @Test
280     public void testReturnValueOfAppendHiddenCommentNodes()
281             throws Exception {
282         final String[] expected = {
283             "9:1: " + getCheckMessage(JavadocContentLocationCheck.class,
284                     MSG_JAVADOC_CONTENT_SECOND_LINE),
285         };
286         verifyWithInlineConfigParser(
287                 getPath("InputJavaParserHiddenComments4.java"), expected);
288     }
289 
290     private static final class CountComments {
291         private final List<String> lineComments = new ArrayList<>();
292         private final List<String> blockComments = new ArrayList<>();
293 
294         private CountComments(DetailAST root) {
295             forEachChild(root);
296         }
297 
298         private void forEachChild(DetailAST root) {
299             for (DetailAST ast = root; ast != null; ast = ast.getNextSibling()) {
300                 if (ast.getType() == TokenTypes.SINGLE_LINE_COMMENT) {
301                     lineComments.add(ast.getLineNo() + "," + ast.getColumnNo());
302                 }
303                 else if (ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) {
304                     blockComments.add(ast.getLineNo() + "," + ast.getColumnNo());
305                 }
306 
307                 forEachChild(ast.getFirstChild());
308             }
309         }
310     }
311 }