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