diff --git a/its/ruling/src/test/resources/guava/java-S9149.json b/its/ruling/src/test/resources/guava/java-S9149.json index 27b7bf41cdf..668d592520d 100644 --- a/its/ruling/src/test/resources/guava/java-S9149.json +++ b/its/ruling/src/test/resources/guava/java-S9149.json @@ -1,7 +1,4 @@ { -"com.google.guava:guava:src/com/google/common/collect/ContiguousSet.java": [ -193 -], "com.google.guava:guava:src/com/google/common/collect/ImmutableBiMap.java": [ 41, 48, @@ -40,43 +37,15 @@ 180, 218 ], -"com.google.guava:guava:src/com/google/common/collect/ImmutableSortedMapFauxverideShim.java": [ -37, -51, -65, -80, -95, -110 -], "com.google.guava:guava:src/com/google/common/collect/ImmutableSortedMultiset.java": [ 63, 171, 189 ], -"com.google.guava:guava:src/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java": [ -44, -58, -72, -86, -100, -115, -130, -145 -], "com.google.guava:guava:src/com/google/common/collect/ImmutableSortedSet.java": [ 78, 200, 237, 256 -], -"com.google.guava:guava:src/com/google/common/collect/ImmutableSortedSetFauxverideShim.java": [ -46, -60, -74, -88, -103, -118, -133, -147 ] } diff --git a/java-checks-test-sources/default/pom.xml b/java-checks-test-sources/default/pom.xml index 4a13f6cb24b..14a5fa4c4a4 100644 --- a/java-checks-test-sources/default/pom.xml +++ b/java-checks-test-sources/default/pom.xml @@ -985,7 +985,7 @@ org.jetbrains annotations - 13.0 + 24.0.1 provided diff --git a/java-checks-test-sources/default/src/main/java/checks/StaticMethodHidingCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/StaticMethodHidingCheckSample.java index fab2f12f23b..482141e71a2 100644 --- a/java-checks-test-sources/default/src/main/java/checks/StaticMethodHidingCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/StaticMethodHidingCheckSample.java @@ -1,6 +1,9 @@ package checks; +import com.google.errorprone.annotations.DoNotCall; +import com.google.errorprone.annotations.InlineMe; import java.util.List; +import org.jetbrains.annotations.ApiStatus; class StaticMethodHidingCheckSample { @@ -223,4 +226,109 @@ static class MultiChild extends MultiParent { static void second() { // Noncompliant {{Rename this method; it hides "second" in "MultiParent".}} } } + + // --- Compliant: intentional hiding with @Deprecated annotation --- + + static class DeprecatingParent { + static void oldMethod() { + } + + static String convert(String input) { + return input; + } + } + + static class DeprecatingChild extends DeprecatingParent { + @Deprecated + static void oldMethod() { // Compliant - intentional hiding with @Deprecated + throw new UnsupportedOperationException(); + } + + @Deprecated + static String convert(String input) { // Compliant - intentional hiding with @Deprecated + throw new UnsupportedOperationException(); + } + } + + // --- Compliant: intentional hiding with @DoNotCall annotation --- + + static class DoNotCallParent { + static void unsafeMethod() { + } + } + + static class DoNotCallChild extends DoNotCallParent { + @DoNotCall("Use alternative method") + static void unsafeMethod() { // Compliant - intentional hiding with @DoNotCall + throw new UnsupportedOperationException(); + } + } + + // --- Compliant: intentional hiding with @InlineMe annotation --- + + static class InlineMeParent { + static String oldFormat(String input) { + return input; + } + } + + static class InlineMeChild extends InlineMeParent { + @InlineMe(replacement = "InlineMeParent.newFormat(input)") + static String oldFormat(String input) { // Compliant - intentional hiding with @InlineMe + return input; + } + } + + // --- Compliant: intentional hiding with @ApiStatus.Obsolete annotation --- + + static class ObsoleteParent { + static void oldApi() { + } + } + + static class ObsoleteChild extends ObsoleteParent { + @ApiStatus.Obsolete + static void oldApi() { // Compliant - intentional hiding with @ApiStatus.Obsolete + } + } + + // --- Compliant: intentional hiding with @ApiStatus.ScheduledForRemoval annotation --- + + static class ScheduledForRemovalParent { + static void legacyMethod() { + } + } + + static class ScheduledForRemovalChild extends ScheduledForRemovalParent { + @ApiStatus.ScheduledForRemoval + static void legacyMethod() { // Compliant - intentional hiding with @ApiStatus.ScheduledForRemoval + } + } + + // --- Compliant: intentional hiding with both @Deprecated and @DoNotCall --- + + static class CombinedParent { + static void legacyApi() { + } + } + + static class CombinedChild extends CombinedParent { + @Deprecated + @DoNotCall("Use newApi instead") + static void legacyApi() { // Compliant - intentional hiding with @Deprecated and @DoNotCall + throw new UnsupportedOperationException(); + } + } + + // --- Noncompliant: hiding without any deprecation annotation --- + + static class PlainParent { + static void compute() { + } + } + + static class PlainChild extends PlainParent { + static void compute() { // Noncompliant {{Rename this method; it hides "compute" in "PlainParent".}} + } + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/StaticMethodHidingCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StaticMethodHidingCheck.java index 5318c0712f0..858900a4ce6 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StaticMethodHidingCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StaticMethodHidingCheck.java @@ -41,7 +41,7 @@ public void visitNode(Tree tree) { } MethodTree methodTree = (MethodTree) tree; Symbol.MethodSymbol methodSymbol = methodTree.symbol(); - if (!methodSymbol.isStatic()) { + if (!methodSymbol.isStatic() || isIntentionalHiding(methodSymbol)) { return; } Symbol.TypeSymbol owner = (Symbol.TypeSymbol) methodSymbol.owner(); @@ -79,6 +79,15 @@ private void reportHidingIssue(MethodTree methodTree, Symbol.MethodSymbol method } } + private static boolean isIntentionalHiding(Symbol.MethodSymbol methodSymbol) { + return methodSymbol.metadata().isAnnotatedWith("java.lang.Deprecated") + || methodSymbol.metadata().isAnnotatedWith("kotlin.Deprecated") + || methodSymbol.metadata().isAnnotatedWith("com.google.errorprone.annotations.DoNotCall") + || methodSymbol.metadata().isAnnotatedWith("com.google.errorprone.annotations.InlineMe") + || methodSymbol.metadata().isAnnotatedWith("org.jetbrains.annotations.ApiStatus$Obsolete") + || methodSymbol.metadata().isAnnotatedWith("org.jetbrains.annotations.ApiStatus$ScheduledForRemoval"); + } + private static boolean hasSameParameterTypes(Symbol.MethodSymbol method, Symbol.MethodSymbol candidate) { List methodParams = method.parameterTypes(); List candidateParams = candidate.parameterTypes();