Skip to content

Commit 1e567de

Browse files
committed
Issue #464: Enum trailing comma check implemented
1 parent 7cbfbaf commit 1e567de

File tree

11 files changed

+290
-3
lines changed

11 files changed

+290
-3
lines changed

eclipsecs-sevntu-plugin/src/com/github/sevntu/checkstyle/checks/coding/checkstyle-metadata.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ EmptyPublicCtorInClassCheck.desc = <p>This Check looks for useless empty public
4343
EmptyPublicCtorInClassCheck.classAnnotationNames = Regex which matches names of class annotations which require class to have public no-argument ctor. Default value is "javax\\.persistence\\.Entity".
4444
EmptyPublicCtorInClassCheck.ctorAnnotationNames = Regex which matches names of ctor annotations which make empty public ctor essential. Default value is "com\\.google\\.inject\\.Inject".
4545
46+
EnumTrailingComma.name = Enum Trailing Comma
47+
EnumTrailingComma.desc = <p>The check demands a comma at the end if neither left nor right curly braces are on the same line as the last element of the enum.</p>
48+
4649
FinalizeImplementationCheck.name = Finalize Implementation
4750
FinalizeImplementationCheck.desc = <p>This Check detects 3 most common cases of incorrect finalize() method implementation:</p><ul><li>negates effect of superclass finalize<br/><code>protected void finalize() { } <br/> protected void finalize() { doSomething(); }</code></li><li>useless (or worse) finalize<br/><code>protected void finalize() { super.finalize(); }</code></li><li>public finalize<br/><code>public void finalize() { try { doSomething(); } finally { super.finalize() } }</code></li></ul>
4851

eclipsecs-sevntu-plugin/src/com/github/sevntu/checkstyle/checks/coding/checkstyle-metadata.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@
162162
<message-key key="empty.public.ctor"/>
163163
</rule-metadata>
164164

165+
<rule-metadata name="%EnumTrailingComma.name" internal-name="EnumTrailingComma" parent="TreeWalker">
166+
<alternative-name internal-name="com.github.sevntu.checkstyle.checks.coding.EnumTrailingComma"/>
167+
<description>%EnumTrailingComma.desc</description>
168+
169+
<message-key key="enum.trailing.comma"/>
170+
</rule-metadata>
171+
165172
<rule-metadata name="%ForbidCertainImportsCheck.name" internal-name="ForbidCertainImportsCheck" parent="TreeWalker">
166173
<alternative-name internal-name="com.github.sevntu.checkstyle.checks.coding.ForbidCertainImportsCheck"/>
167174
<description>%ForbidCertainImportsCheck.desc</description>

