001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 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.regex.Pattern; 023 024import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.TokenTypes; 028import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 029import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 030 031/** 032 * <p> 033 * Checks if unnecessary parentheses are used in a statement or expression. 034 * The check will flag the following with warnings: 035 * </p> 036 * <pre> 037 * return (x); // parens around identifier 038 * return (x + 1); // parens around return value 039 * int x = (y / 2 + 1); // parens around assignment rhs 040 * for (int i = (0); i < 10; i++) { // parens around literal 041 * t -= (z + 1); // parens around assignment rhs 042 * boolean a = (x > 7 && y > 5) // parens around expression 043 * || z < 9; 044 * boolean b = (~a) > -27 // parens around ~a 045 * && (a-- < 30); // parens around expression 046 * </pre> 047 * <p> 048 * The check is not "type aware", that is to say, it can't tell if parentheses 049 * are unnecessary based on the types in an expression. The check is partially aware about 050 * operator precedence but unaware about operator associativity. 051 * It won't catch cases such as: 052 * </p> 053 * <pre> 054 * int x = (a + b) + c; // 1st Case 055 * boolean p = true; // 2nd Case 056 * int q = 4; 057 * int r = 3; 058 * if (p == (q <= r)) {}</pre> 059 * <p> 060 * In the first case, given that <em>a</em>, <em>b</em>, and <em>c</em> are 061 * all {@code int} variables, the parentheses around {@code a + b} 062 * are not needed. 063 * In the second case, parentheses are required as <em>q</em>, <em>r</em> are 064 * of type {@code int} and <em>p</em> is of type {@code boolean} 065 * and removing parentheses will give a compile-time error. Even if <em>q</em> 066 * and <em>r</em> were {@code boolean} still there will be no violation 067 * raised as check is not "type aware". 068 * </p> 069 * <p> 070 * The partial support for operator precedence includes cases of the following type: 071 * </p> 072 * <pre> 073 * boolean a = true, b = true; 074 * boolean c = false, d = false; 075 * if ((a && b) || c) { // violation, unnecessary paren 076 * } 077 * if (a && (b || c)) { // ok 078 * } 079 * if ((a == b) && c) { // violation, unnecessary paren 080 * } 081 * String e = "e"; 082 * if ((e instanceof String) && a || b) { // violation, unnecessary paren 083 * } 084 * int f = 0; 085 * int g = 0; 086 * if (!(f >= g) // ok 087 * && (g > f)) { // violation, unnecessary paren 088 * } 089 * if ((++f) > g && a) { // violation, unnecessary paren 090 * } 091 * </pre> 092 * <ul> 093 * <li> 094 * Property {@code tokens} - tokens to check 095 * Type is {@code java.lang.String[]}. 096 * Validation type is {@code tokenSet}. 097 * Default value is: 098 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR"> 099 * 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 * To configure the check: 184 * </p> 185 * <pre> 186 * <module name="UnnecessaryParentheses"/> 187 * </pre> 188 * <p> 189 * Which results in the following violations: 190 * </p> 191 * <pre> 192 * public int square(int a, int b){ 193 * int square = (a * b); // violation 194 * return (square); // violation 195 * } 196 * int sumOfSquares = 0; 197 * for(int i=(0); i<10; i++){ // violation 198 * int x = (i + 1); // violation 199 * sumOfSquares += (square(x * x)); // violation 200 * } 201 * double num = (10.0); //violation 202 * List<String> list = Arrays.asList("a1", "b1", "c1"); 203 * myList.stream() 204 * .filter((s) -> s.startsWith("c")) // violation 205 * .forEach(System.out::println); 206 * int a = 10, b = 12, c = 15; 207 * boolean x = true, y = false, z= true; 208 * if ((a >= 0 && b <= 9) // violation, unnecessary parenthesis 209 * || (c >= 5 && b <= 5) // violation, unnecessary parenthesis 210 * || (c >= 3 && a <= 7)) { // violation, unnecessary parenthesis 211 * return; 212 * } 213 * if ((-a) != -27 // violation, unnecessary parenthesis 214 * && b > 5) { 215 * return; 216 * } 217 * if (x==(a <= 15)) { // ok 218 * return; 219 * } 220 * if (x==(y == z)) { // ok 221 * return; 222 * } 223 * </pre> 224 * <p> 225 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 226 * </p> 227 * <p> 228 * Violation Message Keys: 229 * </p> 230 * <ul> 231 * <li> 232 * {@code unnecessary.paren.assign} 233 * </li> 234 * <li> 235 * {@code unnecessary.paren.expr} 236 * </li> 237 * <li> 238 * {@code unnecessary.paren.ident} 239 * </li> 240 * <li> 241 * {@code unnecessary.paren.lambda} 242 * </li> 243 * <li> 244 * {@code unnecessary.paren.literal} 245 * </li> 246 * <li> 247 * {@code unnecessary.paren.return} 248 * </li> 249 * <li> 250 * {@code unnecessary.paren.string} 251 * </li> 252 * </ul> 253 * 254 * @since 3.4 255 */ 256@FileStatefulCheck 257public class UnnecessaryParenthesesCheck extends AbstractCheck { 258 259 /** 260 * A key is pointing to the warning message text in "messages.properties" 261 * file. 262 */ 263 public static final String MSG_IDENT = "unnecessary.paren.ident"; 264 265 /** 266 * A key is pointing to the warning message text in "messages.properties" 267 * file. 268 */ 269 public static final String MSG_ASSIGN = "unnecessary.paren.assign"; 270 271 /** 272 * A key is pointing to the warning message text in "messages.properties" 273 * file. 274 */ 275 public static final String MSG_EXPR = "unnecessary.paren.expr"; 276 277 /** 278 * A key is pointing to the warning message text in "messages.properties" 279 * file. 280 */ 281 public static final String MSG_LITERAL = "unnecessary.paren.literal"; 282 283 /** 284 * A key is pointing to the warning message text in "messages.properties" 285 * file. 286 */ 287 public static final String MSG_STRING = "unnecessary.paren.string"; 288 289 /** 290 * A key is pointing to the warning message text in "messages.properties" 291 * file. 292 */ 293 public static final String MSG_RETURN = "unnecessary.paren.return"; 294 295 /** 296 * A key is pointing to the warning message text in "messages.properties" 297 * file. 298 */ 299 public static final String MSG_LAMBDA = "unnecessary.paren.lambda"; 300 301 /** 302 * Compiled pattern used to match newline control characters, for replacement. 303 */ 304 private static final Pattern NEWLINE = Pattern.compile("\\R"); 305 306 /** 307 * String used to amend TEXT_BLOCK_CONTENT so that it matches STRING_LITERAL. 308 */ 309 private static final String QUOTE = "\""; 310 311 /** The maximum string length before we chop the string. */ 312 private static final int MAX_QUOTED_LENGTH = 25; 313 314 /** Token types for literals. */ 315 private static final int[] LITERALS = { 316 TokenTypes.NUM_DOUBLE, 317 TokenTypes.NUM_FLOAT, 318 TokenTypes.NUM_INT, 319 TokenTypes.NUM_LONG, 320 TokenTypes.STRING_LITERAL, 321 TokenTypes.LITERAL_NULL, 322 TokenTypes.LITERAL_FALSE, 323 TokenTypes.LITERAL_TRUE, 324 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 325 }; 326 327 /** Token types for assignment operations. */ 328 private static final int[] ASSIGNMENTS = { 329 TokenTypes.ASSIGN, 330 TokenTypes.BAND_ASSIGN, 331 TokenTypes.BOR_ASSIGN, 332 TokenTypes.BSR_ASSIGN, 333 TokenTypes.BXOR_ASSIGN, 334 TokenTypes.DIV_ASSIGN, 335 TokenTypes.MINUS_ASSIGN, 336 TokenTypes.MOD_ASSIGN, 337 TokenTypes.PLUS_ASSIGN, 338 TokenTypes.SL_ASSIGN, 339 TokenTypes.SR_ASSIGN, 340 TokenTypes.STAR_ASSIGN, 341 }; 342 343 /** Token types for conditional and relational operators. */ 344 private static final int[] CONDITIONALS_AND_RELATIONAL = { 345 TokenTypes.LOR, 346 TokenTypes.LAND, 347 TokenTypes.LITERAL_INSTANCEOF, 348 TokenTypes.GT, 349 TokenTypes.LT, 350 TokenTypes.GE, 351 TokenTypes.LE, 352 TokenTypes.EQUAL, 353 TokenTypes.NOT_EQUAL, 354 }; 355 356 /** Token types for unary and postfix operators. */ 357 private static final int[] UNARY_AND_POSTFIX = { 358 TokenTypes.UNARY_MINUS, 359 TokenTypes.UNARY_PLUS, 360 TokenTypes.INC, 361 TokenTypes.DEC, 362 TokenTypes.LNOT, 363 TokenTypes.BNOT, 364 TokenTypes.POST_INC, 365 TokenTypes.POST_DEC, 366 }; 367 368 /** 369 * Used to test if logging a warning in a parent node may be skipped 370 * because a warning was already logged on an immediate child node. 371 */ 372 private DetailAST parentToSkip; 373 /** Depth of nested assignments. Normally this will be 0 or 1. */ 374 private int assignDepth; 375 376 @Override 377 public int[] getDefaultTokens() { 378 return new int[] { 379 TokenTypes.EXPR, 380 TokenTypes.IDENT, 381 TokenTypes.NUM_DOUBLE, 382 TokenTypes.NUM_FLOAT, 383 TokenTypes.NUM_INT, 384 TokenTypes.NUM_LONG, 385 TokenTypes.STRING_LITERAL, 386 TokenTypes.LITERAL_NULL, 387 TokenTypes.LITERAL_FALSE, 388 TokenTypes.LITERAL_TRUE, 389 TokenTypes.ASSIGN, 390 TokenTypes.BAND_ASSIGN, 391 TokenTypes.BOR_ASSIGN, 392 TokenTypes.BSR_ASSIGN, 393 TokenTypes.BXOR_ASSIGN, 394 TokenTypes.DIV_ASSIGN, 395 TokenTypes.MINUS_ASSIGN, 396 TokenTypes.MOD_ASSIGN, 397 TokenTypes.PLUS_ASSIGN, 398 TokenTypes.SL_ASSIGN, 399 TokenTypes.SR_ASSIGN, 400 TokenTypes.STAR_ASSIGN, 401 TokenTypes.LAMBDA, 402 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 403 TokenTypes.LAND, 404 TokenTypes.LOR, 405 TokenTypes.LITERAL_INSTANCEOF, 406 TokenTypes.GT, 407 TokenTypes.LT, 408 TokenTypes.GE, 409 TokenTypes.LE, 410 TokenTypes.EQUAL, 411 TokenTypes.NOT_EQUAL, 412 TokenTypes.UNARY_MINUS, 413 TokenTypes.UNARY_PLUS, 414 TokenTypes.INC, 415 TokenTypes.DEC, 416 TokenTypes.LNOT, 417 TokenTypes.BNOT, 418 TokenTypes.POST_INC, 419 TokenTypes.POST_DEC, 420 }; 421 } 422 423 @Override 424 public int[] getAcceptableTokens() { 425 return new int[] { 426 TokenTypes.EXPR, 427 TokenTypes.IDENT, 428 TokenTypes.NUM_DOUBLE, 429 TokenTypes.NUM_FLOAT, 430 TokenTypes.NUM_INT, 431 TokenTypes.NUM_LONG, 432 TokenTypes.STRING_LITERAL, 433 TokenTypes.LITERAL_NULL, 434 TokenTypes.LITERAL_FALSE, 435 TokenTypes.LITERAL_TRUE, 436 TokenTypes.ASSIGN, 437 TokenTypes.BAND_ASSIGN, 438 TokenTypes.BOR_ASSIGN, 439 TokenTypes.BSR_ASSIGN, 440 TokenTypes.BXOR_ASSIGN, 441 TokenTypes.DIV_ASSIGN, 442 TokenTypes.MINUS_ASSIGN, 443 TokenTypes.MOD_ASSIGN, 444 TokenTypes.PLUS_ASSIGN, 445 TokenTypes.SL_ASSIGN, 446 TokenTypes.SR_ASSIGN, 447 TokenTypes.STAR_ASSIGN, 448 TokenTypes.LAMBDA, 449 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 450 TokenTypes.LAND, 451 TokenTypes.LOR, 452 TokenTypes.LITERAL_INSTANCEOF, 453 TokenTypes.GT, 454 TokenTypes.LT, 455 TokenTypes.GE, 456 TokenTypes.LE, 457 TokenTypes.EQUAL, 458 TokenTypes.NOT_EQUAL, 459 TokenTypes.UNARY_MINUS, 460 TokenTypes.UNARY_PLUS, 461 TokenTypes.INC, 462 TokenTypes.DEC, 463 TokenTypes.LNOT, 464 TokenTypes.BNOT, 465 TokenTypes.POST_INC, 466 TokenTypes.POST_DEC, 467 }; 468 } 469 470 @Override 471 public int[] getRequiredTokens() { 472 // Check can work with any of acceptable tokens 473 return CommonUtil.EMPTY_INT_ARRAY; 474 } 475 476 // -@cs[CyclomaticComplexity] All logs should be in visit token. 477 @Override 478 public void visitToken(DetailAST ast) { 479 final DetailAST parent = ast.getParent(); 480 481 if (isLambdaSingleParameterSurrounded(ast)) { 482 log(ast, MSG_LAMBDA, ast.getText()); 483 } 484 else if (parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { 485 final int type = ast.getType(); 486 final boolean surrounded = isSurrounded(ast); 487 // An identifier surrounded by parentheses. 488 if (surrounded && type == TokenTypes.IDENT) { 489 parentToSkip = ast.getParent(); 490 log(ast, MSG_IDENT, ast.getText()); 491 } 492 // A literal (numeric or string) surrounded by parentheses. 493 else if (surrounded && TokenUtil.isOfType(type, LITERALS)) { 494 parentToSkip = ast.getParent(); 495 if (type == TokenTypes.STRING_LITERAL) { 496 log(ast, MSG_STRING, 497 chopString(ast.getText())); 498 } 499 else if (type == TokenTypes.TEXT_BLOCK_LITERAL_BEGIN) { 500 // Strip newline control characters to keep message as single-line, add 501 // quotes to make string consistent with STRING_LITERAL 502 final String logString = QUOTE 503 + NEWLINE.matcher( 504 ast.getFirstChild().getText()).replaceAll("\\\\n") 505 + QUOTE; 506 log(ast, MSG_STRING, chopString(logString)); 507 } 508 else { 509 log(ast, MSG_LITERAL, ast.getText()); 510 } 511 } 512 // The rhs of an assignment surrounded by parentheses. 513 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) { 514 assignDepth++; 515 final DetailAST last = ast.getLastChild(); 516 if (last.getType() == TokenTypes.RPAREN) { 517 log(ast, MSG_ASSIGN); 518 } 519 } 520 } 521 } 522 523 @Override 524 public void leaveToken(DetailAST ast) { 525 final int type = ast.getType(); 526 final DetailAST parent = ast.getParent(); 527 528 // shouldn't process assign in annotation pairs 529 if (type != TokenTypes.ASSIGN 530 || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { 531 if (type == TokenTypes.EXPR) { 532 checkExpression(ast); 533 } 534 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) { 535 assignDepth--; 536 } 537 else if (isSurrounded(ast) && unnecessaryParenAroundOperators(ast)) { 538 log(ast.getPreviousSibling(), MSG_EXPR); 539 } 540 } 541 } 542 543 /** 544 * Tests if the given {@code DetailAST} is surrounded by parentheses. 545 * 546 * @param ast the {@code DetailAST} to check if it is surrounded by 547 * parentheses. 548 * @return {@code true} if {@code ast} is surrounded by 549 * parentheses. 550 */ 551 private static boolean isSurrounded(DetailAST ast) { 552 final DetailAST prev = ast.getPreviousSibling(); 553 final DetailAST parent = ast.getParent(); 554 final boolean isPreviousSiblingLeftParenthesis = prev != null 555 && prev.getType() == TokenTypes.LPAREN; 556 final boolean isMethodCallWithUnnecessaryParenthesis = 557 parent.getType() == TokenTypes.METHOD_CALL 558 && parent.getPreviousSibling() != null 559 && parent.getPreviousSibling().getType() == TokenTypes.LPAREN; 560 return isPreviousSiblingLeftParenthesis || isMethodCallWithUnnecessaryParenthesis; 561 } 562 563 /** 564 * Tests if the given expression node is surrounded by parentheses. 565 * 566 * @param ast a {@code DetailAST} whose type is 567 * {@code TokenTypes.EXPR}. 568 * @return {@code true} if the expression is surrounded by 569 * parentheses. 570 */ 571 private static boolean isExprSurrounded(DetailAST ast) { 572 return ast.getFirstChild().getType() == TokenTypes.LPAREN; 573 } 574 575 /** 576 * Checks whether an expression is surrounded by parentheses. 577 * 578 * @param ast the {@code DetailAST} to check if it is surrounded by 579 * parentheses. 580 */ 581 private void checkExpression(DetailAST ast) { 582 // If 'parentToSkip' == 'ast', then we've already logged a 583 // warning about an immediate child node in visitToken, so we don't 584 // need to log another one here. 585 if (parentToSkip != ast && isExprSurrounded(ast)) { 586 if (ast.getParent().getType() == TokenTypes.LITERAL_RETURN) { 587 log(ast, MSG_RETURN); 588 } 589 else if (assignDepth >= 1) { 590 log(ast, MSG_ASSIGN); 591 } 592 else { 593 log(ast, MSG_EXPR); 594 } 595 } 596 597 parentToSkip = null; 598 } 599 600 /** 601 * Checks if conditional, relational, unary and postfix operators 602 * in expressions are surrounded by unnecessary parentheses. 603 * 604 * @param ast the {@code DetailAST} to check if it is surrounded by 605 * unnecessary parentheses. 606 * @return {@code true} if the expression is surrounded by 607 * unnecessary parentheses. 608 */ 609 private static boolean unnecessaryParenAroundOperators(DetailAST ast) { 610 final int type = ast.getType(); 611 final int parentType = ast.getParent().getType(); 612 final boolean isConditional = TokenUtil.isOfType(type, CONDITIONALS_AND_RELATIONAL); 613 boolean result = TokenUtil.isOfType(parentType, CONDITIONALS_AND_RELATIONAL); 614 if (isConditional) { 615 if (type == TokenTypes.LOR) { 616 result = result && !TokenUtil.isOfType(parentType, TokenTypes.LAND); 617 } 618 result = result && !TokenUtil.isOfType(parentType, TokenTypes.EQUAL, 619 TokenTypes.NOT_EQUAL); 620 } 621 else { 622 result = result && TokenUtil.isOfType(type, UNARY_AND_POSTFIX); 623 } 624 return result; 625 } 626 627 /** 628 * Tests if the given node has a single parameter, no defined type, and is surrounded 629 * by parentheses. This condition can only be true for lambdas. 630 * 631 * @param ast a {@code DetailAST} node 632 * @return {@code true} if the lambda has a single parameter, no defined type, and is 633 * surrounded by parentheses. 634 */ 635 private static boolean isLambdaSingleParameterSurrounded(DetailAST ast) { 636 final DetailAST firstChild = ast.getFirstChild(); 637 boolean result = false; 638 if (TokenUtil.isOfType(firstChild, TokenTypes.LPAREN)) { 639 final DetailAST parameters = firstChild.getNextSibling(); 640 if (parameters.getChildCount(TokenTypes.PARAMETER_DEF) == 1 641 && !parameters.getFirstChild().findFirstToken(TokenTypes.TYPE).hasChildren()) { 642 result = true; 643 } 644 } 645 return result; 646 } 647 648 /** 649 * Returns the specified string chopped to {@code MAX_QUOTED_LENGTH} 650 * plus an ellipsis (...) if the length of the string exceeds {@code 651 * MAX_QUOTED_LENGTH}. 652 * 653 * @param value the string to potentially chop. 654 * @return the chopped string if {@code string} is longer than 655 * {@code MAX_QUOTED_LENGTH}; otherwise {@code string}. 656 */ 657 private static String chopString(String value) { 658 String result = value; 659 if (value.length() > MAX_QUOTED_LENGTH) { 660 result = value.substring(0, MAX_QUOTED_LENGTH) + "...\""; 661 } 662 return result; 663 } 664 665}