fix: collect usage info in generic types

This commit is contained in:
Skylot
2025-03-28 20:43:46 +00:00
parent d3a8a43c74
commit 9a692d6be4
3 changed files with 71 additions and 7 deletions
@@ -148,15 +148,19 @@ public class UsageInfo implements IUsageInfoData {
}
}
/**
* Visit all class nodes found in subtypes of the provided type.
*/
private void processType(ArgType type, Consumer<ClassNode> consumer) {
if (type == null) {
if (type == null || type == ArgType.OBJECT) {
return;
}
if (type.isArray()) {
processType(type.getArrayRootElement(), consumer);
return;
}
if (type.isObject() && !type.isGenericType()) {
if (type.isObject()) {
// TODO: support custom handlers via API
ClspClass clsDetails = root.getClsp().getClsDetails(type);
if (clsDetails != null && clsDetails.getSource() == ClspClassSource.APACHE_HTTP_LEGACY_CLIENT) {
root.getGradleInfoStorage().setUseApacheHttpLegacy(true);
@@ -166,19 +170,30 @@ public class UsageInfo implements IUsageInfoData {
consumer.accept(clsNode);
}
List<ArgType> genericTypes = type.getGenericTypes();
if (type.isGeneric() && notEmpty(genericTypes)) {
if (notEmpty(genericTypes)) {
for (ArgType argType : genericTypes) {
processType(argType, consumer);
}
}
List<ArgType> extendTypes = type.getExtendTypes();
if (notEmpty(extendTypes)) {
for (ArgType extendType : extendTypes) {
processType(extendType, consumer);
}
}
ArgType wildcardType = type.getWildcardType();
if (wildcardType != null) {
processType(wildcardType, consumer);
}
// TODO: process 'outer' types (check TestOuterGeneric test)
}
}
private static <T extends Comparable<T>> List<T> sortedList(Set<T> deps) {
if (deps == null || deps.isEmpty()) {
private static <T extends Comparable<T>> List<T> sortedList(Set<T> nodes) {
if (nodes == null || nodes.isEmpty()) {
return Collections.emptyList();
}
List<T> list = new ArrayList<>(deps);
List<T> list = new ArrayList<>(nodes);
Collections.sort(list);
return list;
}
@@ -88,12 +88,14 @@ public class UsageInfoVisitor extends AbstractVisitor {
for (ArgType interfaceType : cls.getInterfaces()) {
usageInfo.clsUse(cls, interfaceType);
}
for (ArgType genericTypeParameter : cls.getGenericTypeParameters()) {
usageInfo.clsUse(cls, genericTypeParameter);
}
for (FieldNode fieldNode : cls.getFields()) {
usageInfo.clsUse(cls, fieldNode.getType());
processAnnotations(fieldNode, usageInfo);
// TODO: process types from field 'constant value'
}
// TODO: process generics
processAnnotations(cls, usageInfo);
for (MethodNode methodNode : cls.getMethods()) {
processMethod(methodNode, usageInfo);
@@ -0,0 +1,47 @@
package jadx.tests.integration.generics;
import java.util.List;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestUsageInGenerics extends IntegrationTest {
public static class TestCls {
public static class A {
}
public static class B<T extends A> {
}
public static class C {
public List<? extends A> list;
}
public <T extends A> T test() {
return null;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
ClassNode testCls = searchCls(cls.getInnerClasses(), "A");
ClassNode bCls = searchCls(cls.getInnerClasses(), "B");
ClassNode cCls = searchCls(cls.getInnerClasses(), "C");
MethodNode testMth = getMethod(cls, "test");
assertThat(testCls.getUseIn()).contains(cls, bCls, cCls);
assertThat(testCls.getUseInMth()).contains(testMth);
assertThat(cls)
.code()
.containsOne("public <T extends A> T test() {");
}
}