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