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