rewrite icon indicating copy to clipboard operation
rewrite copied to clipboard

UseMethod precondition check, fails to detect the usage

Open fmodesto opened this issue 8 months ago • 3 comments

First test pass, second test fails.

The only difference I can see is Map.keySet() returns the Set interface and ImmutableMap.keySet() returns an ImmutableSet.

    @Test
    void usesMethodDeepHierarchy() {
        rewriteRun(
          spec -> spec.recipe(toRecipe(() -> new UsesMethod<>("java.util.Set contains(..)"))),
          java(
            """
              import java.util.Map;
              
              class Test {
                  void test() {
                      Map.of("1", 4, "2", 5).keySet().contains("3");
                  }
              }
              """,
            """
              /*~~>*/import java.util.Map;
              
              class Test {
                  void test() {
                      Map.of("1", 4, "2", 5).keySet().contains("3");
                  }
              }
              """
          )
        );
    }

    @Test
    void usesMethodDeepHierarchyExternalDep() {
        rewriteRun(
          spec -> spec.recipe(toRecipe(() -> new UsesMethod<>("java.util.Set contains(..)")))
            .parser(JavaParser.fromJavaVersion().classpath("guava")),
          java(
            """
              import com.google.common.collect.ImmutableMap;
              
              class Test {
                  void test() {
                      ImmutableMap.of("1", 4, "2", 5).keySet().contains("3");
                  }
              }
              """,
            """
              /*~~>*/import com.google.common.collect.ImmutableMap;
              
              class Test {
                  void test() {
                      ImmutableMap.of("1", 4, "2", 5).keySet().contains("3");
                  }
              }
              """
          )
        );
    }

fmodesto avatar May 04 '25 12:05 fmodesto

Hmm; I indeed see the type information for that Guava contains being linked to com.google.common.collect.ImmutableCollection contains(java.lang.Object), which fits in with their abstract override, which according to our type system is not an override (isOverride() returns false). That then likely factors in with the failed match here.

/cc @knutwannheden

timtebeek avatar May 04 '25 13:05 timtebeek

This comes from a refaster template that generates the precondition:

return Preconditions.check(
        Preconditions.and(
            new UsesType<>("java.util.Map", true),
            new UsesMethod<>("java.util.Map keySet(..)", true),
            new UsesMethod<>("java.util.Set contains(..)", true)
        ),
        javaVisitor
);

Set.contains() overrides the Collection.contains(), I'm not sure if we should add logic there to use the base method.

fmodesto avatar May 05 '25 11:05 fmodesto

The precondition looks correct to me. If the user wanted to match Collection#contains(), then the input parameter should be a Collection and not a Set. But I wonder if we do this correctly when the type doesn't override a specific inherited method 🤔

As far as that isOverride() problem is concerned, that appears to be a bug.

knutwannheden avatar May 05 '25 11:05 knutwannheden