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