Skip to content

Commit b900af3

Browse files
ZacSweerstschuchortdev
authored andcommitted
Update KSP to alpha08 and adopt new ProcessorProvider API
KSP alpha08 introduces a new `SymbolProcessorProvider` API to allow for creating immutable/stateless processors. This updates the KSP APIs to this new pattern, as the old `init()`-based pattern is deprecated and will eventually be removed.
1 parent e45b94f commit b900af3

File tree

4 files changed

+120
-96
lines changed

4 files changed

+120
-96
lines changed

ksp/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
buildscript {
2-
ext.ksp_version='1.4.32-1.0.0-alpha07'
2+
ext.ksp_version='1.4.32-1.0.0-alpha08'
33
}
44

55
dependencies {

ksp/src/main/kotlin/com/tschuchort/compiletesting/Ksp.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package com.tschuchort.compiletesting
66
import com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension
77
import com.google.devtools.ksp.KspOptions
88
import com.google.devtools.ksp.processing.KSPLogger
9-
import com.google.devtools.ksp.processing.SymbolProcessor
9+
import com.google.devtools.ksp.processing.SymbolProcessorProvider
1010
import com.google.devtools.ksp.processing.impl.MessageCollectorBasedKSPLogger
1111
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
1212
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
@@ -23,11 +23,11 @@ import java.io.File
2323
* The list of symbol processors for the kotlin compilation.
2424
* https://goo.gle/ksp
2525
*/
26-
var KotlinCompilation.symbolProcessors: List<SymbolProcessor>
27-
get() = getKspRegistrar().processors
26+
var KotlinCompilation.symbolProcessorProviders: List<SymbolProcessorProvider>
27+
get() = getKspRegistrar().providers
2828
set(value) {
2929
val registrar = getKspRegistrar()
30-
registrar.processors = value
30+
registrar.providers = value
3131
}
3232

3333
/**
@@ -102,16 +102,16 @@ private val KotlinCompilation.kspCachesDir: File
102102
*/
103103
private class KspTestExtension(
104104
options: KspOptions,
105-
processors: List<SymbolProcessor>,
105+
processorProviders: List<SymbolProcessorProvider>,
106106
logger: KSPLogger
107107
) : AbstractKotlinSymbolProcessingExtension(
108108
options = options,
109109
logger = logger,
110110
testMode = false
111111
) {
112-
private val loadedProcessors = processors
112+
private val loadedProviders = processorProviders
113113

114-
override fun loadProcessors() = loadedProcessors
114+
override fun loadProviders() = loadedProviders
115115
}
116116

117117
/**
@@ -120,15 +120,15 @@ private class KspTestExtension(
120120
private class KspCompileTestingComponentRegistrar(
121121
private val compilation: KotlinCompilation
122122
) : ComponentRegistrar {
123-
var processors = emptyList<SymbolProcessor>()
123+
var providers = emptyList<SymbolProcessorProvider>()
124124

125125
var options: MutableMap<String, String> = mutableMapOf()
126126

127127
var incremental: Boolean = false
128128
var incrementalLog: Boolean = false
129129

130130
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
131-
if (processors.isEmpty()) {
131+
if (providers.isEmpty()) {
132132
return
133133
}
134134
val options = KspOptions.Builder().apply {
@@ -180,7 +180,7 @@ private class KspCompileTestingComponentRegistrar(
180180
compilation.verbose
181181
)
182182
)
183-
val registrar = KspTestExtension(options, processors, messageCollectorBasedKSPLogger)
183+
val registrar = KspTestExtension(options, providers, messageCollectorBasedKSPLogger)
184184
AnalysisHandlerExtension.registerExtension(project, registrar)
185185
}
186186
}
Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
11
package com.tschuchort.compiletesting
22

3-
import com.google.devtools.ksp.processing.CodeGenerator
4-
import com.google.devtools.ksp.processing.KSPLogger
5-
import com.google.devtools.ksp.processing.Resolver
6-
import com.google.devtools.ksp.processing.SymbolProcessor
3+
import com.google.devtools.ksp.processing.*
74
import com.google.devtools.ksp.symbol.KSAnnotated
85

96
/**
107
* Helper class to write tests, only used in Ksp Compile Testing tests, not a public API.
118
*/
12-
internal open class AbstractTestSymbolProcessor : SymbolProcessor {
13-
protected lateinit var codeGenerator: CodeGenerator
14-
protected lateinit var logger: KSPLogger
15-
16-
override fun init(options: Map<String, String>, kotlinVersion: KotlinVersion, codeGenerator: CodeGenerator, logger: KSPLogger) {
17-
this.codeGenerator = codeGenerator
18-
this.logger = logger
19-
}
20-
9+
internal open class AbstractTestSymbolProcessor(
10+
protected val codeGenerator: CodeGenerator
11+
) : SymbolProcessor {
2112
override fun process(resolver: Resolver): List<KSAnnotated> {
2213
return emptyList()
2314
}
2415
}
16+
17+
// Would be nice if SymbolProcessorProvider was a fun interface
18+
internal fun processorProviderOf(
19+
body: (
20+
options: Map<String, String>,
21+
kotlinVersion: KotlinVersion,
22+
codeGenerator: CodeGenerator,
23+
logger: KSPLogger
24+
) -> SymbolProcessor
25+
): SymbolProcessorProvider {
26+
return object : SymbolProcessorProvider {
27+
override fun create(
28+
options: Map<String, String>,
29+
kotlinVersion: KotlinVersion,
30+
codeGenerator: CodeGenerator,
31+
logger: KSPLogger
32+
): SymbolProcessor {
33+
return body(options, kotlinVersion, codeGenerator, logger)
34+
}
35+
}
36+
}

ksp/src/test/kotlin/com/tschuchort/compiletesting/KspTest.kt

Lines changed: 84 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.tschuchort.compiletesting
22

3-
import com.google.devtools.ksp.processing.Dependencies
4-
import com.google.devtools.ksp.processing.Resolver
5-
import com.google.devtools.ksp.processing.SymbolProcessor
3+
import com.google.devtools.ksp.processing.*
64
import com.google.devtools.ksp.symbol.KSAnnotated
75
import com.google.devtools.ksp.symbol.KSClassDeclaration
86
import com.nhaarman.mockitokotlin2.any
@@ -21,12 +19,14 @@ class KspTest {
2119
@Test
2220
fun failedKspTest() {
2321
val instance = mock<SymbolProcessor>()
22+
val providerInstance = mock<SymbolProcessorProvider>()
23+
`when`(providerInstance.create(any(), any(), any(), any())).thenReturn(instance)
2424
`when`(instance.process(any())).thenThrow(
2525
RuntimeException("intentional fail")
2626
)
2727
val result = KotlinCompilation().apply {
2828
sources = listOf(DUMMY_KOTLIN_SRC)
29-
symbolProcessors = listOf(instance)
29+
symbolProcessorProviders = listOf(providerInstance)
3030
}.compile()
3131
assertThat(result.exitCode).isEqualTo(ExitCode.INTERNAL_ERROR)
3232
assertThat(result.messages).contains("intentional fail")
@@ -35,13 +35,17 @@ class KspTest {
3535
@Test
3636
fun allProcessorMethodsAreCalled() {
3737
val instance = mock<SymbolProcessor>()
38+
val providerInstance = mock<SymbolProcessorProvider>()
39+
`when`(providerInstance.create(any(), any(), any(), any())).thenReturn(instance)
3840
val result = KotlinCompilation().apply {
3941
sources = listOf(DUMMY_KOTLIN_SRC)
40-
symbolProcessors = listOf(instance)
42+
symbolProcessorProviders = listOf(providerInstance)
4143
}.compile()
4244
assertThat(result.exitCode).isEqualTo(ExitCode.OK)
45+
providerInstance.inOrder {
46+
verify().create(any(), any(), any(), any())
47+
}
4348
instance.inOrder {
44-
verify().init(any(), any(), any(), any())
4549
verify().process(any())
4650
verify().finish()
4751
}
@@ -68,33 +72,34 @@ class KspTest {
6872
}
6973
""".trimIndent()
7074
)
71-
val processor = object : AbstractTestSymbolProcessor() {
72-
override fun process(resolver: Resolver): List<KSAnnotated> {
73-
val symbols = resolver.getSymbolsWithAnnotation("foo.bar.TestAnnotation")
74-
if (symbols.isNotEmpty()) {
75-
assertThat(symbols.size).isEqualTo(1)
76-
val klass = symbols.first()
77-
check(klass is KSClassDeclaration)
78-
val qName = klass.qualifiedName ?: error("should've found qualified name")
79-
val genPackage = "${qName.getQualifier()}.generated"
80-
val genClassName = "${qName.getShortName()}_Gen"
81-
codeGenerator.createNewFile(
82-
dependencies = Dependencies.ALL_FILES,
83-
packageName = genPackage,
84-
fileName = genClassName
85-
).bufferedWriter(Charsets.UTF_8).use {
86-
it.write("""
75+
val result = KotlinCompilation().apply {
76+
sources = listOf(annotation, targetClass)
77+
symbolProcessorProviders = listOf(processorProviderOf { _, _, codeGenerator, logger ->
78+
object : AbstractTestSymbolProcessor(codeGenerator) {
79+
override fun process(resolver: Resolver): List<KSAnnotated> {
80+
val symbols = resolver.getSymbolsWithAnnotation("foo.bar.TestAnnotation")
81+
if (symbols.isNotEmpty()) {
82+
assertThat(symbols.size).isEqualTo(1)
83+
val klass = symbols.first()
84+
check(klass is KSClassDeclaration)
85+
val qName = klass.qualifiedName ?: error("should've found qualified name")
86+
val genPackage = "${qName.getQualifier()}.generated"
87+
val genClassName = "${qName.getShortName()}_Gen"
88+
codeGenerator.createNewFile(
89+
dependencies = Dependencies.ALL_FILES,
90+
packageName = genPackage,
91+
fileName = genClassName
92+
).bufferedWriter(Charsets.UTF_8).use {
93+
it.write("""
8794
package $genPackage
8895
class $genClassName() {}
8996
""".trimIndent())
97+
}
98+
}
99+
return emptyList()
90100
}
91101
}
92-
return emptyList()
93-
}
94-
}
95-
val result = KotlinCompilation().apply {
96-
sources = listOf(annotation, targetClass)
97-
symbolProcessors = listOf(processor)
102+
})
98103
}.compile()
99104
assertThat(result.exitCode).isEqualTo(ExitCode.OK)
100105
}
@@ -113,25 +118,26 @@ class KspTest {
113118
)
114119
val result = KotlinCompilation().apply {
115120
sources = listOf(source)
116-
symbolProcessors = listOf(
117-
ClassGeneratingProcessor("generated", "A"),
118-
ClassGeneratingProcessor("generated", "B"))
119-
symbolProcessors = symbolProcessors + ClassGeneratingProcessor("generated", "C")
121+
symbolProcessorProviders = listOf(
122+
processorProviderOf { _, _, codeGenerator, logger -> ClassGeneratingProcessor(codeGenerator, "generated", "A") },
123+
processorProviderOf { _, _, codeGenerator, logger -> ClassGeneratingProcessor(codeGenerator, "generated", "B") })
124+
symbolProcessorProviders = symbolProcessorProviders +
125+
processorProviderOf { _, _, codeGenerator, logger -> ClassGeneratingProcessor(codeGenerator, "generated", "C") }
120126
}.compile()
121127
assertThat(result.exitCode).isEqualTo(ExitCode.OK)
122128
}
123129

124130
@Test
125131
fun readProcessors() {
126-
val instance1 = mock<SymbolProcessor>()
127-
val instance2 = mock<SymbolProcessor>()
132+
val instance1 = mock<SymbolProcessorProvider>()
133+
val instance2 = mock<SymbolProcessorProvider>()
128134
KotlinCompilation().apply {
129-
symbolProcessors = listOf(instance1)
130-
assertThat(symbolProcessors).containsExactly(instance1)
131-
symbolProcessors = listOf(instance2)
132-
assertThat(symbolProcessors).containsExactly(instance2)
133-
symbolProcessors = symbolProcessors + instance1
134-
assertThat(symbolProcessors).containsExactly(instance2, instance1)
135+
symbolProcessorProviders = listOf(instance1)
136+
assertThat(symbolProcessorProviders).containsExactly(instance1)
137+
symbolProcessorProviders = listOf(instance2)
138+
assertThat(symbolProcessorProviders).containsExactly(instance2)
139+
symbolProcessorProviders = symbolProcessorProviders + instance1
140+
assertThat(symbolProcessorProviders).containsExactly(instance2, instance1)
135141
}
136142
}
137143

@@ -152,7 +158,9 @@ class KspTest {
152158
fun outputDirectoryContents() {
153159
val compilation = KotlinCompilation().apply {
154160
sources = listOf(DUMMY_KOTLIN_SRC)
155-
symbolProcessors = listOf(ClassGeneratingProcessor("generated", "Gen"))
161+
symbolProcessorProviders = listOf(processorProviderOf { _, _, codeGenerator, logger ->
162+
ClassGeneratingProcessor(codeGenerator, "generated", "Gen")
163+
})
156164
}
157165
val result = compilation.compile()
158166
assertThat(result.exitCode).isEqualTo(ExitCode.OK)
@@ -181,32 +189,34 @@ class KspTest {
181189
""".trimIndent()
182190
)
183191
val result = mutableListOf<String>()
184-
val processor = object : AbstractTestSymbolProcessor() {
185-
override fun process(resolver: Resolver): List<KSAnnotated> {
186-
resolver.getSymbolsWithAnnotation(
187-
SuppressWarnings::class.java.canonicalName
188-
).filterIsInstance<KSClassDeclaration>()
189-
.forEach {
190-
result.add(it.qualifiedName!!.asString())
191-
}
192-
return emptyList()
193-
}
194-
}
195192
val compilation = KotlinCompilation().apply {
196193
sources = listOf(javaSource, kotlinSource)
197-
symbolProcessors += processor
194+
symbolProcessorProviders += processorProviderOf { _, _, codeGenerator, logger ->
195+
object : AbstractTestSymbolProcessor(codeGenerator) {
196+
override fun process(resolver: Resolver): List<KSAnnotated> {
197+
resolver.getSymbolsWithAnnotation(
198+
SuppressWarnings::class.java.canonicalName
199+
).filterIsInstance<KSClassDeclaration>()
200+
.forEach {
201+
result.add(it.qualifiedName!!.asString())
202+
}
203+
return emptyList()
204+
}
205+
}
206+
}
198207
}
199208
compilation.compile()
200209
assertThat(result).containsExactlyInAnyOrder(
201210
"JavaSubject", "KotlinSubject"
202211
)
203212
}
204213

205-
internal open class ClassGeneratingProcessor(
214+
internal class ClassGeneratingProcessor(
215+
codeGenerator: CodeGenerator,
206216
private val packageName: String,
207217
private val className: String,
208218
times: Int = 1
209-
) : AbstractTestSymbolProcessor() {
219+
) : AbstractTestSymbolProcessor(codeGenerator) {
210220
val times = AtomicInteger(times)
211221
override fun process(resolver: Resolver): List<KSAnnotated> {
212222
super.process(resolver)
@@ -241,17 +251,18 @@ class KspTest {
241251
class AppCode
242252
""".trimIndent()
243253
)
244-
val processor = object : AbstractTestSymbolProcessor() {
245-
override fun process(resolver: Resolver): List<KSAnnotated> {
246-
logger.logging("This is a log message")
247-
logger.info("This is an info message")
248-
logger.warn("This is an warn message")
249-
return emptyList()
250-
}
251-
}
252254
val result = KotlinCompilation().apply {
253255
sources = listOf(annotation, targetClass)
254-
symbolProcessors = listOf(processor)
256+
symbolProcessorProviders = listOf(processorProviderOf { _, _, codeGenerator, logger ->
257+
object : AbstractTestSymbolProcessor(codeGenerator) {
258+
override fun process(resolver: Resolver): List<KSAnnotated> {
259+
logger.logging("This is a log message")
260+
logger.info("This is an info message")
261+
logger.warn("This is an warn message")
262+
return emptyList()
263+
}
264+
}
265+
})
255266
}.compile()
256267
assertThat(result.exitCode).isEqualTo(ExitCode.OK)
257268
assertThat(result.messages).contains("This is a log message")
@@ -274,16 +285,17 @@ class KspTest {
274285
class AppCode
275286
""".trimIndent()
276287
)
277-
val processor = object : AbstractTestSymbolProcessor() {
278-
override fun process(resolver: Resolver): List<KSAnnotated> {
279-
logger.error("This is an error message")
280-
logger.exception(Throwable("This is a failure"))
281-
return emptyList()
282-
}
283-
}
284288
val result = KotlinCompilation().apply {
285289
sources = listOf(annotation, targetClass)
286-
symbolProcessors = listOf(processor)
290+
symbolProcessorProviders = listOf(processorProviderOf { _, _, codeGenerator, logger ->
291+
object : AbstractTestSymbolProcessor(codeGenerator) {
292+
override fun process(resolver: Resolver): List<KSAnnotated> {
293+
logger.error("This is an error message")
294+
logger.exception(Throwable("This is a failure"))
295+
return emptyList()
296+
}
297+
}
298+
})
287299
}.compile()
288300
assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR)
289301
assertThat(result.messages).contains("This is an error message")

0 commit comments

Comments
 (0)