sevntu-checks/sevntu-checks.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
<property name="ignoreFieldNamePattern" value="serialVersionUID"/>
153153
</module>
154154
<module name="com.github.sevntu.checkstyle.checks.coding.EmptyPublicCtorInClassCheck"/>
155+
<module name="com.github.sevntu.checkstyle.checks.coding.EnumTrailingComma"/>
155156
<module name="com.github.sevntu.checkstyle.checks.coding.DiamondOperatorForVariableDefinitionCheck"/>
156157
<module name="com.github.sevntu.checkstyle.checks.coding.NameConventionForJunit4TestClassesCheck"/>
157158
<module name="com.github.sevntu.checkstyle.checks.coding.UselessSuperCtorCallCheck"/>
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle: Checks Java source code for adherence to a set of rules.
3+
// Copyright (C) 2001-2020 the original author or authors.
4+
//
5+
// This library is free software; you can redistribute it and/or
6+
// modify it under the terms of the GNU Lesser General Public
7+
// License as published by the Free Software Foundation; either
8+
// version 2.1 of the License, or (at your option) any later version.
9+
//
10+
// This library is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
// Lesser General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Lesser General Public
16+
// License along with this library; if not, write to the Free Software
17+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+
////////////////////////////////////////////////////////////////////////////////
19+
20+
package com.github.sevntu.checkstyle.checks.coding;
21+
22+
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
23+
import com.puppycrawl.tools.checkstyle.api.DetailAST;
24+
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
25+
26+
/**
27+
* <p>
28+
* Checks that enums contains a trailing comma.
29+
* </p>
30+
* <pre>
31+
* enum MovieType {
32+
* GOOD,
33+
* BAD,
34+
* UGLY,
35+
* ;
36+
* }
37+
* </pre>
38+
* <p>
39+
* The check demands a comma at the end if neither left nor right curly braces are on the same line
40+
* as the last element of the enum.
41+
* </p>
42+
* <pre>
43+
* enum MovieType {GOOD, BAD, UGLY;}
44+
* enum MovieType {GOOD, BAD, UGLY
45+
* ;}
46+
* enum MovieType {GOOD, BAD,
47+
* UGLY;}
48+
* enum MovieType {GOOD, BAD,
49+
* UGLY; // Violation
50+
* }
51+
* </pre>
52+
* <p>
53+
* Putting this comma in makes it easier to change the order of the elements or add new elements
54+
* at the end. Main benefit of a trailing comma is that when you add new entry to an enum, no
55+
* surrounding lines are changed.
56+
* </p>
57+
* <pre>
58+
* enum MovieType {
59+
* GOOD,
60+
* BAD, //OK
61+
* }
62+
*
63+
* enum MovieType {
64+
* GOOD,
65+
* BAD,
66+
* UGLY, // Just this line added, no other changes
67+
* }
68+
* </pre>
69+
* <p>
70+
* If closing brace is on the same line as trailing comma, this benefit is gone (as the check does
71+
* not demand a certain location of curly braces the following two cases will not produce a
72+
* violation):
73+
* </p>
74+
* <pre>
75+
* enum MovieType {GOOD,
76+
* BAD,} // Trailing comma not needed, line needs to be modified anyway
77+
*
78+
* enum MovieType {GOOD,
79+
* BAD, // Modified line
80+
* UGLY,} // Added line
81+
* </pre>
82+
*
83+
* @author <a href="mailto:[email protected]"> Yasser Aziza </a>
84+
*/
85+
public class EnumTrailingComma extends AbstractCheck {
86+
87+
/**
88+
* Warning message key pointing to the warning message text in "messages.properties" file.
89+
*/
90+
public static final String MSG_KEY = "enum.trailing.comma";
91+
92+
@Override
93+
public int[] getDefaultTokens() {
94+
return new int[] {
95+
TokenTypes.ENUM_CONSTANT_DEF,
96+
};
97+
}
98+
99+
@Override
100+
public int[] getAcceptableTokens() {
101+
return getDefaultTokens();
102+
}
103+
104+
@Override
105+
public int[] getRequiredTokens() {
106+
return getDefaultTokens();
107+
}
108+
109+
@Override
110+
public void visitToken(DetailAST ast) {
111+
final DetailAST nextSibling = ast.getNextSibling();
112+
113+
if (TokenTypes.COMMA != nextSibling.getType() && isSeparateLine(ast)) {
114+
log(ast, MSG_KEY);
115+
}
116+
}
117+
118+
/**
119+
* Checks if the given {@link TokenTypes#ENUM_CONSTANT_DEF} element is in the same line with
120+
* the {@link TokenTypes#LCURLY} or {@link TokenTypes#RCURLY}.
121+
*
122+
* @param ast the enum node
123+
* @return {@code true} if the element is on the same line with {@link TokenTypes#LCURLY} or
124+
* {@link TokenTypes#RCURLY}, {@code false} otherwise.
125+
*/
126+
private static boolean isSeparateLine(DetailAST ast) {
127+
final DetailAST objBlock = ast.getParent();
128+
final DetailAST leftCurly = objBlock.findFirstToken(TokenTypes.LCURLY);
129+
final DetailAST rightCurly = objBlock.findFirstToken(TokenTypes.RCURLY);
130+
131+
return !areOnSameLine(leftCurly, ast)
132+
&& !areOnSameLine(ast, rightCurly);
133+
}
134+
135+
/**
136+
* Determines if two ASTs are on the same line.
137+
*
138+
* @param ast1 the first AST
139+
* @param ast2 the second AST
140+
*
141+
* @return true if they are on the same line.
142+
*/
143+
private static boolean areOnSameLine(DetailAST ast1, DetailAST ast2) {
144+
return ast1.getLineNo() == ast2.getLineNo();
145+
}
146+
147+
}

sevntu-checks/src/main/java/com/github/sevntu/checkstyle/checks/coding/Jsr305AnnotationsCheck.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ private enum NullnessAnnotation {
293293
PARAMETERS_ARE_NULLABLE_BY_DEFAULT("ParametersAreNullableByDefault", PKG_JAVAX_ANNOTATION),
294294
/** ReturnValuesAreNonnullByDefault. */
295295
RETURN_VALUES_ARE_NONNULL_BY_DEFAULT("ReturnValuesAreNonnullByDefault",
296-
"edu.umd.cs.findbugs.annotations");
296+
"edu.umd.cs.findbugs.annotations"),
297+
;
297298

298299
/** The annotation's name. */
299300
private final String annotationName;

sevntu-checks/src/main/java/com/github/sevntu/checkstyle/checks/coding/NumericLiteralNeedsUnderscoreCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ protected enum NumericType {
167167
/**
168168
* Denotes a binary literal. For example, 0b0011
169169
*/
170-
BINARY
170+
BINARY,
171171

172172
}
173173

