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)
This commit is contained in:
@@ -0,0 +1,85 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
// FILE: UnknownQualifier.java
|
||||||
|
import javax.annotation.*;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import javax.annotation.meta.TypeQualifier;
|
||||||
|
import javax.annotation.meta.When;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@TypeQualifier
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface UnknownQualifier {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: UnknownQualifierNickname.java
|
||||||
|
import javax.annotation.*;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import javax.annotation.meta.TypeQualifierNickname;
|
||||||
|
import javax.annotation.meta.When;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@TypeQualifierNickname
|
||||||
|
@UnknownQualifier
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface UnknownQualifierNickname {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: UnknownQualifierDefault.java
|
||||||
|
import javax.annotation.*;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import javax.annotation.meta.TypeQualifierDefault;
|
||||||
|
import javax.annotation.meta.When;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
|
||||||
|
@UnknownQualifier
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface UnknownQualifierDefault {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: UnknownQualifierNicknameDefault.java
|
||||||
|
import javax.annotation.*;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import javax.annotation.meta.TypeQualifierDefault;
|
||||||
|
import javax.annotation.meta.When;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
|
||||||
|
@UnknownQualifierNickname
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface UnknownQualifierNicknameDefault {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: A.java
|
||||||
|
|
||||||
|
import javax.annotation.*;
|
||||||
|
|
||||||
|
@UnknownQualifierDefault
|
||||||
|
public class A {
|
||||||
|
@UnknownQualifierNicknameDefault
|
||||||
|
public static class B {
|
||||||
|
@UnknownQualifier
|
||||||
|
public static String foo(@UnknownQualifierNickname String x) { return null; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
A.B.foo(null).hashCode()
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun main(): kotlin.Unit
|
||||||
|
|
||||||
|
@UnknownQualifierDefault public open class A {
|
||||||
|
public constructor A()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|
||||||
|
@UnknownQualifierNicknameDefault public open class B {
|
||||||
|
public constructor B()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|
||||||
|
// Static members
|
||||||
|
@UnknownQualifier public open fun foo(/*0*/ @UnknownQualifierNickname x: kotlin.String!): kotlin.String!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifier @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownQualifier : kotlin.Annotation {
|
||||||
|
public constructor UnknownQualifier()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
public constructor UnknownQualifierDefault()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifierNickname @UnknownQualifier @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownQualifierNickname : kotlin.Annotation {
|
||||||
|
public constructor UnknownQualifierNickname()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
public constructor UnknownQualifierNicknameDefault()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+6
@@ -60,6 +60,12 @@ public class ForeignAnnotationsNoAnnotationInClasspathTestGenerated extends Abst
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("irrelevantQualifierNicknames.kt")
|
||||||
|
public void testIrrelevantQualifierNicknames() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("jsr305NullabilityNicknames.kt")
|
@TestMetadata("jsr305NullabilityNicknames.kt")
|
||||||
public void testJsr305NullabilityNicknames() throws Exception {
|
public void testJsr305NullabilityNicknames() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
||||||
|
|||||||
+6
@@ -60,6 +60,12 @@ public class ForeignAnnotationsNoAnnotationInClasspathWithFastClassReadingTestGe
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("irrelevantQualifierNicknames.kt")
|
||||||
|
public void testIrrelevantQualifierNicknames() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("jsr305NullabilityNicknames.kt")
|
@TestMetadata("jsr305NullabilityNicknames.kt")
|
||||||
public void testJsr305NullabilityNicknames() throws Exception {
|
public void testJsr305NullabilityNicknames() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ public class ForeignAnnotationsTestGenerated extends AbstractForeignAnnotationsT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("irrelevantQualifierNicknames.kt")
|
||||||
|
public void testIrrelevantQualifierNicknames() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("jsr305NullabilityNicknames.kt")
|
@TestMetadata("jsr305NullabilityNicknames.kt")
|
||||||
public void testJsr305NullabilityNicknames() throws Exception {
|
public void testJsr305NullabilityNicknames() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
||||||
|
|||||||
+6
@@ -60,6 +60,12 @@ public class JavacForeignAnnotationsTestGenerated extends AbstractJavacForeignAn
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("irrelevantQualifierNicknames.kt")
|
||||||
|
public void testIrrelevantQualifierNicknames() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/irrelevantQualifierNicknames.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("jsr305NullabilityNicknames.kt")
|
@TestMetadata("jsr305NullabilityNicknames.kt")
|
||||||
public void testJsr305NullabilityNicknames() throws Exception {
|
public void testJsr305NullabilityNicknames() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305NullabilityNicknames.kt");
|
||||||
|
|||||||
+15
-12
@@ -68,27 +68,30 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifierWithMigrationStatus? {
|
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifierWithMigrationStatus? {
|
||||||
|
extractNullabilityFromKnownAnnotations(annotationDescriptor)?.let { return it }
|
||||||
|
|
||||||
|
val typeQualifierAnnotation =
|
||||||
|
annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(annotationDescriptor)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
val forWarning = annotationTypeQualifierResolver.jsr305State.isWarning()
|
||||||
|
|
||||||
|
return extractNullabilityFromKnownAnnotations(typeQualifierAnnotation)?.copy(isForWarningOnly = forWarning)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractNullabilityFromKnownAnnotations(
|
||||||
|
annotationDescriptor: AnnotationDescriptor
|
||||||
|
): NullabilityQualifierWithMigrationStatus? {
|
||||||
val annotationFqName = annotationDescriptor.fqName ?: return null
|
val annotationFqName = annotationDescriptor.fqName ?: return null
|
||||||
|
|
||||||
return when (annotationFqName) {
|
return when (annotationFqName) {
|
||||||
in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
|
in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
|
||||||
in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||||
JAVAX_NONNULL_ANNOTATION -> annotationDescriptor.extractNullabilityTypeFromArgument()
|
JAVAX_NONNULL_ANNOTATION -> annotationDescriptor.extractNullabilityTypeFromArgument()
|
||||||
else -> {
|
else -> null
|
||||||
val forWarning = annotationTypeQualifierResolver.jsr305State.isWarning()
|
|
||||||
|
|
||||||
val typeQualifierAnnotation =
|
|
||||||
annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(annotationDescriptor)
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
// resolveTypeQualifierAnnotation guarantees that `typeQualifierAnnotation` is javax.annotation.NonNull with argument
|
|
||||||
// or javax.annotation.CheckForNull without arguments
|
|
||||||
extractNullability(typeQualifierAnnotation)?.copy(isForWarningOnly = forWarning)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun <D : CallableMemberDescriptor> enhanceSignatures(c: LazyJavaResolverContext, platformSignatures: Collection<D>): Collection<D> {
|
fun <D : CallableMemberDescriptor> enhanceSignatures(c: LazyJavaResolverContext, platformSignatures: Collection<D>): Collection<D> {
|
||||||
return platformSignatures.map {
|
return platformSignatures.map {
|
||||||
it.enhanceSignature(c)
|
it.enhanceSignature(c)
|
||||||
|
|||||||
Reference in New Issue
Block a user