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:
Denis Zharkov
2018-12-05 16:25:03 +03:00
parent 63907b1a6b
commit 2f8b214e76
12 changed files with 263 additions and 5 deletions
@@ -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;
}
}
@@ -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
}
}
@@ -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 }
@@ -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 {
}
@@ -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)
}
@@ -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");