001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2021 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. It also doesn't know 050 * about operator precedence and associativity; therefore it won't catch 051 * something like 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 * <ul> 070 * <li> 071 * Property {@code tokens} - tokens to check 072 * Type is {@code java.lang.String[]}. 073 * Validation type is {@code tokenSet}. 074 * Default value is: 075 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR"> 076 * EXPR</a>, 077 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#IDENT"> 078 * IDENT</a>, 079 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_DOUBLE"> 080 * NUM_DOUBLE</a>, 081 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_FLOAT"> 082 * NUM_FLOAT</a>, 083 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_INT"> 084 * NUM_INT</a>, 085 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_LONG"> 086 * NUM_LONG</a>, 087 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STRING_LITERAL"> 088 * STRING_LITERAL</a>, 089 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NULL"> 090 * LITERAL_NULL</a>, 091 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FALSE"> 092 * LITERAL_FALSE</a>, 093 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRUE"> 094 * LITERAL_TRUE</a>, 095 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ASSIGN"> 096 * ASSIGN</a>, 097 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND_ASSIGN"> 098 * BAND_ASSIGN</a>, 099 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR_ASSIGN"> 100 * BOR_ASSIGN</a>, 101 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR_ASSIGN"> 102 * BSR_ASSIGN</a>, 103 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR_ASSIGN"> 104 * BXOR_ASSIGN</a>, 105 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV_ASSIGN"> 106 * DIV_ASSIGN</a>, 107 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS_ASSIGN"> 108 * MINUS_ASSIGN</a>, 109 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD_ASSIGN"> 110 * MOD_ASSIGN</a>, 111 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS_ASSIGN"> 112 * PLUS_ASSIGN</a>, 113 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL_ASSIGN"> 114 * SL_ASSIGN</a>, 115 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR_ASSIGN"> 116 * SR_ASSIGN</a>, 117 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR_ASSIGN"> 118 * STAR_ASSIGN</a>, 119 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA"> 120 * LAMBDA</a>, 121 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TEXT_BLOCK_LITERAL_BEGIN"> 122 * TEXT_BLOCK_LITERAL_BEGIN</a>, 123 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAND"> 124 * LAND</a>, 125 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_INSTANCEOF"> 126 * LITERAL_INSTANCEOF</a>, 127 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GT"> 128 * GT</a>, 129 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LT"> 130 * LT</a>, 131 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GE"> 132 * GE</a>, 133 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LE"> 134 * LE</a>, 135 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EQUAL"> 136 * EQUAL</a>, 137 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NOT_EQUAL"> 138 * NOT_EQUAL</a>, 139 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#UNARY_MINUS"> 140 * UNARY_MINUS</a>, 141 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#UNARY_PLUS"> 142 * UNARY_PLUS</a>, 143 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INC"> 144 * INC</a>, 145 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DEC"> 146 * DEC</a>, 147 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LNOT"> 148 * LNOT</a>, 149 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BNOT"> 150 * BNOT</a>, 151 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_INC"> 152 * POST_INC</a>, 153 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_DEC"> 154 * POST_DEC</a>. 155 * </li> 156 * </ul> 157 * <p> 158 * To configure the check: 159 * </p> 160 * <pre> 161 * <module name="UnnecessaryParentheses"/> 162 * </pre> 163 * <p> 164 * Which results in the following violations: 165 * </p> 166 * <pre> 167 * public int square(int a, int b){ 168 * int square = (a * b); // violation 169 * return (square); // violation 170 * } 171 * int sumOfSquares = 0; 172 * for(int i=(0); i<10; i++){ // violation 173 * int x = (i + 1); // violation 174 * sumOfSquares += (square(x * x)); // violation 175 * } 176 * double num = (10.0); //violation 177 * List<String> list = Arrays.asList("a1", "b1", "c1"); 178 * myList.stream() 179 * .filter((s) -> s.startsWith("c")) // violation 180 * .forEach(System.out::println); 181 * int a = 10, b = 12, c = 15; 182 * boolean x = true, y = false, z= true; 183 * if ((a >= 0 && b <= 9) // violation, unnecessary parenthesis 184 * || (c >= 5 && b <= 5) // violation, unnecessary parenthesis 185 * || (c >= 3 && a <= 7)) { // violation, unnecessary parenthesis 186 * return; 187 * } 188 * if ((-a) != -27 // violation, unnecessary parenthesis 189 * && b > 5) { 190 * return; 191 * } 192 * if (x==(a <= 15)) { // ok 193 * return; 194 * } 195 * if (x==(y == z)) { // ok 196 * return; 197 * } 198 * </pre> 199 * <p> 200 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 201 * </p> 202 * <p> 203 * Violation Message Keys: 204 * </p> 205 * <ul> 206 * <li> 207 * {@code unnecessary.paren.assign} 208 * </li> 209 * <li> 210 * {@code unnecessary.paren.expr} 211 * </li> 212 * <li> 213 * {@code unnecessary.paren.ident} 214 * </li> 215 * <li> 216 * {@code unnecessary.paren.lambda} 217 * </li> 218 * <li> 219 * {@code unnecessary.paren.literal} 220 * </li> 221 * <li> 222 * {@code unnecessary.paren.return} 223 * </li> 224 * <li> 225 * {@code unnecessary.paren.string} 226 * </li> 227 * </ul> 228 * 229 * @since 3.4 230 */ 231@FileStatefulCheck 232public class UnnecessaryParenthesesCheck extends AbstractCheck { 233 234 /** 235 * A key is pointing to the warning message text in "messages.properties" 236 * file. 237 */ 238 public static final String MSG_IDENT = "unnecessary.paren.ident"; 239 240 /** 241 * A key is pointing to the warning message text in "messages.properties" 242 * file. 243 */ 244 public static final String MSG_ASSIGN = "unnecessary.paren.assign"; 245 246 /** 247 * A key is pointing to the warning message text in "messages.properties" 248 * file. 249 */ 250 public static final String MSG_EXPR = "unnecessary.paren.expr"; 251 252 /** 253 * A key is pointing to the warning message text in "messages.properties" 254 * file. 255 */ 256 public static final String MSG_LITERAL = "unnecessary.paren.literal"; 257 258 /** 259 * A key is pointing to the warning message text in "messages.properties" 260 * file. 261 */ 262 public static final String MSG_STRING = "unnecessary.paren.string"; 263 264 /** 265 * A key is pointing to the warning message text in "messages.properties" 266 * file. 267 */ 268 public static final String MSG_RETURN = "unnecessary.paren.return"; 269 270 /** 271 * A key is pointing to the warning message text in "messages.properties" 272 * file. 273 */ 274 public static final String MSG_LAMBDA = "unnecessary.paren.lambda"; 275 276 /** 277 * Compiled pattern used to match newline control characters, for replacement. 278 */ 279 private static final Pattern NEWLINE = Pattern.compile("\\R"); 280 281 /** 282 * String used to amend TEXT_BLOCK_CONTENT so that it matches STRING_LITERAL. 283 */ 284 private static final String QUOTE = "\""; 285 286 /** The maximum string length before we chop the string. */ 287 private static final int MAX_QUOTED_LENGTH = 25; 288 289 /** Token types for literals. */ 290 private static final int[] LITERALS = { 291 TokenTypes.NUM_DOUBLE, 292 TokenTypes.NUM_FLOAT, 293 TokenTypes.NUM_INT, 294 TokenTypes.NUM_LONG, 295 TokenTypes.STRING_LITERAL, 296 TokenTypes.LITERAL_NULL, 297 TokenTypes.LITERAL_FALSE, 298 TokenTypes.LITERAL_TRUE, 299 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 300 }; 301 302 /** Token types for assignment operations. */ 303 private static final int[] ASSIGNMENTS = { 304 TokenTypes.ASSIGN, 305 TokenTypes.BAND_ASSIGN, 306 TokenTypes.BOR_ASSIGN, 307 TokenTypes.BSR_ASSIGN, 308 TokenTypes.BXOR_ASSIGN, 309 TokenTypes.DIV_ASSIGN, 310 TokenTypes.MINUS_ASSIGN, 311 TokenTypes.MOD_ASSIGN, 312 TokenTypes.PLUS_ASSIGN, 313 TokenTypes.SL_ASSIGN, 314 TokenTypes.SR_ASSIGN, 315 TokenTypes.STAR_ASSIGN, 316 }; 317 318 /** Token types for conditional and relational operators. */ 319 private static final int[] CONDITIONALS_AND_RELATIONAL = { 320 TokenTypes.LOR, 321 TokenTypes.LAND, 322 TokenTypes.LITERAL_INSTANCEOF, 323 TokenTypes.GT, 324 TokenTypes.LT, 325 TokenTypes.GE, 326 TokenTypes.LE, 327 TokenTypes.EQUAL, 328 TokenTypes.NOT_EQUAL, 329 }; 330 331 /** Token types for unary and postfix operators. */ 332 private static final int[] UNARY_AND_POSTFIX = { 333 TokenTypes.UNARY_MINUS, 334 TokenTypes.UNARY_PLUS, 335 TokenTypes.INC, 336 TokenTypes.DEC, 337 TokenTypes.LNOT, 338 TokenTypes.BNOT, 339 TokenTypes.POST_INC, 340 TokenTypes.POST_DEC, 341 }; 342 343 /** 344 * Used to test if logging a warning in a parent node may be skipped 345 * because a warning was already logged on an immediate child node. 346 */ 347 private DetailAST parentToSkip; 348 /** Depth of nested assignments. Normally this will be 0 or 1. */ 349 private int assignDepth; 350 351 @Override 352 public int[] getDefaultTokens() { 353 return new int[] { 354 TokenTypes.EXPR, 355 TokenTypes.IDENT, 356 TokenTypes.NUM_DOUBLE, 357 TokenTypes.NUM_FLOAT, 358 TokenTypes.NUM_INT, 359 TokenTypes.NUM_LONG, 360 TokenTypes.STRING_LITERAL, 361 TokenTypes.LITERAL_NULL, 362 TokenTypes.LITERAL_FALSE, 363 TokenTypes.LITERAL_TRUE, 364 TokenTypes.ASSIGN, 365 TokenTypes.BAND_ASSIGN, 366 TokenTypes.BOR_ASSIGN, 367 TokenTypes.BSR_ASSIGN, 368 TokenTypes.BXOR_ASSIGN, 369 TokenTypes.DIV_ASSIGN, 370 TokenTypes.MINUS_ASSIGN, 371 TokenTypes.MOD_ASSIGN, 372 TokenTypes.PLUS_ASSIGN, 373 TokenTypes.SL_ASSIGN, 374 TokenTypes.SR_ASSIGN, 375 TokenTypes.STAR_ASSIGN, 376 TokenTypes.LAMBDA, 377 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 378 TokenTypes.LAND, 379 TokenTypes.LITERAL_INSTANCEOF, 380 TokenTypes.GT, 381 TokenTypes.LT, 382 TokenTypes.GE, 383 TokenTypes.LE, 384 TokenTypes.EQUAL, 385 TokenTypes.NOT_EQUAL, 386 TokenTypes.UNARY_MINUS, 387 TokenTypes.UNARY_PLUS, 388 TokenTypes.INC, 389 TokenTypes.DEC, 390 TokenTypes.LNOT, 391 TokenTypes.BNOT, 392 TokenTypes.POST_INC, 393 TokenTypes.POST_DEC, 394 }; 395 } 396 397 @Override 398 public int[] getAcceptableTokens() { 399 return new int[] { 400 TokenTypes.EXPR, 401 TokenTypes.IDENT, 402 TokenTypes.NUM_DOUBLE, 403 TokenTypes.NUM_FLOAT, 404 TokenTypes.NUM_INT, 405 TokenTypes.NUM_LONG, 406 TokenTypes.STRING_LITERAL, 407 TokenTypes.LITERAL_NULL, 408 TokenTypes.LITERAL_FALSE, 409 TokenTypes.LITERAL_TRUE, 410 TokenTypes.ASSIGN, 411 TokenTypes.BAND_ASSIGN, 412 TokenTypes.BOR_ASSIGN, 413 TokenTypes.BSR_ASSIGN, 414 TokenTypes.BXOR_ASSIGN, 415 TokenTypes.DIV_ASSIGN, 416 TokenTypes.MINUS_ASSIGN, 417 TokenTypes.MOD_ASSIGN, 418 TokenTypes.PLUS_ASSIGN, 419 TokenTypes.SL_ASSIGN, 420 TokenTypes.SR_ASSIGN, 421 TokenTypes.STAR_ASSIGN, 422 TokenTypes.LAMBDA, 423 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN, 424 TokenTypes.LAND, 425 TokenTypes.LITERAL_INSTANCEOF, 426 TokenTypes.GT, 427 TokenTypes.LT, 428 TokenTypes.GE, 429 TokenTypes.LE, 430 TokenTypes.EQUAL, 431 TokenTypes.NOT_EQUAL, 432 TokenTypes.UNARY_MINUS, 433 TokenTypes.UNARY_PLUS, 434 TokenTypes.INC, 435 TokenTypes.DEC, 436 TokenTypes.LNOT, 437 TokenTypes.BNOT, 438 TokenTypes.POST_INC, 439 TokenTypes.POST_DEC, 440 }; 441 } 442 443 @Override 444 public int[] getRequiredTokens() { 445 // Check can work with any of acceptable tokens 446 return CommonUtil.EMPTY_INT_ARRAY; 447 } 448 449 // -@cs[CyclomaticComplexity] All logs should be in visit token. 450 @Override 451 public void visitToken(DetailAST ast) { 452 final int type = ast.getType(); 453 final DetailAST parent = ast.getParent(); 454 455 if (type == TokenTypes.LAMBDA && isLambdaSingleParameterSurrounded(ast)) { 456 log(ast, MSG_LAMBDA, ast.getText()); 457 } 458 else if (type != TokenTypes.ASSIGN 459 || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { 460 final boolean surrounded = isSurrounded(ast); 461 // An identifier surrounded by parentheses. 462 if (surrounded && type == TokenTypes.IDENT) { 463 parentToSkip = ast.getParent(); 464 log(ast, MSG_IDENT, ast.getText()); 465 } 466 // A literal (numeric or string) surrounded by parentheses. 467 else if (surrounded && TokenUtil.isOfType(type, LITERALS)) { 468 parentToSkip = ast.getParent(); 469 if (type == TokenTypes.STRING_LITERAL) { 470 log(ast, MSG_STRING, 471 chopString(ast.getText())); 472 } 473 else if (type == TokenTypes.TEXT_BLOCK_LITERAL_BEGIN) { 474 // Strip newline control characters to keep message as single line, add 475 // quotes to make string consistent with STRING_LITERAL 476 final String logString = QUOTE 477 + NEWLINE.matcher( 478 ast.getFirstChild().getText()).replaceAll("\\\\n") 479 + QUOTE; 480 log(ast, MSG_STRING, chopString(logString)); 481 } 482 else { 483 log(ast, MSG_LITERAL, ast.getText()); 484 } 485 } 486 // The rhs of an assignment surrounded by parentheses. 487 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) { 488 assignDepth++; 489 final DetailAST last = ast.getLastChild(); 490 if (last.getType() == TokenTypes.RPAREN) { 491 log(ast, MSG_ASSIGN); 492 } 493 } 494 } 495 } 496 497 @Override 498 public void leaveToken(DetailAST ast) { 499 final int type = ast.getType(); 500 final DetailAST parent = ast.getParent(); 501 502 // shouldn't process assign in annotation pairs 503 if (type != TokenTypes.ASSIGN 504 || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { 505 if (type == TokenTypes.EXPR) { 506 checkExpression(ast); 507 } 508 else if (TokenUtil.isOfType(type, ASSIGNMENTS)) { 509 assignDepth--; 510 } 511 else if (isSurrounded(ast) && checkAroundOperators(ast)) { 512 log(ast.getPreviousSibling(), MSG_EXPR); 513 } 514 } 515 } 516 517 /** 518 * Tests if the given {@code DetailAST} is surrounded by parentheses. 519 * In short, does {@code ast} have a previous sibling whose type is 520 * {@code TokenTypes.LPAREN} and a next sibling whose type is {@code 521 * TokenTypes.RPAREN}. 522 * 523 * @param ast the {@code DetailAST} to check if it is surrounded by 524 * parentheses. 525 * @return {@code true} if {@code ast} is surrounded by 526 * parentheses. 527 */ 528 private static boolean isSurrounded(DetailAST ast) { 529 // if previous sibling is left parenthesis, 530 // next sibling can't be other than right parenthesis 531 final DetailAST prev = ast.getPreviousSibling(); 532 return prev != null && prev.getType() == TokenTypes.LPAREN; 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 (assignDepth >= 1) { 559 log(ast, MSG_ASSIGN); 560 } 561 else if (ast.getParent().getType() == TokenTypes.LITERAL_RETURN) { 562 log(ast, MSG_RETURN); 563 } 564 else { 565 log(ast, MSG_EXPR); 566 } 567 } 568 569 parentToSkip = null; 570 } 571 572 /** 573 * Checks if conditional, relational, unary and postfix operators 574 * in expressions are surrounded by parentheses. 575 * 576 * @param ast the {@code DetailAST} to check if it is surrounded by 577 * parentheses. 578 * @return {@code true} if the expression is surrounded by 579 * parentheses. 580 */ 581 private static boolean checkAroundOperators(DetailAST ast) { 582 final int type = ast.getType(); 583 final int parentType = ast.getParent().getType(); 584 final boolean isConditional = TokenUtil.isOfType(type, CONDITIONALS_AND_RELATIONAL); 585 boolean result = TokenUtil.isOfType(parentType, CONDITIONALS_AND_RELATIONAL); 586 if (isConditional) { 587 result = result && !TokenUtil.isOfType(parentType, TokenTypes.EQUAL, 588 TokenTypes.NOT_EQUAL); 589 } 590 else { 591 result = result && TokenUtil.isOfType(type, UNARY_AND_POSTFIX); 592 } 593 return result; 594 } 595 596 /** 597 * Tests if the given lambda node has a single parameter, no defined type, and is surrounded 598 * by parentheses. 599 * 600 * @param ast a {@code DetailAST} whose type is 601 * {@code TokenTypes.LAMBDA}. 602 * @return {@code true} if the lambda has a single parameter, no defined type, and is 603 * surrounded by parentheses. 604 */ 605 private static boolean isLambdaSingleParameterSurrounded(DetailAST ast) { 606 final DetailAST firstChild = ast.getFirstChild(); 607 boolean result = false; 608 if (firstChild != null && firstChild.getType() == TokenTypes.LPAREN) { 609 final DetailAST parameters = firstChild.getNextSibling(); 610 if (parameters.getChildCount(TokenTypes.PARAMETER_DEF) == 1 611 && !parameters.getFirstChild().findFirstToken(TokenTypes.TYPE).hasChildren()) { 612 result = true; 613 } 614 } 615 return result; 616 } 617 618 /** 619 * Returns the specified string chopped to {@code MAX_QUOTED_LENGTH} 620 * plus an ellipsis (...) if the length of the string exceeds {@code 621 * MAX_QUOTED_LENGTH}. 622 * 623 * @param value the string to potentially chop. 624 * @return the chopped string if {@code string} is longer than 625 * {@code MAX_QUOTED_LENGTH}; otherwise {@code string}. 626 */ 627 private static String chopString(String value) { 628 String result = value; 629 if (value.length() > MAX_QUOTED_LENGTH) { 630 result = value.substring(0, MAX_QUOTED_LENGTH) + "...\""; 631 } 632 return result; 633 } 634 635}