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.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 * <div> 36 * Checks if unnecessary parentheses are used in a statement or expression. 37 * The check will flag the following with warnings: 38 * </div> 39 * <pre> 40 * return (x); // parens around identifier 41 * return (x + 1); // parens around return value 42 * int x = (y / 2 + 1); // parens around assignment rhs 43 * for (int i = (0); i < 10; i++) { // parens around literal 44 * t -= (z + 1); // parens around assignment rhs 45 * boolean a = (x > 7 && y > 5) // parens around expression 46 * || z < 9; 47 * boolean b = (~a) > -27 // parens around ~a 48 * && (a-- < 30); // parens around expression 49 * </pre> 50 * 51 * <p> 52 * The check is not "type aware", that is to say, it can't tell if parentheses 53 * are unnecessary based on the types in an expression. The check is partially aware about 54 * operator precedence but unaware about operator associativity. 55 * It won't catch cases such as: 56 * </p> 57 * <pre> 58 * int x = (a + b) + c; // 1st Case 59 * boolean p = true; // 2nd Case 60 * int q = 4; 61 * int r = 3; 62 * if (p == (q <= r)) {}</pre> 63 * 64 * <p> 65 * In the first case, given that <em>a</em>, <em>b</em>, and <em>c</em> are 66 * all {@code int} variables, the parentheses around {@code a + b} 67 * are not needed. 68 * In the second case, parentheses are required as <em>q</em>, <em>r</em> are 69 * of type {@code int} and <em>p</em> is of type {@code boolean} 70 * and removing parentheses will give a compile-time error. Even if <em>q</em> 71 * and <em>r</em> were {@code boolean} still there will be no violation 72 * raised as check is not "type aware". 73 * </p> 74 * 75 * <p> 76 * The partial support for operator precedence includes cases of the following type: 77 * </p> 78 * <pre> 79 * boolean a = true, b = true; 80 * boolean c = false, d = false; 81 * if ((a && b) || c) { // violation, unnecessary paren 82 * } 83 * if (a && (b || c)) { // ok 84 * } 85 * if ((a == b) && c) { // violation, unnecessary paren 86 * } 87 * String e = "e"; 88 * if ((e instanceof String) && a || b) { // violation, unnecessary paren 89 * } 90 * int f = 0; 91 * int g = 0; 92 * if (!(f >= g) // ok 93 * && (g > f)) { // violation, unnecessary paren 94 * } 95 * if ((++f) > g && a) { // violation, unnecessary paren 96 * } 97 * </pre> 98 * <ul> 99 * <li> 100 * Property {@code tokens} - tokens to check 101 * Type is {@code java.lang.String[]}. 102 * Validation type is {@code tokenSet}. 103 * Default value is: 104 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR"> 105 * EXPR</a>, 106 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#IDENT"> 107 * IDENT</a>, 108 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_DOUBLE"> 109 * NUM_DOUBLE</a>, 110 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_FLOAT"> 111 * NUM_FLOAT</a>, 112 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_INT"> 113 * NUM_INT</a>, 114 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_LONG"> 115 * NUM_LONG</a>, 116 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STRING_LITERAL"> 117 * STRING_LITERAL</a>, 118 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NULL"> 119 * LITERAL_NULL</a>, 120 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FALSE"> 121 * LITERAL_FALSE</a>, 122 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRUE"> 123 * LITERAL_TRUE</a>, 124 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ASSIGN"> 125 * ASSIGN</a>, 126 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND_ASSIGN"> 127 * BAND_ASSIGN</a>, 128 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR_ASSIGN"> 129 * BOR_ASSIGN</a>, 130 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR_ASSIGN"> 131 * BSR_ASSIGN</a>, 132 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR_ASSIGN"> 133 * BXOR_ASSIGN</a>, 134 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV_ASSIGN"> 135 * DIV_ASSIGN</a>, 136 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS_ASSIGN"> 137 * MINUS_ASSIGN</a>, 138 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD_ASSIGN"> 139 * MOD_ASSIGN</a>, 140 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS_ASSIGN"> 141 * PLUS_ASSIGN</a>, 142 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL_ASSIGN"> 143 * SL_ASSIGN</a>, 144 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR_ASSIGN"> 145 * SR_ASSIGN</a>, 146 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR_ASSIGN"> 147 * STAR_ASSIGN</a>, 148 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA"> 149 * LAMBDA</a>, 150 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TEXT_BLOCK_LITERAL_BEGIN"> 151 * TEXT_BLOCK_LITERAL_BEGIN</a>, 152 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAND"> 153 * LAND</a>, 154 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LOR"> 155 * LOR</a>, 156 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_INSTANCEOF"> 157 * LITERAL_INSTANCEOF</a>, 158 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GT"> 159 * GT</a>, 160 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LT"> 161 * LT</a>, 162 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GE"> 163 * GE</a>, 164 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LE"> 165 * LE</a>, 166 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EQUAL"> 167 * EQUAL</a>, 168 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NOT_EQUAL"> 169 * NOT_EQUAL</a>, 170 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#UNARY_MINUS"> 171 * UNARY_MINUS</a>, 172 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#UNARY_PLUS"> 173 * UNARY_PLUS</a>, 174 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INC"> 175 * INC</a>, 176 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DEC"> 177 * DEC</a>, 178 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LNOT"> 179 * LNOT</a>, 180 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BNOT"> 181 * BNOT</a>, 182 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_INC"> 183 * POST_INC</a>, 184 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_DEC"> 185 * POST_DEC</a>. 186 * </li> 187 * </ul> 188 * 189 * <p> 190 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 191 * </p> 192 * 193 * <p> 194 * Violation Message Keys: 195 * </p> 196 * <ul> 197 * <li> 198 * {@code unnecessary.paren.assign} 199 * </li> 200 * <li> 201 * {@code unnecessary.paren.expr} 202 * </li> 203 * <li> 204 * {@code unnecessary.paren.ident} 205 * </li> 206 * <li> 207 * {@code unnecessary.paren.lambda} 208 * </li> 209 * <li> 210 * {@code unnecessary.paren.literal} 211 * </li> 212 * <li> 213 * {@code unnecessary.paren.return} 214 * </li> 215 * <li> 216 * {@code unnecessary.paren.string} 217 * </li> 218 * </ul> 219 * 220 * @since 3.4 221 */ 222 @FileStatefulCheck 223 public class UnnecessaryParenthesesCheck extends AbstractCheck { 224 225 /** 226 * A key is pointing to the warning message text in "messages.properties" 227 * file. 228 */ 229 public static final String MSG_IDENT = "unnecessary.paren.ident"; 230 231 /** 232 * A key is pointing to the warning message text in "messages.properties" 233 * file. 234 */ 235 public static final String MSG_ASSIGN = "unnecessary.paren.assign"; 236 237 /** 238 * A key is pointing to the warning message text in "messages.properties" 239 * file. 240 */ 241 public static final String MSG_EXPR = "unnecessary.paren.expr"; 242 243 /** 244 * A key is pointing to the warning message text in "messages.properties" 245 * file. 246 */ 247 public static final String MSG_LITERAL = "unnecessary.paren.literal"; 248 249 /** 250 * A key is pointing to the warning message text in "messages.properties" 251 * file. 252 */ 253 public static final String MSG_STRING = "unnecessary.paren.string"; 254 255 /** 256 * A key is pointing to the warning message text in "messages.properties" 257 * file. 258 */ 259 public static final String MSG_RETURN = "unnecessary.paren.return"; 260 261 /** 262 * A key is pointing to the warning message text in "messages.properties" 263 * file. 264 */ 265 public static final String MSG_LAMBDA = "unnecessary.paren.lambda"; 266 267 /** 268 * Compiled pattern used to match newline control characters, for replacement. 269 */ 270 private static final Pattern NEWLINE = Pattern.compile("\\R"); 271 272 /** 273 * String used to amend TEXT_BLOCK_CONTENT so that it matches STRING_LITERAL. 274 */ 275 private static final String QUOTE = "\""; 276 277 /** The maximum string length before we chop the string. */ 278 private static final int MAX_QUOTED_LENGTH = 25; 279 280 /** Token types for literals. */ 281 private static final int[] LITERALS = { 282 TokenTypes.NUM_DOUBLE, 283 TokenTypes.NUM_FLOAT, 284 TokenTypes.NUM_INT, 285 TokenTypes.NUM_LONG, 286 TokenTypes.STRING_LITERAL, 287 TokenTypes.LITERAL_NULL, 288 TokenTypes.LITERAL_FALSE, 289 TokenTypes.LITERAL_TRUE, 290 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 291 }; 292 293 /** Token types for assignment operations. */ 294 private static final int[] ASSIGNMENTS = { 295 TokenTypes.ASSIGN, 296 TokenTypes.BAND_ASSIGN, 297 TokenTypes.BOR_ASSIGN, 298 TokenTypes.BSR_ASSIGN, 299 TokenTypes.BXOR_ASSIGN, 300 TokenTypes.DIV_ASSIGN, 301 TokenTypes.MINUS_ASSIGN, 302 TokenTypes.MOD_ASSIGN, 303 TokenTypes.PLUS_ASSIGN, 304 TokenTypes.SL_ASSIGN, 305 TokenTypes.SR_ASSIGN, 306 TokenTypes.STAR_ASSIGN, 307 }; 308 309 /** Token types for conditional operators. */ 310 private static final int[] CONDITIONAL_OPERATOR = { 311 TokenTypes.LOR, 312 TokenTypes.LAND, 313 }; 314 315 /** Token types for relation operator. */ 316 private static final int[] RELATIONAL_OPERATOR = { 317 TokenTypes.LITERAL_INSTANCEOF, 318 TokenTypes.GT, 319 TokenTypes.LT, 320 TokenTypes.GE, 321 TokenTypes.LE, 322 TokenTypes.EQUAL, 323 TokenTypes.NOT_EQUAL, 324 }; 325 326 /** Token types for unary and postfix operators. */ 327 private static final int[] UNARY_AND_POSTFIX = { 328 TokenTypes.UNARY_MINUS, 329 TokenTypes.UNARY_PLUS, 330 TokenTypes.INC, 331 TokenTypes.DEC, 332 TokenTypes.LNOT, 333 TokenTypes.BNOT, 334 TokenTypes.POST_INC, 335 TokenTypes.POST_DEC, 336 }; 337 338 /** Token types for bitwise binary operator. */ 339 private static final int[] BITWISE_BINARY_OPERATORS = { 340 TokenTypes.BXOR, 341 TokenTypes.BOR, 342 TokenTypes.BAND, 343 }; 344 345 /** 346 * Used to test if logging a warning in a parent node may be skipped 347 * because a warning was already logged on an immediate child node. 348 */ 349 private DetailAST parentToSkip; 350 /** Depth of nested assignments. Normally this will be 0 or 1. */ 351 private int assignDepth; 352 353 @Override 354 public int[] getDefaultTokens() { 355 return new int[] { 356 TokenTypes.EXPR, 357 TokenTypes.IDENT, 358 TokenTypes.NUM_DOUBLE, 359 TokenTypes.NUM_FLOAT, 360 TokenTypes.NUM_INT, 361 TokenTypes.NUM_LONG, 362 TokenTypes.STRING_LITERAL, 363 TokenTypes.LITERAL_NULL, 364 TokenTypes.LITERAL_FALSE, 365 TokenTypes.LITERAL_TRUE, 366 TokenTypes.ASSIGN, 367 TokenTypes.BAND_ASSIGN, 368 TokenTypes.BOR_ASSIGN, 369 TokenTypes.BSR_ASSIGN, 370 TokenTypes.BXOR_ASSIGN, 371 TokenTypes.DIV_ASSIGN, 372 TokenTypes.MINUS_ASSIGN, 373 TokenTypes.MOD_ASSIGN, 374 TokenTypes.PLUS_ASSIGN, 375 TokenTypes.SL_ASSIGN, 376 TokenTypes.SR_ASSIGN, 377 TokenTypes.STAR_ASSIGN, 378 TokenTypes.LAMBDA, 379 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 380 TokenTypes.LAND, 381 TokenTypes.LOR, 382 TokenTypes.LITERAL_INSTANCEOF, 383 TokenTypes.GT, 384 TokenTypes.LT, 385 TokenTypes.GE, 386 TokenTypes.LE, 387 TokenTypes.EQUAL, 388 TokenTypes.NOT_EQUAL, 389 TokenTypes.UNARY_MINUS, 390 TokenTypes.UNARY_PLUS, 391 TokenTypes.INC, 392 TokenTypes.DEC, 393 TokenTypes.LNOT, 394 TokenTypes.BNOT, 395 TokenTypes.POST_INC, 396 TokenTypes.POST_DEC, 397 }; 398 } 399 400 @Override 401 public int[] getAcceptableTokens() { 402 return new int[] { 403 TokenTypes.EXPR, 404 TokenTypes.IDENT, 405 TokenTypes.NUM_DOUBLE, 406 TokenTypes.NUM_FLOAT, 407 TokenTypes.NUM_INT, 408 TokenTypes.NUM_LONG, 409 TokenTypes.STRING_LITERAL, 410 TokenTypes.LITERAL_NULL, 411 TokenTypes.LITERAL_FALSE, 412 TokenTypes.LITERAL_TRUE, 413 TokenTypes.ASSIGN, 414 TokenTypes.BAND_ASSIGN, 415 TokenTypes.BOR_ASSIGN, 416 TokenTypes.BSR_ASSIGN, 417 TokenTypes.BXOR_ASSIGN, 418 TokenTypes.DIV_ASSIGN, 419 TokenTypes.MINUS_ASSIGN, 420 TokenTypes.MOD_ASSIGN, 421 TokenTypes.PLUS_ASSIGN, 422 TokenTypes.SL_ASSIGN, 423 TokenTypes.SR_ASSIGN, 424 TokenTypes.STAR_ASSIGN, 425 TokenTypes.LAMBDA, 426 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 427 TokenTypes.LAND, 428 TokenTypes.LOR, 429 TokenTypes.LITERAL_INSTANCEOF, 430 TokenTypes.GT, 431 TokenTypes.LT, 432 TokenTypes.GE, 433 TokenTypes.LE, 434 TokenTypes.EQUAL, 435 TokenTypes.NOT_EQUAL, 436 TokenTypes.UNARY_MINUS, 437 TokenTypes.UNARY_PLUS, 438 TokenTypes.INC, 439 TokenTypes.DEC, 440 TokenTypes.LNOT, 441 TokenTypes.BNOT, 442 TokenTypes.POST_INC, 443 TokenTypes.POST_DEC, 444 TokenTypes.BXOR, 445 TokenTypes.BOR, 446 TokenTypes.BAND, 447 TokenTypes.QUESTION, 448 }; 449 } 450 451 @Override 452 public int[] getRequiredTokens() { 453 // Check can work with any of acceptable tokens 454 return CommonUtil.EMPTY_INT_ARRAY; 455 } 456 457 // -@cs[CyclomaticComplexity] All logs should be in visit token. 458 @Override 459 public void visitToken(DetailAST ast) { 460 final DetailAST parent = ast.getParent(); 461 462 if (isLambdaSingleParameterSurrounded(ast)) { 463 log(ast, MSG_LAMBDA); 464 } 465 else if (ast.getType() == TokenTypes.QUESTION) { 466 getParenthesesChildrenAroundQuestion(ast) 467 .forEach(unnecessaryChild -> log(unnecessaryChild, MSG_EXPR)); 468 } 469 else if (parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { 470 final int type = ast.getType(); 471 final boolean surrounded = isSurrounded(ast); 472 // An identifier surrounded by parentheses. 473 if (surrounded && type == TokenTypes.IDENT) { 474 parentToSkip = ast.getParent(); 475 log(ast, MSG_IDENT, ast.getText()); 476 } 477 // A literal (numeric or string) surrounded by parentheses. 478 else if (surrounded && TokenUtil.isOfType(type, LITERALS)) { 479 parentToSkip = ast.getParent(); 480 if (type == TokenTypes.STRING_LITERAL) { 481 log(ast, MSG_STRING, 482 chopString(ast.getText())); 483 } 484 else if (type == TokenTypes.TEXT_BLOCK_LITERAL_BEGIN) { 485 // Strip newline control characters to keep message as single-line, add 486 // quotes to make string consistent with STRING_LITERAL 487 final String logString = QUOTE 488 + NEWLINE.matcher( 489 ast.getFirstChild().getText()).replaceAll("\\\\n") 490 + QUOTE; 491 log(ast, MSG_STRING, chopString(logString)); 492 } 493 else { 494 log(ast, MSG_LITERAL, ast.getText()); 495 } 496 } 497 // The rhs of an assignment surrounded by parentheses. 498 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) { 499 assignDepth++; 500 final DetailAST last = ast.getLastChild(); 501 if (last.getType() == TokenTypes.RPAREN) { 502 log(ast, MSG_ASSIGN); 503 } 504 } 505 } 506 } 507 508 @Override 509 public void leaveToken(DetailAST ast) { 510 final int type = ast.getType(); 511 final DetailAST parent = ast.getParent(); 512 513 // shouldn't process assign in annotation pairs 514 if (type != TokenTypes.ASSIGN 515 || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { 516 if (type == TokenTypes.EXPR) { 517 checkExpression(ast); 518 } 519 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) { 520 assignDepth--; 521 } 522 else if (isSurrounded(ast) && unnecessaryParenAroundOperators(ast)) { 523 log(ast.getPreviousSibling(), MSG_EXPR); 524 } 525 } 526 } 527 528 /** 529 * Tests if the given {@code DetailAST} is surrounded by parentheses. 530 * 531 * @param ast the {@code DetailAST} to check if it is surrounded by 532 * parentheses. 533 * @return {@code true} if {@code ast} is surrounded by 534 * parentheses. 535 */ 536 private static boolean isSurrounded(DetailAST ast) { 537 final DetailAST prev = ast.getPreviousSibling(); 538 final DetailAST parent = ast.getParent(); 539 final boolean isPreviousSiblingLeftParenthesis = prev != null 540 && prev.getType() == TokenTypes.LPAREN; 541 final boolean isMethodCallWithUnnecessaryParenthesis = 542 parent.getType() == TokenTypes.METHOD_CALL 543 && parent.getPreviousSibling() != null 544 && parent.getPreviousSibling().getType() == TokenTypes.LPAREN; 545 return isPreviousSiblingLeftParenthesis || isMethodCallWithUnnecessaryParenthesis; 546 } 547 548 /** 549 * Tests if the given expression node is surrounded by parentheses. 550 * 551 * @param ast a {@code DetailAST} whose type is 552 * {@code TokenTypes.EXPR}. 553 * @return {@code true} if the expression is surrounded by 554 * parentheses. 555 */ 556 private static boolean isExprSurrounded(DetailAST ast) { 557 return ast.getFirstChild().getType() == TokenTypes.LPAREN; 558 } 559 560 /** 561 * Checks whether an expression is surrounded by parentheses. 562 * 563 * @param ast the {@code DetailAST} to check if it is surrounded by 564 * parentheses. 565 */ 566 private void checkExpression(DetailAST ast) { 567 // If 'parentToSkip' == 'ast', then we've already logged a 568 // warning about an immediate child node in visitToken, so we don't 569 // need to log another one here. 570 if (parentToSkip != ast && isExprSurrounded(ast)) { 571 if (ast.getParent().getType() == TokenTypes.LITERAL_RETURN) { 572 log(ast, MSG_RETURN); 573 } 574 else if (assignDepth >= 1) { 575 log(ast, MSG_ASSIGN); 576 } 577 else { 578 log(ast, MSG_EXPR); 579 } 580 } 581 } 582 583 /** 584 * Checks if conditional, relational, bitwise binary operator, unary and postfix operators 585 * in expressions are surrounded by unnecessary parentheses. 586 * 587 * @param ast the {@code DetailAST} to check if it is surrounded by 588 * unnecessary parentheses. 589 * @return {@code true} if the expression is surrounded by 590 * unnecessary parentheses. 591 */ 592 private static boolean unnecessaryParenAroundOperators(DetailAST ast) { 593 final int type = ast.getType(); 594 final boolean isConditionalOrRelational = TokenUtil.isOfType(type, CONDITIONAL_OPERATOR) 595 || TokenUtil.isOfType(type, RELATIONAL_OPERATOR); 596 final boolean isBitwise = TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS); 597 final boolean hasUnnecessaryParentheses; 598 if (isConditionalOrRelational) { 599 hasUnnecessaryParentheses = checkConditionalOrRelationalOperator(ast); 600 } 601 else if (isBitwise) { 602 hasUnnecessaryParentheses = checkBitwiseBinaryOperator(ast); 603 } 604 else { 605 hasUnnecessaryParentheses = TokenUtil.isOfType(type, UNARY_AND_POSTFIX) 606 && isBitWiseBinaryOrConditionalOrRelationalOperator(ast.getParent().getType()); 607 } 608 return hasUnnecessaryParentheses; 609 } 610 611 /** 612 * Check if conditional or relational operator has unnecessary parentheses. 613 * 614 * @param ast to check if surrounded by unnecessary parentheses 615 * @return true if unnecessary parenthesis 616 */ 617 private static boolean checkConditionalOrRelationalOperator(DetailAST ast) { 618 final int type = ast.getType(); 619 final int parentType = ast.getParent().getType(); 620 final boolean isParentEqualityOperator = 621 TokenUtil.isOfType(parentType, TokenTypes.EQUAL, TokenTypes.NOT_EQUAL); 622 final boolean result; 623 if (type == TokenTypes.LOR) { 624 result = !TokenUtil.isOfType(parentType, TokenTypes.LAND) 625 && !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS); 626 } 627 else if (type == TokenTypes.LAND) { 628 result = !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS); 629 } 630 else { 631 result = true; 632 } 633 return result && !isParentEqualityOperator 634 && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType); 635 } 636 637 /** 638 * Check if bitwise binary operator has unnecessary parentheses. 639 * 640 * @param ast to check if surrounded by unnecessary parentheses 641 * @return true if unnecessary parenthesis 642 */ 643 private static boolean checkBitwiseBinaryOperator(DetailAST ast) { 644 final int type = ast.getType(); 645 final int parentType = ast.getParent().getType(); 646 final boolean result; 647 if (type == TokenTypes.BOR) { 648 result = !TokenUtil.isOfType(parentType, TokenTypes.BAND, TokenTypes.BXOR) 649 && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR); 650 } 651 else if (type == TokenTypes.BXOR) { 652 result = !TokenUtil.isOfType(parentType, TokenTypes.BAND) 653 && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR); 654 } 655 // we deal with bitwise AND here. 656 else { 657 result = !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR); 658 } 659 return result && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType); 660 } 661 662 /** 663 * Check if token type is bitwise binary or conditional or relational operator. 664 * 665 * @param type Token type to check 666 * @return true if it is bitwise binary or conditional operator 667 */ 668 private static boolean isBitWiseBinaryOrConditionalOrRelationalOperator(int type) { 669 return TokenUtil.isOfType(type, CONDITIONAL_OPERATOR) 670 || TokenUtil.isOfType(type, RELATIONAL_OPERATOR) 671 || TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS); 672 } 673 674 /** 675 * Tests if the given node has a single parameter, no defined type, and is surrounded 676 * by parentheses. This condition can only be true for lambdas. 677 * 678 * @param ast a {@code DetailAST} node 679 * @return {@code true} if the lambda has a single parameter, no defined type, and is 680 * surrounded by parentheses. 681 */ 682 private static boolean isLambdaSingleParameterSurrounded(DetailAST ast) { 683 final DetailAST firstChild = ast.getFirstChild(); 684 boolean result = false; 685 if (TokenUtil.isOfType(firstChild, TokenTypes.LPAREN)) { 686 final DetailAST parameters = firstChild.getNextSibling(); 687 if (parameters.getChildCount(TokenTypes.PARAMETER_DEF) == 1 688 && !parameters.getFirstChild().findFirstToken(TokenTypes.TYPE).hasChildren()) { 689 result = true; 690 } 691 } 692 return result; 693 } 694 695 /** 696 * Returns the direct LPAREN tokens children to a given QUESTION token which 697 * contain an expression not a literal variable. 698 * 699 * @param questionToken {@code DetailAST} question token to be checked 700 * @return the direct children to the given question token which their types are LPAREN 701 * tokens and not contain a literal inside the parentheses 702 */ 703 private static List<DetailAST> getParenthesesChildrenAroundQuestion(DetailAST questionToken) { 704 final List<DetailAST> surroundedChildren = new ArrayList<>(); 705 DetailAST directChild = questionToken.getFirstChild(); 706 while (directChild != null) { 707 if (directChild.getType() == TokenTypes.LPAREN 708 && !TokenUtil.isOfType(directChild.getNextSibling(), LITERALS)) { 709 surroundedChildren.add(directChild); 710 } 711 directChild = directChild.getNextSibling(); 712 } 713 return Collections.unmodifiableList(surroundedChildren); 714 } 715 716 /** 717 * Returns the specified string chopped to {@code MAX_QUOTED_LENGTH} 718 * plus an ellipsis (...) if the length of the string exceeds {@code 719 * MAX_QUOTED_LENGTH}. 720 * 721 * @param value the string to potentially chop. 722 * @return the chopped string if {@code string} is longer than 723 * {@code MAX_QUOTED_LENGTH}; otherwise {@code string}. 724 */ 725 private static String chopString(String value) { 726 String result = value; 727 if (value.length() > MAX_QUOTED_LENGTH) { 728 result = value.substring(0, MAX_QUOTED_LENGTH) + "...\""; 729 } 730 return result; 731 } 732 733 }