Add possibility to configure targets for ParenPad check, #1189

This commit is contained in:
Vladislav Lisetskiy 2015-06-17 23:55:02 +03:00 committed by Roman Ivanov
parent 3e25a4911d
commit 7f452f66c8
5 changed files with 325 additions and 66 deletions

View File

@ -907,7 +907,6 @@
<regex><pattern>.*.checks.whitespace.NoWhitespaceAfterCheck</pattern><branchRate>94</branchRate><lineRate>98</lineRate></regex>
<regex><pattern>.*.checks.whitespace.NoWhitespaceBeforeCheck</pattern><branchRate>90</branchRate><lineRate>100</lineRate></regex>
<regex><pattern>.*.checks.whitespace.OperatorWrapCheck</pattern><branchRate>68</branchRate><lineRate>81</lineRate></regex>
<regex><pattern>.*.checks.whitespace.ParenPadCheck</pattern><branchRate>86</branchRate><lineRate>95</lineRate></regex>
<regex><pattern>.*.checks.whitespace.SeparatorWrapCheck</pattern><branchRate>100</branchRate><lineRate>93</lineRate></regex>
<regex><pattern>.*.checks.whitespace.TypecastParenPadCheck</pattern><branchRate>87</branchRate><lineRate>88</lineRate></regex>
<regex><pattern>.*.checks.whitespace.WhitespaceAfterCheck</pattern><branchRate>86</branchRate><lineRate>90</lineRate></regex>

View File

