diff --git a/CHANGELOG.md b/CHANGELOG.md index 613f4c7cab5..84125fd9337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ Currently the versioning policy of this project follows [Semantic Versioning v2. - Do not report BC_UNCONFIRMED_CAST for Java 21's type switches when the switch instruction is TABLESWITCH ([#2782](https://github.com/spotbugs/spotbugs/issues/2782)) - Do not throw exception when inspecting empty switch statements ([#2995](https://github.com/spotbugs/spotbugs/issues/2995)) - Adjust priority since relaxed mode reports even `IGNORED_PRIORITY` ([#2994]https://github.com/spotbugs/spotbugs/issues/2994) +- +### Added +- New detector `FindHiddenMethod` for bug type `HSM_HIDING_METHOD`. This bug is reported whenever a subclass method hides the static method of super class. (See [SEI CERT MET07-J] (https://wiki.sei.cmu.edu/confluence/display/java/MET07-J.+Never+declare+a+class+method+that+hides+a+method+declared+in+a+superclass+or+superinterface)). ## 4.8.5 - 2024-05-03 ### Fixed diff --git a/spotbugs-tests/src/test/java/edu/umd/cs/findbugs/detect/FindHiddenMethodTest.java b/spotbugs-tests/src/test/java/edu/umd/cs/findbugs/detect/FindHiddenMethodTest.java new file mode 100644 index 00000000000..ea0e693a41a --- /dev/null +++ b/spotbugs-tests/src/test/java/edu/umd/cs/findbugs/detect/FindHiddenMethodTest.java @@ -0,0 +1,177 @@ +package edu.umd.cs.findbugs.detect; + +import static edu.umd.cs.findbugs.test.CountMatcher.containsExactly; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; + +import org.junit.jupiter.api.Test; +import edu.umd.cs.findbugs.AbstractIntegrationTest; +import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcher; +import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcherBuilder; + +class FindHiddenMethodTest extends AbstractIntegrationTest { + private final String BUG_TYPE = "HSM_HIDING_METHOD"; + + @Test + void testBadFindHiddenMethodTest() { + performAnalysis("findhiddenmethodtest/GrantAccessStatic.class", + "findhiddenmethodtest/BadFindHiddenMethodTest.class"); + assertHSBCBug("displayAccountStatus", 15); + } + + @Test + void testBadFindHiddenMethodTest2() { + performAnalysis("findhiddenmethodtest/BadSuperClass.class", + "findhiddenmethodtest/BadHidingVsOverriding.class"); + assertHSBCBug("methodHiding", 19); + } + + + @Test + void testBadFindHiddenMethodTest3() { + performAnalysis("findhiddenmethodtest/SuperBadInEqualMultipleMethod.class", + "findhiddenmethodtest/BadInEqualMultipleStaticMethod.class"); + assertHSBCBug("display", 25); + assertHSBCBug("display2", 29); + } + + @Test + void testBadFindHiddenMethodTest4() { + performAnalysis("findhiddenmethodtest/BadMainMethodCheck.class", + "findhiddenmethodtest/BadSuperClassWithMain.class"); + assertHSBCBug("main", 18); + } + + @Test + void testBadFindHiddenMethodTest5() { + performAnalysis("findhiddenmethodtest/BadGrandClass.class", + "findhiddenmethodtest/BadParentClass.class", + "findhiddenmethodtest/BadMultiLevelInheritance.class"); + assertHSBCBug("display", 20); + } + + @Test + void testBadFindHiddenMethodTest6() { + performAnalysis("findhiddenmethodtest/SuperBadMultipleMethods.class", + "findhiddenmethodtest/BadMultipleStaticMethods.class"); + assertHSBCBug("display", 19); + assertHSBCBug("display2", 23); + } + + @Test + void testBadFindHiddenMethodTest7() { + performAnalysis("findhiddenmethodtest/SuperBadNonOrderedMultipleStaticMethods.class", + "findhiddenmethodtest/BadNonOrderedMultipleStaticMethods.class"); + assertHSBCBug("display", 24); + assertHSBCBug("display2", 28); + } + + @Test + void testBadFindHiddenMethodTest8() { + performAnalysis("findhiddenmethodtest/BadOuterInnerClass.class", + "findhiddenmethodtest/BadOuterInnerClass$BadInner.class"); + assertHSBCBug("badMethod", 15); + } + + @Test + void testBadFindHiddenMethodTest9() { + performAnalysis("findhiddenmethodtest/SuperBadProtected.class", + "findhiddenmethodtest/BadProtected.class"); + assertHSBCBug("display", 15); + } + + @Test + void testGoodFindHiddenMethodTest() { + performAnalysis("findhiddenmethodtest/GrantAccess.class", + "findhiddenmethodtest/GoodFindHiddenMethodTest.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest2() { + performAnalysis("findhiddenmethodtest/SuperGoodInEqualMultipleMethod.class", + "findhiddenmethodtest/GoodInEqualNonStaticMethods.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest3() { + performAnalysis("findhiddenmethodtest/GoodSuperClassWithMain.class", + "findhiddenmethodtest/GoodMainMethodCheck.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest4() { + performAnalysis("findhiddenmethodtest/GoodGrandNonStatic.class", + "findhiddenmethodtest/GoodParentNonStatic.class", + "findhiddenmethodtest/GoodMultiLevelInheritanceNonStatic.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest5() { + performAnalysis("findhiddenmethodtest/GoodGrandClassPrivate.class", + "findhiddenmethodtest/GoodParentClassPrivate.class", + "findhiddenmethodtest/GoodMultiLevelInheritancePrivate.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest6() { + performAnalysis("findhiddenmethodtest/SuperMultipleNonStatic.class", + "findhiddenmethodtest/GoodMultipleNonStaticMethods.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest7() { + performAnalysis("findhiddenmethodtest/SuperGoodMultipleStaticMethod.class", + "findhiddenmethodtest/GoodMultipleStaticMethod.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest8() { + performAnalysis("findhiddenmethodtest/SuperGoodNonOrderedMultipleStaticMethods.class", + "findhiddenmethodtest/GoodNonOrderedMultipleNonStaticMethods.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest9() { + performAnalysis("findhiddenmethodtest/GoodOuterInnerClassNonStatic.class", + "findhiddenmethodtest/GoodOuterInnerClassNonStatic$GoodInnerNonStaticGood.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest10() { + performAnalysis("findhiddenmethodtest/GoodOuterInnerClassPrivate.class", + "findhiddenmethodtest/GoodOuterInnerClassPrivate$GoodInnerClassPrivate.class"); + assertNoHSBCBug(); + } + + @Test + void testGoodFindHiddenMethodTest11() { + performAnalysis("findhiddenmethodtest/SuperGoodProtected.class", + "findhiddenmethodtest/GoodProtected.class"); + assertNoHSBCBug(); + } + + private void assertNoHSBCBug() { + final BugInstanceMatcher bugTypeMatcher = new BugInstanceMatcherBuilder() + .bugType(BUG_TYPE) + .build(); + assertThat(getBugCollection(), containsExactly(0, bugTypeMatcher)); + } + + private void assertHSBCBug(String methodName, int lineNumber) { + final BugInstanceMatcher bugTypeMatcher = new BugInstanceMatcherBuilder() + .bugType(BUG_TYPE) + .inMethod(methodName) + .atLine(lineNumber) + .build(); + assertThat(getBugCollection(), hasItem(bugTypeMatcher)); + } +} diff --git a/spotbugs/etc/findbugs.xml b/spotbugs/etc/findbugs.xml index b261aa0a959..c4b0e6dea6e 100644 --- a/spotbugs/etc/findbugs.xml +++ b/spotbugs/etc/findbugs.xml @@ -686,7 +686,10 @@ reports="ENV_USE_PROPERTY_INSTEAD_OF_ENV"/> - + + + + +
+ + Looks for hiding of static, non-private methods by the subclass. + It has exceptions for main method, constructor, inner classes, and auto generated methods. +

+ ]]> +
+
+