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
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,35 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu

JavaType inferredType = null;
if (enclosing instanceof J.MethodInvocation) {
if (shouldRetainOnStaticMethod(methodType)) {
return m;
}
// Cannot remove type parameters if it would introduce ambiguity about which method should be called
J.MethodInvocation enclosingMethod = (J.MethodInvocation) enclosing;
if (enclosingMethod.getMethodType() == null) {
return m;
}
if (!(enclosingMethod.getMethodType().getDeclaringType() instanceof JavaType.Class)) {
return m;
}
JavaType.Class declaringClass = (JavaType.Class) enclosingMethod.getMethodType().getDeclaringType();
// If there's another method on the class with the same name, skip removing type parameters
// More nuanced detection of ambiguity introduction is possible
if (declaringClass.getMethods().stream()
.filter(it -> it.getName().equals(enclosingMethod.getSimpleName()))
.count() > 1) {
return m;
if (enclosingMethod.getSelect() == method) {
// This invocation is the select (receiver) of the enclosing invocation, so the
// enclosing call provides no target type to drive inference of this call's type
// variables. Retain the witness unless those type variables can be inferred from
// this call's own arguments.
if (!canInferTypeArgumentsFromArguments(methodType)) {
return m;
}
} else {
// This invocation is an argument of the enclosing invocation.
if (shouldRetainOnStaticMethod(methodType)) {
return m;
}
// Cannot remove type parameters if it would introduce ambiguity about which method should be called
if (enclosingMethod.getMethodType() == null) {
return m;
}
if (!(enclosingMethod.getMethodType().getDeclaringType() instanceof JavaType.Class)) {
return m;
}
JavaType.Class declaringClass = (JavaType.Class) enclosingMethod.getMethodType().getDeclaringType();
// If there's another method on the class with the same name, skip removing type parameters
// More nuanced detection of ambiguity introduction is possible
if (declaringClass.getMethods().stream()
.filter(it -> it.getName().equals(enclosingMethod.getSimpleName()))
.count() > 1) {
return m;
}
}
inferredType = methodType.getReturnType();
} else if (enclosing instanceof Expression) {
Expand Down Expand Up @@ -143,21 +154,24 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu
}

private boolean shouldRetainOnStaticMethod(JavaType.Method methodType) {
if (!methodType.hasFlags(Flag.Static)) {
return false;
}
// Without arguments, the type parameter cannot be inferred from call-site parameters.
// Removing the explicit type arguments can break overload resolution in the enclosing call.
// 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()) {
return true;
return false;
}
List<String> formalTypeNames = new ArrayList<>(methodType.getDeclaredFormalTypeNames());
methodType.getParameterTypes().stream()
.filter(p -> p instanceof JavaType.Parameterized)
.flatMap(p -> ((JavaType.Parameterized) p).getTypeParameters().stream())
.filter(t -> t instanceof JavaType.GenericTypeVariable)
.forEach(it -> formalTypeNames.remove(((JavaType.GenericTypeVariable) it).getName()));
return !formalTypeNames.isEmpty();
return formalTypeNames.isEmpty();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,36 @@ List<String> test() {
);
}

@Test
void retainsWitnessWhenResultIsSelectOfMethodInvocation() {
rewriteRun(
//language=java
java(
"""
import java.util.List;

class VarDec {
List<String> getVariables() {
return null;
}
}

class Cursor {
<T> T getValue() {
return null;
}
}

class Test {
String test(Cursor c) {
return c.<VarDec>getValue().getVariables().get(0);
}
}
"""
)
);
}

@Nested
class StaticMethods {
static final SourceSpecs GENERIC_CLASS_SOURCE = java(
Expand Down
Loading