Skip to content

Commit c7812be

Browse files
committed
Fix SOE in SignatureEnhancement::extractNullability
The problem was that `resolveTypeQualifierAnnotation` actually doesn't guarantee that `typeQualifierAnnotation` is javax.annotation.NonNull with argument It could be just any type qualifier (see the test)
1 parent 1953f8e commit c7812be

File tree

7 files changed

+172
-12
lines changed

7 files changed

+172
-12
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
2+
3+
// FILE: UnknownQualifier.java
4+
import javax.annotation.*;
5+
import java.lang.annotation.Documented;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
9+
import javax.annotation.meta.TypeQualifier;
10+
import javax.annotation.meta.When;
11+
12+
@Documented
13+
@TypeQualifier
14+
@Retention(RetentionPolicy.RUNTIME)
15+
public @interface UnknownQualifier {
16+
}
17+
18+
// FILE: UnknownQualifierNickname.java
19+
import javax.annotation.*;
20+
import java.lang.annotation.Documented;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
24+
import javax.annotation.meta.TypeQualifierNickname;
25+
import javax.annotation.meta.When;
26+
27+
@Documented
28+
@TypeQualifierNickname
29+
@UnknownQualifier
30+
@Retention(RetentionPolicy.RUNTIME)
31+
public @interface UnknownQualifierNickname {
32+
}
33+
34+
// FILE: UnknownQualifierDefault.java
35+
import javax.annotation.*;
36+
import java.lang.annotation.Documented;
37+
import java.lang.annotation.Retention;
38+
import java.lang.annotation.ElementType;
39+
import java.lang.annotation.RetentionPolicy;
40+
41+
import javax.annotation.meta.TypeQualifierDefault;
42+
import javax.annotation.meta.When;
43+
44+
@Documented
45+
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
46+
@UnknownQualifier
47+
@Retention(RetentionPolicy.RUNTIME)
48+
public @interface UnknownQualifierDefault {
49+
}
50+
51+
// FILE: UnknownQualifierNicknameDefault.java
52+
import javax.annotation.*;
53+
import java.lang.annotation.Documented;
54+
import java.lang.annotation.Retention;
55+
import java.lang.annotation.ElementType;
56+
import java.lang.annotation.RetentionPolicy;
57+
58+
import javax.annotation.meta.TypeQualifierDefault;
59+
import javax.annotation.meta.When;
60+
61+
@Documented
62+
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
63+
@UnknownQualifierNickname
64+
@Retention(RetentionPolicy.RUNTIME)
65+
public @interface UnknownQualifierNicknameDefault {
66+
}
67+
68+
// FILE: A.java
69+
70+
import javax.annotation.*;
71+
72+
@UnknownQualifierDefault
73+
public class A {
74+
@UnknownQualifierNicknameDefault
75+
public static class B {
76+
@UnknownQualifier
77+
public static String foo(@UnknownQualifierNickname String x) { return null; }
78+
}
79+
}
80+
81+
// FILE: main.kt
82+
83+
fun main() {
84+
A.B.foo(null).hashCode()
85+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package
2+
3+
public fun main(): kotlin.Unit
4+
5+
@UnknownQualifierDefault public open class A {
6+
public constructor A()
7+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
8+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
9+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
10+
11+
@UnknownQualifierNicknameDefault public open class B {
12+
public constructor B()
13+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
14+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
15+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
16+
17+
// Static members
18+
@UnknownQualifier public open fun foo(/*0*/ @UnknownQualifierNickname x: kotlin.String!): kotlin.String!
19+
}
20+
}
21+
22+
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifier @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownQualifier : kotlin.Annotation {
23+
public constructor UnknownQualifier()
24+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
25+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
26+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
27+
}
28+
29+
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER}) @UnknownQualifier @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownQualifierDefault : kotlin.Annotation {
30+
public constructor UnknownQualifierDefault()
31+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
32+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
33+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
34+
}
35+
36+
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifierNickname @UnknownQualifier @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownQualifierNickname : kotlin.Annotation {
37+
public constructor UnknownQualifierNickname()
38+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
39+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
40+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
41+
}
42+
43+
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER}) @UnknownQualifierNickname @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownQualifierNicknameDefault : kotlin.Annotation {
44+
public constructor UnknownQualifierNicknameDefault()
45+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
46+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
47+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
48+
}

