diff --git a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties b/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties --- a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties +++ b/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties @@ -332,6 +332,10 @@ DN_org.netbeans.modules.java.hints.ClassStructure.finalMethodInFinalClass=Final method in final class DESC_org.netbeans.modules.java.hints.ClassStructure.finalMethodInFinalClass=Reports any instances of methods being declared final in classes that are declared final. This is unnecessary, and may be confusing. MSG_FinalMethodInFinalClass=Method {0} is declared final in final class +DN_org.netbeans.modules.java.hints.ClassStructure.publicOnInterfaceMember=Superfluous public modifier on interface member +DESC_org.netbeans.modules.java.hints.ClassStructure.publicOnInterfaceMember=All interface members are implicitly public; using the 'public' modifier is superfluous and misleading. +MSG_PublicOnInterfaceMember=Member {0} is declared public in an interface +FIX_PublicOnInterfaceMember=Remove public modifier from interface member DN_org.netbeans.modules.java.hints.ClassStructure.noopMethodInAbstractClass=No-op method in abstract class DESC_org.netbeans.modules.java.hints.ClassStructure.noopMethodInAbstractClass=Reports any instances of no-op methods in abstract classes. It is usually a better design to make such methods abstract themselves, so that classes which inherit the methods will not forget to provide their own implementations. MSG_NoopMethodInAbstractClass=No-op method {0} should be made abstract diff --git a/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java b/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java --- a/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java +++ b/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java @@ -56,6 +56,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; @@ -146,6 +147,41 @@ return null; } + @Hint(category = "class_structure", enabled = true, suppressWarnings = {"PublicOnInterfaceMember"}) + @TriggerTreeKind({Kind.METHOD, Kind.VARIABLE, Kind.CLASS}) + public static ErrorDescription publicOnInterfaceMember(HintContext context) { + final Tree parent = context.getPath().getParentPath().getLeaf(); + if (parent.getKind() == Kind.CLASS) { + if (context.getInfo().getTreeUtilities().isInterface((ClassTree) parent) || context.getInfo().getTreeUtilities().isAnnotation((ClassTree) parent)) { + Tree member = context.getPath().getLeaf(); + ModifiersTree modifiers; + Name name; + switch (member.getKind()) { + case METHOD: + modifiers = ((MethodTree) member).getModifiers(); + name = ((MethodTree) member).getName(); + break; + case VARIABLE: + modifiers = ((VariableTree) member).getModifiers(); + name = ((VariableTree) member).getName(); + break; + case CLASS: + modifiers = ((ClassTree) member).getModifiers(); + name = ((ClassTree) member).getSimpleName(); + break; + default: + throw new AssertionError(); + } + if (modifiers.getFlags().contains(Modifier.PUBLIC)) { + return ErrorDescriptionFactory.forName(context, member, NbBundle.getMessage(ClassStructure.class, "MSG_PublicOnInterfaceMember", name), + FixFactory.removeModifiersFix(context.getInfo(), TreePath.getPath(context.getPath(), modifiers), EnumSet.of(Modifier.PUBLIC), NbBundle.getMessage(ClassStructure.class, "FIX_PublicOnInterfaceMember", name)), + FixFactory.createSuppressWarningsFix(context.getInfo(), context.getPath(), "PublicOnInterfaceMember")); // NOI18N + } + } + } + return null; + } + @Hint(category = "class_structure", enabled = false, suppressWarnings = {"NoopMethodInAbstractClass"}) //NOI18N @TriggerTreeKind(Kind.METHOD) public static ErrorDescription noopMethodInAbstractClass(HintContext context) { diff --git a/java.hints/test/unit/src/org/netbeans/modules/java/hints/Bundle_test.properties b/java.hints/test/unit/src/org/netbeans/modules/java/hints/Bundle_test.properties --- a/java.hints/test/unit/src/org/netbeans/modules/java/hints/Bundle_test.properties +++ b/java.hints/test/unit/src/org/netbeans/modules/java/hints/Bundle_test.properties @@ -63,6 +63,8 @@ FIX_RemoveFinalFromClass=Remove final modifier from the {0} class declaration MSG_FinalMethod=Method {0} is declared final FIX_RemoveFinalFromMethod=Remove final modifier from the {0} method declaration +MSG_PublicOnInterfaceMember=Member {0} is declared public in an interface +FIX_PublicOnInterfaceMember=Remove public modifier from interface member MSG_FinalPrivateMethod=Private method {0} is declared final MSG_FinalStaticMethod=Static method {0} is declared final MSG_FinalMethodInFinalClass=Method {0} is declared final in final class diff --git a/java.hints/test/unit/src/org/netbeans/modules/java/hints/ClassStructureTest.java b/java.hints/test/unit/src/org/netbeans/modules/java/hints/ClassStructureTest.java --- a/java.hints/test/unit/src/org/netbeans/modules/java/hints/ClassStructureTest.java +++ b/java.hints/test/unit/src/org/netbeans/modules/java/hints/ClassStructureTest.java @@ -204,6 +204,62 @@ } @Test + public void testPublicOnInterfaceMember() throws Exception { + performFixTest("test/Test.java", + "package test;\n" + + "public interface Test {\n" + + " public void m();\n" + + "}", + "2:16-2:17:verifier:Member m is declared public in an interface", + "Remove public modifier from interface member", + "package test; public interface Test { void m(); }"); + performAnalysisExcludesTest("test/Test.java", + "package test;\n" + + "public interface Test {\n" + + " void m();\n" + + "}", + "Member m is declared public in an interface"); + performFixTest("test/Test.java", + "package test;\n" + + "public @interface Test {\n" + + " public void m();\n" + + "}", + "2:16-2:17:verifier:Member m is declared public in an interface", + "Remove public modifier from interface member", + "package test; public @interface Test { void m(); }"); + performFixTest("test/Test.java", + "package test;\n" + + "public interface Test {\n" + + " public int X = 5;\n" + + "}", + "2:15-2:16:verifier:Member X is declared public in an interface", + "Remove public modifier from interface member", + "package test; public interface Test { int X = 5; }"); + performAnalysisExcludesTest("test/Test.java", + "package test;\n" + + "public interface Test {\n" + + " class Nested {public int var;}\n" + + "}", + "Member Nested is declared public in an interface"); + performFixTest("test/Test.java", + "package test;\n" + + "public interface Test {\n" + + " public class Nested {}\n" + + "}", + "2:17-2:23:verifier:Member Nested is declared public in an interface", + "Remove public modifier from interface member", + "package test; public interface Test { class Nested {} }"); + performFixTest("test/Test.java", + "package test;\n" + + "public interface Test {\n" + + " public interface Nested {}\n" + + "}", + "2:21-2:27:verifier:Member Nested is declared public in an interface", + "Remove public modifier from interface member", + "package test; public interface Test { interface Nested {} }"); + } + + @Test public void testNoopMethodInAbstractClass() throws Exception { performAnalysisContainsTest("test/Test.java", "package test;\n" +