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