1 /*
2  * Copyright (C) 2021 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.kotlinpoet.ksp.test.processor
17 
18 import com.google.common.truth.Truth.assertThat
19 import com.tschuchort.compiletesting.KotlinCompilation
20 import com.tschuchort.compiletesting.SourceFile
21 import com.tschuchort.compiletesting.SourceFile.Companion.kotlin
22 import com.tschuchort.compiletesting.configureKsp
23 import com.tschuchort.compiletesting.kspProcessorOptions
24 import com.tschuchort.compiletesting.kspSourcesDir
25 import java.io.File
26 import org.junit.Rule
27 import org.junit.Test
28 import org.junit.rules.TemporaryFolder
29 import org.junit.runner.RunWith
30 import org.junit.runners.Parameterized
31 
32 @RunWith(Parameterized::class)
33 class TestProcessorTest(private val useKsp2: Boolean) {
34 
35   companion object {
36     @JvmStatic
37     @Parameterized.Parameters(name = "useKsp2={0}")
datanull38     fun data(): Collection<Array<Any>> {
39       return listOf(
40         arrayOf(false),
41         arrayOf(true),
42       )
43     }
44   }
45 
46   @Rule
47   @JvmField
48   val temporaryFolder: TemporaryFolder = TemporaryFolder()
49 
50   @Test
smokeTestnull51   fun smokeTest() {
52     val compilation = prepareCompilation(
53       kotlin(
54         "Example.kt",
55         """
56            package test
57 
58            import com.squareup.kotlinpoet.ksp.test.processor.AnnotationEnumValue
59            import com.squareup.kotlinpoet.ksp.test.processor.AnotherAnnotation
60            import com.squareup.kotlinpoet.ksp.test.processor.ComprehensiveAnnotation
61            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
62            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotationWithDefaults
63 
64            typealias TypeAliasName = String
65            typealias GenericTypeAlias = List<String>
66            typealias ParameterizedTypeAlias<T> = List<T>
67 
68            @ComprehensiveAnnotation<String>(
69              true, // Omit the name intentionally here to test names are still picked up
70              booleanArray = [true],
71              byte = 0.toByte(),
72              byteArray = [0.toByte()],
73              char = 'a',
74              charArray = ['a', 'b', 'c'],
75              short = 0.toShort(),
76              shortArray = [0.toShort()],
77              int = 0,
78              intArray = [0],
79              long = 0L,
80              longArray = [0L],
81              float = 0f,
82              floatArray = [0f],
83              double = 0.0,
84              doubleArray = [0.0],
85              string = "Hello",
86              stringArray = ["Hello"],
87              someClass = String::class,
88              someClasses = [String::class, Int::class],
89              enumValue = AnnotationEnumValue.ONE,
90              enumValueArray = [AnnotationEnumValue.ONE, AnnotationEnumValue.TWO],
91              anotherAnnotation = AnotherAnnotation("Hello"),
92              anotherAnnotationArray = [AnotherAnnotation("Hello")]
93            )
94            @ExampleAnnotationWithDefaults(
95              true, // Omit the name intentionally here to test names are still picked up
96              booleanArray = [false],
97              byte = 0.toByte(),
98              byteArray = [1.toByte()],
99              char = 'C',
100              charArray = ['C'],
101              short = 0.toShort(),
102              shortArray = [1.toShort()],
103              int = 0,
104              intArray = [1],
105              long = 0L,
106              longArray = [1L],
107              float = 0f,
108              floatArray = [1f],
109              double = 1.0,
110              doubleArray = [0.0],
111              string = "Hello",
112              stringArray = [""],
113              someClass = String::class,
114              someClasses = [Int::class],
115              enumValue = AnnotationEnumValue.ONE,
116              enumValueArray = [AnnotationEnumValue.ONE, AnnotationEnumValue.TWO],
117              anotherAnnotation = AnotherAnnotation(""),
118              anotherAnnotationArray = [AnotherAnnotation("Hello")]
119            )
120            @ExampleAnnotation
121            class SmokeTestClass<T, R : Any, E : Enum<E>> {
122              @field:AnotherAnnotation("siteTargeting")
123              private val propA: String = ""
124              internal val propB: String = ""
125              val propC: Int = 0
126              val propD: Int? = null
127              lateinit var propE: String
128              var propF: T? = null
129 
130              fun functionA(): String {
131                TODO()
132              }
133 
134              fun functionB(): R {
135                TODO()
136              }
137 
138              fun <F> functionC(param1: String, param2: T, param3: F, param4: F?): R {
139                TODO()
140              }
141 
142              suspend fun functionD(
143                param1: () -> String,
144                param2: (String) -> String,
145                param3: String.() -> String,
146                param4: Function0<String>,
147                param5: Function1<String, String>,
148                param6: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit,
149                param7: ((String) -> String)?,
150                param8: suspend () -> String,
151              ) {
152              }
153 
154              // A whole bunch of wild types from Moshi's codegen smoke tests
155              fun wildTypes(
156                age: Int,
157                nationalities: List<String>,
158                weight: Float,
159                tattoos: Boolean = false,
160                race: String?,
161                hasChildren: Boolean = false,
162                favoriteFood: String? = null,
163                favoriteDrink: String? = "Water",
164                wildcardOut: MutableList<out String>,
165                nullableWildcardOut: MutableList<out String?>,
166                wildcardIn: Array<in String>,
167                any: List<*>,
168                anyTwo: List<Any>,
169                anyOut: MutableList<out Any>,
170                nullableAnyOut: MutableList<out Any?>,
171                favoriteThreeNumbers: IntArray,
172                favoriteArrayValues: Array<String>,
173                favoriteNullableArrayValues: Array<String?>,
174                nullableSetListMapArrayNullableIntWithDefault: Set<List<Map<String, Array<IntArray?>>>>?,
175                // These are actually currently rendered incorrectly and always unwrapped
176                aliasedName: TypeAliasName,
177                genericAlias: GenericTypeAlias,
178                parameterizedTypeAlias: ParameterizedTypeAlias<String>,
179                nestedArray: Array<Map<String, Any>>?
180              ) {
181 
182              }
183            }
184            """,
185       ),
186     )
187     val result = compilation.compile()
188     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
189     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestSmokeTestClass.kt")
190       .readText()
191     assertThat(generatedFileText).isEqualTo(
192       """
193       package test
194 
195       import com.squareup.kotlinpoet.ksp.test.processor.AnnotationEnumValue
196       import com.squareup.kotlinpoet.ksp.test.processor.AnotherAnnotation
197       import com.squareup.kotlinpoet.ksp.test.processor.ComprehensiveAnnotation
198       import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotationWithDefaults
199       import kotlin.Any
200       import kotlin.Array
201       import kotlin.Boolean
202       import kotlin.Enum
203       import kotlin.Float
204       import kotlin.Function0
205       import kotlin.Function1
206       import kotlin.Int
207       import kotlin.IntArray
208       import kotlin.String
209       import kotlin.Unit
210       import kotlin.collections.List
211       import kotlin.collections.Map
212       import kotlin.collections.MutableList
213       import kotlin.collections.Set
214 
215       @ComprehensiveAnnotation<String>(
216         boolean = true,
217         booleanArray = booleanArrayOf(true),
218         byte = 0.toByte(),
219         byteArray = byteArrayOf(0.toByte()),
220         char = 'a',
221         charArray = charArrayOf('a', 'b', 'c'),
222         short = 0.toShort(),
223         shortArray = shortArrayOf(0.toShort()),
224         int = 0,
225         intArray = intArrayOf(0),
226         long = 0,
227         longArray = longArrayOf(0),
228         float = 0.0f,
229         floatArray = floatArrayOf(0.0f),
230         double = 0.0,
231         doubleArray = doubleArrayOf(0.0),
232         string = "Hello",
233         stringArray = arrayOf("Hello"),
234         someClass = String::class,
235         someClasses = arrayOf(String::class, Int::class),
236         enumValue = AnnotationEnumValue.ONE,
237         enumValueArray = arrayOf(AnnotationEnumValue.ONE, AnnotationEnumValue.TWO),
238         anotherAnnotation = AnotherAnnotation(input = "Hello"),
239         anotherAnnotationArray = arrayOf(AnotherAnnotation(input = "Hello")),
240         defaultingString = "defaultValue",
241       )
242       @ExampleAnnotationWithDefaults(
243         booleanArray = booleanArrayOf(false),
244         byte = 0.toByte(),
245         short = 0.toShort(),
246         int = 0,
247         long = 0,
248         float = 0.0f,
249         doubleArray = doubleArrayOf(0.0),
250         string = "Hello",
251         someClasses = arrayOf(Int::class),
252         enumValueArray = arrayOf(AnnotationEnumValue.ONE, AnnotationEnumValue.TWO),
253       )
254       public class TestSmokeTestClass<T, R : Any, E : Enum<E>> {
255         @field:AnotherAnnotation(input = "siteTargeting")
256         private val propA: String = TODO()
257 
258         internal val propB: String = TODO()
259 
260         public val propC: Int = TODO()
261 
262         public val propD: Int? = TODO()
263 
264         public lateinit var propE: String
265 
266         public var propF: T? = TODO()
267 
268         public fun functionA(): String = TODO()
269 
270         public fun functionB(): R = TODO()
271 
272         public fun <F> functionC(
273           param1: String,
274           param2: T,
275           param3: F,
276           param4: F?,
277         ): R = TODO()
278 
279         public suspend fun functionD(
280           param1: () -> String,
281           param2: (String) -> String,
282           param3: String.() -> String,
283           param4: Function0<String>,
284           param5: Function1<String, String>,
285           param6: (
286             Int,
287             Int,
288             Int,
289             Int,
290             Int,
291             Int,
292             Int,
293             Int,
294             Int,
295             Int,
296             Int,
297             Int,
298             Int,
299             Int,
300             Int,
301             Int,
302             Int,
303             Int,
304             Int,
305             Int,
306             Int,
307             Int,
308             Int,
309             Int,
310           ) -> Unit,
311           param7: ((String) -> String)?,
312           param8: suspend () -> String,
313         ): Unit = TODO()
314 
315         public fun wildTypes(
316           age: Int,
317           nationalities: List<String>,
318           weight: Float,
319           tattoos: Boolean,
320           race: String?,
321           hasChildren: Boolean,
322           favoriteFood: String?,
323           favoriteDrink: String?,
324           wildcardOut: MutableList<out String>,
325           nullableWildcardOut: MutableList<out String?>,
326           wildcardIn: Array<in String>,
327           any: List<*>,
328           anyTwo: List<Any>,
329           anyOut: MutableList<out Any>,
330           nullableAnyOut: MutableList<*>,
331           favoriteThreeNumbers: IntArray,
332           favoriteArrayValues: Array<String>,
333           favoriteNullableArrayValues: Array<String?>,
334           nullableSetListMapArrayNullableIntWithDefault: Set<List<Map<String, Array<IntArray?>>>>?,
335           aliasedName: TypeAliasName,
336           genericAlias: GenericTypeAlias,
337           parameterizedTypeAlias: ParameterizedTypeAlias<String>,
338           nestedArray: Array<Map<String, Any>>?,
339         ): Unit = TODO()
340       }
341 
342       """.trimIndent(),
343     )
344   }
345 
346   @Test
unwrapTypeAliasesnull347   fun unwrapTypeAliases() {
348     val compilation = prepareCompilation(
349       kotlin(
350         "Example.kt",
351         """
352            package test
353 
354            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
355 
356            typealias TypeAliasName = String
357            typealias GenericTypeAlias = List<String>
358            typealias GenericMapTypeAlias<V, K> = Map<K, V>
359            typealias T1Unused<T1, T2> = Map<T2, String>
360            typealias A1<T1, T2> = A2<T2, T1>
361            typealias A2<T2, T3> = Map<T3, T2>
362 
363            @ExampleAnnotation
364            class Example {
365              fun aliases(
366                aliasedName: TypeAliasName,
367                genericAlias: GenericTypeAlias,
368                genericMapAlias: GenericMapTypeAlias<String, Int>,
369                t1Unused: T1Unused<String, Int>,
370                a1: A1<String, Int>,
371              ) {
372              }
373            }
374            """,
375       ),
376     )
377     compilation.kspProcessorOptions["unwrapTypeAliases"] = "true"
378     val result = compilation.compile()
379     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
380     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestExample.kt")
381       .readText()
382     assertThat(generatedFileText).isEqualTo(
383       """
384       package test
385 
386       import kotlin.Int
387       import kotlin.String
388       import kotlin.Unit
389       import kotlin.collections.List
390       import kotlin.collections.Map
391 
392       public class TestExample {
393         public fun aliases(
394           aliasedName: String,
395           genericAlias: List<String>,
396           genericMapAlias: Map<Int, String>,
397           t1Unused: Map<Int, String>,
398           a1: Map<String, Int>,
399         ): Unit = TODO()
400       }
401 
402       """.trimIndent(),
403     )
404   }
405 
406   @Test
removeDefaultValuesnull407   fun removeDefaultValues() {
408     val compilation = prepareCompilation(
409       kotlin(
410         "Example.kt",
411         """
412            package test
413 
414            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotationWithDefaults
415            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
416            import com.squareup.kotlinpoet.ksp.test.processor.AnotherAnnotation
417            import com.squareup.kotlinpoet.ksp.test.processor.AnnotationEnumValue
418 
419            @ExampleAnnotation
420            @ExampleAnnotationWithDefaults(
421              true, // Omit the name intentionally here to test names are still picked up
422              booleanArray = [true],
423              byte = 1.toByte(),
424              byteArray = [1.toByte()],
425              char = 'C',
426              charArray = ['C'],
427              short = 1.toShort(),
428              shortArray = [1.toShort()],
429              int = 1,
430              intArray = [1],
431              long = 1L,
432              longArray = [1L],
433              float = 1f,
434              floatArray = [1f],
435              double = 1.0,
436              doubleArray = [1.0],
437              string = "",
438              stringArray = [""],
439              someClass = String::class,
440              someClasses = [String::class],
441              enumValue = AnnotationEnumValue.ONE,
442              enumValueArray = [AnnotationEnumValue.ONE],
443              anotherAnnotation = AnotherAnnotation(""),
444              anotherAnnotationArray = [AnotherAnnotation("")]
445            )
446            open class Node<T : Node<T, R>, R : Node<R, T>> {
447              var t: T? = null
448              var r: R? = null
449            }
450            """,
451       ),
452     )
453 
454     val result = compilation.compile()
455     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
456     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestNode.kt")
457       .readText()
458     assertThat(generatedFileText).isEqualTo(
459       """
460       package test
461 
462       import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotationWithDefaults
463 
464       @ExampleAnnotationWithDefaults
465       public open class TestNode<T : Node<T, R>, R : Node<R, T>> {
466         public var t: T? = TODO()
467 
468         public var r: R? = TODO()
469       }
470 
471       """.trimIndent(),
472     )
473   }
474 
475   @Test
complexSelfReferencingTypeArgsnull476   fun complexSelfReferencingTypeArgs() {
477     val compilation = prepareCompilation(
478       kotlin(
479         "Example.kt",
480         """
481            package test
482 
483            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
484 
485            @ExampleAnnotation
486            open class Node<T : Node<T, R>, R : Node<R, T>> {
487              var t: T? = null
488              var r: R? = null
489            }
490            """,
491       ),
492     )
493 
494     val result = compilation.compile()
495     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
496     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestNode.kt")
497       .readText()
498     assertThat(generatedFileText).isEqualTo(
499       """
500       package test
501 
502       public open class TestNode<T : Node<T, R>, R : Node<R, T>> {
503         public var t: T? = TODO()
504 
505         public var r: R? = TODO()
506       }
507 
508       """.trimIndent(),
509     )
510   }
511 
512   @Test
wildcardParameterForRecursiveTypeBoundnull513   fun wildcardParameterForRecursiveTypeBound() {
514     // Enum is an example of a recursive type bound - Enum<E: Enum<E>>
515     val compilation = prepareCompilation(
516       kotlin(
517         "Example.kt",
518         """
519            package test
520 
521            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
522 
523            @ExampleAnnotation
524            class EnumWrapper {
525             val enumValue: Enum<*> = TODO()
526            }
527            """,
528       ),
529     )
530 
531     val result = compilation.compile()
532     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
533     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestEnumWrapper.kt")
534       .readText()
535     assertThat(generatedFileText).isEqualTo(
536       """
537       package test
538 
539       import kotlin.Enum
540 
541       public class TestEnumWrapper {
542         public val enumValue: Enum<*> = TODO()
543       }
544 
545       """.trimIndent(),
546     )
547   }
548 
549   @Test
transitiveAliasesnull550   fun transitiveAliases() {
551     val compilation = prepareCompilation(
552       kotlin(
553         "Example.kt",
554         """
555            package test
556 
557            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
558 
559            typealias Alias23 = (Any) -> Any
560            typealias Alias77<Q> = List<Q>
561            typealias Alias73<Q> = Map<String, Q>
562            typealias Alias55<Q> = Alias73<Q>
563            typealias Alias99<Q> = Alias55<Q>
564            typealias Alias43<Q> = Alias77<Q>
565            typealias Alias47<Q> = Alias43<Q>
566            typealias Alias41<Z, Q> = (Alias43<Z>) -> Alias47<Q>
567 
568            @ExampleAnnotation
569            interface TransitiveAliases {
570               fun <T : Alias41<Alias23, out Alias77<Alias73<Int>>>> bar(vararg arg1: T)
571            }
572            """,
573       ),
574     )
575 
576     val result = compilation.compile()
577     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
578     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestTransitiveAliases.kt")
579       .readText()
580     assertThat(generatedFileText).isEqualTo(
581       """
582     package test
583 
584     import kotlin.Int
585     import kotlin.Unit
586 
587     public class TestTransitiveAliases {
588       public fun <T : Alias41<Alias23, out Alias77<Alias73<Int>>>> bar(vararg arg1: T): Unit = TODO()
589     }
590 
591       """.trimIndent(),
592     )
593   }
594 
595   @Test
aliasAsTypeArgumentnull596   fun aliasAsTypeArgument() {
597     val compilation = prepareCompilation(
598       kotlin(
599         "Example.kt",
600         """
601            package test
602 
603            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
604 
605            typealias Alias997 = Map<String, Int>
606 
607            @ExampleAnnotation
608            interface AliasAsTypeArgument {
609               fun bar(arg1: List<Alias997>)
610            }
611            """,
612       ),
613     )
614 
615     val result = compilation.compile()
616     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
617     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestAliasAsTypeArgument.kt")
618       .readText()
619 
620     assertThat(generatedFileText).isEqualTo(
621       """
622     package test
623 
624     import kotlin.Unit
625     import kotlin.collections.List
626 
627     public class TestAliasAsTypeArgument {
628       public fun bar(arg1: List<Alias997>): Unit = TODO()
629     }
630 
631       """.trimIndent(),
632     )
633   }
634 
635   @Test
varargArgumentnull636   fun varargArgument() {
637     val compilation = prepareCompilation(
638       kotlin(
639         "Example.kt",
640         """
641            package test
642 
643            import com.squareup.kotlinpoet.ksp.test.processor.AnnotationWithVararg
644            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
645 
646            @RequiresOptIn
647            annotation class MyOptIn
648 
649            @ExampleAnnotation
650            @OptIn(MyOptIn::class)
651            @AnnotationWithVararg(0, "one", "two")
652            interface Example
653         """.trimIndent(),
654       ),
655     )
656 
657     val result = compilation.compile()
658     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
659     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestExample.kt")
660       .readText()
661 
662     assertThat(generatedFileText).isEqualTo(
663       """
664       package test
665 
666       import com.squareup.kotlinpoet.ksp.test.processor.AnnotationWithVararg
667       import kotlin.OptIn
668 
669       @OptIn(MyOptIn::class)
670       @AnnotationWithVararg(
671         simpleArg = 0,
672         "one",
673         "two",
674       )
675       public class TestExample
676 
677       """.trimIndent(),
678     )
679   }
680 
681   @Test
regression_1513null682   fun regression_1513() {
683     val compilation = prepareCompilation(
684       kotlin(
685         "Example.kt",
686         """
687            package test
688 
689            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
690 
691            annotation class Inject
692            interface Repository<T>
693            @ExampleAnnotation
694            class RealRepository @Inject constructor() : Repository<String>
695            """,
696       ),
697     )
698 
699     val result = compilation.compile()
700     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
701     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestRealRepository.kt")
702       .readText()
703 
704     assertThat(generatedFileText).isEqualTo(
705       """
706         package test
707 
708         import kotlin.String
709 
710         public class TestRealRepository : Repository<String>
711 
712       """.trimIndent(),
713     )
714   }
715 
716   @Test
regression_1513_annotationnull717   fun regression_1513_annotation() {
718     val compilation = prepareCompilation(
719       kotlin(
720         "Example.kt",
721         """
722            package test
723 
724            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
725 
726            annotation class GenericAnnotation<T>
727 
728            @ExampleAnnotation
729            @GenericAnnotation<String>
730            class RealRepository
731            """,
732       ),
733     )
734 
735     val result = compilation.compile()
736     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
737     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestRealRepository.kt")
738       .readText()
739 
740     assertThat(generatedFileText).isEqualTo(
741       """
742         package test
743 
744         import kotlin.String
745 
746         @GenericAnnotation<String>
747         public class TestRealRepository
748 
749       """.trimIndent(),
750     )
751   }
752 
753   @Test
regression_1304null754   fun regression_1304() {
755     val compilation = prepareCompilation(
756       kotlin(
757         "Example.kt",
758         """
759            package test
760 
761            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
762 
763            interface Flow<T>
764            typealias LeAlias = Map<Int, String>
765 
766            @ExampleAnnotation
767            class RealRepository {
768              lateinit var prop: LeAlias
769              lateinit var complicated: Flow<LeAlias>
770            }
771            """,
772       ),
773     )
774 
775     val result = compilation.compile()
776     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
777     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestRealRepository.kt")
778       .readText()
779 
780     assertThat(generatedFileText).isEqualTo(
781       """
782         package test
783 
784         public class TestRealRepository {
785           public lateinit var prop: LeAlias
786 
787           public lateinit var complicated: Flow<LeAlias>
788         }
789 
790       """.trimIndent(),
791     )
792   }
793 
794   @Test
regression_1304_with_type_parametersnull795   fun regression_1304_with_type_parameters() {
796     val compilation = prepareCompilation(
797       kotlin(
798         "Example.kt",
799         """
800            package test
801 
802            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
803 
804            interface Flow<T>
805            typealias LeAlias<T> = Flow<T>
806 
807            @ExampleAnnotation
808            class RealRepository {
809              lateinit var prop: LeAlias<String>
810            }
811            """,
812       ),
813     )
814 
815     val result = compilation.compile()
816     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
817     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestRealRepository.kt")
818       .readText()
819 
820     assertThat(generatedFileText).isEqualTo(
821       """
822         package test
823 
824         import kotlin.String
825 
826         public class TestRealRepository {
827           public lateinit var prop: LeAlias<String>
828         }
829 
830       """.trimIndent(),
831     )
832   }
833 
834   @Test
intersectionTypesnull835   fun intersectionTypes() {
836     val compilation = prepareCompilation(
837       kotlin(
838         "Example.kt",
839         """
840            package test
841 
842            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
843 
844            @ExampleAnnotation
845            class Example {
846              fun <T> example() where T : Appendable, T : CharSequence {
847 
848              }
849            }
850            """,
851       ),
852     )
853 
854     val result = compilation.compile()
855     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
856     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestExample.kt")
857       .readText()
858 
859     assertThat(generatedFileText).isEqualTo(
860       """
861         package test
862 
863         import kotlin.CharSequence
864         import kotlin.Unit
865         import kotlin.text.Appendable
866 
867         public class TestExample {
868           public fun <T> example(): Unit where T : Appendable, T : CharSequence = TODO()
869         }
870 
871       """.trimIndent(),
872     )
873   }
874 
875   @Test
typeArgsnull876   fun typeArgs() {
877     val compilation = prepareCompilation(
878       kotlin(
879         "Example.kt",
880         """
881            package test
882 
883            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
884            import com.squareup.kotlinpoet.ksp.test.processor.AnnotationWithTypeArgs
885 
886            @ExampleAnnotation
887            @AnnotationWithTypeArgs<String, List<Int>>
888            class Example
889            """,
890       ),
891     )
892 
893     val result = compilation.compile()
894     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
895     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestExample.kt")
896       .readText()
897 
898     assertThat(generatedFileText).isEqualTo(
899       """
900         package test
901 
902         import com.squareup.kotlinpoet.ksp.test.processor.AnnotationWithTypeArgs
903         import kotlin.Int
904         import kotlin.String
905         import kotlin.collections.List
906 
907         @AnnotationWithTypeArgs<String, List<Int>>
908         public class TestExample
909 
910       """.trimIndent(),
911     )
912   }
913 
914   @Test
complexAliasingnull915   fun complexAliasing() {
916     val compilation = prepareCompilation(
917       kotlin(
918         "Example.kt",
919         """
920            package test
921 
922            import javax.inject.Provider
923            import com.squareup.kotlinpoet.ksp.test.processor.ExampleAnnotation
924 
925            typealias DaggerProvider<T> = @JvmSuppressWildcards Provider<T>
926            interface SelectOptions
927            interface SelectHandler<T>
928 
929            @ExampleAnnotation
930            class Example(
931              private val handlers: Map<Class<out SelectOptions>, DaggerProvider<SelectHandler<*>>>,
932            )
933            """,
934       ),
935     )
936 
937     val result = compilation.compile()
938     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
939     val generatedFileText = File(compilation.kspSourcesDir, "kotlin/test/TestExample.kt")
940       .readText()
941 
942     assertThat(generatedFileText).isEqualTo(
943       """
944         package test
945 
946         import java.lang.Class
947         import kotlin.collections.Map
948 
949         public class TestExample {
950           private val handlers: Map<Class<out SelectOptions>, DaggerProvider<SelectHandler<*>>> = TODO()
951         }
952 
953       """.trimIndent(),
954     )
955   }
956 
prepareCompilationnull957   private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation {
958     return KotlinCompilation()
959       .apply {
960         workingDir = temporaryFolder.root
961         inheritClassPath = true
962         sources = sourceFiles.asList()
963         verbose = false
964         configureKsp(useKsp2) {
965           incremental = true // The default now
966           if (!useKsp2) {
967             languageVersion = "1.9"
968             apiVersion = "1.9"
969             // Doesn't exist in KSP 2
970             withCompilation = true
971           }
972           symbolProcessorProviders += TestProcessorProvider()
973         }
974       }
975   }
976 }
977