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