Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -24,10 +24,7 @@
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.NoMissingTypes;
import org.openrewrite.java.service.AnnotationService;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.Space;
import org.openrewrite.java.tree.Statement;
import org.openrewrite.java.tree.TypeUtils;
import org.openrewrite.java.tree.*;

import java.time.Duration;
import java.util.*;
Expand Down Expand Up @@ -152,12 +149,15 @@ private static class VariableUses {
public static Map<J.VariableDeclarations.NamedVariable, List<J.Identifier>> find(J.VariableDeclarations declarations, J.ClassDeclaration parent) {
Map<J.VariableDeclarations.NamedVariable, List<J.Identifier>> found = new IdentityHashMap<>(declarations.getVariables().size());
Map<String, J.VariableDeclarations.NamedVariable> signatureMap = new HashMap<>();
Map<String, J.VariableDeclarations.NamedVariable> nameMap = new HashMap<>();

for (J.VariableDeclarations.NamedVariable variable : declarations.getVariables()) {
if (variable.getVariableType() != null) {
found.computeIfAbsent(variable, k -> new ArrayList<>());
// Note: Using a variable type signature is only safe to find uses of class fields.
signatureMap.put(variable.getVariableType().toString(), variable);
// Also map by name for fallback matching
nameMap.put(variable.getSimpleName(), variable);
}
}

Expand All @@ -167,17 +167,37 @@ public static Map<J.VariableDeclarations.NamedVariable, List<J.Identifier>> find
@Override
public J.Identifier visitIdentifier(J.Identifier identifier,
Map<J.VariableDeclarations.NamedVariable, List<J.Identifier>> identifiers) {
if (identifier.getFieldType() != null && signatureMap.containsKey(identifier.getFieldType().toString())) {
Cursor parent = getCursor().dropParentUntil(is ->
is instanceof J.VariableDeclarations ||
is instanceof J.ClassDeclaration);

if (!(parent.getValue() instanceof J.VariableDeclarations && parent.getValue() == declarations)) {
J.VariableDeclarations.NamedVariable name = signatureMap.get(identifier.getFieldType().toString());
if (declarations.getVariables().contains(name)) {
J.VariableDeclarations.NamedVariable used = signatureMap.get(identifier.getFieldType().toString());
identifiers.computeIfAbsent(used, k -> new ArrayList<>())
.add(identifier);
if (identifier.getFieldType() != null) {
J.VariableDeclarations.NamedVariable match = null;

// First try exact type signature match
String fieldTypeSignature = identifier.getFieldType().toString();
if (signatureMap.containsKey(fieldTypeSignature)) {
match = signatureMap.get(fieldTypeSignature);
} else if (identifier.getFieldType().getOwner() != null) {
// Fallback: match by name if it's a field reference from the same class
J.VariableDeclarations.NamedVariable nameMatch = nameMap.get(identifier.getSimpleName());
if (nameMatch != null && nameMatch.getVariableType() != null) {
// Check if the owner type matches the parent class type
JavaType.FullyQualified ownerType = TypeUtils.asFullyQualified(identifier.getFieldType().getOwner());
JavaType.FullyQualified parentType = parent.getType();
if (ownerType != null && parentType != null &&
ownerType.getFullyQualifiedName().equals(parentType.getFullyQualifiedName())) {
match = nameMatch;
}
}
}

if (match != null) {
Cursor parentCursor = getCursor().dropParentUntil(is ->
is instanceof J.VariableDeclarations ||
is instanceof J.ClassDeclaration);

if (!(parentCursor.getValue() instanceof J.VariableDeclarations && parentCursor.getValue() == declarations)) {
if (declarations.getVariables().contains(match)) {
identifiers.computeIfAbsent(match, k -> new ArrayList<>())
.add(identifier);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,4 +485,51 @@ public class Test {
)
);
}

@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/321")
@Test
void doNotRemoveFieldAfterTypeChange() {
rewriteRun(
spec -> spec.recipes(
new UseCollectionInterfaces(),
new RemoveUnusedPrivateFields()
),
//language=java
java(
"""
import java.util.Arrays;
import java.util.HashSet;

public class Main {
private static final HashSet<String> allowedMethods = new HashSet<>(Arrays.asList(
"GET", "HEAD", "TRACE", "OPTIONS"));

public boolean matches(String method) {
if (allowedMethods.contains(method)) {
return false;
}
return true;
}
}
""",
"""
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Main {
private static final Set<String> allowedMethods = new HashSet<>(Arrays.asList(
"GET", "HEAD", "TRACE", "OPTIONS"));

public boolean matches(String method) {
if (allowedMethods.contains(method)) {
return false;
}
return true;
}
}
"""
)
);
}
}