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