KT UAST: resolve annotation on setter parameter

To do so, for parameter without source PSI (i.e., synthetic),
we keep nullable annotations from java PSI,
while restoring user-given annotations on source.

^KTIJ-34365 fixed

GitOrigin-RevId: b80b98e63bade9f5e78944c4e4c68aa0fcd01c69
This commit is contained in:
Jinseong Jeon
2025-06-19 13:49:41 +00:00
committed by intellij-monorepo-bot
parent c09bed121c
commit 71583dc94a
4 changed files with 73 additions and 4 deletions
@@ -3,6 +3,8 @@ package org.jetbrains.uast.kotlin
import com.intellij.psi.*
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.psi.*
@@ -70,11 +72,24 @@ abstract class AbstractKotlinUVariable(
}
private fun AbstractKotlinUVariable.buildAnnotations(): List<UAnnotation> {
val sourcePsi = sourcePsi ?: return javaPsi.annotations.map { WrappedUAnnotation(it, this) }
val annotations = SmartList<UAnnotation>()
val hasInheritedGenericType = baseResolveProviderService.hasInheritedGenericType(sourcePsi)
if (!hasInheritedGenericType) {
annotations.add(KotlinNullabilityUAnnotation(baseResolveProviderService, sourcePsi, this))
val sourcePsi = sourcePsi
if (sourcePsi != null) {
val hasInheritedGenericType = baseResolveProviderService.hasInheritedGenericType(sourcePsi)
if (!hasInheritedGenericType) {
annotations.add(
KotlinNullabilityUAnnotation(baseResolveProviderService, sourcePsi, this)
)
}
} else {
javaPsi.annotations.filter { psiAnnotation ->
val fqName = psiAnnotation.qualifiedName
fqName == NotNull::class.qualifiedName || fqName == Nullable::class.qualifiedName
}.forEach { psiAnnotation ->
annotations.add(
WrappedUAnnotation(psiAnnotation, this)
)
}
}
// NB: we can't use sourcePsi.annotationEntries directly due to annotation use-site targets.
baseResolveProviderService.getPsiAnnotations(javaPsi).asSequence()
@@ -1474,6 +1474,52 @@ interface UastResolveApiFixtureTestBase {
)
}
fun checkResolveAnnotationOnSetparam(myFixture: JavaCodeInsightTestFixture) {
myFixture.configureByText(
"main.kt",
"""
package test.pkg
annotation class Anno(val attr: Int)
class Test {
@get:Anno(attr = 42)
@setparam:Anno(attr = 21)
var prop = 0
}
""".trimIndent()
)
val uFile = myFixture.file.toUElement()!!
var cnt = 0
uFile.accept(
object : AbstractUastVisitor() {
override fun visitMethod(node: UMethod): Boolean {
val txt = node.sourcePsi?.text
when (node.name) {
"getProp" -> {
cnt++
val anno = node.uAnnotations.single()
val resolved = anno.resolve()
TestCase.assertNotNull(txt, resolved)
TestCase.assertEquals("test.pkg.Anno", resolved!!.qualifiedName)
}
"setProp" -> {
cnt++
val param = node.uastParameters.single()
val anno = param.uAnnotations.single()
val resolved = anno.resolve()
TestCase.assertNotNull(txt, resolved)
TestCase.assertEquals("test.pkg.Anno", resolved!!.qualifiedName)
}
}
return super.visitMethod(node)
}
}
)
TestCase.assertEquals(2, cnt)
}
fun checkResolveDataClassSyntheticMember(myFixture: JavaCodeInsightTestFixture, isK2: Boolean) {
myFixture.configureByText(
"main.kt",
@@ -218,6 +218,10 @@ class FirUastResolveApiFixtureTest : KotlinLightCodeInsightFixtureTestCase(), Ua
checkOperatorMultiResolvable(myFixture)
}
fun testResolveAnnotationOnSetparam() {
checkResolveAnnotationOnSetparam(myFixture)
}
fun testResolveDataClassSyntheticMember() {
checkResolveDataClassSyntheticMember(myFixture, isK2 = true)
}
@@ -178,6 +178,10 @@ class FE1UastResolveApiFixtureTest : KotlinLightCodeInsightFixtureTestCase(), Ua
checkOperatorMultiResolvable(myFixture)
}
fun testResolveAnnotationOnSetparam() {
checkResolveAnnotationOnSetparam(myFixture)
}
fun testResolveDataClassSyntheticMember() {
checkResolveDataClassSyntheticMember(myFixture, isK2 = false)
}