mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin, uast] Support for type annotations in uast toPsiType converter (KTIJ-17990)
GitOrigin-RevId: e8f294316c5d97cf6a2209be5bc7803da49d0732
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f3b92ea371
commit
5c5ec4a543
+59
-3
@@ -2,6 +2,7 @@
|
||||
|
||||
package org.jetbrains.uast.kotlin
|
||||
|
||||
import com.intellij.openapi.diagnostic.Attachment
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.psi.*
|
||||
@@ -10,12 +11,15 @@ import com.intellij.psi.impl.compiled.ClsTypeElementImpl
|
||||
import com.intellij.psi.impl.compiled.SignatureParsing
|
||||
import com.intellij.psi.impl.compiled.StubBuildingVisitor
|
||||
import com.intellij.psi.util.PsiTypesUtil
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.asJava.*
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
|
||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -42,8 +46,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstr
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.sam.SamConstructorDescriptor
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
@@ -168,7 +171,7 @@ internal fun KotlinType.toPsiType(lightDeclaration: PsiModifierListOwner?, conte
|
||||
}
|
||||
}
|
||||
}
|
||||
if (psiType != null) return psiType
|
||||
if (psiType != null) return psiType.annotate(buildAnnotationProvider(this, lightDeclaration ?: context))
|
||||
}
|
||||
|
||||
if (this.containsLocalTypes()) return UastErrorType
|
||||
@@ -205,6 +208,59 @@ internal fun KotlinType.toPsiType(lightDeclaration: PsiModifierListOwner?, conte
|
||||
return ClsTypeElementImpl(parent, typeText, '\u0000').type
|
||||
}
|
||||
|
||||
private fun renderAnnotation(annotation: AnnotationDescriptor): String {
|
||||
val fqn = annotation.fqName?.asString() ?: return "<ERROR>"
|
||||
val valueArguments = annotation.allValueArguments
|
||||
val valueesList = SmartList<String>().apply {
|
||||
for ((k, v) in valueArguments.entries) {
|
||||
add("${k.identifier} = ${renderConstantValue(v)}")
|
||||
}
|
||||
}
|
||||
return "@$fqn(${valueesList.joinToString(", ")})"
|
||||
}
|
||||
|
||||
private fun renderConstantValue(value: ConstantValue<*>?): String? = value?.accept(object : AnnotationArgumentVisitor<String?, String?> {
|
||||
override fun visitLongValue(value: LongValue, data: String?): String = value.value.toString()
|
||||
override fun visitIntValue(value: IntValue, data: String?): String = value.value.toString()
|
||||
override fun visitErrorValue(value: ErrorValue?, data: String?): String? = null
|
||||
override fun visitShortValue(value: ShortValue, data: String?): String = value.value.toString()
|
||||
override fun visitByteValue(value: ByteValue, data: String?): String = value.value.toString()
|
||||
override fun visitDoubleValue(value: DoubleValue, data: String?): String = value.value.toString()
|
||||
override fun visitFloatValue(value: FloatValue, data: String?): String = value.value.toString()
|
||||
override fun visitBooleanValue(value: BooleanValue, data: String?): String = value.value.toString()
|
||||
override fun visitCharValue(value: CharValue, data: String?): String = "'${value.value}'"
|
||||
override fun visitStringValue(value: StringValue, data: String?): String = "\"${value.value}\""
|
||||
override fun visitNullValue(value: NullValue?, data: String?): String = "null"
|
||||
override fun visitEnumValue(value: EnumValue, data: String?): String =
|
||||
value.value.run { first.asSingleFqName().asString() + "." + second.asString() }
|
||||
|
||||
override fun visitArrayValue(value: ArrayValue, data: String?): String =
|
||||
value.value.mapNotNull { renderConstantValue(it) }.joinToString(", ", "{", "}")
|
||||
|
||||
override fun visitAnnotationValue(value: AnnotationValue, data: String?): String = renderAnnotation(value.value)
|
||||
override fun visitKClassValue(value: KClassValue, data: String?): String = value.value.toString() + ".class"
|
||||
override fun visitUByteValue(value: UByteValue, data: String?): String = value.value.toString()
|
||||
override fun visitUShortValue(value: UShortValue, data: String?): String = value.value.toString()
|
||||
override fun visitUIntValue(value: UIntValue, data: String?): String = value.value.toString()
|
||||
override fun visitULongValue(value: ULongValue, data: String?): String = value.value.toString()
|
||||
}, null)
|
||||
|
||||
private fun buildAnnotationProvider(ktType: KotlinType, context: PsiElement): TypeAnnotationProvider {
|
||||
val result = SmartList<PsiAnnotation>()
|
||||
val psiElementFactory = PsiElementFactory.getInstance(context.project)
|
||||
for (annotation in ktType.annotations) {
|
||||
val annotationText = renderAnnotation(annotation)
|
||||
try {
|
||||
result.add(psiElementFactory.createAnnotationFromText(annotationText, context))
|
||||
} catch (e: Exception) {
|
||||
Logger.getInstance("org.jetbrains.uast.kotlin.KotlinInternalUastUtils")
|
||||
.error("failed to create annotation from text", e, Attachment("annotationText.txt", annotationText))
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) return TypeAnnotationProvider.EMPTY
|
||||
return TypeAnnotationProvider.Static.create(result.toArray(PsiAnnotation.EMPTY_ARRAY))
|
||||
}
|
||||
|
||||
internal fun KtTypeReference?.toPsiType(source: UElement, boxed: Boolean = false): PsiType {
|
||||
if (this == null) return UastErrorType
|
||||
return (analyze()[BindingContext.TYPE, this] ?: return UastErrorType).toPsiType(source, this, boxed)
|
||||
|
||||
@@ -19,4 +19,16 @@ class KotlinUastTypesTest : AbstractKotlinTypesTest() {
|
||||
@Test fun testInferenceInsideUnresolvedConstructor() = doTest("InferenceInsideUnresolvedConstructor")
|
||||
|
||||
@Test fun testInnerNonFixedTypeVariable() = doTest("InnerNonFixedTypeVariable")
|
||||
|
||||
@Test fun testAnnotatedTypes() = doTest("AnnotatedTypes")
|
||||
}
|
||||
|
||||
|
||||
@Target(AnnotationTarget.TYPE)
|
||||
annotation class MyAnnotation(val a: Int, val b: String, val c: AnnotationTarget)
|
||||
|
||||
fun foo(list: List<@MyAnnotation(1, "str", AnnotationTarget.TYPE) String>) {
|
||||
val a = list[2]
|
||||
val b: @MyAnnotation(2, "boo", AnnotationTarget.FILE) String = "abc"
|
||||
val c = b
|
||||
}
|
||||
+17
-1
@@ -2,6 +2,8 @@
|
||||
|
||||
package org.jetbrains.uast.test.common.kotlin
|
||||
|
||||
import com.intellij.lang.jvm.annotation.JvmAnnotationConstantValue
|
||||
import com.intellij.lang.jvm.annotation.JvmAnnotationEnumFieldValue
|
||||
import org.jetbrains.uast.UElement
|
||||
import org.jetbrains.uast.UExpression
|
||||
import org.jetbrains.uast.UFile
|
||||
@@ -42,7 +44,21 @@ interface TypesTestBase {
|
||||
builder.append(initialLine)
|
||||
if (node is UExpression) {
|
||||
val value = node.getExpressionType()
|
||||
value?.let { builder.append(" : ").append(it) }
|
||||
value?.let { psiType ->
|
||||
builder.append(" : ")
|
||||
psiType.annotations.takeIf { it.isNotEmpty() }?.joinTo(builder, ", ", "{", "}") { annotation ->
|
||||
"@${annotation.qualifiedName}(${
|
||||
annotation.attributes.joinToString { attr ->
|
||||
attr.attributeName + " = " + when (val v = attr.attributeValue) {
|
||||
is JvmAnnotationConstantValue -> v.constantValue
|
||||
is JvmAnnotationEnumFieldValue -> v.fieldName
|
||||
else -> v
|
||||
}
|
||||
}
|
||||
})"
|
||||
}
|
||||
builder.append(psiType)
|
||||
}
|
||||
}
|
||||
builder.appendLine()
|
||||
level++
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
@Target(AnnotationTarget.TYPE)
|
||||
annotation class MyAnnotation(val a: Int, val b: String, val c: AnnotationTarget)
|
||||
|
||||
fun foo(list: List<@MyAnnotation(1, "str", AnnotationTarget.TYPE) String>) {
|
||||
val a = list[2]
|
||||
val b: @MyAnnotation(2, "boo", AnnotationTarget.FILE) String = "abc"
|
||||
val c = b
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
UFile (package = ) [public final class AnnotatedTypesKt {...]
|
||||
UClass (name = AnnotatedTypesKt) [public final class AnnotatedTypesKt {...}]
|
||||
UMethod (name = foo) [public static final fun foo(@org.jetbrains.annotations.NotNull list: java.util.List<java.lang.String>) : void {...}]
|
||||
UParameter (name = list) [@org.jetbrains.annotations.NotNull var list: java.util.List<java.lang.String>]
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
|
||||
UBlockExpression [{...}] : PsiType:void
|
||||
UDeclarationsExpression [var a: java.lang.String = list[2]]
|
||||
ULocalVariable (name = a) [var a: java.lang.String = list[2]]
|
||||
UArrayAccessExpression [list[2]] : {@MyAnnotation(a = 1, b = str, c = TYPE)}PsiType:String
|
||||
USimpleNameReferenceExpression (identifier = list) [list] : PsiType:List<? extends String>
|
||||
ULiteralExpression (value = 2) [2] : PsiType:int
|
||||
UDeclarationsExpression [var b: java.lang.String = "abc"]
|
||||
ULocalVariable (name = b) [var b: java.lang.String = "abc"]
|
||||
ULiteralExpression (value = "abc") ["abc"] : PsiType:String
|
||||
UDeclarationsExpression [var c: java.lang.String = b]
|
||||
ULocalVariable (name = c) [var c: java.lang.String = b]
|
||||
USimpleNameReferenceExpression (identifier = b) [b] : {@MyAnnotation(a = 2, b = boo, c = FILE)}PsiType:String
|
||||
UClass (name = MyAnnotation) [public abstract annotation MyAnnotation {...}]
|
||||
UAnnotation (fqName = kotlin.annotation.Target) [@kotlin.annotation.Target(allowedTargets = AnnotationTarget.TYPE)]
|
||||
UNamedExpression (name = allowedTargets) [allowedTargets = AnnotationTarget.TYPE]
|
||||
UQualifiedReferenceExpression [AnnotationTarget.TYPE] : PsiType:AnnotationTarget
|
||||
USimpleNameReferenceExpression (identifier = AnnotationTarget) [AnnotationTarget]
|
||||
USimpleNameReferenceExpression (identifier = TYPE) [TYPE] : PsiType:AnnotationTarget
|
||||
UAnnotationMethod (name = a) [public abstract fun a() : int = UastEmptyExpression]
|
||||
UAnnotationMethod (name = b) [public abstract fun b() : java.lang.String = UastEmptyExpression]
|
||||
UAnnotationMethod (name = c) [public abstract fun c() : kotlin.annotation.AnnotationTarget = UastEmptyExpression]
|
||||
Reference in New Issue
Block a user