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.whitespace;
021
022import java.util.Arrays;
023
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
027import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
028
029/**
030 * <p>
031 * Checks the policy on the padding of parentheses; that is whether a space is required
032 * after a left parenthesis and before a right parenthesis, or such spaces are
033 * forbidden. No check occurs at the right parenthesis after an empty for
034 * iterator, at the left parenthesis before an empty for initialization, or at
035 * the right parenthesis of a try-with-resources resource specification where
036 * the last resource variable has a trailing semi-colon.
037 * Use Check <a href="https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad">
038 * EmptyForIteratorPad</a> to validate empty for iterators and
039 * <a href="https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad">
040 * EmptyForInitializerPad</a> to validate empty for initializers.
041 * Typecasts are also not checked, as there is
042 * <a href="https://checkstyle.org/config_whitespace.html#TypecastParenPad">
043 * TypecastParenPad</a> to validate them.
044 * </p>
045 * <ul>
046 * <li>
047 * Property {@code option} - Specify policy on how to pad parentheses.
048 * Default value is {@code nospace}.
049 * </li>
050 * <li>
051 * Property {@code tokens} - tokens to check
052 * Default value is:
053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION">
054 * ANNOTATION</a>,
055 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
056 * ANNOTATION_FIELD_DEF</a>,
057 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL">
058 * CTOR_CALL</a>,
059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
060 * CTOR_DEF</a>,
061 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DOT">
062 * DOT</a>,
063 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
064 * ENUM_CONSTANT_DEF</a>,
065 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR">
066 * EXPR</a>,
067 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
068 * LITERAL_CATCH</a>,
069 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
070 * LITERAL_DO</a>,
071 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
072 * LITERAL_FOR</a>,
073 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
074 * LITERAL_IF</a>,
075 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">
076 * LITERAL_NEW</a>,
077 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
078 * LITERAL_SWITCH</a>,
079 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
080 * LITERAL_SYNCHRONIZED</a>,
081 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
082 * LITERAL_WHILE</a>,
083 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">
084 * METHOD_CALL</a>,
085 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
086 * METHOD_DEF</a>,
087 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">
088 * QUESTION</a>,
089 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE_SPECIFICATION">
090 * RESOURCE_SPECIFICATION</a>,
091 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">
092 * SUPER_CTOR_CALL</a>,
093 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
094 * LAMBDA</a>.
095 * </li>
096 * </ul>
097 * <p>
098 * To configure the check:
099 * </p>
100 * <pre>
101 * &lt;module name=&quot;ParenPad&quot;/&gt;
102 * </pre>
103 * <p>
104 * Example:
105 * </p>
106 * <pre>
107 * class Foo {
108 *
109 *   int n;
110 *
111 *   public void fun() {  // OK
112 *     bar( 1);  // violation, space after left parenthesis
113 *   }
114 *
115 *   public void bar(int k ) {  // violation, space before right parenthesis
116 *     while (k &gt; 0) {  // OK
117 *     }
118 *
119 *     Test obj = new Test(k);  // OK
120 *   }
121 *
122 *   public void fun2() {  // OK
123 *     switch( n) {  // violation, space after left parenthesis
124 *       case 2:
125 *         bar(n);  // OK
126 *       default:
127 *         break;
128 *     }
129 *   }
130 *
131 * }
132 * </pre>
133 * <p>
134 * To configure the check to require spaces for the
135 * parentheses of constructor, method, and super constructor calls:
136 * </p>
137 * <pre>
138 * &lt;module name=&quot;ParenPad&quot;&gt;
139 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_FOR, LITERAL_CATCH,
140 *     SUPER_CTOR_CALL&quot;/&gt;
141 *   &lt;property name=&quot;option&quot; value=&quot;space&quot;/&gt;
142 * &lt;/module&gt;
143 * </pre>
144 * <p>
145 * Example:
146 * </p>
147 * <pre>
148 * class Foo {
149 *
150 *   int x;
151 *
152 *   public Foo(int n) {
153 *   }
154 *
155 *   public void fun() {
156 *     try {
157 *       System.out.println(x);
158 *     } catch( IOException e) {  // violation, no space before right parenthesis
159 *     } catch( Exception e ) {  // OK
160 *     }
161 *
162 *     for ( int i = 0; i &lt; x; i++ ) {  // OK
163 *     }
164 *   }
165 *
166 * }
167 *
168 * class Bar extends Foo {
169 *
170 *   public Bar() {
171 *     super(1 );  // violation, no space after left parenthesis
172 *   }
173 *
174 *   public Bar(int k) {
175 *     super( k ); // OK
176 *
177 *     for ( int i = 0; i &lt; k; i++) {  // violation, no space before right parenthesis
178 *     }
179 *   }
180 *
181 * }
182 * </pre>
183 * <p>
184 * The following cases are not checked:
185 * </p>
186 * <pre>
187 * for ( ; i &lt; j; i++, j--) // no check after left parenthesis
188 * for (Iterator it = xs.iterator(); it.hasNext(); ) // no check before right parenthesis
189 * try (Closeable resource = acquire(); ) // no check before right parenthesis
190 * </pre>
191 *
192 * @since 3.0
193 */
194public class ParenPadCheck extends AbstractParenPadCheck {
195
196    /**
197     * The array of Acceptable Tokens.
198     */
199    private final int[] acceptableTokens;
200
201    /**
202     * Initializes and sorts acceptableTokens to make binary search over it possible.
203     */
204    public ParenPadCheck() {
205        acceptableTokens = makeAcceptableTokens();
206        Arrays.sort(acceptableTokens);
207    }
208
209    @Override
210    public int[] getDefaultTokens() {
211        return makeAcceptableTokens();
212    }
213
214    @Override
215    public int[] getAcceptableTokens() {
216        return makeAcceptableTokens();
217    }
218
219    @Override
220    public int[] getRequiredTokens() {
221        return CommonUtil.EMPTY_INT_ARRAY;
222    }
223
224    @Override
225    public void visitToken(DetailAST ast) {
226        switch (ast.getType()) {
227            case TokenTypes.METHOD_CALL:
228                processLeft(ast);
229                processRight(ast.findFirstToken(TokenTypes.RPAREN));
230                break;
231            case TokenTypes.DOT:
232            case TokenTypes.EXPR:
233            case TokenTypes.QUESTION:
234                processExpression(ast);
235                break;
236            case TokenTypes.LITERAL_FOR:
237                visitLiteralFor(ast);
238                break;
239            case TokenTypes.ANNOTATION:
240            case TokenTypes.ENUM_CONSTANT_DEF:
241            case TokenTypes.LITERAL_NEW:
242            case TokenTypes.LITERAL_SYNCHRONIZED:
243            case TokenTypes.LAMBDA:
244                visitTokenWithOptionalParentheses(ast);
245                break;
246            case TokenTypes.RESOURCE_SPECIFICATION:
247                visitResourceSpecification(ast);
248                break;
249            default:
250                processLeft(ast.findFirstToken(TokenTypes.LPAREN));
251                processRight(ast.findFirstToken(TokenTypes.RPAREN));
252        }
253    }
254
255    /**
256     * Checks parens in token which may not contain parens, e.g.
257     * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION}
258     * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and
259     * {@link TokenTypes#LAMBDA}.
260     *
261     * @param ast the token to check.
262     */
263    private void visitTokenWithOptionalParentheses(DetailAST ast) {
264        final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
265        if (parenAst != null) {
266            processLeft(parenAst);
267            processRight(ast.findFirstToken(TokenTypes.RPAREN));
268        }
269    }
270
271    /**
272     * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}.
273     *
274     * @param ast the token to check.
275     */
276    private void visitResourceSpecification(DetailAST ast) {
277        processLeft(ast.findFirstToken(TokenTypes.LPAREN));
278        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
279        if (!hasPrecedingSemiColon(rparen)) {
280            processRight(rparen);
281        }
282    }
283
284    /**
285     * Checks that a token is preceded by a semi-colon.
286     *
287     * @param ast the token to check
288     * @return whether a token is preceded by a semi-colon
289     */
290    private static boolean hasPrecedingSemiColon(DetailAST ast) {
291        return ast.getPreviousSibling().getType() == TokenTypes.SEMI;
292    }
293
294    /**
295     * Checks parens in {@link TokenTypes#LITERAL_FOR}.
296     *
297     * @param ast the token to check.
298     */
299    private void visitLiteralFor(DetailAST ast) {
300        final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN);
301        if (!isPrecedingEmptyForInit(lparen)) {
302            processLeft(lparen);
303        }
304        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
305        if (!isFollowsEmptyForIterator(rparen)) {
306            processRight(rparen);
307        }
308    }
309
310    /**
311     * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION}
312     * and {@link TokenTypes#METHOD_CALL}.
313     *
314     * @param ast the token to check.
315     */
316    private void processExpression(DetailAST ast) {
317        DetailAST childAst = ast.getFirstChild();
318        while (childAst != null) {
319            if (childAst.getType() == TokenTypes.LPAREN) {
320                processLeft(childAst);
321            }
322            else if (childAst.getType() == TokenTypes.RPAREN && !isInTypecast(childAst)) {
323                processRight(childAst);
324            }
325            else if (!isAcceptableToken(childAst)) {
326                // Traverse all subtree tokens which will never be configured
327                // to be launched in visitToken()
328                processExpression(childAst);
329            }
330            childAst = childAst.getNextSibling();
331        }
332    }
333
334    /**
335     * Checks whether AcceptableTokens contains the given ast.
336     *
337     * @param ast the token to check.
338     * @return true if the ast is in AcceptableTokens.
339     */
340    private boolean isAcceptableToken(DetailAST ast) {
341        boolean result = false;
342        if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) {
343            result = true;
344        }
345        return result;
346    }
347
348    /**
349     * Returns array of acceptable tokens.
350     *
351     * @return acceptableTokens.
352     */
353    private static int[] makeAcceptableTokens() {
354        return new int[] {TokenTypes.ANNOTATION,
355            TokenTypes.ANNOTATION_FIELD_DEF,
356            TokenTypes.CTOR_CALL,
357            TokenTypes.CTOR_DEF,
358            TokenTypes.DOT,
359            TokenTypes.ENUM_CONSTANT_DEF,
360            TokenTypes.EXPR,
361            TokenTypes.LITERAL_CATCH,
362            TokenTypes.LITERAL_DO,
363            TokenTypes.LITERAL_FOR,
364            TokenTypes.LITERAL_IF,
365            TokenTypes.LITERAL_NEW,
366            TokenTypes.LITERAL_SWITCH,
367            TokenTypes.LITERAL_SYNCHRONIZED,
368            TokenTypes.LITERAL_WHILE,
369            TokenTypes.METHOD_CALL,
370            TokenTypes.METHOD_DEF,
371            TokenTypes.QUESTION,
372            TokenTypes.RESOURCE_SPECIFICATION,
373            TokenTypes.SUPER_CTOR_CALL,
374            TokenTypes.LAMBDA,
375        };
376    }
377
378    /**
379     * Checks whether {@link TokenTypes#RPAREN} is a closing paren
380     * of a {@link TokenTypes#TYPECAST}.
381     *
382     * @param ast of a {@link TokenTypes#RPAREN} to check.
383     * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}.
384     */
385    private static boolean isInTypecast(DetailAST ast) {
386        boolean result = false;
387        if (ast.getParent().getType() == TokenTypes.TYPECAST) {
388            final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN);
389            if (TokenUtil.areOnSameLine(firstRparen, ast)
390                    && firstRparen.getColumnNo() == ast.getColumnNo()) {
391                result = true;
392            }
393        }
394        return result;
395    }
396
397    /**
398     * Checks that a token follows an empty for iterator.
399     *
400     * @param ast the token to check
401     * @return whether a token follows an empty for iterator
402     */
403    private static boolean isFollowsEmptyForIterator(DetailAST ast) {
404        boolean result = false;
405        final DetailAST parent = ast.getParent();
406        // Only traditional for statements are examined, not for-each statements
407        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
408            final DetailAST forIterator =
409                parent.findFirstToken(TokenTypes.FOR_ITERATOR);
410            result = !forIterator.hasChildren();
411        }
412        return result;
413    }
414
415    /**
416     * Checks that a token precedes an empty for initializer.
417     *
418     * @param ast the token to check
419     * @return whether a token precedes an empty for initializer
420     */
421    private static boolean isPrecedingEmptyForInit(DetailAST ast) {
422        boolean result = false;
423        final DetailAST parent = ast.getParent();
424        // Only traditional for statements are examined, not for-each statements
425        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
426            final DetailAST forIterator =
427                    parent.findFirstToken(TokenTypes.FOR_INIT);
428            result = !forIterator.hasChildren();
429        }
430        return result;
431    }
432
433}