Skip to content
Closed
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 @@ -21,11 +21,11 @@
import org.openrewrite.java.NoMissingTypes;
import org.openrewrite.java.RemoveUnusedImports;
import org.openrewrite.java.search.FindAnnotations;
import org.openrewrite.java.service.AnnotationService;
import org.openrewrite.java.tree.*;

import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import static java.util.Collections.singleton;

Expand Down Expand Up @@ -75,53 +75,100 @@ private boolean unusedWarningsSuppressed(J classDeclaration) {
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);

JavaType.Method methodType = method.getMethodType();
if (methodType != null && methodType.hasFlags(Flag.Private) &&
!method.isConstructor() &&
service(AnnotationService.class).getAllAnnotations(getCursor()).isEmpty()) {
if (methodType == null) {
return m;
}

// Only consider private, non-constructors.
if (!methodType.hasFlags(Flag.Private) || method.isConstructor()) {
return m;
}

// Serialization hooks & similar: do not remove even if private.
switch (m.getSimpleName()) {
case "readObject":
case "readObjectNoData":
case "readResolve":
case "writeObject":
case "writeReplace":
return m;
}

J.ClassDeclaration classDeclaration = getCursor().firstEnclosing(J.ClassDeclaration.class);
if (classDeclaration == null) {
// If we're missing the enclosing class or CU, bail out.
J.ClassDeclaration classDeclaration = getCursor().firstEnclosing(J.ClassDeclaration.class);
if (classDeclaration == null) {
return m;
}
JavaSourceFile cu = getCursor().firstEnclosingOrThrow(JavaSourceFile.class);

// If referenced anywhere in TypesInUse (e.g., via other classes), keep it.
for (JavaType.Method usedMethodType : cu.getTypesInUse().getUsedMethods()) {
if (methodType.equals(usedMethodType)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the hint, yes will try to undo

return m;
}
switch (m.getSimpleName()) {
case "readObject":
case "readObjectNoData":
case "readResolve":
case "writeObject":
case "writeReplace":
return m;
}

// Do not touch if the compilation unit references JUnit's MethodSource (reflective use).
for (JavaType javaType : cu.getTypesInUse().getTypesInUse()) {
if (TypeUtils.isOfClassType(javaType, "org.junit.jupiter.params.provider.MethodSource")) {
return m;
}
}

JavaSourceFile cu = getCursor().firstEnclosingOrThrow(JavaSourceFile.class);
for (JavaType.Method usedMethodType : cu.getTypesInUse().getUsedMethods()) {
if (methodType.getName().equals(usedMethodType.getName()) && methodType.equals(usedMethodType)) {
return m;
}
// Temporary stop-gap until we have DFA:
// If the declared method shows generic type artifacts, be conservative and keep it.
for (JavaType.Method declared : cu.getTypesInUse().getDeclaredMethods()) {
if (methodType.equals(declared) && m.toString().contains("Generic{")) {
return m;
}
}

for (JavaType javaType : cu.getTypesInUse().getTypesInUse()) {
if (TypeUtils.isOfClassType(javaType, "org.junit.jupiter.params.provider.MethodSource")) {
return m;
// Scan the enclosing class for in-class usages (method calls or method references).
// IMPORTANT: self-recursion alone does NOT count as usage.
AtomicBoolean usedInClass = new AtomicBoolean(false);

new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation call, ExecutionContext ctx2) {
J.MethodInvocation c = super.visitMethodInvocation(call, ctx2);
JavaType.Method calledType = c.getMethodType();
if (calledType != null && calledType.equals(methodType)) {
// Ignore self-recursion: only count if the enclosing method is NOT the same declaration.
J.MethodDeclaration enclosing = getCursor().firstEnclosing(J.MethodDeclaration.class);
if (enclosing == null || enclosing != method) {
usedInClass.set(true);
}
}
return c;
}

// Temporary stop-gap until we have data flow analysis.
// Do not remove method declarations with generic types since the method invocation in `cu.getTypesInUse` will be bounded with a type.
for (JavaType.Method usedMethodType : cu.getTypesInUse().getDeclaredMethods()) {
if (methodType.getName().equals(usedMethodType.getName()) && methodType.equals(usedMethodType) && m.toString().contains("Generic{")) {
return m;
@Override
public J.MemberReference visitMemberReference(J.MemberReference ref, ExecutionContext ctx2) {
J.MemberReference r = super.visitMemberReference(ref, ctx2);
JavaType.Method refType = r.getMethodType();
if (refType != null && refType.equals(methodType)) {
J.MethodDeclaration enclosing = getCursor().firstEnclosing(J.MethodDeclaration.class);
if (enclosing == null || enclosing != method) {
usedInClass.set(true);
}
}
return r;
}
}.visit(classDeclaration, ctx);

doAfterVisit(new RemoveUnusedImports().getVisitor());
//noinspection ConstantConditions
return null;
if (usedInClass.get()) {
return m;
}

return m;
// No external nor internal usages -> remove it.
doAfterVisit(new RemoveUnusedImports().getVisitor());
//noinspection ConstantConditions
return null;
}
};

return Preconditions.check(new NoMissingTypes(), Repeat.repeatUntilStable(visitor));
}
}
Loading