rewrite-static-analysis
rewrite-static-analysis copied to clipboard
`UnnecessaryExplicitTypeArguments` incorrectly removes type parameter needed for dependent type parameters.
The UnnecessaryExplicitTypeArguments recipe incorrectly removed an explicit type parameter in a location where it was necessary (specifically, a method with type parameters where one type parameter extends another).
- This is similar but distinct from the previously fixed issue #164 "Unnecessary explicit type arguments removes necessary type arguments." Here type arguments needed for methods with dependent type parameters (e.g.,
<T, S extends T>).
Minimal Reproduction
import java.util.Comparator;
import java.util.List;
public class MinimalReproOpenRewriteBug {
/**
* A method with stricter type bounds than standard Java APIs.
* The type parameter S extends T, which requires explicit type specification
* when T cannot be inferred from the Comparator argument alone.
*/
public static <T, S extends T> Comparator<Iterable<S>> lexicographical(Comparator<T> comparator) {
return (list1, list2) -> {
var it1 = list1.iterator();
var it2 = list2.iterator();
while (it1.hasNext() && it2.hasNext()) {
int cmp = comparator.compare(it1.next(), it2.next());
if (cmp != 0) {
return cmp;
}
}
return Boolean.compare(it1.hasNext(), it2.hasNext());
};
}
// This works - explicit type parameter
private static final Comparator<Iterable<Integer>> WORKS =
lexicographical(Comparator.<Integer>naturalOrder());
// This fails to compile - type parameter removed by OpenRewrite
private static final Comparator<Iterable<Integer>> FAILS =
lexicographical(Comparator.naturalOrder());
record Example(List<Integer> numbers) implements Comparable<Example> {
private static final Comparator<Example> COMPARATOR =
Comparator.comparing(Example::numbers, WORKS);
@Override
public int compareTo(Example other) {
return COMPARATOR.compare(this, other);
}
}
}
OpenRewrite should preserve the explicit type parameters here. What actually happened it changed:
Comparator.<Integer>naturalOrder()
to:
Comparator.naturalOrder()
This causes the compilation error:
MinimalReproOpenRewriteBug.java:50: error: method lexicographical in class MinimalReproOpenRewriteBug cannot be applied to given types;
lexicographical(Comparator.naturalOrder());
^
- required: Comparator<T#1>
- found: Comparator<T#2>
- reason: inference variable S has incompatible upper bounds T#3,Comparable<? super T#3>,Object,T#1
- where T#1,S,T#2,T#3 are type-variables:
- T#1 extends Object declared in method <T#1,S>lexicographical(Comparator<T#1>)
- S extends T#1 declared in method <T#1,S>lexicographical(Comparator<T#1>)
- T#2 extends Comparable<? super T#2>
- T#3 extends Comparable<? super T#3> declared in method <T#3>naturalOrder()
1 error
Thanks for the report @motlin ! Helpful to see your case of <T, S extends T> Comparator<Iterable<S>>. Now we need to figure out how to recognize that case and avoid removing the explicit type arguments when used as an argument to such a method.