@ -19,6 +19,8 @@
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;
@ -26,9 +28,12 @@ import com.puppycrawl.tools.checkstyle.api.TokenTypes;
* <p>Checks 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, with the exception that it does
* not check for padding of the right parenthesis at an empty for iterator.
* not check for padding of the right parenthesis at an empty for iterator and
* empty for initializer.
* Use Check {@link EmptyForIteratorPadCheck EmptyForIteratorPad} to validate
* empty for iterators.
* empty for iterators and {@link EmptyForInitializerPadCheck EmptyForInitializerPad}
* to validate empty for initializers. Typecasts are also not checked, as there is
* {@link TypecastParenPadCheck TypecastParenPad} to validate them.
* </p>
* <p>
* The policy to verify is specified using the {@link PadOption} class and
@ -36,11 +41,25 @@ import com.puppycrawl.tools.checkstyle.api.TokenTypes;
* </p>
* <p> By default the check will check parentheses that occur with the following
* tokens:
* {@link TokenTypes#ANNOTATION ANNOTATION},
* {@link TokenTypes#ANNOTATION_FIELD_DEF ANNOTATION_FIELD_DEF},
* {@link TokenTypes#CTOR_DEF CTOR_DEF},
* {@link TokenTypes#CTOR_CALL CTOR_CALL},
* {@link TokenTypes#LPAREN LPAREN},
* {@link TokenTypes#ENUM_CONSTANT_DEF ENUM_CONSTANT_DEF},
* {@link TokenTypes#EXPR EXPR},
* {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH},
* {@link TokenTypes#LITERAL_DO LITERAL_DO},
* {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
* {@link TokenTypes#LITERAL_IF LITERAL_IF},
* {@link TokenTypes#LITERAL_NEW LITERAL_NEW},
* {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH},
* {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED},
* {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
* {@link TokenTypes#METHOD_CALL METHOD_CALL},
* {@link TokenTypes#RPAREN RPAREN},
* {@link TokenTypes#METHOD_DEF METHOD_DEF},
* {@link TokenTypes#RESOURCE_SPECIFICATION RESOURCE_SPECIFICATION},
* {@link TokenTypes#SUPER_CTOR_CALL SUPER_CTOR_CALL},
* {@link TokenTypes#QUESTION QUESTION},
* </p>
* <p>
* An example of how to configure the check is:
@ -60,86 +79,200 @@ import com.puppycrawl.tools.checkstyle.api.TokenTypes;
* &lt;/module&gt;
* </pre>
* @author Oliver Burn
* @author Vladislav Lisetskiy
*/
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 new int[] {TokenTypes.RPAREN,
TokenTypes.LPAREN,
TokenTypes.CTOR_CALL,
TokenTypes.SUPER_CTOR_CALL,
TokenTypes.METHOD_CALL,
};
return getAcceptableTokens();
}
@Override
public int[] getAcceptableTokens() {
return new int[] {TokenTypes.RPAREN,
TokenTypes.LPAREN,
TokenTypes.CTOR_CALL,
TokenTypes.SUPER_CTOR_CALL,
TokenTypes.METHOD_CALL,
};
return makeAcceptableTokens();
}
@Override
public void visitToken(DetailAST ast) {
DetailAST theAst = ast;
// Strange logic in this method to guard against checking RPAREN tokens
// that are associated with a TYPECAST token.
if (theAst.getType() != TokenTypes.RPAREN) {
if (theAst.getType() == TokenTypes.CTOR_CALL
|| theAst.getType() == TokenTypes.SUPER_CTOR_CALL) {
theAst = theAst.getFirstChild();
}
if (!isPreceedsEmptyForInit(theAst)) {
processLeft(theAst);
switch (ast.getType()) {
case TokenTypes.METHOD_CALL:
processLeft(ast);
processRight(ast.findFirstToken(TokenTypes.RPAREN));
processExpression(ast);
break;
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:
visitNewEnumConstDefAnnotationSync(ast);
break;
default:
processLeft(ast.findFirstToken(TokenTypes.LPAREN));
processRight(ast.findFirstToken(TokenTypes.RPAREN));
}
}
/**
* Checks parens in {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION}
* {@link TokenTypes#LITERAL_SYNCHRONIZED} and {@link TokenTypes#LITERAL_NEW}.
* @param ast the token to check.
*/
private void visitNewEnumConstDefAnnotationSync(DetailAST ast) {
final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
if (parenAst != null) {
processLeft(parenAst);
processRight(ast.findFirstToken(TokenTypes.RPAREN));
}
}
/**
* Checks parens in {@link TokenTypes#FOR_LITERAL}.
* @param ast the token to check.
*/
private void visitLiteralFor(DetailAST ast) {
DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
if (!isPreceedsEmptyForInit(parenAst)) {
processLeft(parenAst);
}
parenAst = ast.findFirstToken(TokenTypes.RPAREN);
if (!isFollowsEmptyForIterator(parenAst)) {
processRight(parenAst);
}
}
/**
* 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) {
if (ast.branchContains(TokenTypes.LPAREN)) {
DetailAST childAst = ast.getFirstChild();
while (childAst != null) {
if (childAst.getType() == TokenTypes.LPAREN) {
processLeft(childAst);
processExpression(childAst);
}
else if (childAst.getType() == TokenTypes.RPAREN && !isInTypecast(childAst)) {
processRight(childAst);
}
else if (!isAcceptableToken(childAst)) {
//Traverse all subtree tokens which will never be configured
//to be launched in visitToken()
processExpression(childAst);
}
childAst = childAst.getNextSibling();
}
}
else if ((theAst.getParent() == null
|| theAst.getParent().getType() != TokenTypes.TYPECAST
|| theAst.getParent().findFirstToken(TokenTypes.RPAREN)
!= theAst)
&& !isFollowsEmptyForIterator(theAst)) {
processRight(theAst);
}
/**
* 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;
}
/**
* @return acceptableTokens.
*/
private static int[] makeAcceptableTokens() {
return new int[] {TokenTypes.ANNOTATION,
TokenTypes.ANNOTATION_FIELD_DEF,
TokenTypes.CTOR_CALL,
TokenTypes.CTOR_DEF,
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,
};
}
/**
* 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 (firstRparen.getLineNo() == ast.getLineNo()
&& firstRparen.getColumnNo() == ast.getColumnNo()) {
result = true;
}
}
return result;
}
/**
* @param ast the token to check
* @return whether a token follows an empty for iterator
*/
private boolean isFollowsEmptyForIterator(DetailAST ast) {
boolean followsEmptyForIterator = false;
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 != null
&& parent.getType() == TokenTypes.LITERAL_FOR
&& parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
final DetailAST forIterator =
parent.findFirstToken(TokenTypes.FOR_ITERATOR);
followsEmptyForIterator = forIterator.getChildCount() == 0
&& ast == forIterator.getNextSibling();
result = forIterator.getChildCount() == 0;
}
return followsEmptyForIterator;
return result;
}
/**
* @param ast the token to check
* @return whether a token preceeds an empty for initializer
*/
private boolean isPreceedsEmptyForInit(DetailAST ast) {
boolean preceedsEmptyForInintializer = false;
private static boolean isPreceedsEmptyForInit(DetailAST ast) {
boolean result = false;
final DetailAST parent = ast.getParent();
//Only traditional for statements are examined, not for-each statements
if (parent != null
&& parent.getType() == TokenTypes.LITERAL_FOR
&& parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
final DetailAST forIterator =
parent.findFirstToken(TokenTypes.FOR_INIT);
preceedsEmptyForInintializer = forIterator.getChildCount() == 0
&& ast == forIterator.getPreviousSibling();
result = forIterator.getChildCount() == 0;
}
return preceedsEmptyForInintializer;
return result;
}
}

View File

@ -21,6 +21,7 @@ package com.puppycrawl.tools.checkstyle.checks.whitespace;
import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport;
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
import org.junit.Test;
import static com.puppycrawl.tools.checkstyle.checks.whitespace.AbstractParenPadCheck.WS_FOLLOWED;
@ -184,6 +185,42 @@ public class ParenPadCheckTest
"96:47: " + getCheckMessage(WS_PRECEDED, ")"),
"97:42: " + getCheckMessage(WS_PRECEDED, ")"),
"99:44: " + getCheckMessage(WS_PRECEDED, ")"),
"112:17: " + getCheckMessage(WS_FOLLOWED, "("),
"113:23: " + getCheckMessage(WS_FOLLOWED, "("),
"113:25: " + getCheckMessage(WS_FOLLOWED, "("),
"113:31: " + getCheckMessage(WS_PRECEDED, ")"),
"114:26: " + getCheckMessage(WS_FOLLOWED, "("),
"114:28: " + getCheckMessage(WS_FOLLOWED, "("),
"114:34: " + getCheckMessage(WS_PRECEDED, ")"),
"114:50: " + getCheckMessage(WS_PRECEDED, ")"),
"115:26: " + getCheckMessage(WS_FOLLOWED, "("),
"115:28: " + getCheckMessage(WS_FOLLOWED, "("),
"115:35: " + getCheckMessage(WS_PRECEDED, ")"),
"115:53: " + getCheckMessage(WS_PRECEDED, ")"),
"115:55: " + getCheckMessage(WS_PRECEDED, ")"),
"119:17: " + getCheckMessage(WS_FOLLOWED, "("),
"119:22: " + getCheckMessage(WS_PRECEDED, ")"),
"123:30: " + getCheckMessage(WS_FOLLOWED, "("),
"123:44: " + getCheckMessage(WS_PRECEDED, ")"),
"126:22: " + getCheckMessage(WS_FOLLOWED, "("),
"126:22: " + getCheckMessage(WS_PRECEDED, ")"),
"130:19: " + getCheckMessage(WS_FOLLOWED, "("),
"130:19: " + getCheckMessage(WS_PRECEDED, ")"),
};
verify(checkConfig, getPath("whitespace/InputParenPad.java"), expected);
}
@Test
public void testConfigureTokens() throws Exception {
final DefaultConfiguration checkConfig =
createCheckConfig(ParenPadCheck.class);
checkConfig.addAttribute("tokens", "METHOD_CALL");
final String[] expected = {
"90:38: " + getCheckMessage(WS_PRECEDED, ")"),
"112:17: " + getCheckMessage(WS_FOLLOWED, "("),
"113:23: " + getCheckMessage(WS_FOLLOWED, "("),
"115:53: " + getCheckMessage(WS_PRECEDED, ")"),
"115:55: " + getCheckMessage(WS_PRECEDED, ")"),
};
verify(checkConfig, getPath("whitespace/InputParenPad.java"), expected);
}

View File

@ -107,4 +107,29 @@ public class InputParenPad
}
}
}
String foo() {
return ( (Object
) bar( ( 1 > 2 ) ?
( ( 3 < 4 )? false : true ) :
( ( 1 == 1 ) ? false : true) ) ).toString();
}
@MyAnnotation
public boolean bar(boolean a) {
assert ( true );
return true;
}
boolean fooo = this.bar(( true && false ) && true);
}
@interface MyAnnotation {
String someField( ) default "Hello world";
}
enum MyEnum {
SOME_CONSTANT( ) {
int i = (int) (2 * (4 / 2)
);
};
}

View File

@ -712,7 +712,16 @@ for (Iterator foo = very.long.line.iterator();
<p>
Checks the policy on the padding of parentheses; i.e. whether a
space is required after a left parenthesis and before a right
parenthesis, or such spaces are forbidden.
parenthesis, or such spaces are forbidden, with the exception that it does
not check for padding of the right parenthesis at an empty for iterator and
empty for initializer.
Use Check <a
href="config_whitespace.html#EmptyForIteratorPad">EmptyForIteratorPad</a>
to validate empty for iterators and <a
href="config_whitespace.html#EmptyForInitializerPad">EmptyForInitializerPad</a>
to validate empty for initializers. Typecasts are also not checked, as there is <a
href="config_whitespace.html#TypecastParenPad">TypecastParenPad</a>
to validate them.
</p>
</subsection>
@ -736,28 +745,84 @@ for (Iterator foo = very.long.line.iterator();
<td>
subset of tokens <a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION">ANNOTATION</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">ANNOTATION_FIELD_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">CTOR_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL">CTOR_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LPAREN">LPAREN</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">METHOD_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RPAREN">RPAREN</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">SUPER_CTOR_CALL</a>
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">ENUM_CONSTANT_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR">EXPR</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">LITERAL_CATCH</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">LITERAL_DO</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">LITERAL_FOR</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">LITERAL_IF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">LITERAL_NEW</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">LITERAL_SWITCH</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">LITERAL_SYNCHRONIZED</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">LITERAL_WHILE</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">METHOD_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">METHOD_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE_SPECIFICATION">RESOURCE_SPECIFICATION</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">SUPER_CTOR_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">QUESTION</a>
</td>
<td>
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL">CTOR_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LPAREN">LPAREN</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">METHOD_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RPAREN">RPAREN</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">SUPER_CTOR_CALL</a>
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION">ANNOTATION</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">ANNOTATION_FIELD_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">CTOR_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL">CTOR_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">ENUM_CONSTANT_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR">EXPR</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">LITERAL_CATCH</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">LITERAL_DO</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">LITERAL_FOR</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">LITERAL_IF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">LITERAL_NEW</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">LITERAL_SWITCH</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">LITERAL_SYNCHRONIZED</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">LITERAL_WHILE</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">METHOD_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">METHOD_DEF</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE_SPECIFICATION">RESOURCE_SPECIFICATION</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">SUPER_CTOR_CALL</a>,
<a
href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">QUESTION</a>
</td>
</tr>
</table>