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 }