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[] ARRAY_AND_FIELD_ACCESS = {
221 TokenTypes.INDEX_OP,
222 TokenTypes.DOT,
223 TokenTypes.LITERAL_NEW,
224 };
225
226
227 private static final int[] BITWISE_BINARY_OPERATORS = {
228 TokenTypes.BXOR,
229 TokenTypes.BOR,
230 TokenTypes.BAND,
231 };
232
233
234
235
236
237 private DetailAST parentToSkip;
238
239 private int assignDepth;
240
241
242
243
244 public UnnecessaryParenthesesCheck() {
245
246 }
247
248 @Override
249 public int[] getDefaultTokens() {
250 return new int[] {
251 TokenTypes.EXPR,
252 TokenTypes.IDENT,
253 TokenTypes.NUM_DOUBLE,
254 TokenTypes.NUM_FLOAT,
255 TokenTypes.NUM_INT,
256 TokenTypes.NUM_LONG,
257 TokenTypes.STRING_LITERAL,
258 TokenTypes.LITERAL_NULL,
259 TokenTypes.LITERAL_FALSE,
260 TokenTypes.LITERAL_TRUE,
261 TokenTypes.ASSIGN,
262 TokenTypes.BAND_ASSIGN,
263 TokenTypes.BOR_ASSIGN,
264 TokenTypes.BSR_ASSIGN,
265 TokenTypes.BXOR_ASSIGN,
266 TokenTypes.DIV_ASSIGN,
267 TokenTypes.MINUS_ASSIGN,
268 TokenTypes.MOD_ASSIGN,
269 TokenTypes.PLUS_ASSIGN,
270 TokenTypes.SL_ASSIGN,
271 TokenTypes.SR_ASSIGN,
272 TokenTypes.STAR_ASSIGN,
273 TokenTypes.LAMBDA,
274 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
275 TokenTypes.LAND,
276 TokenTypes.LOR,
277 TokenTypes.LITERAL_INSTANCEOF,
278 TokenTypes.GT,
279 TokenTypes.LT,
280 TokenTypes.GE,
281 TokenTypes.LE,
282 TokenTypes.EQUAL,
283 TokenTypes.NOT_EQUAL,
284 TokenTypes.UNARY_MINUS,
285 TokenTypes.UNARY_PLUS,
286 TokenTypes.INC,
287 TokenTypes.DEC,
288 TokenTypes.LNOT,
289 TokenTypes.BNOT,
290 TokenTypes.POST_INC,
291 TokenTypes.POST_DEC,
292 TokenTypes.INDEX_OP,
293 TokenTypes.DOT,
294 TokenTypes.TYPECAST,
295 };
296 }
297
298 @Override
299 public int[] getAcceptableTokens() {
300 return new int[] {
301 TokenTypes.EXPR,
302 TokenTypes.IDENT,
303 TokenTypes.NUM_DOUBLE,
304 TokenTypes.NUM_FLOAT,
305 TokenTypes.NUM_INT,
306 TokenTypes.NUM_LONG,
307 TokenTypes.STRING_LITERAL,
308 TokenTypes.LITERAL_NULL,
309 TokenTypes.LITERAL_FALSE,
310 TokenTypes.LITERAL_TRUE,
311 TokenTypes.ASSIGN,
312 TokenTypes.BAND_ASSIGN,
313 TokenTypes.BOR_ASSIGN,
314 TokenTypes.BSR_ASSIGN,
315 TokenTypes.BXOR_ASSIGN,
316 TokenTypes.DIV_ASSIGN,
317 TokenTypes.MINUS_ASSIGN,
318 TokenTypes.MOD_ASSIGN,
319 TokenTypes.PLUS_ASSIGN,
320 TokenTypes.SL_ASSIGN,
321 TokenTypes.SR_ASSIGN,
322 TokenTypes.STAR_ASSIGN,
323 TokenTypes.LAMBDA,
324 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
325 TokenTypes.LAND,
326 TokenTypes.LOR,
327 TokenTypes.LITERAL_INSTANCEOF,
328 TokenTypes.GT,
329 TokenTypes.LT,
330 TokenTypes.GE,
331 TokenTypes.LE,
332 TokenTypes.EQUAL,
333 TokenTypes.NOT_EQUAL,
334 TokenTypes.UNARY_MINUS,
335 TokenTypes.UNARY_PLUS,
336 TokenTypes.INC,
337 TokenTypes.DEC,
338 TokenTypes.LNOT,
339 TokenTypes.BNOT,
340 TokenTypes.POST_INC,
341 TokenTypes.POST_DEC,
342 TokenTypes.BXOR,
343 TokenTypes.BOR,
344 TokenTypes.BAND,
345 TokenTypes.QUESTION,
346 TokenTypes.INDEX_OP,
347 TokenTypes.DOT,
348 TokenTypes.LITERAL_NEW,
349 TokenTypes.TYPECAST,
350 };
351 }
352
353 @Override
354 public int[] getRequiredTokens() {
355
356 return CommonUtil.EMPTY_INT_ARRAY;
357 }
358
359
360 @Override
361 public void visitToken(DetailAST ast) {
362 final DetailAST parent = ast.getParent();
363
364 if (isLambdaSingleParameterSurrounded(ast)) {
365 log(ast, MSG_LAMBDA);
366 }
367 else if (ast.getType() == TokenTypes.QUESTION) {
368 getParenthesesChildrenAroundQuestion(ast)
369 .forEach(unnecessaryChild -> log(unnecessaryChild, MSG_EXPR));
370 }
371 else if (parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
372 final int type = ast.getType();
373 final boolean surrounded = isSurrounded(getSelfOrParentMethodCall(ast));
374
375 if (surrounded && type == TokenTypes.IDENT) {
376 parentToSkip = ast.getParent();
377 log(ast, MSG_IDENT, ast.getText());
378 }
379
380 else if (surrounded && TokenUtil.isOfType(type, LITERALS)) {
381 parentToSkip = ast.getParent();
382 logLiteral(ast, type);
383 }
384
385 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) {
386 assignDepth++;
387 final DetailAST last = ast.getLastChild();
388 if (last.getType() == TokenTypes.RPAREN) {
389 log(ast, MSG_ASSIGN);
390 }
391 }
392
393 else if (surrounded && type == TokenTypes.TYPECAST) {
394 logUnnecessaryTypeCast(ast);
395 }
396 }
397 }
398
399
400
401
402
403
404
405 private void logLiteral(DetailAST ast, int type) {
406 if (type == TokenTypes.STRING_LITERAL) {
407 log(ast, MSG_STRING,
408 chopString(ast.getText()));
409 }
410 else if (type == TokenTypes.TEXT_BLOCK_LITERAL_BEGIN) {
411
412
413 final String logString = QUOTE
414 + NEWLINE.matcher(
415 ast.getFirstChild().getText()).replaceAll("\\\\n")
416 + QUOTE;
417 log(ast, MSG_STRING, chopString(logString));
418 }
419 else {
420 log(ast, MSG_LITERAL, ast.getText());
421 }
422 }
423
424
425
426
427
428
429
430 private void logUnnecessaryTypeCast(DetailAST ast) {
431 final DetailAST parent = ast.getParent();
432 final int parentType = parent.getType();
433 final boolean isWrappedByOtherRule =
434 parentType == TokenTypes.EXPR
435 || TokenUtil.isOfType(parentType, ASSIGNMENTS);
436 final boolean isReceiverOfMemberAccess =
437 parentType == TokenTypes.DOT
438 || parentType == TokenTypes.INDEX_OP
439 || parentType == TokenTypes.METHOD_REF;
440 if (!isWrappedByOtherRule && !isReceiverOfMemberAccess) {
441 log(ast.getPreviousSibling(), MSG_EXPR);
442 }
443 }
444
445 @Override
446 public void leaveToken(DetailAST ast) {
447 final int type = ast.getType();
448 final DetailAST parent = ast.getParent();
449
450
451 if (type != TokenTypes.ASSIGN
452 || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
453 final DetailAST selfOrParentMethodCall = getSelfOrParentMethodCall(ast);
454 if (type == TokenTypes.EXPR) {
455 checkExpression(ast);
456 }
457 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) {
458 assignDepth--;
459 }
460 else if (isSurrounded(selfOrParentMethodCall) && unnecessaryParenAroundOperators(ast)) {
461 log(selfOrParentMethodCall.getPreviousSibling(), MSG_EXPR);
462 }
463 }
464 }
465
466
467
468
469
470
471
472 private static DetailAST getSelfOrParentMethodCall(DetailAST ast) {
473 DetailAST selfOrParent = ast;
474 if (ast.getParent().getType() == TokenTypes.METHOD_CALL) {
475 selfOrParent = ast.getParent();
476 }
477 return selfOrParent;
478 }
479
480
481
482
483
484
485
486
487
488 private static boolean isSurrounded(DetailAST ast) {
489 final DetailAST prev = ast.getPreviousSibling();
490 return prev != null && prev.getType() == TokenTypes.LPAREN;
491 }
492
493
494
495
496
497
498
499
500
501 private static boolean isExprSurrounded(DetailAST ast) {
502 return ast.getFirstChild().getType() == TokenTypes.LPAREN;
503 }
504
505
506
507
508
509
510
511 private void checkExpression(DetailAST ast) {
512
513
514
515 if (parentToSkip != ast && isExprSurrounded(ast)) {
516 if (ast.getParent().getType() == TokenTypes.LITERAL_RETURN) {
517 log(ast, MSG_RETURN);
518 }
519 else if (assignDepth >= 1) {
520 log(ast, MSG_ASSIGN);
521 }
522 else {
523 log(ast, MSG_EXPR);
524 }
525 }
526 }
527
528
529
530
531
532
533
534
535
536
537 private static boolean unnecessaryParenAroundOperators(DetailAST ast) {
538 final int type = ast.getType();
539 final boolean isConditionalOrRelational = TokenUtil.isOfType(type, CONDITIONAL_OPERATOR)
540 || TokenUtil.isOfType(type, RELATIONAL_OPERATOR);
541 final boolean isBitwise = TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS);
542 final boolean hasUnnecessaryParentheses;
543 if (isConditionalOrRelational) {
544 hasUnnecessaryParentheses = checkConditionalOrRelationalOperator(ast);
545 }
546 else if (isBitwise) {
547 hasUnnecessaryParentheses = checkBitwiseBinaryOperator(ast);
548 }
549 else if (TokenUtil.isOfType(type, ARRAY_AND_FIELD_ACCESS)) {
550 hasUnnecessaryParentheses = isNotFirstArgOfTernary(getSelfOrParentMethodCall(ast));
551 }
552 else {
553 hasUnnecessaryParentheses = TokenUtil.isOfType(type, UNARY_AND_POSTFIX)
554 && isBitWiseBinaryOrConditionalOrRelationalOperator(ast.getParent().getType());
555 }
556 return hasUnnecessaryParentheses;
557 }
558
559
560
561
562
563
564
565 private static boolean isNotFirstArgOfTernary(DetailAST ast) {
566 return ast.getParent().getType() != TokenTypes.QUESTION
567 || !ast.equals(ast.getParent().getFirstChild().getNextSibling());
568 }
569
570
571
572
573
574
575
576 private static boolean checkConditionalOrRelationalOperator(DetailAST ast) {
577 final int type = ast.getType();
578 final int parentType = ast.getParent().getType();
579 final boolean isParentEqualityOperator =
580 TokenUtil.isOfType(parentType, TokenTypes.EQUAL, TokenTypes.NOT_EQUAL);
581 final boolean result;
582 if (type == TokenTypes.LOR) {
583 result = !TokenUtil.isOfType(parentType, TokenTypes.LAND)
584 && !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS);
585 }
586 else if (type == TokenTypes.LAND) {
587 result = !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS);
588 }
589 else {
590 result = true;
591 }
592 return result && !isParentEqualityOperator
593 && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType);
594 }
595
596
597
598
599
600
601
602 private static boolean checkBitwiseBinaryOperator(DetailAST ast) {
603 final int type = ast.getType();
604 final int parentType = ast.getParent().getType();
605 final boolean result;
606 if (type == TokenTypes.BOR) {
607 result = !TokenUtil.isOfType(parentType, TokenTypes.BAND, TokenTypes.BXOR)
608 && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
609 }
610 else if (type == TokenTypes.BXOR) {
611 result = !TokenUtil.isOfType(parentType, TokenTypes.BAND)
612 && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
613 }
614
615 else {
616 result = !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
617 }
618 return result && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType);
619 }
620
621
622
623
624
625
626
627 private static boolean isBitWiseBinaryOrConditionalOrRelationalOperator(int type) {
628 return TokenUtil.isOfType(type, CONDITIONAL_OPERATOR)
629 || TokenUtil.isOfType(type, RELATIONAL_OPERATOR)
630 || TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS);
631 }
632
633
634
635
636
637
638
639
640
641 private static boolean isLambdaSingleParameterSurrounded(DetailAST ast) {
642 final DetailAST firstChild = ast.getFirstChild();
643 boolean result = false;
644 if (TokenUtil.isOfType(firstChild, TokenTypes.LPAREN)) {
645 final DetailAST parameters = firstChild.getNextSibling();
646 if (parameters.getChildCount(TokenTypes.PARAMETER_DEF) == 1
647 && !parameters.getFirstChild().findFirstToken(TokenTypes.TYPE).hasChildren()) {
648 result = true;
649 }
650 }
651 return result;
652 }
653
654
655
656
657
658
659
660
661
662 private static List<DetailAST> getParenthesesChildrenAroundQuestion(DetailAST questionToken) {
663 final List<DetailAST> surroundedChildren = new ArrayList<>();
664 DetailAST directChild = questionToken.getFirstChild();
665 while (directChild != null) {
666 if (directChild.getType() == TokenTypes.LPAREN
667 && !TokenUtil.isOfType(directChild.getNextSibling(), LITERALS)) {
668 surroundedChildren.add(directChild);
669 }
670 directChild = directChild.getNextSibling();
671 }
672 return Collections.unmodifiableList(surroundedChildren);
673 }
674
675
676
677
678
679
680
681
682
683
684 private static String chopString(String value) {
685 String result = value;
686 if (value.length() > MAX_QUOTED_LENGTH) {
687 result = value.substring(0, MAX_QUOTED_LENGTH) + "...\"";
688 }
689 return result;
690 }
691
692 }