[java] Make CommentDefaultAccessModifier work for top-level classes, enums and annotations

This commit is contained in:
Boris Petrov
2019-04-28 20:26:09 +03:00
parent 64e66b9d30
commit 58b3aaa0da
5 changed files with 92 additions and 12 deletions

View File

@ -11,10 +11,12 @@ import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
@ -84,11 +86,28 @@ public class CommentDefaultAccessModifierRule extends AbstractIgnoredAnnotationR
return super.visit(decl, data);
}
@Override
public Object visit(final ASTAnnotationTypeDeclaration decl, final Object data) {
if (!decl.isNested() && shouldReportTypeDeclaration(decl)) { // check for top-level annotation declarations
addViolationWithMessage(data, decl, String.format(MESSAGE, decl.getImage(), "top-level annotation"));
}
return super.visit(decl, data);
}
@Override
public Object visit(final ASTEnumDeclaration decl, final Object data) {
if (!decl.isNested() && shouldReportTypeDeclaration(decl)) { // check for top-level enums
addViolationWithMessage(data, decl, String.format(MESSAGE, decl.getImage(), "top-level enum"));
}
return super.visit(decl, data);
}
@Override
public Object visit(final ASTClassOrInterfaceDeclaration decl, final Object data) {
// check for nested classes
if (decl.isNested() && shouldReport(decl)) {
if (decl.isNested() && shouldReport(decl)) { // check for nested classes
addViolationWithMessage(data, decl, String.format(MESSAGE, decl.getImage(), "nested class"));
} else if (!decl.isNested() && shouldReportTypeDeclaration(decl)) { // and for top-level ones
addViolationWithMessage(data, decl, String.format(MESSAGE, decl.getImage(), "top-level class"));
}
return super.visit(decl, data);
}
@ -106,14 +125,15 @@ public class CommentDefaultAccessModifierRule extends AbstractIgnoredAnnotationR
.getFirstParentOfType(AbstractAnyTypeDeclaration.class);
boolean isConcreteClass = parentClassOrInterface.getTypeKind() == ASTAnyTypeDeclaration.TypeKind.CLASS;
boolean isEnumConstructor = parentClassOrInterface.getTypeKind() == ASTAnyTypeDeclaration.TypeKind.ENUM
&& decl instanceof ASTConstructorDeclaration;
// ignore if it's an Interface / Annotation / Enum constructor
return isConcreteClass && !isEnumConstructor
// check if the field/method/nested class has a default access
// modifier
&& decl.isPackagePrivate()
// ignore if it's an Interface / Annotation
return isConcreteClass && shouldReportTypeDeclaration(decl);
}
private boolean shouldReportTypeDeclaration(final AbstractJavaAccessNode decl) {
// check if the class/method/field has a default access
// modifier
return decl.isPackagePrivate()
// if is a default access modifier check if there is a comment
// in this line
&& !interestingLineNumberComments.contains(decl.getBeginLine())

View File

@ -402,7 +402,7 @@ public class Éléphant {}
message="Missing commented default access modifier"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#commentdefaultaccessmodifier">
<description>
To avoid mistakes if we want that a Method, Constructor, Field or Nested class have a default access modifier
To avoid mistakes if we want that an Annotation, Class, Enum, Method, Constructor or Field have a default access modifier
we must add a comment at the beginning of it's declaration.
By default the comment must be `/* default */` or `/* package */`, if you want another, you have to provide a regular expression.
This rule ignores by default all cases that have a @VisibleForTesting annotation. Use the

View File

@ -115,6 +115,66 @@ public class Foo {
<code-ref id="nested-class-with-default-access-modifier"/>
</test-code>
<code-fragment id="top-level-annotations-with-default-access-modifier"><![CDATA[
@interface Bar {}
public @interface Foo {}
@SomeAnnotation
@interface Baz {}
@VisibleForTesting
@interface Foobar {}
/* default */ @interface FoobarWithComment {}
]]></code-fragment>
<test-code>
<description>Top-level annotations with default access modifier</description>
<expected-problems>2</expected-problems>
<expected-linenumbers>1,6</expected-linenumbers>
<code-ref id="top-level-annotations-with-default-access-modifier"/>
</test-code>
<code-fragment id="top-level-enums-with-default-access-modifier"><![CDATA[
enum Bar {}
public enum Foo {}
@SomeAnnotation
enum Baz {}
@VisibleForTesting
enum Foobar {}
/* default */ enum FoobarWithComment {}
]]></code-fragment>
<test-code>
<description>Top-level enums with default access modifier</description>
<expected-problems>2</expected-problems>
<expected-linenumbers>1,6</expected-linenumbers>
<code-ref id="top-level-enums-with-default-access-modifier"/>
</test-code>
<code-fragment id="top-level-classes-with-default-access-modifier"><![CDATA[
class Bar {}
public class Foo {}
@SomeAnnotation
class Baz {}
@VisibleForTesting
class Foobar {}
/* default */ class FoobarWithComment {}
]]></code-fragment>
<test-code>
<description>Top-level classes with default access modifier</description>
<expected-problems>2</expected-problems>
<expected-linenumbers>1,6</expected-linenumbers>
<code-ref id="top-level-classes-with-default-access-modifier"/>
</test-code>
<code-fragment id="own-regex-to-default-access-modifier-rule"><![CDATA[
public class Foo {
/* default */ final String stringValue = "stringValue";