Analysis API: introduce annotation value for KClass

This commit is contained in:
Ilya Kirillov
2021-11-24 12:38:03 +01:00
parent f722a54c78
commit 673459580c
16 changed files with 86 additions and 17 deletions
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.inference.CapturedType
@@ -280,8 +281,16 @@ internal fun ConstantValue<*>.toKtConstantValue(): KtConstantValue {
internal fun ConstantValue<*>.toKtAnnotationValue(): KtAnnotationValue {
return when (this) {
is ArrayValue -> KtArrayAnnotationValue(value.map { it.toKtAnnotationValue() }, null)
is EnumValue -> KtEnumEntryAnnotationValue(CallableId(enumClassId, enumEntryName), null)
is ArrayValue -> KtArrayAnnotationValue(value.map { it.toKtAnnotationValue() }, sourcePsi = null)
is EnumValue -> KtEnumEntryAnnotationValue(CallableId(enumClassId, enumEntryName), sourcePsi = null)
is KClassValue -> when (val value = value) {
is KClassValue.Value.LocalClass -> {
val descriptor = value.type.constructor.declarationDescriptor as ClassDescriptor
KtLocalKClassAnnotationValue(descriptor.source.getPsi() as KtClassOrObject, sourcePsi = null)
}
is KClassValue.Value.NormalClass -> KtNonLocalKClassAnnotationValue(value.classId, sourcePsi = null)
}
is AnnotationValue -> {
KtAnnotationApplicationValue(
KtAnnotationApplication(
@@ -292,7 +301,6 @@ internal fun ConstantValue<*>.toKtAnnotationValue(): KtAnnotationValue {
)
)
}
is KClassValue -> KtKClassValue(cl)
else -> {
KtConstantAnnotationValue(toKtConstantValue())
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.NewCapturedType
import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor
import org.jetbrains.kotlin.types.typeUtil.builtIns
import kotlin.reflect.KClass
internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, private val isDebugText: Boolean = false) {
private companion object {
@@ -118,6 +119,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr
is KtEnumEntryAnnotationValue -> append(value.callableId)
is KtConstantAnnotationValue -> append(value.constantValue.constantValueKind.asString).append("(").append(value.constantValue.value).append(")")
KtUnsupportedAnnotationValue -> append(KtUnsupportedAnnotationValue::class.java.simpleName)
is KtKClassAnnotationValue -> append(value.render())
}
}
@@ -78,6 +78,7 @@ internal object FirAnnotationValueConverter {
private fun FirExpression.convertConstantExpression(
session: FirSession,
): KtAnnotationValue? {
val sourcePsi = psi as? KtElement
return when (this) {
is FirConstExpression<*> -> convertConstantExpression()
is FirNamedArgumentExpression -> {
@@ -87,11 +88,11 @@ internal object FirAnnotationValueConverter {
expression.convertConstantExpression(session)
}
is FirVarargArgumentsExpression -> {
arguments.convertConstantExpression(session).toArrayConstantValueIfNecessary(psi as? KtElement)
arguments.convertConstantExpression(session).toArrayConstantValueIfNecessary(sourcePsi)
}
is FirArrayOfCall -> {
// Desugared collection literals.
KtArrayAnnotationValue(argumentList.arguments.convertConstantExpression(session), psi as? KtElement)
KtArrayAnnotationValue(argumentList.arguments.convertConstantExpression(session), sourcePsi)
}
is FirFunctionCall -> {
val reference = calleeReference as? FirResolvedNamedReference ?: return null
@@ -126,11 +127,19 @@ internal object FirAnnotationValueConverter {
val reference = calleeReference as? FirResolvedNamedReference ?: return null
when (val resolvedSymbol = reference.resolvedSymbol) {
is FirEnumEntrySymbol -> {
KtEnumEntryAnnotationValue(resolvedSymbol.callableId, psi as? KtElement)
KtEnumEntryAnnotationValue(resolvedSymbol.callableId, sourcePsi)
}
else -> null
}
}
is FirGetClassCall -> {
val symbol = (argument as FirResolvedQualifier).symbol
when {
symbol == null -> KtErrorClassAnnotationValue(sourcePsi)
symbol.classId.isLocal -> KtLocalKClassAnnotationValue(symbol.fir.psi as KtClassOrObject, sourcePsi)
else -> KtNonLocalKClassAnnotationValue(symbol.classId, sourcePsi)
}
}
else -> null
} ?: FirCompileTimeConstantEvaluator.evaluate(this)?.convertConstantExpression()
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.api.annotations
import org.jetbrains.kotlin.analysis.api.base.KtConstantValue
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
/**
@@ -47,6 +48,22 @@ public class KtAnnotationApplicationValue(
}
public sealed class KtKClassAnnotationValue : KtAnnotationValue()
public class KtNonLocalKClassAnnotationValue(
public val classId: ClassId,
override val sourcePsi: KtElement?,
) : KtKClassAnnotationValue()
public class KtLocalKClassAnnotationValue(
public val ktClass: KtClassOrObject,
override val sourcePsi: KtElement?,
) : KtKClassAnnotationValue()
public class KtErrorClassAnnotationValue(
override val sourcePsi: KtElement?,
) : KtKClassAnnotationValue()
public class KtEnumEntryAnnotationValue(
public val callableId: CallableId?,
override val sourcePsi: KtElement?,
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.analysis.api.annotations
import org.jetbrains.kotlin.renderer.render
public object KtAnnotationValueRenderer {
public fun render(value: KtAnnotationValue): String = buildString {
renderConstantValue(value)
@@ -25,11 +27,23 @@ public object KtAnnotationValueRenderer {
renderConstantAnnotationValue(value)
}
KtUnsupportedAnnotationValue -> {
append("KtUnsupportedConstantValue")
append("error(\"non-annotation value\")")
}
is KtKClassAnnotationValue -> {
renderKClassAnnotationValue(value)
}
}
}
private fun StringBuilder.renderKClassAnnotationValue(value: KtKClassAnnotationValue) {
when (value) {
is KtErrorClassAnnotationValue -> append("UNRESOLVED_CLASS")
is KtLocalKClassAnnotationValue -> append(value.ktClass.nameAsName?.render())
is KtNonLocalKClassAnnotationValue -> append(value.classId.asSingleFqName().render())
}
append("::class")
}
private fun StringBuilder.renderConstantAnnotationValue(value: KtConstantAnnotationValue) {
append(KtConstantValueRenderer.render(value.constantValue))
}
@@ -1,5 +1,5 @@
KtDeclaration: KtClass Foo
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,5 +1,5 @@
KtDeclaration: KtNamedFunction foo
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,5 +1,5 @@
KtDeclaration: KtProperty foo
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,5 +1,5 @@
KtDeclaration: KtTypeAlias Foo
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,5 +1,5 @@
KtDeclaration: KtPropertyAccessor null
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,5 +1,5 @@
KtDeclaration: KtParameter bar
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,5 +1,5 @@
KtDeclaration: KtProperty foo
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,5 +1,5 @@
KtDeclaration: KtPropertyAccessor null
annotations: [
A(a = 1, c = KtUnsupportedConstantValue)
A(a = 1, c = kotlin.Int::class)
psi: KtAnnotationEntry
]
@@ -1,3 +1,3 @@
expression: 42u
constant: 42
constantValueKind: Long
constantValueKind: UInt
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.light.classes.symbol
import com.intellij.psi.*
import com.intellij.psi.util.TypeConversionUtil
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightMember
@@ -20,8 +21,10 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.*
import org.jetbrains.kotlin.analysis.api.types.*
import org.jetbrains.kotlin.asJava.elements.psiType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.constants.KClassValue
import java.util.*
internal fun <L : Any> L.invalidAccess(): Nothing =
@@ -199,6 +202,21 @@ internal fun KtAnnotationValue.toAnnotationMemberValue(parent: PsiElement): PsiA
FirPsiExpression(sourcePsi, parent, psiExpression)
}
KtUnsupportedAnnotationValue -> null
is KtErrorClassAnnotationValue -> null
is KtLocalKClassAnnotationValue -> null
is KtNonLocalKClassAnnotationValue -> toAnnotationMemberValue(parent)
}
}
private fun KtNonLocalKClassAnnotationValue.toAnnotationMemberValue(parent: PsiElement): PsiExpression? {
val fqName = classId.asSingleFqName()
val canonicalText = psiType(
fqName.asString(), parent, boxPrimitiveType = false /* TODO value.arrayNestedness > 0*/,
).let(TypeConversionUtil::erasure).getCanonicalText(false)
return try {
PsiElementFactory.getInstance(parent.project).createExpressionFromText("$canonicalText.class", parent)
} catch (_: IncorrectOperationException) {
null
}
}
@@ -220,4 +238,5 @@ internal fun KtConstantValue.createPsiLiteral(parent: PsiElement): PsiExpression
}
}
internal fun BitSet.copy(): BitSet = clone() as BitSet
@@ -80,7 +80,7 @@ class KtLightPsiClassObjectAccessExpression(override val kotlinOrigin: KtClassLi
override fun getOperand(): PsiTypeElement = LightTypeElement(kotlinOrigin.manager, type)
}
internal fun psiType(kotlinFqName: String, context: PsiElement, boxPrimitiveType: Boolean = false): PsiType {
fun psiType(kotlinFqName: String, context: PsiElement, boxPrimitiveType: Boolean = false): PsiType {
if (!boxPrimitiveType) {
when (kotlinFqName) {
"kotlin.Int" -> return PsiType.INT