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();