Skip to content

Commit d4b5e62

Browse files
committed
Type annotation bugfixes (classgraph#402)
1 parent 774b169 commit d4b5e62

4 files changed

Lines changed: 104 additions & 37 deletions

File tree

src/main/java/io/github/classgraph/ClassInfo.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,8 +2036,7 @@ private MethodInfoList getMethodInfo(final String methodName, final boolean getN
20362036
for (final MethodInfo mi : ci.getDeclaredMethodInfo(methodName, getNormalMethods, getConstructorMethods,
20372037
getStaticInitializerMethods)) {
20382038
// If method/constructor has not been overridden by method of same name and type descriptor
2039-
if (nameAndTypeDescriptorSet
2040-
.add(new SimpleEntry<>(mi.getName(), mi.getTypeDescriptor().toString()))) {
2039+
if (nameAndTypeDescriptorSet.add(new SimpleEntry<>(mi.getName(), mi.getTypeDescriptorStr()))) {
20412040
// Add method/constructor to output order
20422041
methodInfoList.add(mi);
20432042
}

src/main/java/io/github/classgraph/Classfile.java

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,20 +1581,35 @@ private void readMethods() throws IOException, ClassfileFormatException {
15811581
public void decorate(final MethodTypeSignature methodTypeSignature) {
15821582
if (targetType == 0x01) {
15831583
// Type parameter declaration of generic method or constructor
1584-
methodTypeSignature.getTypeParameters().get(typeParameterIndex)
1585-
.addTypeAnnotation(typePath, annotationInfo);
1586-
} else if (targetType == 0x12) {
1587-
// Type in bound of type parameter declaration of generic method or constructor
1588-
final TypeParameter typeParameter = methodTypeSignature
1589-
.getTypeParameters().get(typeParameterIndex);
1590-
// TODO: documentation does not specify if bound index should be treated this way *******************
1591-
if (boundIndex == 0) {
1592-
typeParameter.getClassBound().addTypeAnnotation(typePath,
1584+
final List<TypeParameter> typeParameters = methodTypeSignature
1585+
.getTypeParameters();
1586+
if (typeParameters != null
1587+
&& typeParameterIndex < typeParameters.size()) {
1588+
typeParameters.get(typeParameterIndex).addTypeAnnotation(typePath,
15931589
annotationInfo);
1594-
} else {
1595-
typeParameter.getInterfaceBounds().get(boundIndex - 1)
1596-
.addTypeAnnotation(typePath, annotationInfo);
15971590
}
1591+
// else this is a method type descriptor, not a method type signature,
1592+
// so there are no type parameters
1593+
} else if (targetType == 0x12) {
1594+
// Type in bound of type parameter declaration of generic method or
1595+
// constructor
1596+
final List<TypeParameter> typeParameters = methodTypeSignature
1597+
.getTypeParameters();
1598+
if (typeParameters != null
1599+
&& typeParameterIndex < typeParameters.size()) {
1600+
final TypeParameter typeParameter = typeParameters
1601+
.get(typeParameterIndex);
1602+
// boundIndex == 0 => class bound; boundIndex > 0 => interface bound
1603+
if (boundIndex == 0) {
1604+
typeParameter.getClassBound().addTypeAnnotation(typePath,
1605+
annotationInfo);
1606+
} else {
1607+
typeParameter.getInterfaceBounds().get(boundIndex - 1)
1608+
.addTypeAnnotation(typePath, annotationInfo);
1609+
}
1610+
}
1611+
// else this is a method type descriptor, not a method type signature,
1612+
// so there are no type parameters
15981613
} else if (targetType == 0x14) {
15991614
// Return type of method, or type of newly constructed object
16001615
methodTypeSignature.getResultType().addTypeAnnotation(typePath,
@@ -1603,14 +1618,13 @@ public void decorate(final MethodTypeSignature methodTypeSignature) {
16031618
// Receiver type of method or constructor (explicit receiver parameter)
16041619
final List<TypeSignature> paramTypeSignatures = methodTypeSignature
16051620
.getParameterTypeSignatures();
1606-
if (paramTypeSignatures.size() == 0) {
1607-
throw new IllegalArgumentException(
1608-
"No explicit receiver parameter");
1621+
if (paramTypeSignatures != null && paramTypeSignatures.size() > 0) {
1622+
// TODO: is this the right way to apply a receiver type annotation? **************************
1623+
final TypeSignature receiverParamTypeSignature = paramTypeSignatures
1624+
.get(0);
1625+
receiverParamTypeSignature.addTypeAnnotation(typePath,
1626+
annotationInfo);
16091627
}
1610-
// TODO: is this the right way to apply a receiver type annotation?
1611-
final TypeSignature receiverParamTypeSignature = paramTypeSignatures
1612-
.get(0);
1613-
receiverParamTypeSignature.addTypeAnnotation(typePath, annotationInfo);
16141628
} else if (targetType == 0x16) {
16151629
// Type in formal parameter declaration of method, constructor,
16161630
// or lambda expression
@@ -1624,8 +1638,13 @@ public void decorate(final MethodTypeSignature methodTypeSignature) {
16241638
}
16251639
} else if (targetType == 0x17) {
16261640
// Type in throws clause of method or constructor
1627-
methodTypeSignature.getThrowsSignatures().get(throwsTypeIndex)
1628-
.addTypeAnnotation(typePath, annotationInfo);
1641+
final List<ClassRefOrTypeVariableSignature> throwsSignatures = //
1642+
methodTypeSignature.getThrowsSignatures();
1643+
if (throwsSignatures != null
1644+
&& throwsTypeIndex < throwsSignatures.size()) {
1645+
throwsSignatures.get(throwsTypeIndex).addTypeAnnotation(typePath,
1646+
annotationInfo);
1647+
}
16291648
}
16301649
}
16311650
});
@@ -1741,8 +1760,12 @@ private void readClassAttributes() throws IOException, ClassfileFormatException
17411760
public void decorate(final ClassTypeSignature classTypeSignature) {
17421761
if (targetType == 0x00) {
17431762
// Type parameter declaration of generic class or interface
1744-
classTypeSignature.getTypeParameters().get(typeParameterIndex)
1745-
.addTypeAnnotation(typePath, annotationInfo);
1763+
final List<TypeParameter> typeParameters = classTypeSignature
1764+
.getTypeParameters();
1765+
if (typeParameters != null && typeParameterIndex < typeParameters.size()) {
1766+
typeParameters.get(typeParameterIndex).addTypeAnnotation(typePath,
1767+
annotationInfo);
1768+
}
17461769
} else if (targetType == 0x10) {
17471770
if (supertypeIndex == 65535) {
17481771
// Type in extends clause of class declaration
@@ -1755,14 +1778,18 @@ public void decorate(final ClassTypeSignature classTypeSignature) {
17551778
}
17561779
} else if (targetType == 0x11) {
17571780
// Type in bound of type parameter declaration of generic class or interface
1758-
final TypeParameter typeParameter = classTypeSignature.getTypeParameters()
1759-
.get(typeParameterIndex);
1760-
// TODO: documentation does not specify if bound index should be treated this way *******************
1761-
if (boundIndex == 0) {
1762-
typeParameter.getClassBound().addTypeAnnotation(typePath, annotationInfo);
1763-
} else {
1764-
typeParameter.getInterfaceBounds().get(boundIndex - 1)
1765-
.addTypeAnnotation(typePath, annotationInfo);
1781+
final List<TypeParameter> typeParameters = classTypeSignature
1782+
.getTypeParameters();
1783+
if (typeParameters != null && typeParameterIndex < typeParameters.size()) {
1784+
final TypeParameter typeParameter = typeParameters.get(typeParameterIndex);
1785+
// boundIndex == 0 => class bound; boundIndex > 0 => interface bound
1786+
if (boundIndex == 0) {
1787+
typeParameter.getClassBound().addTypeAnnotation(typePath,
1788+
annotationInfo);
1789+
} else {
1790+
typeParameter.getInterfaceBounds().get(boundIndex - 1)
1791+
.addTypeAnnotation(typePath, annotationInfo);
1792+
}
17661793
}
17671794
}
17681795
}

src/main/java/io/github/classgraph/MethodInfo.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -933,14 +933,14 @@ public String toString() {
933933

934934
MethodParameterInfo.modifiersToString(paramInfo.getModifiers(), buf);
935935

936-
final TypeSignature paramType = paramInfo.getTypeSignatureOrTypeDescriptor();
936+
final TypeSignature paramTypeSignature = paramInfo.getTypeSignatureOrTypeDescriptor();
937937
if (i == varArgsParamIndex) {
938938
// Show varargs params correctly -- replace last "[]" with "..."
939-
if (!(paramType instanceof ArrayTypeSignature)) {
939+
if (!(paramTypeSignature instanceof ArrayTypeSignature)) {
940940
throw new IllegalArgumentException(
941941
"Got non-array type for last parameter of varargs method " + name);
942942
}
943-
final ArrayTypeSignature arrayType = (ArrayTypeSignature) paramType;
943+
final ArrayTypeSignature arrayType = (ArrayTypeSignature) paramTypeSignature;
944944
if (arrayType.getNumDimensions() == 0) {
945945
throw new IllegalArgumentException(
946946
"Got a zero-dimension array type for last parameter of varargs method " + name);
@@ -951,7 +951,18 @@ public String toString() {
951951
}
952952
buf.append("...");
953953
} else {
954-
buf.append(paramType.toString());
954+
// Exclude parameter annotations from type annotations at toplevel of type signature,
955+
// so that annotation is not listed twice
956+
final AnnotationInfoList annotationsToExclude;
957+
if (paramInfo.annotationInfo == null || paramInfo.annotationInfo.length == 0) {
958+
annotationsToExclude = null;
959+
} else {
960+
annotationsToExclude = new AnnotationInfoList(paramInfo.annotationInfo.length);
961+
for (int j = 0; j < paramInfo.annotationInfo.length; j++) {
962+
annotationsToExclude.add(paramInfo.annotationInfo[j]);
963+
}
964+
}
965+
paramTypeSignature.toStringInternal(/* useSimpleNames = */ false, annotationsToExclude, buf);
955966
}
956967

957968
if (hasParamNames) {

src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import io.github.classgraph.ClassGraph;
1313
import io.github.classgraph.ClassInfo;
14+
import io.github.classgraph.MethodParameterInfo;
1415
import io.github.classgraph.ScanResult;
1516

1617
/**
@@ -109,6 +110,28 @@ class Z4 {
109110

110111
List<X3.Y3.@A Z3> xyz4;
111112

113+
static class U {
114+
}
115+
116+
interface V {
117+
}
118+
119+
<@A T extends @B U> @D U t(@E T t) {
120+
return null;
121+
}
122+
123+
static class P<@A T extends @B U & @C V> {
124+
public void explicitReceiver(@F P<T> this) {
125+
}
126+
}
127+
128+
/**
129+
* Convert class names to short names.
130+
*
131+
* @param type
132+
* the type
133+
* @return the class names as short names
134+
*/
112135
private static String shortNames(final Object type) {
113136
return type.toString().replace(TypeAnnotationTest.class.getName() + ".", "")
114137
.replace(TypeAnnotationTest.class.getName() + "$", "")
@@ -150,6 +173,13 @@ void fieldsWithTypeAnnotations() {
150173
assertThat(shortNames(classInfo.getFieldInfo("xyz3"))).isEqualTo("List<X3.Y3.@A Z3> xyz3");
151174

152175
assertThat(shortNames(classInfo.getFieldInfo("xyz4"))).isEqualTo("List<X3.Y3.@A Z3> xyz4");
176+
177+
assertThat(shortNames(classInfo.getMethodInfo("t").get(0)))
178+
.isEqualTo("<@A T extends @B U> @D U t(@E T)");
179+
180+
final ClassInfo pClassInfo = scanResult.getClassInfo(P.class.getName());
181+
182+
assertThat(shortNames(pClassInfo)).isEqualTo("static class P<@A T extends @B U & @C V>");
153183
}
154184
}
155185
}

0 commit comments

Comments
 (0)