Skip to content

Commit 015f926

Browse files
committed
handle receiver annotation use site target
1 parent dd557ac commit 015f926

File tree

8 files changed

+148
-14
lines changed

8 files changed

+148
-14
lines changed

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSFunctionDeclarationImpl.kt

+15-2
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,22 @@ class KSFunctionDeclarationImpl private constructor(internal val ktFunctionSymbo
7777
null
7878
} else {
7979
(ktFunctionSymbol.psiIfSource() as? KtFunction)?.receiverTypeReference
80-
?.let { KSTypeReferenceImpl.getCached(it, this@KSFunctionDeclarationImpl) }
80+
?.let {
81+
// receivers are modeled as parameter in AA therefore annotations are stored in
82+
// the corresponding receiver parameter, need to pass it to the `KSTypeReferenceImpl`
83+
KSTypeReferenceImpl.getCached(
84+
it,
85+
this@KSFunctionDeclarationImpl,
86+
ktFunctionSymbol.receiverParameter?.annotations ?: emptyList()
87+
)
88+
}
8189
?: ktFunctionSymbol.receiverType?.let {
82-
KSTypeReferenceResolvedImpl.getCached(it, this@KSFunctionDeclarationImpl)
90+
KSTypeReferenceResolvedImpl.getCached(
91+
it,
92+
this@KSFunctionDeclarationImpl,
93+
-1,
94+
ktFunctionSymbol.receiverParameter?.annotations ?: emptyList()
95+
)
8396
}
8497
}
8598
}

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSPropertyDeclarationImpl.kt

