1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.regex.Pattern;
26  
27  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
28  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29  import com.puppycrawl.tools.checkstyle.api.DetailAST;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
32  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100 
101 
102 
103 @FileStatefulCheck
104 public class UnnecessaryParenthesesCheck extends AbstractCheck {
105 
106     
107 
108 
109 
110     public static final String MSG_IDENT = "unnecessary.paren.ident";
111 
112     
113 
114 
115 
116     public static final String MSG_ASSIGN = "unnecessary.paren.assign";
117 
118     
119 
120 
121 
122     public static final String MSG_EXPR = "unnecessary.paren.expr";
123 
124     
125 
126 
127 
128     public static final String MSG_LITERAL = "unnecessary.paren.literal";
129 
130     
131 
132 
133 
134     public static final String MSG_STRING = "unnecessary.paren.string";
135 
136     
137 
138 
139 
140     public static final String MSG_RETURN = "unnecessary.paren.return";
141 
142     
143 
144 
145 
146     public static final String MSG_LAMBDA = "unnecessary.paren.lambda";
147 
148     
149 
150 
151     private static final Pattern NEWLINE = Pattern.compile("\\R");
152 
153     
154 
155 
156     private static final String QUOTE = "\"";
157 
158     
159     private static final int MAX_QUOTED_LENGTH = 25;
160 
161     
162     private static final int[] LITERALS = {
163         TokenTypes.NUM_DOUBLE,
164         TokenTypes.NUM_FLOAT,
165         TokenTypes.NUM_INT,
166         TokenTypes.NUM_LONG,
167         TokenTypes.STRING_LITERAL,
168         TokenTypes.LITERAL_NULL,
169         TokenTypes.LITERAL_FALSE,
170         TokenTypes.LITERAL_TRUE,
171         TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
172     };
173 
174     
175     private static final int[] ASSIGNMENTS = {
176         TokenTypes.ASSIGN,
177         TokenTypes.BAND_ASSIGN,
178         TokenTypes.BOR_ASSIGN,
179         TokenTypes.BSR_ASSIGN,
180         TokenTypes.BXOR_ASSIGN,
181         TokenTypes.DIV_ASSIGN,
182         TokenTypes.MINUS_ASSIGN,
183         TokenTypes.MOD_ASSIGN,
184         TokenTypes.PLUS_ASSIGN,
185         TokenTypes.SL_ASSIGN,
186         TokenTypes.SR_ASSIGN,
187         TokenTypes.STAR_ASSIGN,
188     };
189 
190     
191     private static final int[] CONDITIONAL_OPERATOR = {
192         TokenTypes.LOR,
193         TokenTypes.LAND,
194     };
195 
196     
197     private static final int[] RELATIONAL_OPERATOR = {
198         TokenTypes.LITERAL_INSTANCEOF,
199         TokenTypes.GT,
200         TokenTypes.LT,
201         TokenTypes.GE,
202         TokenTypes.LE,
203         TokenTypes.EQUAL,
204         TokenTypes.NOT_EQUAL,
205     };
206 
207     
208     private static final int[] UNARY_AND_POSTFIX = {
209         TokenTypes.UNARY_MINUS,
210         TokenTypes.UNARY_PLUS,
211         TokenTypes.INC,
212         TokenTypes.DEC,
213         TokenTypes.LNOT,
214         TokenTypes.BNOT,
215         TokenTypes.POST_INC,
216         TokenTypes.POST_DEC,
217     };
218 
219     
220     private static final int[] BITWISE_BINARY_OPERATORS = {
221         TokenTypes.BXOR,
222         TokenTypes.BOR,
223         TokenTypes.BAND,
224     };
225 
226     
227 
228 
229 
230     private DetailAST parentToSkip;
231     
232     private int assignDepth;
233 
234     @Override
235     public int[] getDefaultTokens() {
236         return new int[] {
237             TokenTypes.EXPR,
238             TokenTypes.IDENT,
239             TokenTypes.NUM_DOUBLE,
240             TokenTypes.NUM_FLOAT,
241             TokenTypes.NUM_INT,
242             TokenTypes.NUM_LONG,
243             TokenTypes.STRING_LITERAL,
244             TokenTypes.LITERAL_NULL,
245             TokenTypes.LITERAL_FALSE,
246             TokenTypes.LITERAL_TRUE,
247             TokenTypes.ASSIGN,
248             TokenTypes.BAND_ASSIGN,
249             TokenTypes.BOR_ASSIGN,
250             TokenTypes.BSR_ASSIGN,
251             TokenTypes.BXOR_ASSIGN,
252             TokenTypes.DIV_ASSIGN,
253             TokenTypes.MINUS_ASSIGN,
254             TokenTypes.MOD_ASSIGN,
255             TokenTypes.PLUS_ASSIGN,
256             TokenTypes.SL_ASSIGN,
257             TokenTypes.SR_ASSIGN,
258             TokenTypes.STAR_ASSIGN,
259             TokenTypes.LAMBDA,
260             TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
261             TokenTypes.LAND,
262             TokenTypes.LOR,
263             TokenTypes.LITERAL_INSTANCEOF,
264             TokenTypes.GT,
265             TokenTypes.LT,
266             TokenTypes.GE,
267             TokenTypes.LE,
268             TokenTypes.EQUAL,
269             TokenTypes.NOT_EQUAL,
270             TokenTypes.UNARY_MINUS,
271             TokenTypes.UNARY_PLUS,
272             TokenTypes.INC,
273             TokenTypes.DEC,
274             TokenTypes.LNOT,
275             TokenTypes.BNOT,
276             TokenTypes.POST_INC,
277             TokenTypes.POST_DEC,
278         };
279     }
280 
281     @Override
282     public int[] getAcceptableTokens() {
283         return new int[] {
284             TokenTypes.EXPR,
285             TokenTypes.IDENT,
286             TokenTypes.NUM_DOUBLE,
287             TokenTypes.NUM_FLOAT,
288             TokenTypes.NUM_INT,
289             TokenTypes.NUM_LONG,
290             TokenTypes.STRING_LITERAL,
291             TokenTypes.LITERAL_NULL,
292             TokenTypes.LITERAL_FALSE,
293             TokenTypes.LITERAL_TRUE,
294             TokenTypes.ASSIGN,
295             TokenTypes.BAND_ASSIGN,
296             TokenTypes.BOR_ASSIGN,
297             TokenTypes.BSR_ASSIGN,
298             TokenTypes.BXOR_ASSIGN,
299             TokenTypes.DIV_ASSIGN,
300             TokenTypes.MINUS_ASSIGN,
301             TokenTypes.MOD_ASSIGN,
302             TokenTypes.PLUS_ASSIGN,
303             TokenTypes.SL_ASSIGN,
304             TokenTypes.SR_ASSIGN,
305             TokenTypes.STAR_ASSIGN,
306             TokenTypes.LAMBDA,
307             TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
308             TokenTypes.LAND,
309             TokenTypes.LOR,
310             TokenTypes.LITERAL_INSTANCEOF,
311             TokenTypes.GT,
312             TokenTypes.LT,
313             TokenTypes.GE,
314             TokenTypes.LE,
315             TokenTypes.EQUAL,
316             TokenTypes.NOT_EQUAL,
317             TokenTypes.UNARY_MINUS,
318             TokenTypes.UNARY_PLUS,
319             TokenTypes.INC,
320             TokenTypes.DEC,
321             TokenTypes.LNOT,
322             TokenTypes.BNOT,
323             TokenTypes.POST_INC,
324             TokenTypes.POST_DEC,
325             TokenTypes.BXOR,
326             TokenTypes.BOR,
327             TokenTypes.BAND,
328             TokenTypes.QUESTION,
329         };
330     }
331 
332     @Override
333     public int[] getRequiredTokens() {
334         
335         return CommonUtil.EMPTY_INT_ARRAY;
336     }
337 
338     
339     @Override
340     public void visitToken(DetailAST ast) {
341         final DetailAST parent = ast.getParent();
342 
343         if (isLambdaSingleParameterSurrounded(ast)) {
344             log(ast, MSG_LAMBDA);
345         }
346         else if (ast.getType() == TokenTypes.QUESTION) {
347             getParenthesesChildrenAroundQuestion(ast)
348                 .forEach(unnecessaryChild -> log(unnecessaryChild, MSG_EXPR));
349         }
350         else if (parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
351             final int type = ast.getType();
352             final boolean surrounded = isSurrounded(ast);
353             
354             if (surrounded && type == TokenTypes.IDENT) {
355                 parentToSkip = ast.getParent();
356                 log(ast, MSG_IDENT, ast.getText());
357             }
358             
359             else if (surrounded && TokenUtil.isOfType(type, LITERALS)) {
360                 parentToSkip = ast.getParent();
361                 if (type == TokenTypes.STRING_LITERAL) {
362                     log(ast, MSG_STRING,
363                         chopString(ast.getText()));
364                 }
365                 else if (type == TokenTypes.TEXT_BLOCK_LITERAL_BEGIN) {
366                     
367                     
368                     final String logString = QUOTE
369                         + NEWLINE.matcher(
370                             ast.getFirstChild().getText()).replaceAll("\\\\n")
371                         + QUOTE;
372                     log(ast, MSG_STRING, chopString(logString));
373                 }
374                 else {
375                     log(ast, MSG_LITERAL, ast.getText());
376                 }
377             }
378             
379             else if (TokenUtil.isOfType(type, ASSIGNMENTS)) {
380                 assignDepth++;
381                 final DetailAST last = ast.getLastChild();
382                 if (last.getType() == TokenTypes.RPAREN) {
383                     log(ast, MSG_ASSIGN);
384                 }
385             }
386         }
387     }
388 
389     @Override
390     public void leaveToken(DetailAST ast) {
391         final int type = ast.getType();
392         final DetailAST parent = ast.getParent();
393 
394         
395         if (type != TokenTypes.ASSIGN
396             || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
397             if (type == TokenTypes.EXPR) {
398                 checkExpression(ast);
399             }
400             else if (TokenUtil.isOfType(type, ASSIGNMENTS)) {
401                 assignDepth--;
402             }
403             else if (isSurrounded(ast) && unnecessaryParenAroundOperators(ast)) {
404                 log(ast.getPreviousSibling(), MSG_EXPR);
405             }
406         }
407     }
408 
409     
410 
411 
412 
413 
414 
415 
416 
417     private static boolean isSurrounded(DetailAST ast) {
418         final DetailAST prev = ast.getPreviousSibling();
419         final DetailAST parent = ast.getParent();
420         final boolean isPreviousSiblingLeftParenthesis = prev != null
421                 && prev.getType() == TokenTypes.LPAREN;
422         final boolean isMethodCallWithUnnecessaryParenthesis =
423                 parent.getType() == TokenTypes.METHOD_CALL
424                 && parent.getPreviousSibling() != null
425                 && parent.getPreviousSibling().getType() == TokenTypes.LPAREN;
426         return isPreviousSiblingLeftParenthesis || isMethodCallWithUnnecessaryParenthesis;
427     }
428 
429     
430 
431 
432 
433 
434 
435 
436 
437     private static boolean isExprSurrounded(DetailAST ast) {
438         return ast.getFirstChild().getType() == TokenTypes.LPAREN;
439     }
440 
441     
442 
443 
444 
445 
446 
447     private void checkExpression(DetailAST ast) {
448         
449         
450         
451         if (parentToSkip != ast && isExprSurrounded(ast)) {
452             if (ast.getParent().getType() == TokenTypes.LITERAL_RETURN) {
453                 log(ast, MSG_RETURN);
454             }
455             else if (assignDepth >= 1) {
456                 log(ast, MSG_ASSIGN);
457             }
458             else {
459                 log(ast, MSG_EXPR);
460             }
461         }
462     }
463 
464     
465 
466 
467 
468 
469 
470 
471 
472 
473     private static boolean unnecessaryParenAroundOperators(DetailAST ast) {
474         final int type = ast.getType();
475         final boolean isConditionalOrRelational = TokenUtil.isOfType(type, CONDITIONAL_OPERATOR)
476                         || TokenUtil.isOfType(type, RELATIONAL_OPERATOR);
477         final boolean isBitwise = TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS);
478         final boolean hasUnnecessaryParentheses;
479         if (isConditionalOrRelational) {
480             hasUnnecessaryParentheses = checkConditionalOrRelationalOperator(ast);
481         }
482         else if (isBitwise) {
483             hasUnnecessaryParentheses = checkBitwiseBinaryOperator(ast);
484         }
485         else {
486             hasUnnecessaryParentheses = TokenUtil.isOfType(type, UNARY_AND_POSTFIX)
487                     && isBitWiseBinaryOrConditionalOrRelationalOperator(ast.getParent().getType());
488         }
489         return hasUnnecessaryParentheses;
490     }
491 
492     
493 
494 
495 
496 
497 
498     private static boolean checkConditionalOrRelationalOperator(DetailAST ast) {
499         final int type = ast.getType();
500         final int parentType = ast.getParent().getType();
501         final boolean isParentEqualityOperator =
502                 TokenUtil.isOfType(parentType, TokenTypes.EQUAL, TokenTypes.NOT_EQUAL);
503         final boolean result;
504         if (type == TokenTypes.LOR) {
505             result = !TokenUtil.isOfType(parentType, TokenTypes.LAND)
506                     && !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS);
507         }
508         else if (type == TokenTypes.LAND) {
509             result = !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS);
510         }
511         else {
512             result = true;
513         }
514         return result && !isParentEqualityOperator
515                 && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType);
516     }
517 
518     
519 
520 
521 
522 
523 
524     private static boolean checkBitwiseBinaryOperator(DetailAST ast) {
525         final int type = ast.getType();
526         final int parentType = ast.getParent().getType();
527         final boolean result;
528         if (type == TokenTypes.BOR) {
529             result = !TokenUtil.isOfType(parentType, TokenTypes.BAND, TokenTypes.BXOR)
530                     && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
531         }
532         else if (type == TokenTypes.BXOR) {
533             result = !TokenUtil.isOfType(parentType, TokenTypes.BAND)
534                     && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
535         }
536         
537         else {
538             result = !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
539         }
540         return result && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType);
541     }
542 
543     
544 
545 
546 
547 
548 
549     private static boolean isBitWiseBinaryOrConditionalOrRelationalOperator(int type) {
550         return TokenUtil.isOfType(type, CONDITIONAL_OPERATOR)
551                 || TokenUtil.isOfType(type, RELATIONAL_OPERATOR)
552                 || TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS);
553     }
554 
555     
556 
557 
558 
559 
560 
561 
562 
563     private static boolean isLambdaSingleParameterSurrounded(DetailAST ast) {
564         final DetailAST firstChild = ast.getFirstChild();
565         boolean result = false;
566         if (TokenUtil.isOfType(firstChild, TokenTypes.LPAREN)) {
567             final DetailAST parameters = firstChild.getNextSibling();
568             if (parameters.getChildCount(TokenTypes.PARAMETER_DEF) == 1
569                     && !parameters.getFirstChild().findFirstToken(TokenTypes.TYPE).hasChildren()) {
570                 result = true;
571             }
572         }
573         return result;
574     }
575 
576     
577 
578 
579 
580 
581 
582 
583 
584     private static List<DetailAST> getParenthesesChildrenAroundQuestion(DetailAST questionToken) {
585         final List<DetailAST> surroundedChildren = new ArrayList<>();
586         DetailAST directChild = questionToken.getFirstChild();
587         while (directChild != null) {
588             if (directChild.getType() == TokenTypes.LPAREN
589                     && !TokenUtil.isOfType(directChild.getNextSibling(), LITERALS)) {
590                 surroundedChildren.add(directChild);
591             }
592             directChild = directChild.getNextSibling();
593         }
594         return Collections.unmodifiableList(surroundedChildren);
595     }
596 
597     
598 
599 
600 
601 
602 
603 
604 
605 
606     private static String chopString(String value) {
607         String result = value;
608         if (value.length() > MAX_QUOTED_LENGTH) {
609             result = value.substring(0, MAX_QUOTED_LENGTH) + "...\"";
610         }
611         return result;
612     }
613 
614 }