Skip to content

Commit 4286438

Browse files
committed
Prohibit protected methods without @OverRide in final classes
Closes gh-466
1 parent 2fac772 commit 4286438

File tree

9 files changed

+134
-3
lines changed

9 files changed

+134
-3
lines changed

spring-javaformat/spring-javaformat-checkstyle/src/main/java/io/spring/javaformat/checkstyle/check/SpringMethodVisibilityCheck.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@
2020
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
2121

2222
/**
23-
* Checks that protected, package-private and private classes to not have public methods
24-
* unless they are also annotated with {@link Override @Override}.
23+
* Check for compliance with Spring-style method visibility. Checks that:
24+
*
25+
* <ul>
26+
* <li>package-private and private classes do not have public methods
27+
* unless they are also annotated with {@link Override @Override}
28+
* <li>final classes do not have protected methods unless that are also
29+
* annotated with {@link Override @Override}
30+
* </ul>
2531
*
2632
* @author Phillip Webb
33+
* @author Andy Wilkinson
2734
*/
2835
public class SpringMethodVisibilityCheck extends AbstractSpringCheck {
2936

@@ -38,6 +45,9 @@ public void visitToken(DetailAST ast) {
3845
if (modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null) {
3946
visitPublicMethod(modifiers, ast);
4047
}
48+
else if (modifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) != null) {
49+
visitProtectedMethod(modifiers, ast);
50+
}
4151
}
4252

4353
private void visitPublicMethod(DetailAST modifiers, DetailAST method) {
@@ -56,6 +66,18 @@ private void visitPublicMethod(DetailAST modifiers, DetailAST method) {
5666
log(ident.getLineNo(), ident.getColumnNo(), "methodvisibility.publicMethod", ident.getText());
5767
}
5868

69+
private void visitProtectedMethod(DetailAST modifiers, DetailAST method) {
70+
if (hasOverrideAnnotation(modifiers)) {
71+
return;
72+
}
73+
DetailAST classDef = getClassDef(method.getParent());
74+
if (classDef == null || !isFinal(classDef)) {
75+
return;
76+
}
77+
DetailAST ident = method.findFirstToken(TokenTypes.IDENT);
78+
log(ident.getLineNo(), ident.getColumnNo(), "methodvisibility.protectedMethodInFinalClass", ident.getText());
79+
}
80+
5981
private boolean hasOverrideAnnotation(DetailAST modifiers) {
6082
DetailAST candidate = modifiers.getFirstChild();
6183
while (candidate != null) {
@@ -98,4 +120,12 @@ private boolean isPublicOrProtected(DetailAST ast) {
98120
|| modifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) != null;
99121
}
100122

123+
private boolean isFinal(DetailAST ast) {
124+
DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
125+
if (modifiers == null) {
126+
return false;
127+
}
128+
return modifiers.findFirstToken(TokenTypes.FINAL) != null;
129+
}
130+
101131
}

spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/check/messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ lambda.unnecessaryBlock=Lambda block is unnecessary.
3131
lambda.unnecessaryParen=Lambda argument has unnecessary parentheses.
3232
methodorder.outOfOrder=Method ''{0}'' is out of order, expected {1}.
3333
methodvisibility.publicMethod=Method ''{0}'' in private class should not be public.
34+
methodvisibility.protectedMethodInFinalClass=Method ''{0}'' in final class should be package-private rather than protected.
3435
nothis.unexpected=Reference to instance variable ''{0}'' should not use \"this.\".
3536
nullability.bannedImport=Nullability should be expressed using JSpecify. Replace ''{0}'' with ''{1}''.
3637
nullability.annotationLocation=''{0}'' should only be used immediately before a type.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
+0 errors
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
+Method 'bad' in final class should be package-private rather than protected.
2+
+Method 'badStatic' in final class should be package-private rather than protected.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
+0 errors
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Good visibility because, while class is final, protected methods are
19+
* annotated with {@code @Override}.
20+
*
21+
* @author Andy Wilkinson
22+
*/
23+
public final class MethodVisibilityFinalClassWithOverride {
24+
25+
@Override
26+
protected void good() {
27+
}
28+
29+
@Override
30+
protected static void goodStatic() {
31+
}
32+
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Bad visibility because class is final.
19+
*
20+
* @author Andy Wilkinson
21+
*/
22+
public final class MethodVisibilityFinalClassWithProtectedMethod {
23+
24+
protected void bad() {
25+
}
26+
27+
protected static void badStatic() {
28+
}
29+
30+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Good visibility because class is protected.
19+
*
20+
* @author Phillip Webb
21+
*/
22+
protected class MethodVisibilityProtectedWithPublicMethod {
23+
24+
MethodVisibilityPackagePrivateWithPublicMethod() {
25+
}
26+
27+
public void bad() {
28+
}
29+
30+
public static void badStatic() {
31+
}
32+
33+
}

spring-javaformat/spring-javaformat-checkstyle/src/test/resources/source/MethodVisibilityWithOverride.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
/**
18-
* Bad visibility because of public method.
18+
* Good visibility because of {@code @Override} annotation.
1919
*
2020
* @author Phillip Webb
2121
*/

0 commit comments

Comments
 (0)