+17-3
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,23 @@ class KSPropertyDeclarationImpl private constructor(internal val ktPropertySymbo
102102

103103
override val extensionReceiver: KSTypeReference? by lazy {
104104
(ktPropertySymbol.psiIfSource() as? KtProperty)?.receiverTypeReference
105-
?.let { KSTypeReferenceImpl.getCached(it, this) }
106-
?: ktPropertySymbol.receiverType
107-
?.let { KSTypeReferenceResolvedImpl.getCached(it, this@KSPropertyDeclarationImpl) }
105+
?.let {
106+
// receivers are modeled as parameter in AA therefore annotations are stored in
107+
// the corresponding receiver parameter, need to pass it to the `KSTypeReferenceImpl`
108+
KSTypeReferenceImpl.getCached(
109+
it,
110+
this,
111+
ktPropertySymbol.receiverParameter?.annotations ?: emptyList()
112+
)
113+
}
114+
?: ktPropertySymbol.receiverType?.let {
115+
KSTypeReferenceResolvedImpl.getCached(
116+
it,
117+
this@KSPropertyDeclarationImpl,
118+
-1,
119+
ktPropertySymbol.receiverParameter?.annotations ?: emptyList()
120+
)
121+
}
108122
}
109123

110124
override val type: KSTypeReference by lazy {

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSTypeReferenceImpl.kt

+10-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.google.devtools.ksp.symbol.KSVisitor
2929
import com.google.devtools.ksp.symbol.Location
3030
import com.google.devtools.ksp.symbol.Modifier
3131
import com.google.devtools.ksp.symbol.Origin
32+
import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotation
3233
import org.jetbrains.kotlin.analysis.api.types.KtType
3334
import org.jetbrains.kotlin.psi.KtAnnotationEntry
3435
import org.jetbrains.kotlin.psi.KtDynamicType
@@ -38,12 +39,17 @@ import org.jetbrains.kotlin.psi.KtUserType
3839

3940
class KSTypeReferenceImpl(
4041
private val ktTypeReference: KtTypeReference,
41-
override val parent: KSNode?
42+
override val parent: KSNode?,
43+
private val additionalAnnotations: List<KaAnnotation>
4244
) : KSTypeReference {
4345
companion object : KSObjectCache<IdKeyPair<KtTypeReference, KSNode?>, KSTypeReference>() {
44-
fun getCached(ktTypeReference: KtTypeReference, parent: KSNode? = null): KSTypeReference {
46+
fun getCached(
47+
ktTypeReference: KtTypeReference,
48+
parent: KSNode? = null,
49+
additionalAnnotations: List<KaAnnotation> = emptyList()
50+
): KSTypeReference {
4551
return cache.getOrPut(IdKeyPair(ktTypeReference, parent)) {
46-
KSTypeReferenceImpl(ktTypeReference, parent)
52+
KSTypeReferenceImpl(ktTypeReference, parent, additionalAnnotations)
4753
}
4854
}
4955
}
@@ -79,7 +85,7 @@ class KSTypeReferenceImpl(
7985
(ktTypeReference.annotationEntries.asSequence() + innerAnnotations.asSequence().flatten())
8086
.map { annotationEntry ->
8187
KSAnnotationImpl.getCached(annotationEntry, this@KSTypeReferenceImpl) {
82-
ktType.annotations.single {
88+
(ktType.annotations + additionalAnnotations).single {
8389
it.psi == annotationEntry
8490
}
8591
}

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/resolved/KSTypeReferenceResolvedImpl.kt

+14-4
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,27 @@ import com.google.devtools.ksp.symbol.*
3434
import com.intellij.psi.PsiClass
3535
import com.intellij.psi.PsiTypeParameter
3636
import com.intellij.psi.impl.source.PsiClassReferenceType
37+
import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotation
3738
import org.jetbrains.kotlin.analysis.api.types.*
3839
import org.jetbrains.kotlin.psi.KtClassOrObject
3940
import org.jetbrains.kotlin.psi.KtTypeParameter
4041

4142
class KSTypeReferenceResolvedImpl private constructor(
4243
private val ktType: KtType,
4344
override val parent: KSNode?,
44-
private val index: Int
45+
private val index: Int,
46+
private val additionalAnnotations: List<KaAnnotation>
4547
) : KSTypeReference, Deferrable {
4648
companion object : KSObjectCache<IdKeyTriple<KtType, KSNode?, Int>, KSTypeReference>() {
47-
fun getCached(type: KtType, parent: KSNode? = null, index: Int = -1): KSTypeReference =
48-
cache.getOrPut(IdKeyTriple(type, parent, index)) { KSTypeReferenceResolvedImpl(type, parent, index) }
49+
fun getCached(
50+
type: KtType,
51+
parent: KSNode? = null,
52+
index: Int = -1,
53+
additionalAnnotations: List<KaAnnotation> = emptyList()
54+
): KSTypeReference =
55+
cache.getOrPut(IdKeyTriple(type, parent, index)) {
56+
KSTypeReferenceResolvedImpl(type, parent, index, additionalAnnotations)
57+
}
4958
}
5059

5160
override val element: KSReferenceElement? by lazy {
@@ -62,7 +71,8 @@ class KSTypeReferenceResolvedImpl private constructor(
6271
}
6372

6473
override val annotations: Sequence<KSAnnotation> by lazy {
65-
ktType.annotations(this)
74+
ktType.annotations(this) +
75+
additionalAnnotations.asSequence().map { KSAnnotationResolvedImpl.getCached(it, this) }
6676
}
6777

6878
override val origin: Origin = parent?.origin ?: Origin.SYNTHETIC

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/util.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ internal fun KtSymbol.toKSNode(): KSNode {
354354
internal fun ClassId.toKtClassSymbol(): KtClassOrObjectSymbol? {
355355
return analyze {
356356
if (this@toKtClassSymbol.isLocal) {
357-
this@toKtClassSymbol.outerClassId?.toKtClassSymbol()?.getDeclaredMemberScope()?.getClassifierSymbols {
357+
this@toKtClassSymbol.outerClassId?.toKtClassSymbol()?.getDeclaredMemberScope()?.classifiers {
358358
it.asString() == this@toKtClassSymbol.shortClassName.asString()
359359
}?.singleOrNull() as? KtClassOrObjectSymbol
360360
} else {

kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt

+6
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ class KSPAATest : AbstractKSPAATest() {
7676
runTest("../test-utils/testData/api/annotationOnConstructorParameter.kt")
7777
}
7878

79+
@TestMetadata("annotationOnReceiver.kt")
80+
@Test
81+
fun testAnnotationOnReceiver() {
82+
runTest("../test-utils/testData/api/annotationOnReceiver.kt")
83+
}
84+
7985
@TestMetadata("annotationWithArbitraryClassValue.kt")
8086
@Test
8187
fun testAnnotationWithArbitraryClassValue() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.google.devtools.ksp.processor
2+
3+
import com.google.devtools.ksp.getClassDeclarationByName
4+
import com.google.devtools.ksp.getDeclaredFunctions
5+
import com.google.devtools.ksp.getDeclaredProperties
6+
import com.google.devtools.ksp.processing.Resolver
7+
import com.google.devtools.ksp.symbol.KSAnnotated
8+
9+
class AnnotationOnReceiverProcessor : AbstractTestProcessor() {
10+
val results = mutableListOf<String>()
11+
12+
override fun toResult(): List<String> {
13+
return results
14+
}
15+
16+
override fun process(resolver: Resolver): List<KSAnnotated> {
17+
listOf("Test", "TestLib").forEach {
18+
resolver.getClassDeclarationByName(it)!!.let { cls ->
19+
cls.getDeclaredFunctions().forEach { method ->
20+
method.extensionReceiver.let { receiver ->
21+
if (receiver != null) {
22+
results.add(
23+
receiver.annotations.map {
24+
it.annotationType.toString() + it.arguments.toString()
25+
}.joinToString()
26+
)
27+
}
28+
}
29+
}
30+
cls.getDeclaredProperties().forEach { prop ->
31+
prop.extensionReceiver.let { receiver ->
32+
if (receiver != null) {
33+
results.add(
34+
receiver.annotations.map {
35+
it.annotationType.toString() + it.arguments.toString()
36+
}.joinToString()
37+
)
38+
}
39+
}
40+
}
41+
}
42+
}
43+
return emptyList()
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// TEST PROCESSOR: AnnotationOnReceiverProcessor
19+
// EXPECTED:
20+
// MyAnnotation[]
21+
// MyAnnotation[]
22+
// MyAnnotation[]
23+
// MyAnnotation[]
24+
// END
25+
26+
// MODULE: lib
27+
// FILE: TestLib.kt
28+
annotation class MyAnnotation
29+
class TestLib {
30+
fun @receiver:MyAnnotation String.extF(): String = TODO()
31+
val @receiver:MyAnnotation String.extP: String
32+
get() = TODO()
33+
}
34+
// MODULE: main(lib)
35+
// FILE: Test.kt
36+
class Test {
37+
fun @receiver:MyAnnotation String.extF(): String = TODO()
38+
val @receiver:MyAnnotation String.extP: String
39+
get() = TODO()
40+
}

0 commit comments

Comments
 (0)