compiler/tests/org/jetbrains/kotlin/checkers/ForeignAnnotationsNoAnnotationInClasspathTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public void testFindBugsSimple() throws Exception {
6060
doTest(fileName);
6161
}
6262

63+
@TestMetadata("irrelevantQualifierNicknames.kt")
64+
public void testIrrelevantQualifierNicknames() throws Exception {
65+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
66+
doTest(fileName);
67+
}
68+
6369
@TestMetadata("jsr305NullabilityNicknames.kt")
6470
public void testJsr305NullabilityNicknames() throws Exception {
6571
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");

compiler/tests/org/jetbrains/kotlin/checkers/ForeignAnnotationsNoAnnotationInClasspathWithFastClassReadingTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public void testFindBugsSimple() throws Exception {
6060
doTest(fileName);
6161
}
6262

63+
@TestMetadata("irrelevantQualifierNicknames.kt")
64+
public void testIrrelevantQualifierNicknames() throws Exception {
65+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
66+
doTest(fileName);
67+
}
68+
6369
@TestMetadata("jsr305NullabilityNicknames.kt")
6470
public void testJsr305NullabilityNicknames() throws Exception {
6571
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");

compiler/tests/org/jetbrains/kotlin/checkers/ForeignAnnotationsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public void testFindBugsSimple() throws Exception {
6060
doTest(fileName);
6161
}
6262

63+
@TestMetadata("irrelevantQualifierNicknames.kt")
64+
public void testIrrelevantQualifierNicknames() throws Exception {
65+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
66+
doTest(fileName);
67+
}
68+
6369
@TestMetadata("jsr305NullabilityNicknames.kt")
6470
public void testJsr305NullabilityNicknames() throws Exception {
6571
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");

compiler/tests/org/jetbrains/kotlin/checkers/javac/JavacForeignAnnotationsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public void testFindBugsSimple() throws Exception {
6060
doTest(fileName);
6161
}
6262

63+
@TestMetadata("irrelevantQualifierNicknames.kt")
64+
public void testIrrelevantQualifierNicknames() throws Exception {
65+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
66+
doTest(fileName);
67+
}
68+
6369
@TestMetadata("jsr305NullabilityNicknames.kt")
6470
public void testJsr305NullabilityNicknames() throws Exception {
6571
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");

core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,30 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
6868
}
6969

7070
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifierWithMigrationStatus? {
71+
extractNullabilityFromKnownAnnotations(annotationDescriptor)?.let { return it }
72+
73+
val typeQualifierAnnotation =
74+
annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(annotationDescriptor)
75+
?: return null
76+
77+
val forWarning = annotationTypeQualifierResolver.jsr305State.isWarning()
78+
79+
return extractNullabilityFromKnownAnnotations(typeQualifierAnnotation)?.copy(isForWarningOnly = forWarning)
80+
}
81+
82+
private fun extractNullabilityFromKnownAnnotations(
83+
annotationDescriptor: AnnotationDescriptor
84+
): NullabilityQualifierWithMigrationStatus? {
7185
val annotationFqName = annotationDescriptor.fqName ?: return null
7286

7387
return when (annotationFqName) {
7488
in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
7589
in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
7690
JAVAX_NONNULL_ANNOTATION -> annotationDescriptor.extractNullabilityTypeFromArgument()
77-
else -> {
78-
val forWarning = annotationTypeQualifierResolver.jsr305State.isWarning()
79-
80-
val typeQualifierAnnotation =
81-
annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(annotationDescriptor)
82-
?: return null
83-
84-
// resolveTypeQualifierAnnotation guarantees that `typeQualifierAnnotation` is javax.annotation.NonNull with argument
85-
// or javax.annotation.CheckForNull without arguments
86-
extractNullability(typeQualifierAnnotation)?.copy(isForWarningOnly = forWarning)
87-
}
91+
else -> null
8892
}
8993
}
9094

91-
9295
fun <D : CallableMemberDescriptor> enhanceSignatures(c: LazyJavaResolverContext, platformSignatures: Collection<D>): Collection<D> {
9396
return platformSignatures.map {
9497
it.enhanceSignature(c)

0 commit comments

Comments
 (0)