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