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