diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java index b66d08bef..832f4af40 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java @@ -63,8 +63,8 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu return m; } } else { - // This invocation is an argument of the enclosing invocation. - if (shouldRetainOnStaticMethod(methodType)) { + // As above, retain unless inferable from this call's own arguments (static or not). + if (!canInferTypeArgumentsFromArguments(methodType)) { return m; } // Cannot remove type parameters if it would introduce ambiguity about which method should be called @@ -153,13 +153,6 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu return samReturn; } - private boolean shouldRetainOnStaticMethod(JavaType.Method methodType) { - // Without a target type, removing the explicit type arguments of a static method can break - // overload resolution in the enclosing call when the type variables are not inferable from - // the call's own arguments. - return methodType.hasFlags(Flag.Static) && !canInferTypeArgumentsFromArguments(methodType); - } - private boolean canInferTypeArgumentsFromArguments(JavaType.Method methodType) { // Without arguments, the type parameters cannot be inferred from call-site arguments. if (methodType.getParameterTypes().isEmpty()) { diff --git a/src/test/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArgumentsTest.java b/src/test/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArgumentsTest.java index 19bd9cfd5..58c7b81c6 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArgumentsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArgumentsTest.java @@ -246,6 +246,75 @@ String test(Cursor c) { ); } + @Test + void retainsWitnessOnInstanceMethodPassedAsArgumentToGenericMethod() { + // STATE_VALUE isn't inferable from source()'s (zero) args or from of()'s target type. + rewriteRun( + //language=java + java( + """ + interface Transition { + STATE_VALUE source(); + } + + class Pair> { + static > Pair of(STATE_VALUE a, STATE_VALUE b) { + return new Pair<>(); + } + } + + class Test> { + Pair test(Transition t, STATE_VALUE b) { + return Pair.of(t.source(), b); + } + } + """ + ) + ); + } + + @Test + void removesWitnessOnInstanceMethodArgumentWhenInferableFromOwnArguments() { + // Unlike above, T is inferable from identity()'s own argument, so it's still removable. + rewriteRun( + //language=java + java( + """ + class Holder { + T identity(T value) { + return value; + } + } + + class Test { + void accept(Object o) { + } + + void test(Holder h) { + accept(h.identity("x")); + } + } + """, + """ + class Holder { + T identity(T value) { + return value; + } + } + + class Test { + void accept(Object o) { + } + + void test(Holder h) { + accept(h.identity("x")); + } + } + """ + ) + ); + } + @Nested class StaticMethods { static final SourceSpecs GENERIC_CLASS_SOURCE = java(