Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InstanceOf: no action if type casts with specific type params used #338

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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 @@ -153,14 +153,20 @@ private static class ExpressionAndType {
private final JavaType type;
}

@Data
private static class VariableAndTypeTree {
private final J.VariableDeclarations.NamedVariable variable;
private final TypeTree type;
}

@Data
private static class InstanceOfPatternReplacements {
private final J root;
private final Map<ExpressionAndType, J.InstanceOf> instanceOfs = new HashMap<>();
private final Map<J.InstanceOf, Set<J>> contexts = new HashMap<>();
private final Map<J.InstanceOf, Set<Cursor>> contextScopes = new HashMap<>();
private final Map<J.TypeCast, J.InstanceOf> replacements = new HashMap<>();
private final Map<J.InstanceOf, J.VariableDeclarations.NamedVariable> variablesToDelete = new HashMap<>();
private final Map<J.InstanceOf, VariableAndTypeTree> variablesToDelete = new HashMap<>();

public void registerInstanceOf(J.InstanceOf instanceOf, Set<J> contexts) {
Expression expression = instanceOf.getExpression();
Expand Down Expand Up @@ -192,13 +198,21 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) {
for (Iterator<?> it = cursor.getPath(); it.hasNext(); ) {
Object next = it.next();
if (validContexts.contains(next)) {
if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable &&
!variablesToDelete.containsKey(instanceOf)) {
variablesToDelete.put(instanceOf, parent.getValue());
if (isAcceptableTypeCast(typeCast) && isTheSameAsOtherTypeCasts(typeCast, instanceOf)) {
if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable &&
!variablesToDelete.containsKey(instanceOf)) {
variablesToDelete.put(instanceOf, new VariableAndTypeTree(parent.getValue(), parent.firstEnclosing(J.VariableDeclarations.class).getTypeExpression()));
} else {
replacements.put(typeCast, instanceOf);
}
contextScopes.computeIfAbsent(instanceOf, k -> new HashSet<>()).add(cursor);
} else {
replacements.put(typeCast, instanceOf);
replacements.entrySet().removeIf(e -> e.getValue() == instanceOf);
variablesToDelete.remove(instanceOf);
contextScopes.remove(instanceOf);
contexts.remove(instanceOf);
instanceOfs.entrySet().removeIf(e -> e.getValue() == instanceOf);
}
contextScopes.computeIfAbsent(instanceOf, k -> new HashSet<>()).add(cursor);
break;
} else if (root == next) {
break;
Expand All @@ -207,6 +221,24 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) {
}
}

private boolean isAcceptableTypeCast(J.TypeCast typeCast) {
TypeTree typeTree = typeCast.getClazz().getTree();
if (typeTree instanceof J.ParameterizedType) {
return ((J.ParameterizedType) typeTree).getTypeParameters().stream().allMatch(J.Wildcard.class::isInstance);
}
return true;
}

private boolean isTheSameAsOtherTypeCasts(J.TypeCast typeCast, J.InstanceOf instanceOf) {
return replacements
.entrySet()
.stream()
.filter(e -> e.getValue() == instanceOf)
.findFirst()
.map(e -> e.getKey().getType().equals(typeCast.getType()))
.orElse(true);
}

public boolean isEmpty() {
return replacements.isEmpty() && variablesToDelete.isEmpty();
}
Expand All @@ -225,24 +257,13 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) {
name,
type,
null));
JavaType.FullyQualified fqType = TypeUtils.asFullyQualified(type);
if (fqType != null && !fqType.getTypeParameters().isEmpty() && !(instanceOf.getClazz() instanceof J.ParameterizedType)) {
TypedTree oldTypeTree = (TypedTree) instanceOf.getClazz();

// Each type parameter is turned into a wildcard, i.e. `List` -> `List<?>` or `Map.Entry` -> `Map.Entry<?,?>`
List<Expression> wildcardsList = IntStream.range(0, fqType.getTypeParameters().size())
.mapToObj(i -> new J.Wildcard(randomId(), Space.EMPTY, Markers.EMPTY, null, null))
.collect(Collectors.toList());

J.ParameterizedType newTypeTree = new J.ParameterizedType(
randomId(),
oldTypeTree.getPrefix(),
Markers.EMPTY,
oldTypeTree.withPrefix(Space.EMPTY),
null,
oldTypeTree.getType()
).withTypeParameters(wildcardsList);
result = result.withClazz(newTypeTree);
J currentTypeTree = instanceOf.getClazz();
TypeTree typeCastTypeTree = computeTypeTreeFromTypeCasts(instanceOf);
// If type tree from typa cast is not parameterized then NVM. Instance of should already have proper type
if (typeCastTypeTree != null && typeCastTypeTree instanceof J.ParameterizedType) {
J.ParameterizedType parameterizedType = (J.ParameterizedType) typeCastTypeTree;
result = result.withClazz(parameterizedType.withId(Tree.randomId()).withPrefix(currentTypeTree.getPrefix()));
}

// update entry in replacements to share the pattern variable name
Expand All @@ -254,13 +275,30 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) {
return result;
}

private TypeTree computeTypeTreeFromTypeCasts(J.InstanceOf instanceOf) {
TypeTree typeCastTypeTree = replacements
.entrySet()
.stream()
.filter(e -> e.getValue() == instanceOf)
.findFirst()
.map(e -> e.getKey().getClazz().getTree())
.orElse(null);
if (typeCastTypeTree == null) {
VariableAndTypeTree variable = variablesToDelete.get(instanceOf);
if (variable != null) {
typeCastTypeTree = variable.getType();
}
}
return typeCastTypeTree;
}

private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) {
VariableNameStrategy strategy;
if (root instanceof J.If) {
J.VariableDeclarations.NamedVariable variable = variablesToDelete.get(instanceOf);
strategy = variable != null ?
VariableNameStrategy.exact(variable.getSimpleName()) :
VariableNameStrategy.normal(contextScopes.get(instanceOf));
VariableAndTypeTree variableData = variablesToDelete.get(instanceOf);
strategy = variableData != null
? VariableNameStrategy.exact(variableData.getVariable().getSimpleName())
: VariableNameStrategy.normal(contextScopes.get(instanceOf));
} else {
strategy = VariableNameStrategy.short_();
}
Expand Down Expand Up @@ -288,7 +326,7 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) {
}

public @Nullable J processVariableDeclarations(J.VariableDeclarations multiVariable) {
return multiVariable.getVariables().stream().anyMatch(variablesToDelete::containsValue) ? null : multiVariable;
return multiVariable.getVariables().stream().anyMatch(v -> variablesToDelete.values().stream().anyMatch(vd -> vd.getVariable() == v)) ? null : multiVariable;
}
}

Expand Down
Loading
Loading