xref: /aosp_15_r20/external/kotlinpoet/docs/interop-ksp.md (revision 3c321d951dd070fb96f8ba59e952ffc3131379a0)
1*3c321d95SSadaf EbrahimiKSP Extensions for KotlinPoet
2*3c321d95SSadaf Ebrahimi==============
3*3c321d95SSadaf Ebrahimi
4*3c321d95SSadaf Ebrahimi`interop:ksp` is an interop API for converting
5*3c321d95SSadaf Ebrahimi[Kotlin Symbol Processing][ksp] (KSP) types to KotlinPoet types and
6*3c321d95SSadaf Ebrahimiwriting to KSP `CodeGenerator`.
7*3c321d95SSadaf Ebrahimi
8*3c321d95SSadaf Ebrahimi```kotlin
9*3c321d95SSadaf Ebrahimidependencies {
10*3c321d95SSadaf Ebrahimi  implementation("com.squareup:kotlinpoet-ksp:<version>")
11*3c321d95SSadaf Ebrahimi}
12*3c321d95SSadaf Ebrahimi```
13*3c321d95SSadaf Ebrahimi
14*3c321d95SSadaf Ebrahimi### Examples
15*3c321d95SSadaf Ebrahimi
16*3c321d95SSadaf EbrahimiExamples are based on reading the following property as a `KSProperty`:
17*3c321d95SSadaf Ebrahimi
18*3c321d95SSadaf Ebrahimi```kotlin
19*3c321d95SSadaf Ebrahimiclass Taco {
20*3c321d95SSadaf Ebrahimi  internal inline val seasoning: String get() = "spicy"
21*3c321d95SSadaf Ebrahimi}
22*3c321d95SSadaf Ebrahimi```
23*3c321d95SSadaf Ebrahimi
24*3c321d95SSadaf Ebrahimi**Convert a `KSType` to a `TypeName`**
25*3c321d95SSadaf Ebrahimi
26*3c321d95SSadaf Ebrahimi```kotlin
27*3c321d95SSadaf Ebrahimi// returns a `ClassName` of value `kotlin.String`
28*3c321d95SSadaf EbrahimiseasoningKsProperty.type.toTypeName()
29*3c321d95SSadaf Ebrahimi```
30*3c321d95SSadaf Ebrahimi
31*3c321d95SSadaf Ebrahimi**Convert a `Modifier` to a `KModifier`**
32*3c321d95SSadaf Ebrahimi
33*3c321d95SSadaf Ebrahimi```kotlin
34*3c321d95SSadaf Ebrahimi// returns `[KModifier.INLINE]`
35*3c321d95SSadaf EbrahimiseasoningKsProperty.modifiers.mapNotNull { it.toKModifier() }
36*3c321d95SSadaf Ebrahimi```
37*3c321d95SSadaf Ebrahimi
38*3c321d95SSadaf Ebrahimi**Convert a `Visibility` to a `KModifier`**
39*3c321d95SSadaf Ebrahimi
40*3c321d95SSadaf Ebrahimi```kotlin
41*3c321d95SSadaf Ebrahimi// returns `KModifier.INTERNAL`
42*3c321d95SSadaf EbrahimiseasoningKsProperty.getVisibility().toKModifier()
43*3c321d95SSadaf Ebrahimi```
44*3c321d95SSadaf Ebrahimi
45*3c321d95SSadaf Ebrahimi**Write to `CodeGenerator`**
46*3c321d95SSadaf Ebrahimi
47*3c321d95SSadaf EbrahimiTo write a `FileSpec` to a KSP `CodeGenerator`, simply call the `FileSpec.writeTo(CodeGenerator, ...)`
48*3c321d95SSadaf Ebrahimiextension function.
49*3c321d95SSadaf Ebrahimi
50*3c321d95SSadaf Ebrahimi```kotlin
51*3c321d95SSadaf EbrahimifileSpec.writeTo(codeGenerator)
52*3c321d95SSadaf Ebrahimi```
53*3c321d95SSadaf Ebrahimi
54*3c321d95SSadaf Ebrahimi### Type Parameters
55*3c321d95SSadaf Ebrahimi
56*3c321d95SSadaf EbrahimiType parameters can be declared on classes, functions, and typealiases. These parameters are then
57*3c321d95SSadaf Ebrahimiavailable to all of its enclosed elements. In order for these elements to resolve these in KSP, you
58*3c321d95SSadaf Ebrahimimust be able to reference these type parameters by their _index_.
59*3c321d95SSadaf Ebrahimi
60*3c321d95SSadaf EbrahimiIn `kotlinpoet-ksp` this is orchestrated by the `TypeParameterResolver` API, which can be passed
61*3c321d95SSadaf Ebrahimiinto most `toTypeName()` (or similar) functions to give them access to enclosing type parameters
62*3c321d95SSadaf Ebrahimithat they may reference.
63*3c321d95SSadaf Ebrahimi
64*3c321d95SSadaf EbrahimiThe canonical way to create an instance of this is to call `toTypeParameterResolver()` on a
65*3c321d95SSadaf Ebrahimi`List<KSTypeParameter>`.
66*3c321d95SSadaf Ebrahimi
67*3c321d95SSadaf EbrahimiConsider the following class and function
68*3c321d95SSadaf Ebrahimi
69*3c321d95SSadaf Ebrahimi```kotlin
70*3c321d95SSadaf Ebrahimiabstract class Taco<T> {
71*3c321d95SSadaf Ebrahimi  abstract val seasoning: T
72*3c321d95SSadaf Ebrahimi}
73*3c321d95SSadaf Ebrahimi```
74*3c321d95SSadaf Ebrahimi
75*3c321d95SSadaf EbrahimiTo properly resolve the type of `seasoning`, we need to pass the class `TypeParameterResolver` to
76*3c321d95SSadaf Ebrahimi`toTypeName()` so that it can properly resolve it.
77*3c321d95SSadaf Ebrahimi
78*3c321d95SSadaf Ebrahimi```kotlin
79*3c321d95SSadaf Ebrahimival classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver()
80*3c321d95SSadaf Ebrahimi// returns `T`
81*3c321d95SSadaf Ebrahimival seasoningType = seasoningKsProperty.type.toTypeName(classTypeParams)
82*3c321d95SSadaf Ebrahimi```
83*3c321d95SSadaf Ebrahimi
84*3c321d95SSadaf Ebrahimi`TypeParameterResolver` is also composable to allow for multi-level nesting. `toTypeParameterResolver()`
85*3c321d95SSadaf Ebrahimihas an optional `parent` parameter to provide a parent instance.
86*3c321d95SSadaf Ebrahimi
87*3c321d95SSadaf EbrahimiConsider our previous example again, but this time with a function that defines its own type parameters.
88*3c321d95SSadaf Ebrahimi
89*3c321d95SSadaf Ebrahimi```kotlin
90*3c321d95SSadaf Ebrahimiclass Taco<T> {
91*3c321d95SSadaf Ebrahimi  fun <E> getShellOfType(param1: E, param2: T) {
92*3c321d95SSadaf Ebrahimi
93*3c321d95SSadaf Ebrahimi  }
94*3c321d95SSadaf Ebrahimi}
95*3c321d95SSadaf Ebrahimi```
96*3c321d95SSadaf Ebrahimi
97*3c321d95SSadaf EbrahimiTo resolve its parameters, we need to create a `TypeParameterResolver` from the function's
98*3c321d95SSadaf Ebrahimi`typeParameters` and _compose_ it with the enclosing class's type parameters as a `parent`.
99*3c321d95SSadaf Ebrahimi
100*3c321d95SSadaf Ebrahimi```kotlin
101*3c321d95SSadaf Ebrahimival classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver()
102*3c321d95SSadaf Ebrahimival functionTypeParams = ksFunction.typeParameters.toTypeParameterResolver(parent = classTypeParams)
103*3c321d95SSadaf Ebrahimi// returns `[E, T]`
104*3c321d95SSadaf Ebrahimival seasoningType = ksFunction.parameterTypes.map { it.toTypeName(functionTypeParams) }
105*3c321d95SSadaf Ebrahimi```
106*3c321d95SSadaf Ebrahimi
107*3c321d95SSadaf Ebrahimi### Incremental Processing
108*3c321d95SSadaf Ebrahimi
109*3c321d95SSadaf EbrahimiKSP supports [incremental processing][incremental] as
110*3c321d95SSadaf Ebrahimilong as symbol processors properly indicate originating files in generated new files and whether or
111*3c321d95SSadaf Ebrahiminot they are `aggregating`. `kotlinpoet-ksp` supports this via `OriginatingKSFiles`, which is a simple
112*3c321d95SSadaf EbrahimiAPI that sits atop KotlinPoet's `Taggable` API. To use this, simply add relevant originating files to
113*3c321d95SSadaf Ebrahimiany `TypeSpec`, `TypeAliasSpec`, `PropertySpec`, or `FunSpec` builders.
114*3c321d95SSadaf Ebrahimi
115*3c321d95SSadaf Ebrahimi```kotlin
116*3c321d95SSadaf Ebrahimival functionBuilder = FunSpec.builder("sayHello")
117*3c321d95SSadaf Ebrahimi  .addOriginatingKSFile(sourceKsFile)
118*3c321d95SSadaf Ebrahimi  .build()
119*3c321d95SSadaf Ebrahimi```
120*3c321d95SSadaf Ebrahimi
121*3c321d95SSadaf EbrahimiLike KotlinPoet's _originating elements_ support for javac annotation processors, calling the
122*3c321d95SSadaf Ebrahimi`FileSpec.writeTo(CodeGenerator, ...)` function will automatically collect and de-dupe these originating
123*3c321d95SSadaf Ebrahimi`KSFile` references and automatically assemble them in the underlying `Dependencies` for KSP's reference.
124*3c321d95SSadaf Ebrahimi
125*3c321d95SSadaf EbrahimiOptionally you can define your own collection of files and pass them to the `writeTo` function, but usually
126*3c321d95SSadaf Ebrahimiyou don't need to do this manually.
127*3c321d95SSadaf Ebrahimi
128*3c321d95SSadaf EbrahimiLastly - `FileSpec.writeTo(CodeGenerator, ...)` also requires you to specify if your processor is
129*3c321d95SSadaf Ebrahimi_aggregating_ or not via required parameter by the same name.
130*3c321d95SSadaf Ebrahimi
131*3c321d95SSadaf Ebrahimi### TypeAlias Handling
132*3c321d95SSadaf Ebrahimi
133*3c321d95SSadaf EbrahimiFor `typealias` types, KSP interop will store a `TypeAliasTag` in the `TypeName`'s tags with a reference to the abbreviated type. This can be useful for APIs that want to resolve all un-aliased types.
134*3c321d95SSadaf Ebrahimi
135*3c321d95SSadaf Ebrahimi [ksp]: https://github.com/google/ksp
136*3c321d95SSadaf Ebrahimi [incremental]: https://github.com/google/ksp/blob/main/docs/incremental.md
137