Skip to content

Use caller reference in UseMethod #5391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

fmodesto
Copy link
Contributor

@fmodesto fmodesto commented May 6, 2025

What's changed?

Modified UsesMethod to track the actual invocation target type, rather than just the method's declaring type.

Example Case:

class A { public void foo() {} }
interface I { void foo(); }
class B extends A implements I {}

class Test {
  void test() {
    new B().foo();
  }
}

The declaring type of the method foo is A. But that misses matches on the interface I

What's your motivation?

Anything in particular you'd like reviewers to focus on?

This has a downstream regression in rewrite-static-analysis in CompareEnumsWithEqualityOperator. UseMethod matches for the exact java.lang.Enum equals(java.lang.Object) without overrides. This change would make the actual enum to use equals: com.pkg.MyEnum equals(java.lang.Object).

Checklist

  • I've added unit tests to cover both positive and negative cases
  • I've read and applied the recipe conventions and best practices
  • I've used the IntelliJ IDEA auto-formatter on affected files

@timtebeek
Copy link
Member

Thanks for the help! Copying the failure from the logs here to make it easier to follow up:

JavadocTest > methodFoundInSuperclassBecauseSuperclassFieldsTakesPresenceOverInterfaceFields() FAILED
    org.opentest4j.AssertionFailedError: expected: <SomeParent2> but was: <Test>
        at app//org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
        at app//org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
        at app//org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
        at app//org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
        at app//org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
        at app//org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1145)
        at app//org.openrewrite.java.tree.JavadocTest.lambda$methodFoundInSuperclassBecauseSuperclassFieldsTakesPresenceOverInterfaceFields$4(JavadocTest.java:1262)
        at app//org.openrewrite.java.tree.JavadocTest$$Lambda$1064/0x00000001005bb440.accept0(Unknown Source)
        at app//org.openrewrite.internal.ThrowingConsumer.accept(ThrowingConsumer.java:26)
        at app//org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:576)
        at app//org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:130)
        at app//org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:125)
        at app//org.openrewrite.java.tree.JavadocTest.methodFoundInSuperclassBecauseSuperclassFieldsTakesPresenceOverInterfaceFields(JavadocTest.java:1234)

@Test
void methodFoundInSuperclassBecauseSuperclassFieldsTakesPresenceOverInterfaceFields() {
rewriteRun(
java(
"""
import javax.swing.text.html.HTML.Tag;
interface SomeInterface {
int test();
}
abstract class SomeParent2 implements SomeInterface {
abstract boolean test();
}
abstract class SomeParent extends SomeParent2 implements SomeInterface {
}
"""
),
java(
"""
class Test extends SomeParent implements SomeInterface {
/**
* @see #test()
*/
void method() {}
}
""",
spec -> spec.afterRecipe(cu -> {
assertEquals("test", cu.getTypesInUse().getUsedMethods().iterator().next().getName());
assertEquals("SomeParent2", cu.getTypesInUse().getUsedMethods().iterator().next().getDeclaringType().getFullyQualifiedName());
})
)
);
}

@timtebeek timtebeek moved this from In Progress to Ready to Review in OpenRewrite May 7, 2025
@timtebeek
Copy link
Member

timtebeek commented May 7, 2025

Great to see! Linking this discussion here as I imagine we'd need that visit(memberRef.getContaining(), compareTo.getContaining()); reintroduced here before a merge:

@fmodesto fmodesto marked this pull request as draft May 7, 2025 19:59
@fmodesto
Copy link
Contributor Author

fmodesto commented May 7, 2025

Causes failures in rewrite-static-analysis

@fmodesto
Copy link
Contributor Author

fmodesto commented May 7, 2025

Not sure about this one, the issue is in rewrite-static-analysis CompareEnumsWithEqualityOperator UseMethod matches for the exact java.lang.Enum equals(java.lang.Object) without overrides, now it comes as the.actual.Enum equals(java.lang.Object)

@fmodesto fmodesto marked this pull request as ready for review May 9, 2025 05:41
@timtebeek timtebeek requested a review from knutwannheden May 9, 2025 09:25
@timtebeek timtebeek added the enhancement New feature or request label May 9, 2025
Copy link
Contributor

@knutwannheden knutwannheden left a comment

Choose a reason for hiding this comment

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

I am not opposed to this change. Just hesitate because it changes the TypesInUse cache.

@ValueSource(strings = {
"java.util.Collection contains(..)",
"java.util.Set contains(..)",
"com.google.common.collect.ImmutableSet contains(..)",
Copy link
Contributor

Choose a reason for hiding this comment

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

As ImmutableSet doesn't actually declare contains(Object) I think we should correct this:

Suggested change
"com.google.common.collect.ImmutableSet contains(..)",
"com.google.common.collect.ImmutableCollection contains(..)",

@Issue("https://github.com/openrewrite/rewrite/issues/5376")
@ValueSource(strings = {
"java.util.Collection contains(..)",
"java.util.Set contains(..)",
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that this is what the issue is about. Appears to be quite the corner case, as in this case the implementation is inherited from ImmutableCollection but at the same time ImmutableSet also implements Set, which indeed overrides the contains(Object) method.

Curiously enough opening the call graph for Set#contains(Object) in IDEA doesn't show this call site either.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: Ready to Review
Development

Successfully merging this pull request may close these issues.

3 participants