sevntu-checks/src/main/resources/com/github/sevntu/checkstyle/checks/coding/messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ custom.declaration.order.method=Method definition in wrong order. Expected ''{0}
1414
diamond.operator.for.variable.definition=Diamond operator expected.
1515
either.log.or.throw=Either log or throw exception.
1616
empty.public.ctor=This empty public constructor is useless.
17+
enum.trailing.comma=Enum should contain trailing comma.
1718
finalize.implementation.missed.super.finalize=You have to call super.finalize() right after finally opening brace.
1819
finalize.implementation.missed.try.finally=finalize() method should contain try-finally block with super.finalize() call inside finally block.
1920
finalize.implementation.public=finalize() method should have a "protected" visibility.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle: Checks Java source code for adherence to a set of rules.
3+
// Copyright (C) 2001-2020 the original author or authors.
4+
//
5+
// This library is free software; you can redistribute it and/or
6+
// modify it under the terms of the GNU Lesser General Public
7+
// License as published by the Free Software Foundation; either
8+
// version 2.1 of the License, or (at your option) any later version.
9+
//
10+
// This library is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
// Lesser General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Lesser General Public
16+
// License along with this library; if not, write to the Free Software
17+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+
////////////////////////////////////////////////////////////////////////////////
19+
20+
package com.github.sevntu.checkstyle.checks.coding;
21+
22+
import static com.github.sevntu.checkstyle.checks.coding.EnumTrailingComma.MSG_KEY;
23+
24+
import org.junit.Test;
25+
26+
import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
27+
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
28+
29+
/**
30+
* @author <a href="mailto:[email protected]"> Yasser Aziza </a>
31+
*/
32+
public class EnumTrailingCommaTest extends AbstractModuleTestSupport {
33+
34+
/**
35+
* An error message for current check.
36+
*/
37+
private final String warningMessage = getCheckMessage(MSG_KEY);
38+
39+
@Override
40+
protected String getPackageLocation() {
41+
return "com/github/sevntu/checkstyle/checks/coding";
42+
}
43+
44+
@Test
45+
public void testDefault() throws Exception {
46+
final DefaultConfiguration checkConfig = createModuleConfig(EnumTrailingComma.class);
47+
48+
final String[] expected = {
49+
"14:9: " + warningMessage,
50+
"19:9: " + warningMessage,
51+
"27:9: " + warningMessage,
52+
"39:9: " + warningMessage,
53+
};
54+
55+
verify(checkConfig, getPath("InputEnumTrailingCommaCheck.java"), expected);
56+
}
57+
58+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.github.sevntu.checkstyle.checks.coding;
2+
3+
public class InputEnumTrailingCommaCheck {
4+
5+
enum MovieType {
6+
GOOD,
7+
BAD,
8+
UGLY,
9+
;
10+
}
11+
12+
enum Gender {
13+
FEMALE,
14+
MALE // Violation
15+
}
16+
17+
enum Language {
18+
ENGLISH,
19+
GERMAN // Violation
20+
;
21+
}
22+
23+
// Violation
24+
enum ErrorMessage {
25+
USER_DOES_NOT_EXIST,
26+
INVALID_USER_PASS,
27+
EVERYTHING_ELSE; // Violation
28+
}
29+
30+
enum BinType {ZERO, ONE
31+
;}
32+
33+
enum SameLine {FIRST, SECOND, THIRD;}
34+
35+
enum LineBreak {FIRST, SECOND,
36+
THIRD;}
37+
38+
enum LastLineBreak {FIRST, SECOND,
39+
THIRD; // Violation
40+
}
41+
42+
enum LastBreakSemicolon {FIRST, SECOND, THIRD
43+
;
44+
}
45+
46+
enum RgbColor {
47+
RED,
48+
GREEN,
49+
BLUE
50+
,
51+
}
52+
53+
enum OperatingSystem {
54+
LINUX,
55+
MAC
56+
,}
57+
58+
enum EMPTY {E
59+
}
60+
61+
}

sevntu-checkstyle-sonar-plugin/src/main/resources/com/github/sevntu/checkstyle/sonar/checkstyle-extensions.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,14 @@
346346
</param>
347347
</rule>
348348

349+
<rule>
350+
<key>com.github.sevntu.checkstyle.checks.coding.EnumTrailingComma</key>
351+
<name>Enum Trailing Comma</name>
352+
<tag>coding</tag>
353+
<description>This check ensures that enums contains a trailing comma.</description>
354+
<configKey>Checker/TreeWalker/com.github.sevntu.checkstyle.checks.coding.EnumTrailingComma</configKey>
355+
</rule>
356+
349357
<rule>
350358
<key>com.github.sevntu.checkstyle.checks.coding.FinalizeImplementationCheck</key>
351359
<name>Finalize Implementation Check</name>

0 commit comments

Comments
 (0)