Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 0 additions & 31 deletions its/ruling/src/test/resources/guava/java-S9149.json
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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
]
}
2 changes: 1 addition & 1 deletion java-checks-test-sources/default/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<version>24.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {

Expand Down Expand Up @@ -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".}}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be worth considering additional common decorators with similar meaning:

  • com.google.errorprone.annotations.InlineMe
  • org.jetbrains.annotations.ApiStatus.Obsolete
  • org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
  • kotlin.Deprecated

Also, have you considered updating the RSPEC to document these exceptions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion! I've added all four annotations to isIntentionalHiding:

  • com.google.errorprone.annotations.InlineMe
  • org.jetbrains.annotations.ApiStatus.Obsolete
  • org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
  • kotlin.Deprecated

Test cases have been added for InlineMe, ApiStatus.Obsolete, and ApiStatus.ScheduledForRemoval. The kotlin.Deprecated annotation can't be used from Java source code (it requires Kotlin-specific parameter types), but it's kept in the implementation to handle Kotlin-compiled bytecode from binary dependencies.

I also bumped the JetBrains annotations dependency from 13.0 to 24.0.1 in the test sources to get the ApiStatus annotations.

|| 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");
}
Comment on lines +82 to +89

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: @deprecated blanket-suppresses S9149, risking false negatives

isIntentionalHiding treats any @deprecated hiding method as intentional. @deprecated is applied for many reasons unrelated to method hiding (e.g. a method scheduled for removal that accidentally hides a parent static method), so this may suppress genuinely accidental hiding that developers still want flagged. This is a reasonable tradeoff to remove the Guava FPs, but consider whether @DonotCall alone (a much more specific signal) would suffice, or document this broadening in the rule metadata so users understand deprecated methods are now exempt.

Was this helpful? React with 👍 / 👎


private static boolean hasSameParameterTypes(Symbol.MethodSymbol method, Symbol.MethodSymbol candidate) {
List<Type> methodParams = method.parameterTypes();
List<Type> candidateParams = candidate.parameterTypes();
Expand Down
Loading