From 28d3a625672767a8072a7c5d1657e4e642d98b8f Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 30 Nov 2018 17:55:34 +0300 Subject: [PATCH] Separate nullability annotation processing for ultra-light classes KtLightNullabilityAnnotation is specifically designed for old light classes and includes a bunch of hacks that are applicable for them So, we have to introduce own hack into it (see org.jetbrains.kotlin.asJava.classes.KtUltraLightParameter#getTypeForNullability) that is a bit of hard to support when extending hierarchy --- .../asJava/classes/ultraLightAnnotations.kt | 43 +++++++++++++++++++ .../kotlin/asJava/classes/ultraLightField.kt | 15 ++++--- .../kotlin/asJava/classes/ultraLightMethod.kt | 34 +++++++++++++-- .../kotlin/asJava/classes/ultraLightPsi.kt | 9 +++- .../asJava/elements/KtLightModifierList.kt | 12 +++--- .../asJava/elements/lightAnnotations.kt | 2 +- .../asJava/elements/lightAnnotations.kt.173 | 2 +- .../inferringAnonymousObjectTypes.java | 11 +---- .../resolve/AbstractIdeLightClassTest.kt | 2 +- 9 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt new file mode 100644 index 00000000000..df2fc73f96b --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt @@ -0,0 +1,43 @@ +package org.jetbrains.kotlin.asJava.classes + +import com.intellij.lang.jvm.JvmModifier +import com.intellij.psi.* +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable +import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.isError +import org.jetbrains.kotlin.types.typeUtil.TypeNullability +import org.jetbrains.kotlin.types.typeUtil.isTypeParameter +import org.jetbrains.kotlin.types.typeUtil.nullability + +class KtUltraLightNullabilityAnnotation( + member: KtUltraLightElementWithNullabilityAnnotation<*, *>, + parent: PsiElement +) : KtLightNullabilityAnnotation>(member, parent) { + override fun getQualifiedName(): String? { + val kotlinType = member.kotlinTypeForNullabilityAnnotation?.takeUnless(KotlinType::isError) ?: return null + val psiType = member.psiTypeForNullabilityAnnotation ?: return null + if (member.isPrivateOrParameterInPrivateMethod() || psiType is PsiPrimitiveType) return null + + if (kotlinType.isTypeParameter()) { + if (!TypeUtils.hasNullableSuperType(kotlinType)) return NotNull::class.java.name + if (!kotlinType.isMarkedNullable) return null + } + + val nullability = kotlinType.nullability() + return when (nullability) { + TypeNullability.NOT_NULL -> NotNull::class.java.name + TypeNullability.NULLABLE -> Nullable::class.java.name + TypeNullability.FLEXIBLE -> null + } + } +} + +fun PsiModifierListOwner.isPrivateOrParameterInPrivateMethod(): Boolean { + if (hasModifier(JvmModifier.PRIVATE)) return true + if (this !is PsiParameter) return false + val parentMethod = declarationScope as? PsiMethod ?: return false + return parentMethod.hasModifier(JvmModifier.PRIVATE) +} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt index 3d8d5e1ff1b..83f774ff172 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt @@ -22,10 +22,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtEnumEntry -import org.jetbrains.kotlin.psi.KtNamedDeclaration -import org.jetbrains.kotlin.psi.KtObjectDeclaration -import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.VOLATILE_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind @@ -37,7 +34,8 @@ internal open class KtUltraLightField( private val containingClass: KtUltraLightClass, private val support: UltraLightSupport, modifiers: Set -) : LightFieldBuilder(name, PsiType.NULL, declaration), KtLightField { +) : LightFieldBuilder(name, PsiType.NULL, declaration), KtLightField, + KtUltraLightElementWithNullabilityAnnotation { private val modList = object : KtLightSimpleModifierList(this, modifiers) { override fun hasModifierProperty(name: String): Boolean = when (name) { PsiModifier.VOLATILE -> hasFieldAnnotation(VOLATILE_ANNOTATION_FQ_NAME) @@ -84,6 +82,13 @@ internal open class KtUltraLightField( } } + override val kotlinTypeForNullabilityAnnotation: KotlinType? + // We don't generate nullability annotations for non-backing fields in backend + get() = kotlinType?.takeUnless { declaration is KtEnumEntry || declaration is KtObjectDeclaration } + + override val psiTypeForNullabilityAnnotation: PsiType? + get() = type + private val _type: PsiType by lazyPub { fun nonExistent() = JavaPsiFacade.getElementFactory(project).createTypeFromText("error.NonExistentClass", declaration) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt index cce06f079a9..be96c49f492 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt @@ -13,10 +13,10 @@ import com.intellij.psi.impl.light.LightTypeParameterListBuilder import org.jetbrains.annotations.NonNls import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration -import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl import org.jetbrains.kotlin.asJava.elements.KtLightSimpleModifierList +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.FunctionCodegen import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor @@ -33,7 +33,19 @@ internal class KtUltraLightMethod( originalElement: KtDeclaration, private val support: UltraLightSupport, containingClass: KtUltraLightClass -) : KtLightMethodImpl({ delegate }, LightMemberOriginForDeclaration(originalElement, JvmDeclarationOriginKind.OTHER), containingClass) { +) : KtLightMethodImpl( + { delegate }, + LightMemberOriginForDeclaration( + originalElement, JvmDeclarationOriginKind.OTHER + ), + containingClass +), KtUltraLightElementWithNullabilityAnnotation { + + override val kotlinTypeForNullabilityAnnotation: KotlinType? + get() = kotlinOrigin?.getKotlinType() + + override val psiTypeForNullabilityAnnotation: PsiType? + get() = returnType // These two overrides are necessary because ones from KtLightMethodImpl suppose that clsDelegate.returnTypeElement is valid // While here we only set return type for LightMethodBuilder (see org.jetbrains.kotlin.asJava.classes.KtUltraLightClass.asJavaMethod) @@ -80,8 +92,7 @@ internal class KtUltraLightParameter( PsiType.NULL, method, method.language -), - KtLightDeclaration { +), KtUltraLightElementWithNullabilityAnnotation { override val clsDelegate: PsiParameter get() = throw IllegalStateException("Cls delegate shouldn't be loaded for ultra-light PSI!") @@ -103,6 +114,21 @@ internal class KtUltraLightParameter( else -> kotlinOrigin.getKotlinType() } } + + override val kotlinTypeForNullabilityAnnotation: KotlinType? + get() { + val type = kotlinType + return if (isVarArgs && type != null && KotlinBuiltIns.isArray(type)) { + type.arguments[0].type + } else { + type + } + } + + override val psiTypeForNullabilityAnnotation: PsiType? + get() = type + + private val _type: PsiType by lazyPub { val kotlinType = kotlinType ?: return@lazyPub PsiType.NULL val containingDescriptor = containingFunction.resolve() as? CallableDescriptor ?: return@lazyPub PsiType.NULL diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt index 31c3ee12153..d3f6a69947f 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt @@ -15,6 +15,7 @@ import com.intellij.psi.impl.light.LightParameterListBuilder import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.builder.LightClassData +import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration import org.jetbrains.kotlin.asJava.elements.KtLightField import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.builtins.KotlinBuiltIns @@ -384,7 +385,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor if (numberOfDefaultParametersToAdd >= 0) numberOfDefaultParametersToAdd else - // Just to avoid computing the actual number of default parameters, we use an upper bound + // Just to avoid computing the actual number of default parameters, we use an upper bound ktFunction.valueParameters.size for (parameter in ktFunction.valueParameters) { @@ -597,3 +598,9 @@ interface UltraLightSupport { fun findAnnotation(owner: KtAnnotated, fqName: FqName): Pair? fun isTooComplexForUltraLightGeneration(element: KtClassOrObject): Boolean } + +interface KtUltraLightElementWithNullabilityAnnotation : KtLightDeclaration, + PsiModifierListOwner { + val kotlinTypeForNullabilityAnnotation: KotlinType? + val psiTypeForNullabilityAnnotation: PsiType? +} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightModifierList.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightModifierList.kt index 57d751f9bb5..e093c0db7b6 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightModifierList.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightModifierList.kt @@ -22,9 +22,7 @@ import com.intellij.psi.PsiModifierList import com.intellij.psi.PsiModifierListOwner import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration -import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration -import org.jetbrains.kotlin.asJava.classes.KtUltraLightParameter -import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.asJava.classes.* import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -77,8 +75,12 @@ abstract class KtLightModifierList && modifierListOwner !is KtLightFieldImpl.KtLightEnumConstant) || modifierListOwner is LightParameter ) { - return annotationsForEntries + - listOf(KtLightNullabilityAnnotation(modifierListOwner as KtLightElement<*, PsiModifierListOwner>, this)) + val nullabilityAnnotation = when (modifierListOwner) { + is KtUltraLightElementWithNullabilityAnnotation<*, *> -> KtUltraLightNullabilityAnnotation(modifierListOwner, this) + else -> KtLightNullabilityAnnotation(modifierListOwner as KtLightElement<*, PsiModifierListOwner>, this) + } + + return annotationsForEntries + listOf(nullabilityAnnotation) } return annotationsForEntries } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt index 7ab66dd878e..23272eb4ef1 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt @@ -233,7 +233,7 @@ class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBa override fun getAttributes(): Array = emptyArray() } -class KtLightNullabilityAnnotation(val member: KtLightElement<*, PsiModifierListOwner>, parent: PsiElement) : +open class KtLightNullabilityAnnotation>(val member: D, parent: PsiElement) : KtLightAbstractAnnotation(parent, { // searching for last because nullability annotations are generated after backend generates source annotations getClsNullabilityAnnotation(member) ?: KtLightNonExistentAnnotation(member) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 index 9f5fc9197bc..5ce28aff1db 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 @@ -234,7 +234,7 @@ class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBa override fun getAttributes(): Array = emptyArray() } -class KtLightNullabilityAnnotation(val member: KtLightElement<*, PsiModifierListOwner>, parent: PsiElement) : +open class KtLightNullabilityAnnotation>(val member: D, parent: PsiElement) : KtLightAbstractAnnotation(parent, { // searching for last because nullability annotations are generated after backend generates source annotations getClsNullabilityAnnotation(member) ?: KtLightNonExistentAnnotation(member) diff --git a/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.java b/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.java index 689f2f5526c..9f8ce27644f 100644 --- a/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.java +++ b/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.java @@ -1,5 +1,4 @@ public final class Prop /* Prop*/ { - @org.jetbrains.annotations.NotNull() private final java.lang.Object someProp; @null() @@ -11,13 +10,11 @@ public final class Fun /* Fun*/ { @null() public Fun(); - @org.jetbrains.annotations.NotNull() private final java.lang.Object someFun(); } public final class ArrayOfAnonymous /* ArrayOfAnonymous*/ { - @org.jetbrains.annotations.NotNull() private final java.lang.Object[] a1; @null() @@ -29,11 +26,10 @@ public final class ArrayOfAnonymous /* ArrayOfAnonymous*/ { } final class C /* C*/ { - @org.jetbrains.annotations.NotNull() - private final kotlin.jvm.functions.Function0 initChild; - private final int y; + private final kotlin.jvm.functions.Function0 initChild; + @null() public C(int); @@ -54,7 +50,6 @@ public abstract class Super /* Super*/ { } public final class Sub /* Sub*/ extends Super { - @org.jetbrains.annotations.NotNull() private final java.lang.Object[] a; @null() @@ -66,7 +61,6 @@ public final class Sub /* Sub*/ extends Super { } public final class ValidPublicSupertype /* ValidPublicSupertype*/ { - @org.jetbrains.annotations.NotNull() private final java.lang.Runnable x; @null() @@ -84,7 +78,6 @@ public abstract interface I /* I*/ { } public final class InvalidPublicSupertype /* InvalidPublicSupertype*/ { - @org.jetbrains.annotations.NotNull() private final java.lang.Runnable x; @null() diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt index 31bbc335c10..88edd394a5b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt @@ -272,7 +272,7 @@ object LightClassLazinessChecker { } clsAnnotations.zip(lightAnnotations).forEach { (clsAnnotation, lightAnnotation) -> - if (lightAnnotation !is KtLightNullabilityAnnotation) + if (lightAnnotation !is KtLightNullabilityAnnotation<*>) assertNotNull( lightAnnotation!!.nameReferenceElement, "nameReferenceElement should be not null for $lightAnnotation of ${lightAnnotation.javaClass}"