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
This commit is contained in:
+9
@@ -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<KtLightAbstractAnnotation> getGivenAnnotations() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+147
@@ -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<KtLightAbstractAnnotation> = 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 <T : PsiAnnotationMemberValue?> 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<PsiNameValuePair> by lazyPub {
|
||||
annotationDescriptor.allValueArguments.map {
|
||||
PsiNameValuePairForAnnotationArgument(it.key.asString(), it.value, ultraLightSupport, this)
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
override fun getAttributes(): Array<PsiNameValuePair> = _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<PsiAnnotationMemberValue>
|
||||
) : KtLightElementBase(lightParent), PsiArrayInitializerMemberValue {
|
||||
|
||||
override val kotlinOrigin: KtElement? get() = null
|
||||
|
||||
override fun getInitializers(): Array<PsiAnnotationMemberValue> = 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 -> "<undefined value>"
|
||||
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
|
||||
|
||||
@@ -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<KtLightAbstractAnnotation>
|
||||
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<KtLightAbstractAnnotation>
|
||||
get() = descriptor.obtainLightAnnotations(support, this)
|
||||
}
|
||||
|
||||
@@ -670,6 +670,7 @@ interface UltraLightSupport {
|
||||
fun isTooComplexForUltraLightGeneration(element: KtClassOrObject): Boolean
|
||||
val deprecationResolver: DeprecationResolver
|
||||
val typeMapper: KotlinTypeMapper
|
||||
val moduleDescriptor: ModuleDescriptor
|
||||
}
|
||||
|
||||
interface KtUltraLightElementWithNullabilityAnnotation<out T : KtDeclaration, out D : PsiModifierListOwner> : KtLightDeclaration<T, D>,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||
interface KtLightElement<out T : KtElement, out D : PsiElement> : PsiElement {
|
||||
val kotlinOrigin: T?
|
||||
|
||||
val givenAnnotations: List<KtLightAbstractAnnotation>? get() = null
|
||||
|
||||
val clsDelegate: D
|
||||
}
|
||||
|
||||
@@ -46,4 +48,4 @@ interface KtLightMethod : PsiAnnotationMethod, KtLightMember<PsiMethod> {
|
||||
|| lightMemberOrigin?.originKind == JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL
|
||||
|
||||
val isMangled: Boolean
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -66,7 +66,8 @@ abstract class KtLightModifierList<out T : KtLightElement<KtModifierListOwner, P
|
||||
override fun toString() = "Light modifier list of $owner"
|
||||
|
||||
private fun computeAnnotations(): List<KtLightAbstractAnnotation> {
|
||||
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 }
|
||||
|
||||
+8
@@ -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<KtTypeParameter, PsiTypeParameter> {
|
||||
private final PsiTypeParameterListOwner owner;
|
||||
@@ -172,4 +174,10 @@ public class KtLightTypeParameter
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<KtLightAbstractAnnotation> getGivenAnnotations() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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<KClass<*>>,
|
||||
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 {
|
||||
|
||||
}
|
||||
+4
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user