001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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 com.puppycrawl.tools.checkstyle.FileStatefulCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
027
028/**
029 * <p>
030 * Checks if unnecessary parentheses are used in a statement or expression.
031 * The check will flag the following with warnings:
032 * </p>
033 * <pre>
034 *     return (x);          // parens around identifier
035 *     return (x + 1);      // parens around return value
036 *     int x = (y / 2 + 1); // parens around assignment rhs
037 *     for (int i = (0); i &lt; 10; i++) {  // parens around literal
038 *     t -= (z + 1);        // parens around assignment rhs</pre>
039 * <p>
040 * The check is not "type aware", that is to say, it can't tell if parentheses
041 * are unnecessary based on the types in an expression.  It also doesn't know
042 * about operator precedence and associativity; therefore it won't catch
043 * something like
044 * </p>
045 * <pre>
046 *     int x = (a + b) + c;</pre>
047 * <p>
048 * In the above case, given that <em>a</em>, <em>b</em>, and <em>c</em> are
049 * all {@code int} variables, the parentheses around {@code a + b}
050 * are not needed.
051 * </p>
052 * <ul>
053 * <li>
054 * Property {@code tokens} - tokens to check
055 * Type is {@code int[]}.
056 * Default value is:
057 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR">
058 * EXPR</a>,
059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#IDENT">
060 * IDENT</a>,
061 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_DOUBLE">
062 * NUM_DOUBLE</a>,
063 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_FLOAT">
064 * NUM_FLOAT</a>,
065 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_INT">
066 * NUM_INT</a>,
067 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NUM_LONG">
068 * NUM_LONG</a>,
069 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STRING_LITERAL">
070 * STRING_LITERAL</a>,
071 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NULL">
072 * LITERAL_NULL</a>,
073 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FALSE">
074 * LITERAL_FALSE</a>,
075 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRUE">
076 * LITERAL_TRUE</a>,
077 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ASSIGN">
078 * ASSIGN</a>,
079 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND_ASSIGN">
080 * BAND_ASSIGN</a>,
081 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR_ASSIGN">
082 * BOR_ASSIGN</a>,
083 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR_ASSIGN">
084 * BSR_ASSIGN</a>,
085 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR_ASSIGN">
086 * BXOR_ASSIGN</a>,
087 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV_ASSIGN">
088 * DIV_ASSIGN</a>,
089 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS_ASSIGN">
090 * MINUS_ASSIGN</a>,
091 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD_ASSIGN">
092 * MOD_ASSIGN</a>,
093 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS_ASSIGN">
094 * PLUS_ASSIGN</a>,
095 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL_ASSIGN">
096 * SL_ASSIGN</a>,
097 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR_ASSIGN">
098 * SR_ASSIGN</a>,
099 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR_ASSIGN">
100 * STAR_ASSIGN</a>,
101 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
102 * LAMBDA</a>.
103 * </li>
104 * </ul>
105 * <p>
106 * To configure the check:
107 * </p>
108 * <pre>
109 * &lt;module name=&quot;UnnecessaryParentheses&quot;/&gt;
110 * </pre>
111 * <p>
112 * Which results in the following violations:
113 * </p>
114 * <pre>
115 * public int square(int a, int b){
116 *   int square = (a * b); //violation
117 *   return (square); //violation
118 * }
119 * int sumOfSquares = 0;
120 * for(int i=(0); i&lt;10; i++){ //violation
121 *   int x = (i + 1); //violation
122 *   sumOfSquares += (square(x * x)); //violation
123 * }
124 * double num = (10.0); //violation
125 * List&lt;String&gt; list = Arrays.asList(&quot;a1&quot;, &quot;b1&quot;, &quot;c1&quot;);
126 * myList.stream()
127 *   .filter((s) -&gt; s.startsWith(&quot;c&quot;)) //violation
128 *   .forEach(System.out::println);
129 * </pre>
130 * <p>
131 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
132 * </p>
133 * <p>
134 * Violation Message Keys:
135 * </p>
136 * <ul>
137 * <li>
138 * {@code unnecessary.paren.assign}
139 * </li>
140 * <li>
141 * {@code unnecessary.paren.expr}
142 * </li>
143 * <li>
144 * {@code unnecessary.paren.ident}
145 * </li>
146 * <li>
147 * {@code unnecessary.paren.lambda}
148 * </li>
149 * <li>
150 * {@code unnecessary.paren.literal}
151 * </li>
152 * <li>
153 * {@code unnecessary.paren.return}
154 * </li>
155 * <li>
156 * {@code unnecessary.paren.string}
157 * </li>
158 * </ul>
159 *
160 * @since 3.4
161 */
162@FileStatefulCheck
163public class UnnecessaryParenthesesCheck extends AbstractCheck {
164
165    /**
166     * A key is pointing to the warning message text in "messages.properties"
167     * file.
168     */
169    public static final String MSG_IDENT = "unnecessary.paren.ident";
170
171    /**
172     * A key is pointing to the warning message text in "messages.properties"
173     * file.
174     */
175    public static final String MSG_ASSIGN = "unnecessary.paren.assign";
176
177    /**
178     * A key is pointing to the warning message text in "messages.properties"
179     * file.
180     */
181    public static final String MSG_EXPR = "unnecessary.paren.expr";
182
183    /**
184     * A key is pointing to the warning message text in "messages.properties"
185     * file.
186     */
187    public static final String MSG_LITERAL = "unnecessary.paren.literal";
188
189    /**
190     * A key is pointing to the warning message text in "messages.properties"
191     * file.
192     */
193    public static final String MSG_STRING = "unnecessary.paren.string";
194
195    /**
196     * A key is pointing to the warning message text in "messages.properties"
197     * file.
198     */
199    public static final String MSG_RETURN = "unnecessary.paren.return";
200
201    /**
202     * A key is pointing to the warning message text in "messages.properties"
203     * file.
204     */
205    public static final String MSG_LAMBDA = "unnecessary.paren.lambda";
206
207    /** The maximum string length before we chop the string. */
208    private static final int MAX_QUOTED_LENGTH = 25;
209
210    /** Token types for literals. */
211    private static final int[] LITERALS = {
212        TokenTypes.NUM_DOUBLE,
213        TokenTypes.NUM_FLOAT,
214        TokenTypes.NUM_INT,
215        TokenTypes.NUM_LONG,
216        TokenTypes.STRING_LITERAL,
217        TokenTypes.LITERAL_NULL,
218        TokenTypes.LITERAL_FALSE,
219        TokenTypes.LITERAL_TRUE,
220    };
221
222    /** Token types for assignment operations. */
223    private static final int[] ASSIGNMENTS = {
224        TokenTypes.ASSIGN,
225        TokenTypes.BAND_ASSIGN,
226        TokenTypes.BOR_ASSIGN,
227        TokenTypes.BSR_ASSIGN,
228        TokenTypes.BXOR_ASSIGN,
229        TokenTypes.DIV_ASSIGN,
230        TokenTypes.MINUS_ASSIGN,
231        TokenTypes.MOD_ASSIGN,
232        TokenTypes.PLUS_ASSIGN,
233        TokenTypes.SL_ASSIGN,
234        TokenTypes.SR_ASSIGN,
235        TokenTypes.STAR_ASSIGN,
236    };
237
238    /**
239     * Used to test if logging a warning in a parent node may be skipped
240     * because a warning was already logged on an immediate child node.
241     */
242    private DetailAST parentToSkip;
243    /** Depth of nested assignments.  Normally this will be 0 or 1. */
244    private int assignDepth;
245
246    @Override
247    public int[] getDefaultTokens() {
248        return new int[] {
249            TokenTypes.EXPR,
250            TokenTypes.IDENT,
251            TokenTypes.NUM_DOUBLE,
252            TokenTypes.NUM_FLOAT,
253            TokenTypes.NUM_INT,
254            TokenTypes.NUM_LONG,
255            TokenTypes.STRING_LITERAL,
256            TokenTypes.LITERAL_NULL,
257            TokenTypes.LITERAL_FALSE,
258            TokenTypes.LITERAL_TRUE,
259            TokenTypes.ASSIGN,
260            TokenTypes.BAND_ASSIGN,
261            TokenTypes.BOR_ASSIGN,
262            TokenTypes.BSR_ASSIGN,
263            TokenTypes.BXOR_ASSIGN,
264            TokenTypes.DIV_ASSIGN,
265            TokenTypes.MINUS_ASSIGN,
266            TokenTypes.MOD_ASSIGN,
267            TokenTypes.PLUS_ASSIGN,
268            TokenTypes.SL_ASSIGN,
269            TokenTypes.SR_ASSIGN,
270            TokenTypes.STAR_ASSIGN,
271            TokenTypes.LAMBDA,
272        };
273    }
274
275    @Override
276    public int[] getAcceptableTokens() {
277        return new int[] {
278            TokenTypes.EXPR,
279            TokenTypes.IDENT,
280            TokenTypes.NUM_DOUBLE,
281            TokenTypes.NUM_FLOAT,
282            TokenTypes.NUM_INT,
283            TokenTypes.NUM_LONG,
284            TokenTypes.STRING_LITERAL,
285            TokenTypes.LITERAL_NULL,
286            TokenTypes.LITERAL_FALSE,
287            TokenTypes.LITERAL_TRUE,
288            TokenTypes.ASSIGN,
289            TokenTypes.BAND_ASSIGN,
290            TokenTypes.BOR_ASSIGN,
291            TokenTypes.BSR_ASSIGN,
292            TokenTypes.BXOR_ASSIGN,
293            TokenTypes.DIV_ASSIGN,
294            TokenTypes.MINUS_ASSIGN,
295            TokenTypes.MOD_ASSIGN,
296            TokenTypes.PLUS_ASSIGN,
297            TokenTypes.SL_ASSIGN,
298            TokenTypes.SR_ASSIGN,
299            TokenTypes.STAR_ASSIGN,
300            TokenTypes.LAMBDA,
301        };
302    }
303
304    @Override
305    public int[] getRequiredTokens() {
306        // Check can work with any of acceptable tokens
307        return CommonUtil.EMPTY_INT_ARRAY;
308    }
309
310    // -@cs[CyclomaticComplexity] All logs should be in visit token.
311    @Override
312    public void visitToken(DetailAST ast) {
313        final int type = ast.getType();
314        final DetailAST parent = ast.getParent();
315
316        if (type == TokenTypes.LAMBDA && isLambdaSingleParameterSurrounded(ast)) {
317            log(ast, MSG_LAMBDA, ast.getText());
318        }
319        else if (type != TokenTypes.ASSIGN
320            || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
321            final boolean surrounded = isSurrounded(ast);
322            // An identifier surrounded by parentheses.
323            if (surrounded && type == TokenTypes.IDENT) {
324                parentToSkip = ast.getParent();
325                log(ast, MSG_IDENT, ast.getText());
326            }
327            // A literal (numeric or string) surrounded by parentheses.
328            else if (surrounded && isInTokenList(type, LITERALS)) {
329                parentToSkip = ast.getParent();
330                if (type == TokenTypes.STRING_LITERAL) {
331                    log(ast, MSG_STRING,
332                        chopString(ast.getText()));
333                }
334                else {
335                    log(ast, MSG_LITERAL, ast.getText());
336                }
337            }
338            // The rhs of an assignment surrounded by parentheses.
339            else if (isInTokenList(type, ASSIGNMENTS)) {
340                assignDepth++;
341                final DetailAST last = ast.getLastChild();
342                if (last.getType() == TokenTypes.RPAREN) {
343                    log(ast, MSG_ASSIGN);
344                }
345            }
346        }
347    }
348
349    @Override
350    public void leaveToken(DetailAST ast) {
351        final int type = ast.getType();
352        final DetailAST parent = ast.getParent();
353
354        // shouldn't process assign in annotation pairs
355        if (type != TokenTypes.ASSIGN
356            || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
357            // An expression is surrounded by parentheses.
358            if (type == TokenTypes.EXPR) {
359                // If 'parentToSkip' == 'ast', then we've already logged a
360                // warning about an immediate child node in visitToken, so we don't
361                // need to log another one here.
362
363                if (parentToSkip != ast && isExprSurrounded(ast)) {
364                    if (assignDepth >= 1) {
365                        log(ast, MSG_ASSIGN);
366                    }
367                    else if (ast.getParent().getType() == TokenTypes.LITERAL_RETURN) {
368                        log(ast, MSG_RETURN);
369                    }
370                    else {
371                        log(ast, MSG_EXPR);
372                    }
373                }
374
375                parentToSkip = null;
376            }
377            else if (isInTokenList(type, ASSIGNMENTS)) {
378                assignDepth--;
379            }
380        }
381    }
382
383    /**
384     * Tests if the given {@code DetailAST} is surrounded by parentheses.
385     * In short, does {@code ast} have a previous sibling whose type is
386     * {@code TokenTypes.LPAREN} and a next sibling whose type is {@code
387     * TokenTypes.RPAREN}.
388     *
389     * @param ast the {@code DetailAST} to check if it is surrounded by
390     *        parentheses.
391     * @return {@code true} if {@code ast} is surrounded by
392     *         parentheses.
393     */
394    private static boolean isSurrounded(DetailAST ast) {
395        // if previous sibling is left parenthesis,
396        // next sibling can't be other than right parenthesis
397        final DetailAST prev = ast.getPreviousSibling();
398        return prev != null && prev.getType() == TokenTypes.LPAREN;
399    }
400
401    /**
402     * Tests if the given expression node is surrounded by parentheses.
403     *
404     * @param ast a {@code DetailAST} whose type is
405     *        {@code TokenTypes.EXPR}.
406     * @return {@code true} if the expression is surrounded by
407     *         parentheses.
408     */
409    private static boolean isExprSurrounded(DetailAST ast) {
410        return ast.getFirstChild().getType() == TokenTypes.LPAREN;
411    }
412
413    /**
414     * Tests if the given lambda node has a single parameter, no defined type, and is surrounded
415     * by parentheses.
416     *
417     * @param ast a {@code DetailAST} whose type is
418     *        {@code TokenTypes.LAMBDA}.
419     * @return {@code true} if the lambda has a single parameter, no defined type, and is
420     *         surrounded by parentheses.
421     */
422    private static boolean isLambdaSingleParameterSurrounded(DetailAST ast) {
423        final DetailAST firstChild = ast.getFirstChild();
424        boolean result = false;
425        if (firstChild.getType() == TokenTypes.LPAREN) {
426            final DetailAST parameters = firstChild.getNextSibling();
427            if (parameters.getChildCount(TokenTypes.PARAMETER_DEF) == 1
428                    && !parameters.getFirstChild().findFirstToken(TokenTypes.TYPE).hasChildren()) {
429                result = true;
430            }
431        }
432        return result;
433    }
434
435    /**
436     * Check if the given token type can be found in an array of token types.
437     *
438     * @param type the token type.
439     * @param tokens an array of token types to search.
440     * @return {@code true} if {@code type} was found in {@code
441     *         tokens}.
442     */
443    private static boolean isInTokenList(int type, int... tokens) {
444        // NOTE: Given the small size of the two arrays searched, I'm not sure
445        //       it's worth bothering with doing a binary search or using a
446        //       HashMap to do the searches.
447
448        boolean found = false;
449        for (int i = 0; i < tokens.length && !found; i++) {
450            found = tokens[i] == type;
451        }
452        return found;
453    }
454
455    /**
456     * Returns the specified string chopped to {@code MAX_QUOTED_LENGTH}
457     * plus an ellipsis (...) if the length of the string exceeds {@code
458     * MAX_QUOTED_LENGTH}.
459     *
460     * @param value the string to potentially chop.
461     * @return the chopped string if {@code string} is longer than
462     *         {@code MAX_QUOTED_LENGTH}; otherwise {@code string}.
463     */
464    private static String chopString(String value) {
465        String result = value;
466        if (value.length() > MAX_QUOTED_LENGTH) {
467            result = value.substring(0, MAX_QUOTED_LENGTH) + "...\"";
468        }
469        return result;
470    }
471
472}