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