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 @@ -23,11 +23,14 @@
import org.openrewrite.java.tree.*;
import org.openrewrite.marker.Markers;

import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.UnaryOperator;

import static java.util.Collections.emptyList;
import static java.util.Collections.newSetFromMap;
import static java.util.Collections.singleton;
import static java.util.Objects.requireNonNull;
import static org.openrewrite.Tree.randomId;
Expand Down Expand Up @@ -216,23 +219,108 @@ private static boolean isInsideInitializer(Cursor cursor, int nestedBlockLevel)
* @return true iff is initialized by static method
*/
public static boolean initializedByStaticMethod(@Nullable Expression initializer) {
if (initializer == null) {
J.MethodInvocation invocation = getMethodInvocation(initializer);
if (invocation == null || invocation.getMethodType() == null) {
return false;
}
return invocation.getMethodType().hasFlags(Flag.Static);
}

/**
* Checks whether the initializer {@linkplain Expression} is a {@linkplain J.MethodInvocation} of a generic method
* whose type parameter is only inferable from the declared type, as in {@code <T> T getArgument(int index)}.
*
* @param initializer {@linkplain J.VariableDeclarations.NamedVariable#getInitializer()} value
* @return true iff is initialized by a generic method that needs the declared type to infer its return type
*/
public static boolean initializedByUnresolvableGenericMethod(@Nullable Expression initializer) {
J.MethodInvocation invocation = getMethodInvocation(initializer);
return invocation != null && isUnresolvableGenericMethod(invocation);
}

private static J.@Nullable MethodInvocation getMethodInvocation(@Nullable Expression initializer) {
if (initializer == null) {
return null;
}
initializer = initializer.unwrap();

if (!(initializer instanceof J.MethodInvocation)) {
// no MethodInvocation -> false
return null;
}

return (J.MethodInvocation) initializer;
}

private static boolean isUnresolvableGenericMethod(J.MethodInvocation mi) {
JavaType.Method mt = mi.getMethodType();
if (mt == null || mi.getTypeParameters() != null) {
return false;
}
// The invocation's types are already resolved against the declared type, so the declaration is consulted instead
return declaresUninferableReturnType(mt.getDeclaringType(), mt.getName(), mt.getParameterTypes().size(), newIdentitySet());
}

private static boolean declaresUninferableReturnType(JavaType.@Nullable FullyQualified clazz, String name, int arity, Set<JavaType> seen) {
if (clazz == null || !seen.add(clazz)) {
return false;
}
for (JavaType.Method method : clazz.getMethods()) {
if (name.equals(method.getName()) && method.getParameterTypes().size() == arity && returnsUninferableTypeParameter(method)) {
return true;
}
}
if (declaresUninferableReturnType(clazz.getSupertype(), name, arity, seen)) {
return true;
}
for (JavaType.FullyQualified anInterface : clazz.getInterfaces()) {
if (declaresUninferableReturnType(anInterface, name, arity, seen)) {
return true;
}
}
return false;
}

J.MethodInvocation invocation = (J.MethodInvocation) initializer;
if (invocation.getMethodType() == null) {
// not a static method -> false
private static boolean returnsUninferableTypeParameter(JavaType.Method method) {
for (String typeParameterName : method.getDeclaredFormalTypeNames()) {
if (mentions(method.getReturnType(), typeParameterName, newIdentitySet()) &&
!mentionsAny(method.getParameterTypes(), typeParameterName, newIdentitySet())) {
return true;
}
}
return false;
}

private static boolean mentions(JavaType type, String typeParameterName, Set<JavaType> seen) {
if (!seen.add(type)) {
return false;
}
if (type instanceof JavaType.GenericTypeVariable) {
JavaType.GenericTypeVariable generic = (JavaType.GenericTypeVariable) type;
if (typeParameterName.equals(generic.getName())) {
return true;
}
return mentionsAny(generic.getBounds(), typeParameterName, seen);
}
if (type instanceof JavaType.Array) {
return mentions(((JavaType.Array) type).getElemType(), typeParameterName, seen);
}
if (type instanceof JavaType.Parameterized) {
return mentionsAny(((JavaType.Parameterized) type).getTypeParameters(), typeParameterName, seen);
}
return false;
}

return invocation.getMethodType().hasFlags(Flag.Static);
private static boolean mentionsAny(List<JavaType> types, String typeParameterName, Set<JavaType> seen) {
for (JavaType type : types) {
if (mentions(type, typeParameterName, seen)) {
return true;
}
}
return false;
}

private static Set<JavaType> newIdentitySet() {
return newSetFromMap(new IdentityHashMap<>());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
}

// Recipe specific
boolean isPrimitive = DeclarationCheck.isPrimitive(vd);
boolean usesNoGenerics = !DeclarationCheck.useGenerics(vd);
boolean usesTernary = DeclarationCheck.initializedByTernary(vd);
if (isPrimitive || usesTernary || usesNoGenerics) {
if (DeclarationCheck.isPrimitive(vd) ||
DeclarationCheck.initializedByTernary(vd) ||
!DeclarationCheck.useGenerics(vd)) {
return vd;
}

Expand All @@ -73,9 +72,7 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
J.MethodInvocation invocation = (J.MethodInvocation) originalInitializer.unwrap();

// If no type parameters and no arguments are present, we assume the type is too hard to determine
boolean hasNoTypeParams = invocation.getTypeParameters() == null;
boolean argumentsEmpty = allArgumentsEmpty(invocation);
if (hasNoTypeParams && argumentsEmpty) {
if (invocation.getTypeParameters() == null && allArgumentsEmpty(invocation)) {
return vd;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
return vd;
}

boolean isPrimitive = DeclarationCheck.isPrimitive(vd);
boolean usesGenerics = DeclarationCheck.useGenerics(vd);
boolean usesTernary = DeclarationCheck.initializedByTernary(vd);
Expression initializer = vd.getVariables().get(0).getInitializer();
boolean usesArrayInitializer = initializer instanceof J.NewArray;
boolean initializedByStaticMethod = DeclarationCheck.initializedByStaticMethod(initializer);
if (isPrimitive || usesGenerics || usesTernary || usesArrayInitializer || initializedByStaticMethod) {
if (DeclarationCheck.isPrimitive(vd) ||
DeclarationCheck.useGenerics(vd) ||
DeclarationCheck.initializedByTernary(vd) ||
initializer instanceof J.NewArray ||
DeclarationCheck.initializedByStaticMethod(initializer) ||
DeclarationCheck.initializedByUnresolvableGenericMethod(initializer)) {
return vd;
}

Expand Down
Loading
Loading