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