From 2f8b214e760db4dc3e318c3ee5455da7ad3485c8 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 5 Dec 2018 16:25:03 +0300 Subject: [PATCH] Support annotation based on descriptor in ultra-light classes It helps to generate properly the annotations for delegates Also note in test, that annotations in Base class have really weird text of arguments and also don't have names --- .../FakeLightClassForFileOfPackage.java | 9 ++ .../asJava/classes/ultraLightAnnotations.kt | 147 ++++++++++++++++++ .../kotlin/asJava/classes/ultraLightMethod.kt | 9 +- .../kotlin/asJava/classes/ultraLightPsi.kt | 1 + .../kotlin/asJava/classes/ultraLightUtils.kt | 10 +- .../kotlin/asJava/elements/KtLightElements.kt | 4 +- .../asJava/elements/KtLightModifierList.kt | 3 +- .../asJava/elements/KtLightTypeParameter.java | 8 + .../delegatesWithAnnotations.java | 42 +++++ .../delegatesWithAnnotations.kt | 25 +++ .../resolve/IDELightClassGenerationSupport.kt | 5 +- .../UltraLightClassLoadingTestGenerated.java | 5 + 12 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.java create mode 100644 compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.kt diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/FakeLightClassForFileOfPackage.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/FakeLightClassForFileOfPackage.java index adaf67bcd59..9bccca502fb 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/FakeLightClassForFileOfPackage.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/FakeLightClassForFileOfPackage.java @@ -23,12 +23,15 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.impl.light.AbstractLightClass; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation; import org.jetbrains.kotlin.asJava.finder.JavaElementFinder; import org.jetbrains.kotlin.idea.KotlinLanguage; import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind; import org.jetbrains.kotlin.psi.KtClassOrObject; import org.jetbrains.kotlin.psi.KtFile; +import java.util.List; + /** * This class serves as a workaround for usages of {@link JavaElementFinder#findClasses} which eventually only need names of files * containing the class. When queried for a package class (e.g. test/TestPackage), {@code findClasses} along with a @@ -111,4 +114,10 @@ public class FakeLightClassForFileOfPackage extends AbstractLightClass implement public LightClassOriginKind getOriginKind() { return LightClassOriginKind.SOURCE; } + + @Nullable + @Override + public List getGivenAnnotations() { + return null; + } } 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 index df2fc73f96b..eb9ff23f2ef 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt @@ -2,9 +2,22 @@ package org.jetbrains.kotlin.asJava.classes import com.intellij.lang.jvm.JvmModifier import com.intellij.psi.* +import com.intellij.psi.impl.PsiImplUtil +import com.intellij.psi.impl.light.LightIdentifier +import com.intellij.psi.impl.light.LightTypeElement +import com.intellij.psi.impl.source.PsiClassReferenceType +import com.intellij.psi.meta.PsiMetaData import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable +import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation +import org.jetbrains.kotlin.asJava.elements.KtLightElementBase import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.load.kotlin.TypeMappingMode +import org.jetbrains.kotlin.psi.KtCallElement +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.isError @@ -35,6 +48,140 @@ class KtUltraLightNullabilityAnnotation( } } +fun DeclarationDescriptor.obtainLightAnnotations( + ultraLightSupport: UltraLightSupport, + parent: PsiElement +): List = annotations.map { KtUltraLightAnnotationForDescriptor(it, ultraLightSupport, parent) } + +class KtUltraLightAnnotationForDescriptor( + private val annotationDescriptor: AnnotationDescriptor, + private val ultraLightSupport: UltraLightSupport, + parent: PsiElement +) : KtLightAbstractAnnotation(parent, { error("clsDelegate for annotation based on descriptor: $annotationDescriptor") }) { + override fun getNameReferenceElement(): PsiJavaCodeReferenceElement? = null + + override fun getMetaData(): PsiMetaData? = null + + private val parameterList = ParameterListImpl() + + override fun getParameterList(): PsiAnnotationParameterList = parameterList + + override val kotlinOrigin: KtCallElement? get() = null + + override fun setDeclaredAttributeValue(p0: String?, p1: T?) = cannotModify() + + override fun findAttributeValue(attributeName: String?): PsiAnnotationMemberValue? = + PsiImplUtil.findAttributeValue(this, attributeName) + + override fun findDeclaredAttributeValue(attributeName: String?) = + PsiImplUtil.findDeclaredAttributeValue(this, attributeName) + + override fun getQualifiedName() = annotationDescriptor.fqName?.asString() + + private inner class ParameterListImpl : KtLightElementBase(this@KtUltraLightAnnotationForDescriptor), PsiAnnotationParameterList { + private val _attributes: Array by lazyPub { + annotationDescriptor.allValueArguments.map { + PsiNameValuePairForAnnotationArgument(it.key.asString(), it.value, ultraLightSupport, this) + }.toTypedArray() + } + + override fun getAttributes(): Array = _attributes + + override val kotlinOrigin: KtElement? get() = null + } + + override fun getText() = "@$qualifiedName(" + parameterList.attributes.joinToString { it.name + "=" + it.value?.text } + ")" +} + +private class PsiNameValuePairForAnnotationArgument( + private val _name: String = "", + private val constantValue: ConstantValue<*>, + private val ultraLightSupport: UltraLightSupport, + parent: PsiElement +) : KtLightElementBase(parent), PsiNameValuePair { + override val kotlinOrigin: KtElement? get() = null + + private val _value by lazyPub { + constantValue.toAnnotationMemberValue(this, ultraLightSupport) + } + + override fun setValue(p0: PsiAnnotationMemberValue) = cannotModify() + + override fun getNameIdentifier() = LightIdentifier(parent.manager, _name) + + override fun getValue(): PsiAnnotationMemberValue? = _value + + override fun getLiteralValue(): String? = (value as? PsiLiteralExpression)?.value?.toString() + + override fun getName() = _name +} + +private fun ConstantValue<*>.toAnnotationMemberValue( + parent: PsiElement, ultraLightSupport: UltraLightSupport +): PsiAnnotationMemberValue? = when (this) { + + is AnnotationValue -> KtUltraLightAnnotationForDescriptor(value, ultraLightSupport, parent) + + is ArrayValue -> + KtUltraLightPsiArrayInitializerMemberValue(lightParent = parent) { arrayLiteralParent -> + this.value.mapNotNull { element -> element.toAnnotationMemberValue(arrayLiteralParent, ultraLightSupport) } + } + + is KClassValue -> KtUltraLightPsiClassObjectAccessExpression(this, ultraLightSupport, parent) + + is ErrorValue -> null + + else -> KtUltraLightPsiLiteralForConstantValue(this, ultraLightSupport, parent) +} + +private class KtUltraLightPsiArrayInitializerMemberValue( + val lightParent: PsiElement, + private val arguments: (KtUltraLightPsiArrayInitializerMemberValue) -> List +) : KtLightElementBase(lightParent), PsiArrayInitializerMemberValue { + + override val kotlinOrigin: KtElement? get() = null + + override fun getInitializers(): Array = arguments(this).toTypedArray() + + override fun getParent(): PsiElement = lightParent + override fun isPhysical(): Boolean = false + + override fun getText(): String = "{" + initializers.joinToString { it.text } + "}" +} + +private open class KtUltraLightPsiLiteralForConstantValue( + private val constantValue: ConstantValue<*>, + private val ultraLightSupport: UltraLightSupport, + lightParent: PsiElement +) : KtLightElementBase(lightParent), PsiLiteralExpression { + override val kotlinOrigin: KtElement? get() = null + + override fun getValue(): Any? = constantValue.value + + override fun getType() = + constantValue.getType(ultraLightSupport.moduleDescriptor).asPsiType(ultraLightSupport, TypeMappingMode.DEFAULT, this) + + override fun getText() = when (constantValue) { + is NullValue -> "" + is StringValue -> "\"${value.toString()}\"" + is EnumValue -> constantValue.enumClassId.shortClassName.asString() + "." + constantValue.enumEntryName.asString() + else -> value.toString() + } +} + +private class KtUltraLightPsiClassObjectAccessExpression( + kClassValue: KClassValue, + ultraLightSupport: UltraLightSupport, + parent: PsiElement +) : KtUltraLightPsiLiteralForConstantValue(kClassValue, ultraLightSupport, parent), PsiClassObjectAccessExpression { + override fun getOperand(): PsiTypeElement { + val argument = (type as? PsiClassReferenceType)?.parameters?.getOrNull(0) + return LightTypeElement(parent.manager, argument ?: type) + } + + override fun getText() = operand.text + ".class" +} + fun PsiModifierListOwner.isPrivateOrParameterInPrivateMethod(): Boolean { if (hasModifier(JvmModifier.PRIVATE)) return true if (this !is PsiParameter) return false 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 cf13aacd87a..c49b813151b 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 @@ -12,6 +12,7 @@ import com.intellij.psi.impl.light.LightMethodBuilder import com.intellij.psi.impl.light.LightTypeParameterListBuilder import org.jetbrains.annotations.NonNls import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration +import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl import org.jetbrains.kotlin.asJava.elements.KtLightSimpleModifierList @@ -109,12 +110,15 @@ internal class KtUltraLightMethodForDescriptor( override val kotlinTypeForNullabilityAnnotation: KotlinType? get() = descriptor.returnType + + override val givenAnnotations: List + get() = descriptor.obtainLightAnnotations(support, this) } internal abstract class KtUltraLightParameter( name: String, override val kotlinOrigin: KtDeclaration?, - private val support: UltraLightSupport, + protected val support: UltraLightSupport, method: KtLightMethod ) : org.jetbrains.kotlin.asJava.elements.LightParameter( name, @@ -246,4 +250,7 @@ internal class KtUltraLightParameterForDescriptor( override fun computeContainingDescriptor() = descriptor.containingDeclaration as? CallableMemberDescriptor override fun isVarArgs() = (descriptor as? ValueParameterDescriptor)?.varargElementType != null + + override val givenAnnotations: List + get() = descriptor.obtainLightAnnotations(support, this) } 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 c88180f679a..c68d72d9043 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 @@ -670,6 +670,7 @@ interface UltraLightSupport { fun isTooComplexForUltraLightGeneration(element: KtClassOrObject): Boolean val deprecationResolver: DeprecationResolver val typeMapper: KotlinTypeMapper + val moduleDescriptor: ModuleDescriptor } interface KtUltraLightElementWithNullabilityAnnotation : KtLightDeclaration, diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt index 60f4db191a5..35b052c424b 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt @@ -144,7 +144,15 @@ internal fun UltraLightSupport.mapType( ): PsiType { val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.SKIP_CHECKS) mapTypeToSignatureWriter(typeMapper, signatureWriter) - val signature = StringCharacterIterator(signatureWriter.toString()) + val canonicalSignature = signatureWriter.toString() + return createTypeFromCanonicalText(canonicalSignature, psiContext) +} + +fun createTypeFromCanonicalText( + canonicalSignature: String, + psiContext: PsiElement +): PsiType { + val signature = StringCharacterIterator(canonicalSignature) val javaType = SignatureParsing.parseTypeString(signature, StubBuildingVisitor.GUESSING_MAPPER) val typeInfo = TypeInfo.fromString(javaType, false) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt index 49beb6ba892..70608ee9a9f 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt @@ -27,6 +27,8 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind interface KtLightElement : PsiElement { val kotlinOrigin: T? + val givenAnnotations: List? get() = null + val clsDelegate: D } @@ -46,4 +48,4 @@ interface KtLightMethod : PsiAnnotationMethod, KtLightMember { || lightMemberOrigin?.originKind == JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL val isMangled: Boolean -} \ No newline at end of file +} 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 124df015e11..7b1f149acde 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 @@ -66,7 +66,8 @@ abstract class KtLightModifierList { - val annotationsForEntries = lightAnnotationsForEntries(this) + val annotationsForEntries = + owner.givenAnnotations ?: lightAnnotationsForEntries(this) val modifierListOwner = parent if (modifierListOwner is KtLightClassForSourceDeclaration && modifierListOwner.isAnnotationType) { val sourceAnnotationNames = annotationsForEntries.mapTo(mutableSetOf()) { it.qualifiedName } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightTypeParameter.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightTypeParameter.java index d8e71cb77f3..4a288edbddc 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightTypeParameter.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightTypeParameter.java @@ -28,6 +28,8 @@ import org.jetbrains.kotlin.idea.KotlinLanguage; import org.jetbrains.kotlin.psi.KtTypeParameter; import org.jetbrains.kotlin.psi.KtTypeParameterListOwner; +import java.util.List; + public class KtLightTypeParameter extends AbstractLightClass implements PsiTypeParameter, KtLightDeclaration { private final PsiTypeParameterListOwner owner; @@ -172,4 +174,10 @@ public class KtLightTypeParameter } return false; } + + @Nullable + @Override + public List getGivenAnnotations() { + return null; + } } diff --git a/compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.java b/compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.java new file mode 100644 index 00000000000..1feac1d5b9b --- /dev/null +++ b/compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.java @@ -0,0 +1,42 @@ +@java.lang.annotation.Retention(null=java.lang.annotation.RetentionPolicy.RUNTIME) +public abstract @interface SimpleAnn /* SimpleAnn*/ { + @null() + public abstract java.lang.String value(); + +} + +@java.lang.annotation.Retention(null=java.lang.annotation.RetentionPolicy.RUNTIME) +public abstract @interface Ann /* Ann*/ { + @null() + public abstract SimpleAnn[] t(); + + @null() + public abstract java.lang.Class z(); + + @null() + public abstract java.lang.Class[] e(); + + @null() + public abstract java.lang.String y(); + + @null() + public abstract kotlin.DeprecationLevel depr(); + + public abstract int x(); + +} + +public abstract interface Base /* Base*/ { + @Ann(null=1, null="134", null=String::class, null=arrayOf(Int::class, Double::class), null=DeprecationLevel.WARNING, null=SimpleAnn("243"), null=SimpleAnn("4324")) + public abstract void foo(@Ann(null=2, null="324", null=Ann::class, null=arrayOf(Byte::class, Base::class), null=DeprecationLevel.WARNING, null=SimpleAnn("687"), null=SimpleAnn("78")) @org.jetbrains.annotations.NotNull() java.lang.String); + +} + +public final class Derived /* Derived*/ implements Base { + @Ann(x=1, y="134", z=String.class, e={Integer.class, Double.class}, depr=DeprecationLevel.WARNING, t={@SimpleAnn(value="243"), @SimpleAnn(value="4324")}) + public void foo(@Ann(x=2, y="324", z=Ann.class, e={Byte.class, Base.class}, depr=DeprecationLevel.WARNING, t={@SimpleAnn(value="687"), @SimpleAnn(value="78")}) @org.jetbrains.annotations.NotNull() java.lang.String); + + @null() + public Derived(@org.jetbrains.annotations.NotNull() Base); + +} diff --git a/compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.kt b/compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.kt new file mode 100644 index 00000000000..d0b4393ecb9 --- /dev/null +++ b/compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.kt @@ -0,0 +1,25 @@ +import kotlin.reflect.KClass + +/** should load cls */ +annotation class SimpleAnn(val value: String) + +/** should load cls */ +annotation class Ann( + val x: Int, + val y: String, + val z: KClass<*>, + val e: Array>, + val depr: DeprecationLevel + vararg val t: SimpleAnn +) + +interface Base { + @Ann(1, "134", String::class, arrayOf(Int::class, Double::class), DeprecationLevel.WARNING, SimpleAnn("243"), SimpleAnn("4324")) + fun foo( + @Ann(2, "324", Ann::class, arrayOf(Byte::class, Base::class), DeprecationLevel.WARNING, SimpleAnn("687"), SimpleAnn("78")) x: String + ) +} + +class Derived(b: Base) : Base by b { + +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index 7f95cb87008..e0a3dc5558d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -90,8 +90,11 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG return false } + override val moduleDescriptor by lazyPub { + element.getResolutionFacade().moduleDescriptor + } + override val moduleName: String by lazyPub { - val moduleDescriptor = element.getResolutionFacade().moduleDescriptor JvmCodegenUtil.getModuleName(moduleDescriptor) } diff --git a/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java index cbc5f733fe5..de841a0e22d 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java @@ -54,6 +54,11 @@ public class UltraLightClassLoadingTestGenerated extends AbstractUltraLightClass runTest("compiler/testData/asJava/ultraLightClasses/dataClasses.kt"); } + @TestMetadata("delegatesWithAnnotations.kt") + public void testDelegatesWithAnnotations() throws Exception { + runTest("compiler/testData/asJava/ultraLightClasses/delegatesWithAnnotations.kt"); + } + @TestMetadata("delegatingToInterfaces.kt") public void testDelegatingToInterfaces() throws Exception { runTest("compiler/testData/asJava/ultraLightClasses/delegatingToInterfaces.kt");