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