All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.puppycrawl.tools.checkstyle.checks.whitespace.ParenPadCheck Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2021 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

package com.puppycrawl.tools.checkstyle.checks.whitespace;

import java.util.Arrays;

import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;

/**
 * 

* Checks the policy on the padding of parentheses; that is whether a space is required * after a left parenthesis and before a right parenthesis, or such spaces are * forbidden. No check occurs at the right parenthesis after an empty for * iterator, at the left parenthesis before an empty for initialization, or at * the right parenthesis of a try-with-resources resource specification where * the last resource variable has a trailing semi-colon. * Use Check * EmptyForIteratorPad to validate empty for iterators and * * EmptyForInitializerPad to validate empty for initializers. * Typecasts are also not checked, as there is * * TypecastParenPad to validate them. *

* *

* To configure the check: *

*
 * <module name="ParenPad"/>
 * 
*

* Example: *

*
 * class Foo {
 *
 *   int n;
 *
 *   public void fun() {  // OK
 *     bar( 1);  // violation, space after left parenthesis
 *   }
 *
 *   public void bar(int k ) {  // violation, space before right parenthesis
 *     while (k > 0) {  // OK
 *     }
 *
 *     Test obj = new Test(k);  // OK
 *   }
 *
 *   public void fun2() {  // OK
 *     switch( n) {  // violation, space after left parenthesis
 *       case 2:
 *         bar(n);  // OK
 *       default:
 *         break;
 *     }
 *   }
 *
 * }
 * 
*

* To configure the check to require spaces for the * parentheses of constructor, method, and super constructor calls: *

*
 * <module name="ParenPad">
 *   <property name="tokens" value="LITERAL_FOR, LITERAL_CATCH,
 *     SUPER_CTOR_CALL"/>
 *   <property name="option" value="space"/>
 * </module>
 * 
*

* Example: *

*
 * class Foo {
 *
 *   int x;
 *
 *   public Foo(int n) {
 *   }
 *
 *   public void fun() {
 *     try {
 *       System.out.println(x);
 *     } catch( IOException e) {  // violation, no space before right parenthesis
 *     } catch( Exception e ) {  // OK
 *     }
 *
 *     for ( int i = 0; i < x; i++ ) {  // OK
 *     }
 *   }
 *
 * }
 *
 * class Bar extends Foo {
 *
 *   public Bar() {
 *     super(1 );  // violation, no space after left parenthesis
 *   }
 *
 *   public Bar(int k) {
 *     super( k ); // OK
 *
 *     for ( int i = 0; i < k; i++) {  // violation, no space before right parenthesis
 *     }
 *   }
 *
 * }
 * 
*

* The following cases are not checked: *

*
 * for ( ; i < j; i++, j--) // no check after left parenthesis
 * for (Iterator it = xs.iterator(); it.hasNext(); ) // no check before right parenthesis
 * try (Closeable resource = acquire(); ) // no check before right parenthesis
 * 
*

* Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} *

*

* Violation Message Keys: *

*
    *
  • * {@code ws.followed} *
  • *
  • * {@code ws.notFollowed} *
  • *
  • * {@code ws.notPreceded} *
  • *
  • * {@code ws.preceded} *
  • *
* * @since 3.0 */ public class ParenPadCheck extends AbstractParenPadCheck { /** * The array of Acceptable Tokens. */ private final int[] acceptableTokens; /** * Initializes and sorts acceptableTokens to make binary search over it possible. */ public ParenPadCheck() { acceptableTokens = makeAcceptableTokens(); Arrays.sort(acceptableTokens); } @Override public int[] getDefaultTokens() { return makeAcceptableTokens(); } @Override public int[] getAcceptableTokens() { return makeAcceptableTokens(); } @Override public int[] getRequiredTokens() { return CommonUtil.EMPTY_INT_ARRAY; } @Override public void visitToken(DetailAST ast) { switch (ast.getType()) { case TokenTypes.METHOD_CALL: processLeft(ast); processRight(ast.findFirstToken(TokenTypes.RPAREN)); break; case TokenTypes.DOT: case TokenTypes.EXPR: case TokenTypes.QUESTION: processExpression(ast); break; case TokenTypes.LITERAL_FOR: visitLiteralFor(ast); break; case TokenTypes.ANNOTATION: case TokenTypes.ENUM_CONSTANT_DEF: case TokenTypes.LITERAL_NEW: case TokenTypes.LITERAL_SYNCHRONIZED: case TokenTypes.LAMBDA: visitTokenWithOptionalParentheses(ast); break; case TokenTypes.RESOURCE_SPECIFICATION: visitResourceSpecification(ast); break; default: processLeft(ast.findFirstToken(TokenTypes.LPAREN)); processRight(ast.findFirstToken(TokenTypes.RPAREN)); } } /** * Checks parens in token which may not contain parens, e.g. * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION} * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and * {@link TokenTypes#LAMBDA}. * * @param ast the token to check. */ private void visitTokenWithOptionalParentheses(DetailAST ast) { final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN); if (parenAst != null) { processLeft(parenAst); processRight(ast.findFirstToken(TokenTypes.RPAREN)); } } /** * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}. * * @param ast the token to check. */ private void visitResourceSpecification(DetailAST ast) { processLeft(ast.findFirstToken(TokenTypes.LPAREN)); final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); if (!hasPrecedingSemiColon(rparen)) { processRight(rparen); } } /** * Checks that a token is preceded by a semi-colon. * * @param ast the token to check * @return whether a token is preceded by a semi-colon */ private static boolean hasPrecedingSemiColon(DetailAST ast) { return ast.getPreviousSibling().getType() == TokenTypes.SEMI; } /** * Checks parens in {@link TokenTypes#LITERAL_FOR}. * * @param ast the token to check. */ private void visitLiteralFor(DetailAST ast) { final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN); if (!isPrecedingEmptyForInit(lparen)) { processLeft(lparen); } final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); if (!isFollowsEmptyForIterator(rparen)) { processRight(rparen); } } /** * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION} * and {@link TokenTypes#METHOD_CALL}. * * @param ast the token to check. */ private void processExpression(DetailAST ast) { DetailAST currentNode = ast.getFirstChild(); while (currentNode != null) { if (currentNode.getType() == TokenTypes.LPAREN) { processLeft(currentNode); } else if (currentNode.getType() == TokenTypes.RPAREN && !isInTypecast(currentNode)) { processRight(currentNode); } else if (currentNode.hasChildren() && !isAcceptableToken(currentNode)) { // Traverse all subtree tokens which will never be configured // to be launched in visitToken() currentNode = currentNode.getFirstChild(); continue; } // Go up after processing the last child while (currentNode.getNextSibling() == null && currentNode.getParent() != ast) { currentNode = currentNode.getParent(); } currentNode = currentNode.getNextSibling(); } } /** * Checks whether AcceptableTokens contains the given ast. * * @param ast the token to check. * @return true if the ast is in AcceptableTokens. */ private boolean isAcceptableToken(DetailAST ast) { boolean result = false; if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) { result = true; } return result; } /** * Returns array of acceptable tokens. * * @return acceptableTokens. */ private static int[] makeAcceptableTokens() { return new int[] {TokenTypes.ANNOTATION, TokenTypes.ANNOTATION_FIELD_DEF, TokenTypes.CTOR_CALL, TokenTypes.CTOR_DEF, TokenTypes.DOT, TokenTypes.ENUM_CONSTANT_DEF, TokenTypes.EXPR, TokenTypes.LITERAL_CATCH, TokenTypes.LITERAL_DO, TokenTypes.LITERAL_FOR, TokenTypes.LITERAL_IF, TokenTypes.LITERAL_NEW, TokenTypes.LITERAL_SWITCH, TokenTypes.LITERAL_SYNCHRONIZED, TokenTypes.LITERAL_WHILE, TokenTypes.METHOD_CALL, TokenTypes.METHOD_DEF, TokenTypes.QUESTION, TokenTypes.RESOURCE_SPECIFICATION, TokenTypes.SUPER_CTOR_CALL, TokenTypes.LAMBDA, TokenTypes.RECORD_DEF, }; } /** * Checks whether {@link TokenTypes#RPAREN} is a closing paren * of a {@link TokenTypes#TYPECAST}. * * @param ast of a {@link TokenTypes#RPAREN} to check. * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}. */ private static boolean isInTypecast(DetailAST ast) { boolean result = false; if (ast.getParent().getType() == TokenTypes.TYPECAST) { final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN); if (TokenUtil.areOnSameLine(firstRparen, ast) && firstRparen.getColumnNo() == ast.getColumnNo()) { result = true; } } return result; } /** * Checks that a token follows an empty for iterator. * * @param ast the token to check * @return whether a token follows an empty for iterator */ private static boolean isFollowsEmptyForIterator(DetailAST ast) { boolean result = false; final DetailAST parent = ast.getParent(); // Only traditional for statements are examined, not for-each statements if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { final DetailAST forIterator = parent.findFirstToken(TokenTypes.FOR_ITERATOR); result = !forIterator.hasChildren(); } return result; } /** * Checks that a token precedes an empty for initializer. * * @param ast the token to check * @return whether a token precedes an empty for initializer */ private static boolean isPrecedingEmptyForInit(DetailAST ast) { boolean result = false; final DetailAST parent = ast.getParent(); // Only traditional for statements are examined, not for-each statements if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { final DetailAST forIterator = parent.findFirstToken(TokenTypes.FOR_INIT); result = !forIterator.hasChildren(); } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy