xref: /aosp_15_r20/external/kotlinpoet/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeSpecTest.kt (revision 3c321d951dd070fb96f8ba59e952ffc3131379a0)
1 /*
2  * Copyright (C) 2015 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
17 
18 import com.google.common.collect.ImmutableMap
19 import com.google.common.truth.Truth.assertThat
20 import com.google.testing.compile.CompilationRule
21 import com.squareup.kotlinpoet.KModifier.ABSTRACT
22 import com.squareup.kotlinpoet.KModifier.DATA
23 import com.squareup.kotlinpoet.KModifier.IN
24 import com.squareup.kotlinpoet.KModifier.INNER
25 import com.squareup.kotlinpoet.KModifier.INTERNAL
26 import com.squareup.kotlinpoet.KModifier.PRIVATE
27 import com.squareup.kotlinpoet.KModifier.PUBLIC
28 import com.squareup.kotlinpoet.KModifier.SEALED
29 import com.squareup.kotlinpoet.KModifier.VARARG
30 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
31 import com.squareup.kotlinpoet.jvm.throws
32 import java.io.IOException
33 import java.io.Serializable
34 import java.lang.Deprecated
35 import java.math.BigDecimal
36 import java.util.AbstractSet
37 import java.util.Collections
38 import java.util.Comparator
39 import java.util.EventListener
40 import java.util.Locale
41 import java.util.Random
42 import java.util.concurrent.Callable
43 import java.util.function.Consumer
44 import java.util.logging.Logger
45 import javax.lang.model.element.TypeElement
46 import kotlin.reflect.KClass
47 import kotlin.reflect.KFunction
48 import kotlin.test.Ignore
49 import kotlin.test.Test
50 import kotlin.test.assertEquals
51 import kotlin.test.assertFailsWith
52 import kotlin.test.fail
53 import org.junit.Rule
54 
55 @OptIn(ExperimentalKotlinPoetApi::class)
56 class TypeSpecTest {
57   private val tacosPackage = "com.squareup.tacos"
58 
59   @Rule @JvmField
60   val compilation = CompilationRule()
61 
getElementnull62   private fun getElement(`class`: Class<*>): TypeElement {
63     return compilation.elements.getTypeElement(`class`.canonicalName)
64   }
65 
getElementnull66   private fun getElement(`class`: KClass<*>): TypeElement {
67     return getElement(`class`.java)
68   }
69 
basicnull70   @Test fun basic() {
71     val taco = TypeSpec.classBuilder("Taco")
72       .addFunction(
73         FunSpec.builder("toString")
74           .addModifiers(KModifier.PUBLIC, KModifier.FINAL, KModifier.OVERRIDE)
75           .returns(String::class)
76           .addStatement("return %S", "taco")
77           .build(),
78       )
79       .build()
80     assertThat(toString(taco)).isEqualTo(
81       """
82         |package com.squareup.tacos
83         |
84         |import kotlin.String
85         |
86         |public class Taco {
87         |  public final override fun toString(): String = "taco"
88         |}
89         |
90       """.trimMargin(),
91     )
92     assertEquals(1906837485, taco.hashCode().toLong()) // Update expected number if source changes.
93   }
94 
interestingTypesnull95   @Test fun interestingTypes() {
96     val listOfAny = List::class.asClassName().parameterizedBy(STAR)
97     val listOfExtends = List::class.asClassName()
98       .parameterizedBy(WildcardTypeName.producerOf(Serializable::class))
99     val listOfSuper = List::class.asClassName()
100       .parameterizedBy(WildcardTypeName.consumerOf(String::class))
101     val taco = TypeSpec.classBuilder("Taco")
102       .addProperty("star", listOfAny)
103       .addProperty("outSerializable", listOfExtends)
104       .addProperty("inString", listOfSuper)
105       .build()
106     assertThat(toString(taco)).isEqualTo(
107       """
108         |package com.squareup.tacos
109         |
110         |import java.io.Serializable
111         |import kotlin.String
112         |import kotlin.collections.List
113         |
114         |public class Taco {
115         |  public val star: List<*>
116         |
117         |  public val outSerializable: List<out Serializable>
118         |
119         |  public val inString: List<in String>
120         |}
121         |
122       """.trimMargin(),
123     )
124   }
125 
anonymousInnerClassnull126   @Test fun anonymousInnerClass() {
127     val foo = ClassName(tacosPackage, "Foo")
128     val bar = ClassName(tacosPackage, "Bar")
129     val thingThang = ClassName(tacosPackage, "Thing", "Thang")
130     val thingThangOfFooBar = thingThang.parameterizedBy(foo, bar)
131     val thung = ClassName(tacosPackage, "Thung")
132     val simpleThung = ClassName(tacosPackage, "SimpleThung")
133     val thungOfSuperBar = thung.parameterizedBy(WildcardTypeName.consumerOf(bar))
134     val thungOfSuperFoo = thung.parameterizedBy(WildcardTypeName.consumerOf(foo))
135     val simpleThungOfBar = simpleThung.parameterizedBy(bar)
136 
137     val thungParameter = ParameterSpec.builder("thung", thungOfSuperFoo)
138       .build()
139     val aSimpleThung = TypeSpec.anonymousClassBuilder()
140       .superclass(simpleThungOfBar)
141       .addSuperclassConstructorParameter("%N", thungParameter)
142       .addFunction(
143         FunSpec.builder("doSomething")
144           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
145           .addParameter("bar", bar)
146           .addCode("/* code snippets */\n")
147           .build(),
148       )
149       .build()
150     val aThingThang = TypeSpec.anonymousClassBuilder()
151       .superclass(thingThangOfFooBar)
152       .addFunction(
153         FunSpec.builder("call")
154           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
155           .returns(thungOfSuperBar)
156           .addParameter(thungParameter)
157           .addStatement("return %L", aSimpleThung)
158           .build(),
159       )
160       .build()
161     val taco = TypeSpec.classBuilder("Taco")
162       .addProperty(
163         PropertySpec.builder("NAME", thingThangOfFooBar)
164           .initializer("%L", aThingThang)
165           .build(),
166       )
167       .build()
168 
169     assertThat(toString(taco)).isEqualTo(
170       """
171         |package com.squareup.tacos
172         |
173         |public class Taco {
174         |  public val NAME: Thing.Thang<Foo, Bar> = object : Thing.Thang<Foo, Bar>() {
175         |    public override fun call(thung: Thung<in Foo>): Thung<in Bar> = object : SimpleThung<Bar>(thung)
176         |        {
177         |      public override fun doSomething(bar: Bar) {
178         |        /* code snippets */
179         |      }
180         |    }
181         |  }
182         |}
183         |
184       """.trimMargin(),
185     )
186   }
187 
anonymousClassWithSuperClassConstructorCallnull188   @Test fun anonymousClassWithSuperClassConstructorCall() {
189     val superclass = ArrayList::class.parameterizedBy(String::class)
190     val anonymousClass = TypeSpec.anonymousClassBuilder()
191       .addSuperclassConstructorParameter("%L", "4")
192       .superclass(superclass)
193       .build()
194     val taco = TypeSpec.classBuilder("Taco")
195       .addProperty(
196         PropertySpec.builder("names", superclass)
197           .initializer("%L", anonymousClass)
198           .build(),
199       ).build()
200 
201     assertThat(toString(taco)).isEqualTo(
202       """
203         |package com.squareup.tacos
204         |
205         |import java.util.ArrayList
206         |import kotlin.String
207         |
208         |public class Taco {
209         |  public val names: ArrayList<String> = object : ArrayList<String>(4) {
210         |  }
211         |}
212         |
213       """.trimMargin(),
214     )
215   }
216 
217   // https://github.com/square/kotlinpoet/issues/315
anonymousClassWithMultipleSuperTypesnull218   @Test fun anonymousClassWithMultipleSuperTypes() {
219     val superclass = ClassName("com.squareup.wire", "Message")
220     val anonymousClass = TypeSpec.anonymousClassBuilder()
221       .superclass(superclass)
222       .addSuperinterface(Runnable::class)
223       .addFunction(
224         FunSpec.builder("run")
225           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
226           .addCode("/* code snippets */\n")
227           .build(),
228       ).build()
229     val taco = TypeSpec.classBuilder("Taco")
230       .addProperty(
231         PropertySpec.builder("NAME", Runnable::class)
232           .initializer("%L", anonymousClass)
233           .build(),
234       ).build()
235 
236     assertThat(toString(taco)).isEqualTo(
237       """
238         |package com.squareup.tacos
239         |
240         |import com.squareup.wire.Message
241         |import java.lang.Runnable
242         |
243         |public class Taco {
244         |  public val NAME: Runnable = object : Message(), Runnable {
245         |    public override fun run() {
246         |      /* code snippets */
247         |    }
248         |  }
249         |}
250         |
251       """.trimMargin(),
252     )
253   }
254 
anonymousClassWithoutSuperTypenull255   @Test fun anonymousClassWithoutSuperType() {
256     val anonymousClass = TypeSpec.anonymousClassBuilder().build()
257     val taco = TypeSpec.classBuilder("Taco")
258       .addProperty(
259         PropertySpec.builder("NAME", Any::class)
260           .initializer("%L", anonymousClass)
261           .build(),
262       ).build()
263 
264     assertThat(toString(taco)).isEqualTo(
265       """
266         |package com.squareup.tacos
267         |
268         |import kotlin.Any
269         |
270         |public class Taco {
271         |  public val NAME: Any = object {
272         |  }
273         |}
274         |
275       """.trimMargin(),
276     )
277   }
278 
annotatedParametersnull279   @Test fun annotatedParameters() {
280     val service = TypeSpec.classBuilder("Foo")
281       .addFunction(
282         FunSpec.constructorBuilder()
283           .addModifiers(KModifier.PUBLIC)
284           .addParameter("id", Long::class)
285           .addParameter(
286             ParameterSpec.builder("one", String::class)
287               .addAnnotation(ClassName(tacosPackage, "Ping"))
288               .build(),
289           )
290           .addParameter(
291             ParameterSpec.builder("two", String::class)
292               .addAnnotation(ClassName(tacosPackage, "Ping"))
293               .build(),
294           )
295           .addParameter(
296             ParameterSpec.builder("three", String::class)
297               .addAnnotation(
298                 AnnotationSpec.builder(ClassName(tacosPackage, "Pong"))
299                   .addMember("%S", "pong")
300                   .build(),
301               )
302               .build(),
303           )
304           .addParameter(
305             ParameterSpec.builder("four", String::class)
306               .addAnnotation(ClassName(tacosPackage, "Ping"))
307               .build(),
308           )
309           .addCode("/* code snippets */\n")
310           .build(),
311       )
312       .build()
313 
314     assertThat(toString(service)).isEqualTo(
315       """
316         |package com.squareup.tacos
317         |
318         |import kotlin.Long
319         |import kotlin.String
320         |
321         |public class Foo {
322         |  public constructor(
323         |    id: Long,
324         |    @Ping one: String,
325         |    @Ping two: String,
326         |    @Pong("pong") three: String,
327         |    @Ping four: String,
328         |  ) {
329         |    /* code snippets */
330         |  }
331         |}
332         |
333       """.trimMargin(),
334     )
335   }
336 
337   /**
338    * We had a bug where annotations were preventing us from doing the right thing when resolving
339    * imports. https://github.com/square/javapoet/issues/422
340    */
annotationsAndJavaLangTypesnull341   @Test fun annotationsAndJavaLangTypes() {
342     val freeRange = ClassName("javax.annotation", "FreeRange")
343     val taco = TypeSpec.classBuilder("EthicalTaco")
344       .addProperty(
345         "meat",
346         String::class.asClassName()
347           .copy(annotations = listOf(AnnotationSpec.builder(freeRange).build())),
348       )
349       .build()
350 
351     assertThat(toString(taco)).isEqualTo(
352       """
353         |package com.squareup.tacos
354         |
355         |import javax.`annotation`.FreeRange
356         |import kotlin.String
357         |
358         |public class EthicalTaco {
359         |  public val meat: @FreeRange String
360         |}
361         |
362       """.trimMargin(),
363     )
364   }
365 
retrofitStyleInterfacenull366   @Test fun retrofitStyleInterface() {
367     val observable = ClassName(tacosPackage, "Observable")
368     val fooBar = ClassName(tacosPackage, "FooBar")
369     val thing = ClassName(tacosPackage, "Thing")
370     val things = ClassName(tacosPackage, "Things")
371     val map = Map::class.asClassName()
372     val string = String::class.asClassName()
373     val headers = ClassName(tacosPackage, "Headers")
374     val post = ClassName(tacosPackage, "POST")
375     val body = ClassName(tacosPackage, "Body")
376     val queryMap = ClassName(tacosPackage, "QueryMap")
377     val header = ClassName(tacosPackage, "Header")
378     val service = TypeSpec.interfaceBuilder("Service")
379       .addFunction(
380         FunSpec.builder("fooBar")
381           .addModifiers(KModifier.PUBLIC, KModifier.ABSTRACT)
382           .addAnnotation(
383             AnnotationSpec.builder(headers)
384               .addMember("%S", "Accept: application/json")
385               .addMember("%S", "User-Agent: foobar")
386               .build(),
387           )
388           .addAnnotation(
389             AnnotationSpec.builder(post)
390               .addMember("%S", "/foo/bar")
391               .build(),
392           )
393           .returns(observable.parameterizedBy(fooBar))
394           .addParameter(
395             ParameterSpec.builder("things", things.parameterizedBy(thing))
396               .addAnnotation(body)
397               .build(),
398           )
399           .addParameter(
400             ParameterSpec.builder("query", map.parameterizedBy(string, string))
401               .addAnnotation(
402                 AnnotationSpec.builder(queryMap)
403                   .addMember("encodeValues = %L", "false")
404                   .build(),
405               )
406               .build(),
407           )
408           .addParameter(
409             ParameterSpec.builder("authorization", string)
410               .addAnnotation(
411                 AnnotationSpec.builder(header)
412                   .addMember("%S", "Authorization")
413                   .build(),
414               )
415               .build(),
416           )
417           .build(),
418       )
419       .build()
420 
421     assertThat(toString(service)).isEqualTo(
422       """
423         |package com.squareup.tacos
424         |
425         |import kotlin.String
426         |import kotlin.collections.Map
427         |
428         |public interface Service {
429         |  @Headers(
430         |    "Accept: application/json",
431         |    "User-Agent: foobar",
432         |  )
433         |  @POST("/foo/bar")
434         |  public fun fooBar(
435         |    @Body things: Things<Thing>,
436         |    @QueryMap(encodeValues = false) query: Map<String, String>,
437         |    @Header("Authorization") authorization: String,
438         |  ): Observable<FooBar>
439         |}
440         |
441       """.trimMargin(),
442     )
443   }
444 
annotatedPropertynull445   @Test fun annotatedProperty() {
446     val taco = TypeSpec.classBuilder("Taco")
447       .addProperty(
448         PropertySpec.builder("thing", String::class, KModifier.PRIVATE)
449           .addAnnotation(
450             AnnotationSpec.builder(ClassName(tacosPackage, "JsonAdapter"))
451               .addMember("%T::class", ClassName(tacosPackage, "Foo"))
452               .build(),
453           )
454           .build(),
455       )
456       .build()
457     assertThat(toString(taco)).isEqualTo(
458       """
459         |package com.squareup.tacos
460         |
461         |import kotlin.String
462         |
463         |public class Taco {
464         |  @JsonAdapter(Foo::class)
465         |  private val thing: String
466         |}
467         |
468       """.trimMargin(),
469     )
470   }
471 
annotatedPropertyUseSiteTargetnull472   @Test fun annotatedPropertyUseSiteTarget() {
473     val taco = TypeSpec.classBuilder("Taco")
474       .addProperty(
475         PropertySpec.builder("thing", String::class, KModifier.PRIVATE)
476           .addAnnotation(
477             AnnotationSpec.builder(ClassName(tacosPackage, "JsonAdapter"))
478               .addMember("%T::class", ClassName(tacosPackage, "Foo"))
479               .useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
480               .build(),
481           )
482           .build(),
483       )
484       .build()
485     assertThat(toString(taco)).isEqualTo(
486       """
487         |package com.squareup.tacos
488         |
489         |import kotlin.String
490         |
491         |public class Taco {
492         |  @field:JsonAdapter(Foo::class)
493         |  private val thing: String
494         |}
495         |
496       """.trimMargin(),
497     )
498   }
499 
annotatedClassnull500   @Test fun annotatedClass() {
501     val someType = ClassName(tacosPackage, "SomeType")
502     val taco = TypeSpec.classBuilder("Foo")
503       .addAnnotation(
504         AnnotationSpec.builder(ClassName(tacosPackage, "Something"))
505           .addMember("%T.%N", someType, "PROPERTY")
506           .addMember("%L", 12)
507           .addMember("%S", "goodbye")
508           .build(),
509       )
510       .addModifiers(KModifier.PUBLIC)
511       .build()
512     assertThat(toString(taco)).isEqualTo(
513       """
514         |package com.squareup.tacos
515         |
516         |@Something(
517         |  SomeType.PROPERTY,
518         |  12,
519         |  "goodbye",
520         |)
521         |public class Foo
522         |
523       """.trimMargin(),
524     )
525   }
526 
enumWithSubclassingnull527   @Test fun enumWithSubclassing() {
528     val roshambo = TypeSpec.enumBuilder("Roshambo")
529       .addModifiers(KModifier.PUBLIC)
530       .addEnumConstant(
531         "ROCK",
532         TypeSpec.anonymousClassBuilder()
533           .addKdoc("Avalanche!\n")
534           .build(),
535       )
536       .addEnumConstant(
537         "PAPER",
538         TypeSpec.anonymousClassBuilder()
539           .addSuperclassConstructorParameter("%S", "flat")
540           .addFunction(
541             FunSpec.builder("toString")
542               .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE, KModifier.OVERRIDE)
543               .returns(String::class)
544               .addCode("return %S\n", "paper airplane!")
545               .build(),
546           )
547           .build(),
548       )
549       .addEnumConstant(
550         "SCISSORS",
551         TypeSpec.anonymousClassBuilder()
552           .addSuperclassConstructorParameter("%S", "peace sign")
553           .build(),
554       )
555       .addProperty(
556         PropertySpec.builder("handPosition", String::class, KModifier.PRIVATE)
557           .initializer("handPosition")
558           .build(),
559       )
560       .primaryConstructor(
561         FunSpec.constructorBuilder()
562           .addParameter("handPosition", String::class)
563           .build(),
564       )
565       .addFunction(
566         FunSpec.constructorBuilder()
567           .addCode("this(%S)\n", "fist")
568           .build(),
569       )
570       .build()
571     assertThat(toString(roshambo)).isEqualTo(
572       """
573         |package com.squareup.tacos
574         |
575         |import kotlin.String
576         |
577         |public enum class Roshambo(
578         |  private val handPosition: String,
579         |) {
580         |  /**
581         |   * Avalanche!
582         |   */
583         |  ROCK,
584         |  PAPER("flat") {
585         |    public override fun toString(): String = "paper airplane!"
586         |  },
587         |  SCISSORS("peace sign"),
588         |  ;
589         |
590         |  public constructor() {
591         |    this("fist")
592         |  }
593         |}
594         |
595       """.trimMargin(),
596     )
597   }
598 
enumWithPrimaryConstructorAndMultipleInterfacesnull599   @Test fun enumWithPrimaryConstructorAndMultipleInterfaces() {
600     val roshambo = TypeSpec.enumBuilder("Roshambo")
601       .addSuperinterface(Runnable::class)
602       .addSuperinterface(Cloneable::class)
603       .addEnumConstant(
604         "SCISSORS",
605         TypeSpec.anonymousClassBuilder()
606           .addSuperclassConstructorParameter("%S", "peace sign")
607           .build(),
608       )
609       .addProperty(
610         PropertySpec.builder("handPosition", String::class, KModifier.PRIVATE)
611           .initializer("handPosition")
612           .build(),
613       )
614       .primaryConstructor(
615         FunSpec.constructorBuilder()
616           .addParameter("handPosition", String::class)
617           .build(),
618       )
619       .build()
620     assertThat(toString(roshambo)).isEqualTo(
621       """
622         |package com.squareup.tacos
623         |
624         |import java.lang.Runnable
625         |import kotlin.Cloneable
626         |import kotlin.String
627         |
628         |public enum class Roshambo(
629         |  private val handPosition: String,
630         |) : Runnable,
631         |    Cloneable {
632         |  SCISSORS("peace sign"),
633         |  ;
634         |}
635         |
636       """.trimMargin(),
637     )
638   }
639 
640   /** https://github.com/square/javapoet/issues/193  */
enumsMayDefineAbstractFunctionsnull641   @Test fun enumsMayDefineAbstractFunctions() {
642     val roshambo = TypeSpec.enumBuilder("Tortilla")
643       .addModifiers(KModifier.PUBLIC)
644       .addEnumConstant(
645         "CORN",
646         TypeSpec.anonymousClassBuilder()
647           .addFunction(
648             FunSpec.builder("fold")
649               .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
650               .build(),
651           )
652           .build(),
653       )
654       .addFunction(
655         FunSpec.builder("fold")
656           .addModifiers(KModifier.PUBLIC, KModifier.ABSTRACT)
657           .build(),
658       )
659       .build()
660     assertThat(toString(roshambo)).isEqualTo(
661       """
662         |package com.squareup.tacos
663         |
664         |public enum class Tortilla {
665         |  CORN {
666         |    public override fun fold() {
667         |    }
668         |  },
669         |  ;
670         |
671         |  public abstract fun fold()
672         |}
673         |
674       """.trimMargin(),
675     )
676   }
677 
enumsMayHavePrivateConstructorValsnull678   @Test fun enumsMayHavePrivateConstructorVals() {
679     val enum = TypeSpec.enumBuilder("MyEnum")
680       .primaryConstructor(
681         FunSpec.constructorBuilder().addParameter("number", Int::class).build(),
682       )
683       .addProperty(
684         PropertySpec.builder("number", Int::class)
685           .addModifiers(PRIVATE)
686           .initializer("number")
687           .build(),
688       )
689       .build()
690     assertThat(toString(enum)).isEqualTo(
691       """
692         |package com.squareup.tacos
693         |
694         |import kotlin.Int
695         |
696         |public enum class MyEnum(
697         |  private val number: Int,
698         |)
699         |
700       """.trimMargin(),
701     )
702   }
703 
classesMayHavePrivateConstructorPropertiesInTheirPrimaryConstructorsnull704   @Test fun classesMayHavePrivateConstructorPropertiesInTheirPrimaryConstructors() {
705     val myClass = TypeSpec.classBuilder("MyClass")
706       .primaryConstructor(
707         FunSpec.constructorBuilder().addParameter("number", Int::class).build(),
708       )
709       .addProperty(
710         PropertySpec.builder("number", Int::class)
711           .initializer("number")
712           .addModifiers(PRIVATE)
713           .build(),
714       )
715       .build()
716     assertThat(toString(myClass)).isEqualTo(
717       """
718         |package com.squareup.tacos
719         |
720         |import kotlin.Int
721         |
722         |public class MyClass(
723         |  private val number: Int,
724         |)
725         |
726       """.trimMargin(),
727     )
728   }
729 
sealedClassesMayDefineAbstractMembersnull730   @Test fun sealedClassesMayDefineAbstractMembers() {
731     val sealedClass = TypeSpec.classBuilder("Sealed")
732       .addModifiers(KModifier.SEALED)
733       .addProperty(PropertySpec.builder("name", String::class).addModifiers(ABSTRACT).build())
734       .addFunction(FunSpec.builder("fold").addModifiers(KModifier.PUBLIC, KModifier.ABSTRACT).build())
735       .build()
736     assertThat(toString(sealedClass)).isEqualTo(
737       """
738         |package com.squareup.tacos
739         |
740         |import kotlin.String
741         |
742         |public sealed class Sealed {
743         |  public abstract val name: String
744         |
745         |  public abstract fun fold()
746         |}
747         |
748       """.trimMargin(),
749     )
750   }
751 
classesMayHaveVarargConstructorPropertiesnull752   @Test fun classesMayHaveVarargConstructorProperties() {
753     val variable = TypeSpec.classBuilder("Variable")
754       .primaryConstructor(
755         FunSpec.constructorBuilder()
756           .addParameter(ParameterSpec.builder("name", String::class, VARARG).build())
757           .build(),
758       )
759       .addProperty(PropertySpec.builder("name", String::class).initializer("name").build())
760       .build()
761     assertThat(toString(variable)).isEqualTo(
762       """
763         |package com.squareup.tacos
764         |
765         |import kotlin.String
766         |
767         |public class Variable(
768         |  public vararg val name: String,
769         |)
770         |
771       """.trimMargin(),
772     )
773   }
774 
775   /** https://github.com/square/kotlinpoet/issues/942  */
noConstructorPropertiesWithCustomGetternull776   @Test fun noConstructorPropertiesWithCustomGetter() {
777     val taco = TypeSpec.classBuilder("ObservantTaco")
778       .primaryConstructor(
779         FunSpec.constructorBuilder()
780           .addParameter(ParameterSpec.builder("contents", String::class).build())
781           .build(),
782       )
783       .addProperty(
784         PropertySpec.builder("contents", String::class).initializer("contents")
785           .getter(FunSpec.getterBuilder().addCode("println(%S)\nreturn field", "contents observed!").build())
786           .build(),
787       )
788       .build()
789     assertThat(toString(taco)).isEqualTo(
790       """
791         |package com.squareup.tacos
792         |
793         |import kotlin.String
794         |
795         |public class ObservantTaco(
796         |  contents: String,
797         |) {
798         |  public val contents: String = contents
799         |    get() {
800         |      println("contents observed!")
801         |      return field
802         |    }
803         |}
804         |
805       """.trimMargin(),
806     )
807   }
808 
noConstructorPropertiesWithCustomSetternull809   @Test fun noConstructorPropertiesWithCustomSetter() {
810     val taco = TypeSpec.classBuilder("ObservantTaco")
811       .primaryConstructor(
812         FunSpec.constructorBuilder()
813           .addParameter(ParameterSpec.builder("contents", String::class).build())
814           .build(),
815       )
816       .addProperty(
817         PropertySpec.builder("contents", String::class).initializer("contents")
818           .mutable()
819           .setter(
820             FunSpec.setterBuilder()
821               .addParameter("value", String::class)
822               .addCode("println(%S)\nfield = value", "contents changed!").build(),
823           )
824           .build(),
825       )
826       .build()
827     assertThat(toString(taco)).isEqualTo(
828       """
829         |package com.squareup.tacos
830         |
831         |import kotlin.String
832         |
833         |public class ObservantTaco(
834         |  contents: String,
835         |) {
836         |  public var contents: String = contents
837         |    set(`value`) {
838         |      println("contents changed!")
839         |      field = value
840         |    }
841         |}
842         |
843       """.trimMargin(),
844     )
845   }
846 
onlyEnumsMayHaveEnumConstantsnull847   @Test fun onlyEnumsMayHaveEnumConstants() {
848     assertThrows<IllegalStateException> {
849       TypeSpec.classBuilder("Roshambo")
850         .addEnumConstant("ROCK")
851         .build()
852     }
853   }
854 
855   /** https://github.com/square/kotlinpoet/issues/621  */
enumWithMembersButNoConstanstsnull856   @Test fun enumWithMembersButNoConstansts() {
857     val roshambo = TypeSpec.enumBuilder("RenderPassCreate")
858       .addType(TypeSpec.companionObjectBuilder().build())
859       .build()
860     assertThat(toString(roshambo)).isEqualTo(
861       """
862         |package com.squareup.tacos
863         |
864         |public enum class RenderPassCreate {
865         |  ;
866         |  public companion object
867         |}
868         |
869       """.trimMargin(),
870     )
871   }
872 
enumWithMembersButNoConstructorCallnull873   @Test fun enumWithMembersButNoConstructorCall() {
874     val roshambo = TypeSpec.enumBuilder("Roshambo")
875       .addEnumConstant(
876         "SPOCK",
877         TypeSpec.anonymousClassBuilder()
878           .addFunction(
879             FunSpec.builder("toString")
880               .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
881               .returns(String::class)
882               .addStatement("return %S", "west side")
883               .build(),
884           )
885           .build(),
886       )
887       .build()
888     assertThat(toString(roshambo)).isEqualTo(
889       """
890         |package com.squareup.tacos
891         |
892         |import kotlin.String
893         |
894         |public enum class Roshambo {
895         |  SPOCK {
896         |    public override fun toString(): String = "west side"
897         |  },
898         |}
899         |
900       """.trimMargin(),
901     )
902   }
903 
904   /** https://github.com/square/javapoet/issues/253  */
enumWithAnnotatedValuesnull905   @Test fun enumWithAnnotatedValues() {
906     val roshambo = TypeSpec.enumBuilder("Roshambo")
907       .addModifiers(KModifier.PUBLIC)
908       .addEnumConstant(
909         "ROCK",
910         TypeSpec.anonymousClassBuilder()
911           .addAnnotation(java.lang.Deprecated::class)
912           .build(),
913       )
914       .addEnumConstant("PAPER")
915       .addEnumConstant("SCISSORS")
916       .build()
917     assertThat(toString(roshambo)).isEqualTo(
918       """
919         |package com.squareup.tacos
920         |
921         |import java.lang.Deprecated
922         |
923         |public enum class Roshambo {
924         |  @Deprecated
925         |  ROCK,
926         |  PAPER,
927         |  SCISSORS,
928         |}
929         |
930       """.trimMargin(),
931     )
932   }
933 
funThrowsnull934   @Test fun funThrows() {
935     val taco = TypeSpec.classBuilder("Taco")
936       .addModifiers(KModifier.ABSTRACT)
937       .addFunction(
938         FunSpec.builder("throwOne")
939           .throws(IOException::class)
940           .build(),
941       )
942       .addFunction(
943         FunSpec.builder("throwTwo")
944           .throws(IOException::class.asClassName(), ClassName(tacosPackage, "SourCreamException"))
945           .build(),
946       )
947       .addFunction(
948         FunSpec.builder("abstractThrow")
949           .addModifiers(KModifier.ABSTRACT)
950           .throws(IOException::class)
951           .build(),
952       )
953       .addFunction(
954         FunSpec.builder("nativeThrow")
955           .addModifiers(KModifier.EXTERNAL)
956           .throws(IOException::class)
957           .build(),
958       )
959       .build()
960     assertThat(toString(taco)).isEqualTo(
961       """
962         |package com.squareup.tacos
963         |
964         |import java.io.IOException
965         |import kotlin.jvm.Throws
966         |
967         |public abstract class Taco {
968         |  @Throws(IOException::class)
969         |  public fun throwOne() {
970         |  }
971         |
972         |  @Throws(
973         |    IOException::class,
974         |    SourCreamException::class,
975         |  )
976         |  public fun throwTwo() {
977         |  }
978         |
979         |  @Throws(IOException::class)
980         |  public abstract fun abstractThrow()
981         |
982         |  @Throws(IOException::class)
983         |  public external fun nativeThrow()
984         |}
985         |
986       """.trimMargin(),
987     )
988   }
989 
typeVariablesnull990   @Test fun typeVariables() {
991     val t = TypeVariableName("T")
992     val p = TypeVariableName("P", Number::class)
993     val location = ClassName(tacosPackage, "Location")
994     val typeSpec = TypeSpec.classBuilder("Location")
995       .addTypeVariable(t)
996       .addTypeVariable(p)
997       .addSuperinterface(Comparable::class.asClassName().parameterizedBy(p))
998       .addProperty("label", t)
999       .addProperty("x", p)
1000       .addProperty("y", p)
1001       .addFunction(
1002         FunSpec.builder("compareTo")
1003           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
1004           .returns(Int::class)
1005           .addParameter("p", p)
1006           .addStatement("return 0")
1007           .build(),
1008       )
1009       .addFunction(
1010         FunSpec.builder("of")
1011           .addModifiers(KModifier.PUBLIC)
1012           .addTypeVariable(t)
1013           .addTypeVariable(p)
1014           .returns(location.parameterizedBy(t, p))
1015           .addParameter("label", t)
1016           .addParameter("x", p)
1017           .addParameter("y", p)
1018           .addStatement("throw %T(%S)", UnsupportedOperationException::class, "TODO")
1019           .build(),
1020       )
1021       .build()
1022     assertThat(toString(typeSpec)).isEqualTo(
1023       """
1024         |package com.squareup.tacos
1025         |
1026         |import java.lang.UnsupportedOperationException
1027         |import kotlin.Comparable
1028         |import kotlin.Int
1029         |import kotlin.Number
1030         |
1031         |public class Location<T, P : Number> : Comparable<P> {
1032         |  public val label: T
1033         |
1034         |  public val x: P
1035         |
1036         |  public val y: P
1037         |
1038         |  public override fun compareTo(p: P): Int = 0
1039         |
1040         |  public fun <T, P : Number> of(
1041         |    label: T,
1042         |    x: P,
1043         |    y: P,
1044         |  ): Location<T, P> = throw UnsupportedOperationException("TODO")
1045         |}
1046         |
1047       """.trimMargin(),
1048     )
1049   }
1050 
typeVariableWithBoundsnull1051   @Test fun typeVariableWithBounds() {
1052     val a = AnnotationSpec.builder(ClassName("com.squareup.tacos", "A")).build()
1053     val p = TypeVariableName("P", Number::class)
1054     val q = TypeVariableName("Q", Number::class).copy(annotations = listOf(a)) as TypeVariableName
1055     val typeSpec = TypeSpec.classBuilder("Location")
1056       .addTypeVariable(p.copy(bounds = p.bounds + listOf(Comparable::class.asTypeName())))
1057       .addTypeVariable(q.copy(bounds = q.bounds + listOf(Comparable::class.asTypeName())))
1058       .addProperty("x", p)
1059       .addProperty("y", q)
1060       .primaryConstructor(FunSpec.constructorBuilder().build())
1061       .superclass(Number::class)
1062       .addSuperinterface(Comparable::class)
1063       .build()
1064     assertThat(toString(typeSpec)).isEqualTo(
1065       """
1066         |package com.squareup.tacos
1067         |
1068         |import kotlin.Comparable
1069         |import kotlin.Number
1070         |
1071         |public class Location<P, Q>() : Number(), Comparable where P : Number, P : Comparable, Q : Number, Q
1072         |    : Comparable {
1073         |  public val x: P
1074         |
1075         |  public val y: @A Q
1076         |}
1077         |
1078       """.trimMargin(),
1079     )
1080   }
1081 
classImplementsExtendsnull1082   @Test fun classImplementsExtends() {
1083     val taco = ClassName(tacosPackage, "Taco")
1084     val food = ClassName("com.squareup.tacos", "Food")
1085     val typeSpec = TypeSpec.classBuilder("Taco")
1086       .addModifiers(KModifier.ABSTRACT)
1087       .superclass(AbstractSet::class.asClassName().parameterizedBy(food))
1088       .addSuperinterface(Serializable::class)
1089       .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco))
1090       .build()
1091     assertThat(toString(typeSpec)).isEqualTo(
1092       """
1093         |package com.squareup.tacos
1094         |
1095         |import java.io.Serializable
1096         |import java.util.AbstractSet
1097         |import kotlin.Comparable
1098         |
1099         |public abstract class Taco : AbstractSet<Food>(), Serializable, Comparable<Taco>
1100         |
1101       """.trimMargin(),
1102     )
1103   }
1104 
classImplementsExtendsPrimaryConstructorNoParamsnull1105   @Test fun classImplementsExtendsPrimaryConstructorNoParams() {
1106     val taco = ClassName(tacosPackage, "Taco")
1107     val food = ClassName("com.squareup.tacos", "Food")
1108     val typeSpec = TypeSpec.classBuilder("Taco")
1109       .addModifiers(ABSTRACT)
1110       .superclass(AbstractSet::class.asClassName().parameterizedBy(food))
1111       .addSuperinterface(Serializable::class)
1112       .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco))
1113       .primaryConstructor(FunSpec.constructorBuilder().build())
1114       .build()
1115     assertThat(toString(typeSpec)).isEqualTo(
1116       """
1117         |package com.squareup.tacos
1118         |
1119         |import java.io.Serializable
1120         |import java.util.AbstractSet
1121         |import kotlin.Comparable
1122         |
1123         |public abstract class Taco() : AbstractSet<Food>(), Serializable, Comparable<Taco>
1124         |
1125       """.trimMargin(),
1126     )
1127   }
1128 
classImplementsExtendsPrimaryConstructorWithParamsnull1129   @Test fun classImplementsExtendsPrimaryConstructorWithParams() {
1130     val taco = ClassName(tacosPackage, "Taco")
1131     val food = ClassName("com.squareup.tacos", "Food")
1132     val typeSpec = TypeSpec.classBuilder("Taco")
1133       .addModifiers(ABSTRACT)
1134       .superclass(AbstractSet::class.asClassName().parameterizedBy(food))
1135       .addSuperinterface(Serializable::class)
1136       .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco))
1137       .primaryConstructor(
1138         FunSpec.constructorBuilder()
1139           .addParameter("name", String::class)
1140           .build(),
1141       )
1142       .build()
1143     assertThat(toString(typeSpec)).isEqualTo(
1144       """
1145         |package com.squareup.tacos
1146         |
1147         |import java.io.Serializable
1148         |import java.util.AbstractSet
1149         |import kotlin.Comparable
1150         |import kotlin.String
1151         |
1152         |public abstract class Taco(
1153         |  name: String,
1154         |) : AbstractSet<Food>(),
1155         |    Serializable,
1156         |    Comparable<Taco>
1157         |
1158       """.trimMargin(),
1159     )
1160   }
1161 
classImplementsExtendsSameNamenull1162   @Test fun classImplementsExtendsSameName() {
1163     val javapoetTaco = ClassName(tacosPackage, "Taco")
1164     val tacoBellTaco = ClassName("com.taco.bell", "Taco")
1165     val fishTaco = ClassName("org.fish.taco", "Taco")
1166     val typeSpec = TypeSpec.classBuilder("Taco")
1167       .superclass(fishTaco)
1168       .addSuperinterface(Comparable::class.asClassName().parameterizedBy(javapoetTaco))
1169       .addSuperinterface(tacoBellTaco)
1170       .build()
1171     assertThat(toString(typeSpec)).isEqualTo(
1172       """
1173         |package com.squareup.tacos
1174         |
1175         |import kotlin.Comparable
1176         |
1177         |public class Taco : org.fish.taco.Taco(), Comparable<Taco>, com.taco.bell.Taco
1178         |
1179       """.trimMargin(),
1180     )
1181   }
1182 
classImplementsInnerClassnull1183   @Test fun classImplementsInnerClass() {
1184     val outer = ClassName(tacosPackage, "Outer")
1185     val inner = outer.nestedClass("Inner")
1186     val callable = Callable::class.asClassName()
1187     val typeSpec = TypeSpec.classBuilder("Outer")
1188       .superclass(callable.parameterizedBy(inner))
1189       .addType(
1190         TypeSpec.classBuilder("Inner")
1191           .addModifiers(KModifier.INNER)
1192           .build(),
1193       )
1194       .build()
1195 
1196     assertThat(toString(typeSpec)).isEqualTo(
1197       """
1198         |package com.squareup.tacos
1199         |
1200         |import java.util.concurrent.Callable
1201         |
1202         |public class Outer : Callable<Outer.Inner>() {
1203         |  public inner class Inner
1204         |}
1205         |
1206       """.trimMargin(),
1207     )
1208   }
1209 
enumImplementsnull1210   @Test fun enumImplements() {
1211     val typeSpec = TypeSpec.enumBuilder("Food")
1212       .addSuperinterface(Serializable::class)
1213       .addSuperinterface(Cloneable::class)
1214       .addEnumConstant("LEAN_GROUND_BEEF")
1215       .addEnumConstant("SHREDDED_CHEESE")
1216       .build()
1217     assertThat(toString(typeSpec)).isEqualTo(
1218       """
1219         |package com.squareup.tacos
1220         |
1221         |import java.io.Serializable
1222         |import kotlin.Cloneable
1223         |
1224         |public enum class Food : Serializable, Cloneable {
1225         |  LEAN_GROUND_BEEF,
1226         |  SHREDDED_CHEESE,
1227         |}
1228         |
1229       """.trimMargin(),
1230     )
1231   }
1232 
enumWithConstructorsAndKeywordsnull1233   @Test fun enumWithConstructorsAndKeywords() {
1234     val primaryConstructor = FunSpec.constructorBuilder()
1235       .addParameter("value", Int::class)
1236       .build()
1237     val typeSpec = TypeSpec.enumBuilder("Sort")
1238       .primaryConstructor(primaryConstructor)
1239       .addEnumConstant(
1240         "open",
1241         TypeSpec.anonymousClassBuilder()
1242           .addSuperclassConstructorParameter("%L", 0)
1243           .build(),
1244       )
1245       .addEnumConstant(
1246         "closed",
1247         TypeSpec.anonymousClassBuilder()
1248           .addSuperclassConstructorParameter("%L", 1)
1249           .build(),
1250       )
1251       .build()
1252     assertThat(toString(typeSpec)).isEqualTo(
1253       """
1254         |package com.squareup.tacos
1255         |
1256         |import kotlin.Int
1257         |
1258         |public enum class Sort(
1259         |  `value`: Int,
1260         |) {
1261         |  `open`(0),
1262         |  closed(1),
1263         |}
1264         |
1265       """.trimMargin(),
1266     )
1267   }
1268 
interfaceExtendsnull1269   @Test fun interfaceExtends() {
1270     val taco = ClassName(tacosPackage, "Taco")
1271     val typeSpec = TypeSpec.interfaceBuilder("Taco")
1272       .addSuperinterface(Serializable::class)
1273       .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco))
1274       .build()
1275     assertThat(toString(typeSpec)).isEqualTo(
1276       """
1277         |package com.squareup.tacos
1278         |
1279         |import java.io.Serializable
1280         |import kotlin.Comparable
1281         |
1282         |public interface Taco : Serializable, Comparable<Taco>
1283         |
1284       """.trimMargin(),
1285     )
1286   }
1287 
funInterfacenull1288   @Test fun funInterface() {
1289     val taco = ClassName(tacosPackage, "Taco")
1290     val typeSpec = TypeSpec.funInterfaceBuilder(taco)
1291       .addFunction(
1292         FunSpec.builder("sam")
1293           .addModifiers(ABSTRACT)
1294           .build(),
1295       )
1296       .addFunction(FunSpec.builder("notSam").build())
1297       .build()
1298     assertThat(typeSpec.isFunctionalInterface).isTrue()
1299     assertThat(toString(typeSpec)).isEqualTo(
1300       """
1301         |package com.squareup.tacos
1302         |
1303         |public fun interface Taco {
1304         |  public fun sam()
1305         |
1306         |  public fun notSam() {
1307         |  }
1308         |}
1309         |
1310       """.trimMargin(),
1311     )
1312   }
1313 
funInterface_empty_shouldErrornull1314   @Test fun funInterface_empty_shouldError() {
1315     assertThrows<IllegalStateException> {
1316       TypeSpec.funInterfaceBuilder("Taco")
1317         .build()
1318     }.hasMessageThat()
1319       .contains("Functional interfaces must have exactly one abstract function. Contained 0")
1320   }
1321 
funInterface_multipleAbstract_shouldErrornull1322   @Test fun funInterface_multipleAbstract_shouldError() {
1323     assertThrows<IllegalStateException> {
1324       TypeSpec.funInterfaceBuilder("Taco")
1325         .addFunction(
1326           FunSpec.builder("fun1")
1327             .addModifiers(ABSTRACT)
1328             .build(),
1329         )
1330         .addFunction(
1331           FunSpec.builder("fun2")
1332             .addModifiers(ABSTRACT)
1333             .build(),
1334         )
1335         .build()
1336     }.hasMessageThat()
1337       .contains("Functional interfaces must have exactly one abstract function. Contained 2")
1338   }
1339 
nestedClassesnull1340   @Test fun nestedClasses() {
1341     val taco = ClassName(tacosPackage, "Combo", "Taco")
1342     val topping = ClassName(tacosPackage, "Combo", "Taco", "Topping")
1343     val chips = ClassName(tacosPackage, "Combo", "Chips")
1344     val sauce = ClassName(tacosPackage, "Combo", "Sauce")
1345     val typeSpec = TypeSpec.classBuilder("Combo")
1346       .addProperty("taco", taco)
1347       .addProperty("chips", chips)
1348       .addType(
1349         TypeSpec.classBuilder(taco.simpleName)
1350           .addProperty("toppings", List::class.asClassName().parameterizedBy(topping))
1351           .addProperty("sauce", sauce)
1352           .addType(
1353             TypeSpec.enumBuilder(topping.simpleName)
1354               .addEnumConstant("SHREDDED_CHEESE")
1355               .addEnumConstant("LEAN_GROUND_BEEF")
1356               .build(),
1357           )
1358           .build(),
1359       )
1360       .addType(
1361         TypeSpec.classBuilder(chips.simpleName)
1362           .addProperty("topping", topping)
1363           .addProperty("dippingSauce", sauce)
1364           .build(),
1365       )
1366       .addType(
1367         TypeSpec.enumBuilder(sauce.simpleName)
1368           .addEnumConstant("SOUR_CREAM")
1369           .addEnumConstant("SALSA")
1370           .addEnumConstant("QUESO")
1371           .addEnumConstant("MILD")
1372           .addEnumConstant("FIRE")
1373           .build(),
1374       )
1375       .build()
1376 
1377     assertThat(toString(typeSpec)).isEqualTo(
1378       """
1379         |package com.squareup.tacos
1380         |
1381         |import kotlin.collections.List
1382         |
1383         |public class Combo {
1384         |  public val taco: Taco
1385         |
1386         |  public val chips: Chips
1387         |
1388         |  public class Taco {
1389         |    public val toppings: List<Topping>
1390         |
1391         |    public val sauce: Sauce
1392         |
1393         |    public enum class Topping {
1394         |      SHREDDED_CHEESE,
1395         |      LEAN_GROUND_BEEF,
1396         |    }
1397         |  }
1398         |
1399         |  public class Chips {
1400         |    public val topping: Taco.Topping
1401         |
1402         |    public val dippingSauce: Sauce
1403         |  }
1404         |
1405         |  public enum class Sauce {
1406         |    SOUR_CREAM,
1407         |    SALSA,
1408         |    QUESO,
1409         |    MILD,
1410         |    FIRE,
1411         |  }
1412         |}
1413         |
1414       """.trimMargin(),
1415     )
1416   }
1417 
annotationnull1418   @Test fun annotation() {
1419     val annotation = TypeSpec.annotationBuilder("MyAnnotation")
1420       .addModifiers(KModifier.PUBLIC)
1421       .primaryConstructor(
1422         FunSpec.constructorBuilder()
1423           .addParameter(
1424             ParameterSpec.builder("test", Int::class)
1425               .build(),
1426           )
1427           .build(),
1428       )
1429       .addProperty(
1430         PropertySpec.builder("test", Int::class)
1431           .initializer("test")
1432           .build(),
1433       )
1434       .build()
1435 
1436     assertThat(toString(annotation)).isEqualTo(
1437       """
1438         |package com.squareup.tacos
1439         |
1440         |import kotlin.Int
1441         |
1442         |public annotation class MyAnnotation(
1443         |  public val test: Int,
1444         |)
1445         |
1446       """.trimMargin(),
1447     )
1448   }
1449 
annotationWithNestedTypesnull1450   @Test fun annotationWithNestedTypes() {
1451     val annotationName = ClassName(tacosPackage, "TacoDelivery")
1452     val kindName = annotationName.nestedClass("Kind")
1453     val annotation = TypeSpec.annotationBuilder(annotationName)
1454       .addModifiers(PUBLIC)
1455       .primaryConstructor(
1456         FunSpec.constructorBuilder()
1457           .addParameter(
1458             ParameterSpec.builder("kind", kindName)
1459               .build(),
1460           )
1461           .addParameter(
1462             ParameterSpec.builder("quantity", Int::class)
1463               .defaultValue("QUANTITY_DEFAULT")
1464               .build(),
1465           )
1466           .build(),
1467       )
1468       .addProperty(
1469         PropertySpec.builder("kind", kindName)
1470           .initializer("kind")
1471           .build(),
1472       )
1473       .addProperty(
1474         PropertySpec.builder("quantity", Int::class)
1475           .initializer("quantity")
1476           .build(),
1477       )
1478       .addType(
1479         TypeSpec.enumBuilder("Kind")
1480           .addEnumConstant("SOFT")
1481           .addEnumConstant("HARD")
1482           .build(),
1483       )
1484       .addType(
1485         TypeSpec.companionObjectBuilder()
1486           .addProperty(
1487             PropertySpec
1488               .builder("QUANTITY_DEFAULT", Int::class, KModifier.CONST)
1489               .initializer("%L", 10_000)
1490               .build(),
1491           )
1492           .build(),
1493       )
1494       .build()
1495 
1496     assertThat(toString(annotation)).isEqualTo(
1497       """
1498         |package com.squareup.tacos
1499         |
1500         |import kotlin.Int
1501         |
1502         |public annotation class TacoDelivery(
1503         |  public val kind: Kind,
1504         |  public val quantity: Int = QUANTITY_DEFAULT,
1505         |) {
1506         |  public enum class Kind {
1507         |    SOFT,
1508         |    HARD,
1509         |  }
1510         |
1511         |  public companion object {
1512         |    public const val QUANTITY_DEFAULT: Int = 10_000
1513         |  }
1514         |}
1515         |
1516       """.trimMargin(),
1517     )
1518   }
1519 
1520   @Ignore @Test
innerAnnotationInAnnotationDeclarationnull1521   fun innerAnnotationInAnnotationDeclaration() {
1522     val bar = TypeSpec.annotationBuilder("Bar")
1523       .primaryConstructor(
1524         FunSpec.constructorBuilder()
1525           .addParameter(
1526             ParameterSpec.builder("value", java.lang.Deprecated::class)
1527               .build(),
1528           )
1529           .build(),
1530       )
1531       .addProperty(
1532         PropertySpec.builder("value", java.lang.Deprecated::class)
1533           .initializer("value")
1534           .build(),
1535       )
1536       .build()
1537 
1538     assertThat(toString(bar)).isEqualTo(
1539       """
1540         |package com.squareup.tacos
1541         |
1542         |import java.lang.Deprecated
1543         |
1544         |annotation class Bar() {
1545         |  fun value(): Deprecated default @Deprecated
1546         |}
1547         |
1548       """.trimMargin(),
1549     )
1550   }
1551 
interfaceWithPropertiesnull1552   @Test fun interfaceWithProperties() {
1553     val taco = TypeSpec.interfaceBuilder("Taco")
1554       .addProperty("v", Int::class)
1555       .build()
1556 
1557     assertThat(toString(taco)).isEqualTo(
1558       """
1559         |package com.squareup.tacos
1560         |
1561         |import kotlin.Int
1562         |
1563         |public interface Taco {
1564         |  public val v: Int
1565         |}
1566         |
1567       """.trimMargin(),
1568     )
1569   }
1570 
expectClassnull1571   @Test fun expectClass() {
1572     val classA = TypeSpec.classBuilder("ClassA")
1573       .addModifiers(KModifier.EXPECT)
1574       .addFunction(
1575         FunSpec.builder("test")
1576           .build(),
1577       )
1578       .build()
1579 
1580     assertThat(classA.toString()).isEqualTo(
1581       """
1582       |public expect class ClassA {
1583       |  public fun test()
1584       |}
1585       |
1586       """.trimMargin(),
1587     )
1588   }
1589 
nestedExpectCompanionObjectWithFunctionnull1590   @Test fun nestedExpectCompanionObjectWithFunction() {
1591     val classA = TypeSpec.classBuilder("ClassA")
1592       .addModifiers(KModifier.EXPECT)
1593       .addType(
1594         TypeSpec.companionObjectBuilder()
1595           .addFunction(
1596             FunSpec.builder("test")
1597               .build(),
1598           )
1599           .build(),
1600       )
1601       .build()
1602 
1603     assertThat(classA.toString()).isEqualTo(
1604       """
1605       |public expect class ClassA {
1606       |  public companion object {
1607       |    public fun test()
1608       |  }
1609       |}
1610       |
1611       """.trimMargin(),
1612     )
1613   }
1614 
nestedExpectClassWithFunctionnull1615   @Test fun nestedExpectClassWithFunction() {
1616     val classA = TypeSpec.classBuilder("ClassA")
1617       .addModifiers(KModifier.EXPECT)
1618       .addType(
1619         TypeSpec.classBuilder("ClassB")
1620           .addFunction(
1621             FunSpec.builder("test")
1622               .build(),
1623           )
1624           .build(),
1625       )
1626       .build()
1627 
1628     assertThat(classA.toString()).isEqualTo(
1629       """
1630       |public expect class ClassA {
1631       |  public class ClassB {
1632       |    public fun test()
1633       |  }
1634       |}
1635       |
1636       """.trimMargin(),
1637     )
1638   }
1639 
deeplyNestedExpectClassWithFunctionnull1640   @Test fun deeplyNestedExpectClassWithFunction() {
1641     val classA = TypeSpec.classBuilder("ClassA")
1642       .addModifiers(KModifier.EXPECT)
1643       .addType(
1644         TypeSpec.classBuilder("ClassB")
1645           .addType(
1646             TypeSpec.classBuilder("ClassC")
1647               .addFunction(
1648                 FunSpec.builder("test")
1649                   .build(),
1650               )
1651               .build(),
1652           )
1653           .build(),
1654       )
1655       .build()
1656 
1657     assertThat(classA.toString()).isEqualTo(
1658       """
1659       |public expect class ClassA {
1660       |  public class ClassB {
1661       |    public class ClassC {
1662       |      public fun test()
1663       |    }
1664       |  }
1665       |}
1666       |
1667       """.trimMargin(),
1668     )
1669   }
1670 
veryDeeplyNestedExpectClassWithFunctionnull1671   @Test fun veryDeeplyNestedExpectClassWithFunction() {
1672     val classA = TypeSpec.classBuilder("ClassA")
1673       .addModifiers(KModifier.EXPECT)
1674       .addType(
1675         TypeSpec.classBuilder("ClassB")
1676           .addType(
1677             TypeSpec.classBuilder("ClassC")
1678               .addType(
1679                 TypeSpec.classBuilder("ClassD")
1680                   .addFunction(
1681                     FunSpec.builder("test")
1682                       .build(),
1683                   )
1684                   .build(),
1685               )
1686               .build(),
1687           )
1688           .build(),
1689       )
1690       .build()
1691 
1692     assertThat(classA.toString()).isEqualTo(
1693       """
1694       |public expect class ClassA {
1695       |  public class ClassB {
1696       |    public class ClassC {
1697       |      public class ClassD {
1698       |        public fun test()
1699       |      }
1700       |    }
1701       |  }
1702       |}
1703       |
1704       """.trimMargin(),
1705     )
1706   }
1707 
deeplyNestedExpectClassWithConstructornull1708   @Test fun deeplyNestedExpectClassWithConstructor() {
1709     val classA = TypeSpec.classBuilder("ClassA")
1710       .addModifiers(KModifier.EXPECT)
1711       .addType(
1712         TypeSpec.classBuilder("ClassB")
1713           .addType(
1714             TypeSpec.classBuilder("ClassC")
1715               .addFunction(
1716                 FunSpec.constructorBuilder()
1717                   .build(),
1718               )
1719               .build(),
1720           )
1721           .build(),
1722       )
1723       .build()
1724 
1725     assertThat(classA.toString()).isEqualTo(
1726       """
1727       |public expect class ClassA {
1728       |  public class ClassB {
1729       |    public class ClassC {
1730       |      public constructor()
1731       |    }
1732       |  }
1733       |}
1734       |
1735       """.trimMargin(),
1736     )
1737   }
1738 
veryDeeplyNestedExpectClassWithConstructornull1739   @Test fun veryDeeplyNestedExpectClassWithConstructor() {
1740     val classA = TypeSpec.classBuilder("ClassA")
1741       .addModifiers(KModifier.EXPECT)
1742       .addType(
1743         TypeSpec.classBuilder("ClassB")
1744           .addType(
1745             TypeSpec.classBuilder("ClassC")
1746               .addType(
1747                 TypeSpec.classBuilder("ClassD")
1748                   .addFunction(
1749                     FunSpec.constructorBuilder()
1750                       .build(),
1751                   )
1752                   .build(),
1753               )
1754               .build(),
1755           )
1756           .build(),
1757       )
1758       .build()
1759 
1760     assertThat(classA.toString()).isEqualTo(
1761       """
1762       |public expect class ClassA {
1763       |  public class ClassB {
1764       |    public class ClassC {
1765       |      public class ClassD {
1766       |        public constructor()
1767       |      }
1768       |    }
1769       |  }
1770       |}
1771       |
1772       """.trimMargin(),
1773     )
1774   }
1775 
interfaceWithMethodsnull1776   @Test fun interfaceWithMethods() {
1777     val taco = TypeSpec.interfaceBuilder("Taco")
1778       .addFunction(
1779         FunSpec.builder("aMethod")
1780           .addModifiers(KModifier.ABSTRACT)
1781           .build(),
1782       )
1783       .addFunction(FunSpec.builder("aDefaultMethod").build())
1784       .addFunction(
1785         FunSpec.builder("aPrivateMethod")
1786           .addModifiers(KModifier.PRIVATE)
1787           .build(),
1788       )
1789       .build()
1790 
1791     assertThat(toString(taco)).isEqualTo(
1792       """
1793         |package com.squareup.tacos
1794         |
1795         |public interface Taco {
1796         |  public fun aMethod()
1797         |
1798         |  public fun aDefaultMethod() {
1799         |  }
1800         |
1801         |  private fun aPrivateMethod() {
1802         |  }
1803         |}
1804         |
1805       """.trimMargin(),
1806     )
1807   }
1808 
referencedAndDeclaredSimpleNamesConflictnull1809   @Test fun referencedAndDeclaredSimpleNamesConflict() {
1810     val internalTop = PropertySpec.builder(
1811       "internalTop",
1812       ClassName(tacosPackage, "Top"),
1813     ).build()
1814     val internalBottom = PropertySpec.builder(
1815       "internalBottom",
1816       ClassName(tacosPackage, "Top", "Middle", "Bottom"),
1817     ).build()
1818     val externalTop = PropertySpec.builder(
1819       "externalTop",
1820       ClassName(donutsPackage, "Top"),
1821     ).build()
1822     val externalBottom = PropertySpec.builder(
1823       "externalBottom",
1824       ClassName(donutsPackage, "Bottom"),
1825     ).build()
1826     val top = TypeSpec.classBuilder("Top")
1827       .addProperty(internalTop)
1828       .addProperty(internalBottom)
1829       .addProperty(externalTop)
1830       .addProperty(externalBottom)
1831       .addType(
1832         TypeSpec.classBuilder("Middle")
1833           .addProperty(internalTop)
1834           .addProperty(internalBottom)
1835           .addProperty(externalTop)
1836           .addProperty(externalBottom)
1837           .addType(
1838             TypeSpec.classBuilder("Bottom")
1839               .addProperty(internalTop)
1840               .addProperty(internalBottom)
1841               .addProperty(externalTop)
1842               .addProperty(externalBottom)
1843               .build(),
1844           )
1845           .build(),
1846       )
1847       .build()
1848     assertThat(toString(top)).isEqualTo(
1849       """
1850         |package com.squareup.tacos
1851         |
1852         |import com.squareup.donuts.Bottom
1853         |
1854         |public class Top {
1855         |  public val internalTop: Top
1856         |
1857         |  public val internalBottom: Middle.Bottom
1858         |
1859         |  public val externalTop: com.squareup.donuts.Top
1860         |
1861         |  public val externalBottom: Bottom
1862         |
1863         |  public class Middle {
1864         |    public val internalTop: Top
1865         |
1866         |    public val internalBottom: Bottom
1867         |
1868         |    public val externalTop: com.squareup.donuts.Top
1869         |
1870         |    public val externalBottom: com.squareup.donuts.Bottom
1871         |
1872         |    public class Bottom {
1873         |      public val internalTop: Top
1874         |
1875         |      public val internalBottom: Bottom
1876         |
1877         |      public val externalTop: com.squareup.donuts.Top
1878         |
1879         |      public val externalBottom: com.squareup.donuts.Bottom
1880         |    }
1881         |  }
1882         |}
1883         |
1884       """.trimMargin(),
1885     )
1886   }
1887 
simpleNamesConflictInThisAndOtherPackagenull1888   @Test fun simpleNamesConflictInThisAndOtherPackage() {
1889     val internalOther = PropertySpec.builder(
1890       "internalOther",
1891       ClassName(tacosPackage, "Other"),
1892     ).build()
1893     val externalOther = PropertySpec.builder(
1894       "externalOther",
1895       ClassName(donutsPackage, "Other"),
1896     ).build()
1897     val gen = TypeSpec.classBuilder("Gen")
1898       .addProperty(internalOther)
1899       .addProperty(externalOther)
1900       .build()
1901     assertThat(toString(gen)).isEqualTo(
1902       """
1903         |package com.squareup.tacos
1904         |
1905         |public class Gen {
1906         |  public val internalOther: Other
1907         |
1908         |  public val externalOther: com.squareup.donuts.Other
1909         |}
1910         |
1911       """.trimMargin(),
1912     )
1913   }
1914 
intersectionTypenull1915   @Test fun intersectionType() {
1916     val typeVariable = TypeVariableName("T", Comparator::class, Serializable::class)
1917     val taco = TypeSpec.classBuilder("Taco")
1918       .addFunction(
1919         FunSpec.builder("getComparator")
1920           .addTypeVariable(typeVariable)
1921           .returns(typeVariable)
1922           .addStatement("return null")
1923           .build(),
1924       )
1925       .build()
1926     assertThat(toString(taco)).isEqualTo(
1927       """
1928         |package com.squareup.tacos
1929         |
1930         |import java.io.Serializable
1931         |import java.util.Comparator
1932         |
1933         |public class Taco {
1934         |  public fun <T> getComparator(): T where T : Comparator, T : Serializable = null
1935         |}
1936         |
1937       """.trimMargin(),
1938     )
1939   }
1940 
primitiveArrayTypenull1941   @Test fun primitiveArrayType() {
1942     val taco = TypeSpec.classBuilder("Taco")
1943       .addProperty("ints", IntArray::class)
1944       .build()
1945     assertThat(toString(taco)).isEqualTo(
1946       """
1947         |package com.squareup.tacos
1948         |
1949         |import kotlin.IntArray
1950         |
1951         |public class Taco {
1952         |  public val ints: IntArray
1953         |}
1954         |
1955       """.trimMargin(),
1956     )
1957   }
1958 
kdocnull1959   @Test fun kdoc() {
1960     val taco = TypeSpec.classBuilder("Taco")
1961       .addKdoc("A hard or soft tortilla, loosely folded and filled with whatever\n")
1962       .addKdoc("[random][%T] tex-mex stuff we could find in the pantry\n", Random::class)
1963       .addKdoc(CodeBlock.of("and some [%T] cheese.\n", String::class))
1964       .addProperty(
1965         PropertySpec.builder("soft", Boolean::class)
1966           .addKdoc("True for a soft flour tortilla; false for a crunchy corn tortilla.\n")
1967           .build(),
1968       )
1969       .addFunction(
1970         FunSpec.builder("refold")
1971           .addKdoc(
1972             "Folds the back of this taco to reduce sauce leakage.\n" +
1973               "\n" +
1974               "For [%T#KOREAN], the front may also be folded.\n",
1975             Locale::class,
1976           )
1977           .addParameter("locale", Locale::class)
1978           .build(),
1979       )
1980       .build()
1981     // Mentioning a type in KDoc will not cause an import to be added (java.util.Random here), but
1982     // the short name will be used if it's already imported (java.util.Locale here).
1983     assertThat(toString(taco)).isEqualTo(
1984       """
1985         |package com.squareup.tacos
1986         |
1987         |import java.util.Locale
1988         |import kotlin.Boolean
1989         |
1990         |/**
1991         | * A hard or soft tortilla, loosely folded and filled with whatever
1992         | * [random][java.util.Random] tex-mex stuff we could find in the pantry
1993         | * and some [kotlin.String] cheese.
1994         | */
1995         |public class Taco {
1996         |  /**
1997         |   * True for a soft flour tortilla; false for a crunchy corn tortilla.
1998         |   */
1999         |  public val soft: Boolean
2000         |
2001         |  /**
2002         |   * Folds the back of this taco to reduce sauce leakage.
2003         |   *
2004         |   * For [Locale#KOREAN], the front may also be folded.
2005         |   */
2006         |  public fun refold(locale: Locale) {
2007         |  }
2008         |}
2009         |
2010       """.trimMargin(),
2011     )
2012   }
2013 
kdocWithParametersnull2014   @Test fun kdocWithParameters() {
2015     val taco = TypeSpec.classBuilder("Taco")
2016       .addKdoc("A hard or soft tortilla, loosely folded and filled with whatever\n")
2017       .addKdoc("[random][%T] tex-mex stuff we could find in the pantry\n", Random::class)
2018       .addKdoc(CodeBlock.of("and some [%T] cheese.\n", String::class))
2019       .primaryConstructor(
2020         FunSpec.constructorBuilder()
2021           .addParameter(
2022             ParameterSpec.builder("temperature", Double::class)
2023               .addKdoc(
2024                 CodeBlock.of(
2025                   "%L",
2026                   """
2027                     |Taco temperature. Can be as cold as the famous ice tacos from
2028                     |the Andes, or hot with lava-like cheese from the depths of
2029                     |the Ninth Circle.
2030                     |
2031                   """.trimMargin(),
2032                 ),
2033               )
2034               .build(),
2035           )
2036           .addParameter("soft", Boolean::class)
2037           .addParameter(
2038             ParameterSpec.builder("mild", Boolean::class)
2039               .addKdoc(CodeBlock.of("%L", "Whether the taco is mild (ew) or crunchy (ye).\n"))
2040               .build(),
2041           )
2042           .addParameter("nodoc", Int::class)
2043           .build(),
2044       )
2045       .addProperty(
2046         PropertySpec.builder("soft", Boolean::class)
2047           .addKdoc("True for a soft flour tortilla; false for a crunchy corn tortilla.\n")
2048           .initializer("soft")
2049           .build(),
2050       )
2051       .addProperty(
2052         PropertySpec.builder("mild", Boolean::class)
2053           .addKdoc("No one likes mild tacos.")
2054           .initializer("mild")
2055           .build(),
2056       )
2057       .addProperty(
2058         PropertySpec.builder("nodoc", Int::class, KModifier.PRIVATE)
2059           .initializer("nodoc")
2060           .build(),
2061       )
2062       .build()
2063     assertThat(toString(taco)).isEqualTo(
2064       """
2065         |package com.squareup.tacos
2066         |
2067         |import kotlin.Boolean
2068         |import kotlin.Double
2069         |import kotlin.Int
2070         |
2071         |/**
2072         | * A hard or soft tortilla, loosely folded and filled with whatever
2073         | * [random][java.util.Random] tex-mex stuff we could find in the pantry
2074         | * and some [kotlin.String] cheese.
2075         | *
2076         | * @param temperature Taco temperature. Can be as cold as the famous ice tacos from
2077         | * the Andes, or hot with lava-like cheese from the depths of
2078         | * the Ninth Circle.
2079         | * @param mild Whether the taco is mild (ew) or crunchy (ye).
2080         | */
2081         |public class Taco(
2082         |  temperature: Double,
2083         |  /**
2084         |   * True for a soft flour tortilla; false for a crunchy corn tortilla.
2085         |   */
2086         |  public val soft: Boolean,
2087         |  /**
2088         |   * No one likes mild tacos.
2089         |   */
2090         |  public val mild: Boolean,
2091         |  private val nodoc: Int,
2092         |)
2093         |
2094       """.trimMargin(),
2095     )
2096   }
2097 
annotationsInAnnotationsnull2098   @Test fun annotationsInAnnotations() {
2099     val beef = ClassName(tacosPackage, "Beef")
2100     val chicken = ClassName(tacosPackage, "Chicken")
2101     val option = ClassName(tacosPackage, "Option")
2102     val mealDeal = ClassName(tacosPackage, "MealDeal")
2103     val menu = TypeSpec.classBuilder("Menu")
2104       .addAnnotation(
2105         AnnotationSpec.builder(mealDeal)
2106           .addMember("%L = %L", "price", 500)
2107           .addMember(
2108             "%L = [%L, %L]",
2109             "options",
2110             AnnotationSpec.builder(option)
2111               .addMember("%S", "taco")
2112               .addMember("%T::class", beef)
2113               .build(),
2114             AnnotationSpec.builder(option)
2115               .addMember("%S", "quesadilla")
2116               .addMember("%T::class", chicken)
2117               .build(),
2118           )
2119           .build(),
2120       )
2121       .build()
2122     assertThat(toString(menu)).isEqualTo(
2123       """
2124         |package com.squareup.tacos
2125         |
2126         |@MealDeal(
2127         |  price = 500,
2128         |  options = [Option("taco", Beef::class), Option("quesadilla", Chicken::class)],
2129         |)
2130         |public class Menu
2131         |
2132       """.trimMargin(),
2133     )
2134   }
2135 
varargsnull2136   @Test fun varargs() {
2137     val taqueria = TypeSpec.classBuilder("Taqueria")
2138       .addFunction(
2139         FunSpec.builder("prepare")
2140           .addParameter("workers", Int::class)
2141           .addParameter("jobs", Runnable::class.asClassName(), VARARG)
2142           .build(),
2143       )
2144       .build()
2145     assertThat(toString(taqueria)).isEqualTo(
2146       """
2147         |package com.squareup.tacos
2148         |
2149         |import java.lang.Runnable
2150         |import kotlin.Int
2151         |
2152         |public class Taqueria {
2153         |  public fun prepare(workers: Int, vararg jobs: Runnable) {
2154         |  }
2155         |}
2156         |
2157       """.trimMargin(),
2158     )
2159   }
2160 
varargsNotLastnull2161   @Test fun varargsNotLast() {
2162     val taqueria = TypeSpec.classBuilder("Taqueria")
2163       .addFunction(
2164         FunSpec.builder("prepare")
2165           .addParameter("workers", Int::class)
2166           .addParameter("jobs", Runnable::class.asClassName(), VARARG)
2167           .addParameter("start", Boolean::class.asClassName())
2168           .build(),
2169       )
2170       .build()
2171     assertThat(toString(taqueria)).isEqualTo(
2172       """
2173         |package com.squareup.tacos
2174         |
2175         |import java.lang.Runnable
2176         |import kotlin.Boolean
2177         |import kotlin.Int
2178         |
2179         |public class Taqueria {
2180         |  public fun prepare(
2181         |    workers: Int,
2182         |    vararg jobs: Runnable,
2183         |    start: Boolean,
2184         |  ) {
2185         |  }
2186         |}
2187         |
2188       """.trimMargin(),
2189     )
2190   }
2191 
codeBlocksnull2192   @Test fun codeBlocks() {
2193     val ifBlock = CodeBlock.builder()
2194       .beginControlFlow("if (a != b)")
2195       .addStatement("return i")
2196       .endControlFlow()
2197       .build()
2198     val funBody = CodeBlock.builder()
2199       .addStatement("val size = %T.min(listA.size, listB.size)", Math::class)
2200       .beginControlFlow("for (i in 0..<size)")
2201       .addStatement("val %N = %N[i]", "a", "listA")
2202       .addStatement("val %N = %N[i]", "b", "listB")
2203       .add("%L", ifBlock)
2204       .endControlFlow()
2205       .addStatement("return size")
2206       .build()
2207     val propertyBlock = CodeBlock.builder()
2208       .add("%T.<%T, %T>builder()", ImmutableMap::class, String::class, String::class)
2209       .add("\n.add(%S, %S)", '\'', "&#39;")
2210       .add("\n.add(%S, %S)", '&', "&amp;")
2211       .add("\n.add(%S, %S)", '<', "&lt;")
2212       .add("\n.add(%S, %S)", '>', "&gt;")
2213       .add("\n.build()")
2214       .build()
2215     val escapeHtml = PropertySpec.builder(
2216       "ESCAPE_HTML",
2217       Map::class.parameterizedBy(String::class, String::class),
2218     )
2219       .addModifiers(KModifier.PRIVATE)
2220       .initializer(propertyBlock)
2221       .build()
2222     val util = TypeSpec.classBuilder("Util")
2223       .addProperty(escapeHtml)
2224       .addFunction(
2225         FunSpec.builder("commonPrefixLength")
2226           .returns(Int::class)
2227           .addParameter("listA", List::class.parameterizedBy(String::class))
2228           .addParameter("listB", List::class.parameterizedBy(String::class))
2229           .addCode(funBody)
2230           .build(),
2231       )
2232       .build()
2233     assertThat(toString(util)).isEqualTo(
2234       """
2235         |package com.squareup.tacos
2236         |
2237         |import com.google.common.collect.ImmutableMap
2238         |import java.lang.Math
2239         |import kotlin.Int
2240         |import kotlin.String
2241         |import kotlin.collections.List
2242         |import kotlin.collections.Map
2243         |
2244         |public class Util {
2245         |  private val ESCAPE_HTML: Map<String, String> = ImmutableMap.<String, String>builder()
2246         |      .add("'", "&#39;")
2247         |      .add("&", "&amp;")
2248         |      .add("<", "&lt;")
2249         |      .add(">", "&gt;")
2250         |      .build()
2251         |
2252         |  public fun commonPrefixLength(listA: List<String>, listB: List<String>): Int {
2253         |    val size = Math.min(listA.size, listB.size)
2254         |    for (i in 0..<size) {
2255         |      val a = listA[i]
2256         |      val b = listB[i]
2257         |      if (a != b) {
2258         |        return i
2259         |      }
2260         |    }
2261         |    return size
2262         |  }
2263         |}
2264         |
2265       """.trimMargin(),
2266     )
2267   }
2268 
indexedElseIfnull2269   @Test fun indexedElseIf() {
2270     val taco = TypeSpec.classBuilder("Taco")
2271       .addFunction(
2272         FunSpec.builder("choices")
2273           .beginControlFlow("if (%1L != null || %1L == %2L)", "taco", "otherTaco")
2274           .addStatement("%T.out.println(%S)", System::class, "only one taco? NOO!")
2275           .nextControlFlow("else if (%1L.%3L && %2L.%3L)", "taco", "otherTaco", "isSupreme()")
2276           .addStatement("%T.out.println(%S)", System::class, "taco heaven")
2277           .endControlFlow()
2278           .build(),
2279       )
2280       .build()
2281     assertThat(toString(taco)).isEqualTo(
2282       """
2283         |package com.squareup.tacos
2284         |
2285         |import java.lang.System
2286         |
2287         |public class Taco {
2288         |  public fun choices() {
2289         |    if (taco != null || taco == otherTaco) {
2290         |      System.out.println("only one taco? NOO!")
2291         |    } else if (taco.isSupreme() && otherTaco.isSupreme()) {
2292         |      System.out.println("taco heaven")
2293         |    }
2294         |  }
2295         |}
2296         |
2297       """.trimMargin(),
2298     )
2299   }
2300 
elseIfnull2301   @Test fun elseIf() {
2302     val taco = TypeSpec.classBuilder("Taco")
2303       .addFunction(
2304         FunSpec.builder("choices")
2305           .beginControlFlow("if (5 < 4) ")
2306           .addStatement("%T.out.println(%S)", System::class, "wat")
2307           .nextControlFlow("else if (5 < 6)")
2308           .addStatement("%T.out.println(%S)", System::class, "hello")
2309           .endControlFlow()
2310           .build(),
2311       )
2312       .build()
2313     assertThat(toString(taco)).isEqualTo(
2314       """
2315         |package com.squareup.tacos
2316         |
2317         |import java.lang.System
2318         |
2319         |public class Taco {
2320         |  public fun choices() {
2321         |    if (5 < 4)  {
2322         |      System.out.println("wat")
2323         |    } else if (5 < 6) {
2324         |      System.out.println("hello")
2325         |    }
2326         |  }
2327         |}
2328         |
2329       """.trimMargin(),
2330     )
2331   }
2332 
inlineIndentnull2333   @Test fun inlineIndent() {
2334     val taco = TypeSpec.classBuilder("Taco")
2335       .addFunction(
2336         FunSpec.builder("inlineIndent")
2337           .addCode("if (3 < 4) {\n⇥%T.out.println(%S);\n⇤}\n", System::class, "hello")
2338           .build(),
2339       )
2340       .build()
2341     assertThat(toString(taco)).isEqualTo(
2342       """
2343         |package com.squareup.tacos
2344         |
2345         |import java.lang.System
2346         |
2347         |public class Taco {
2348         |  public fun inlineIndent() {
2349         |    if (3 < 4) {
2350         |      System.out.println("hello");
2351         |    }
2352         |  }
2353         |}
2354         |
2355       """.trimMargin(),
2356     )
2357   }
2358 
defaultModifiersForMemberInterfacesAndEnumsnull2359   @Test fun defaultModifiersForMemberInterfacesAndEnums() {
2360     val taco = TypeSpec.classBuilder("Taco")
2361       .addType(
2362         TypeSpec.classBuilder("Meat")
2363           .build(),
2364       )
2365       .addType(
2366         TypeSpec.interfaceBuilder("Tortilla")
2367           .build(),
2368       )
2369       .addType(
2370         TypeSpec.enumBuilder("Topping")
2371           .addEnumConstant("SALSA")
2372           .build(),
2373       )
2374       .build()
2375     assertThat(toString(taco)).isEqualTo(
2376       """
2377         |package com.squareup.tacos
2378         |
2379         |public class Taco {
2380         |  public class Meat
2381         |
2382         |  public interface Tortilla
2383         |
2384         |  public enum class Topping {
2385         |    SALSA,
2386         |  }
2387         |}
2388         |
2389       """.trimMargin(),
2390     )
2391   }
2392 
membersOrderingnull2393   @Test fun membersOrdering() {
2394     // Hand out names in reverse-alphabetical order to defend against unexpected sorting.
2395     val taco = TypeSpec.classBuilder("Members")
2396       .addType(TypeSpec.classBuilder("Z").build())
2397       .addType(TypeSpec.classBuilder("Y").build())
2398       .addProperty("W", String::class)
2399       .addProperty("U", String::class)
2400       .addFunction(FunSpec.builder("T").build())
2401       .addFunction(FunSpec.builder("S").build())
2402       .addFunction(FunSpec.builder("R").build())
2403       .addFunction(FunSpec.builder("Q").build())
2404       .addFunction(
2405         FunSpec.constructorBuilder()
2406           .addParameter("p", Int::class)
2407           .build(),
2408       )
2409       .addFunction(
2410         FunSpec.constructorBuilder()
2411           .addParameter("o", Long::class)
2412           .build(),
2413       )
2414       .build()
2415     // Static properties, instance properties, constructors, functions, classes.
2416     assertThat(toString(taco)).isEqualTo(
2417       """
2418         |package com.squareup.tacos
2419         |
2420         |import kotlin.Int
2421         |import kotlin.Long
2422         |import kotlin.String
2423         |
2424         |public class Members {
2425         |  public val W: String
2426         |
2427         |  public val U: String
2428         |
2429         |  public constructor(p: Int)
2430         |
2431         |  public constructor(o: Long)
2432         |
2433         |  public fun T() {
2434         |  }
2435         |
2436         |  public fun S() {
2437         |  }
2438         |
2439         |  public fun R() {
2440         |  }
2441         |
2442         |  public fun Q() {
2443         |  }
2444         |
2445         |  public class Z
2446         |
2447         |  public class Y
2448         |}
2449         |
2450       """.trimMargin(),
2451     )
2452   }
2453 
nativeFunctionsnull2454   @Test fun nativeFunctions() {
2455     val taco = TypeSpec.classBuilder("Taco")
2456       .addFunction(
2457         FunSpec.builder("nativeInt")
2458           .addModifiers(KModifier.EXTERNAL)
2459           .returns(Int::class)
2460           .build(),
2461       )
2462       .build()
2463     assertThat(toString(taco)).isEqualTo(
2464       """
2465         |package com.squareup.tacos
2466         |
2467         |import kotlin.Int
2468         |
2469         |public class Taco {
2470         |  public external fun nativeInt(): Int
2471         |}
2472         |
2473       """.trimMargin(),
2474     )
2475   }
2476 
nullStringLiteralnull2477   @Test fun nullStringLiteral() {
2478     val taco = TypeSpec.classBuilder("Taco")
2479       .addProperty(
2480         PropertySpec.builder("NULL", String::class)
2481           .initializer("%S", null)
2482           .build(),
2483       )
2484       .build()
2485     assertThat(toString(taco)).isEqualTo(
2486       """
2487         |package com.squareup.tacos
2488         |
2489         |import kotlin.String
2490         |
2491         |public class Taco {
2492         |  public val NULL: String = null
2493         |}
2494         |
2495       """.trimMargin(),
2496     )
2497   }
2498 
annotationToStringnull2499   @Test fun annotationToString() {
2500     val annotation = AnnotationSpec.builder(SuppressWarnings::class)
2501       .addMember("%S", "unused")
2502       .build()
2503     assertThat(annotation.toString()).isEqualTo("@java.lang.SuppressWarnings(\"unused\")")
2504   }
2505 
codeBlockToStringnull2506   @Test fun codeBlockToString() {
2507     val codeBlock = CodeBlock.builder()
2508       .addStatement("%T %N = %S.substring(0, 3)", String::class, "s", "taco")
2509       .build()
2510     assertThat(codeBlock.toString()).isEqualTo("kotlin.String s = \"taco\".substring(0, 3)\n")
2511   }
2512 
propertyToStringnull2513   @Test fun propertyToString() {
2514     val property = PropertySpec.builder("s", String::class)
2515       .initializer("%S.substring(0, 3)", "taco")
2516       .build()
2517     assertThat(property.toString())
2518       .isEqualTo("val s: kotlin.String = \"taco\".substring(0, 3)\n")
2519   }
2520 
functionToStringnull2521   @Test fun functionToString() {
2522     val funSpec = FunSpec.builder("toString")
2523       .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
2524       .returns(String::class)
2525       .addStatement("return %S", "taco")
2526       .build()
2527     assertThat(funSpec.toString())
2528       .isEqualTo("public override fun toString(): kotlin.String = \"taco\"\n")
2529   }
2530 
constructorToStringnull2531   @Test fun constructorToString() {
2532     val constructor = FunSpec.constructorBuilder()
2533       .addModifiers(KModifier.PUBLIC)
2534       .addParameter("taco", ClassName(tacosPackage, "Taco"))
2535       .addStatement("this.%N = %N", "taco", "taco")
2536       .build()
2537     assertThat(constructor.toString()).isEqualTo(
2538       "" +
2539         "public constructor(taco: com.squareup.tacos.Taco) {\n" +
2540         "  this.taco = taco\n" +
2541         "}\n",
2542     )
2543   }
2544 
parameterToStringnull2545   @Test fun parameterToString() {
2546     val parameter = ParameterSpec.builder("taco", ClassName(tacosPackage, "Taco"))
2547       .addModifiers(KModifier.CROSSINLINE)
2548       .addAnnotation(ClassName("javax.annotation", "Nullable"))
2549       .build()
2550     assertThat(parameter.toString())
2551       .isEqualTo("@javax.`annotation`.Nullable crossinline taco: com.squareup.tacos.Taco")
2552   }
2553 
classToStringnull2554   @Test fun classToString() {
2555     val type = TypeSpec.classBuilder("Taco")
2556       .build()
2557     assertThat(type.toString()).isEqualTo(
2558       "" +
2559         "public class Taco\n",
2560     )
2561   }
2562 
anonymousClassToStringnull2563   @Test fun anonymousClassToString() {
2564     val type = TypeSpec.anonymousClassBuilder()
2565       .addSuperinterface(Runnable::class)
2566       .addFunction(
2567         FunSpec.builder("run")
2568           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
2569           .build(),
2570       )
2571       .build()
2572     assertThat(type.toString()).isEqualTo(
2573       """
2574         |object : java.lang.Runnable {
2575         |  public override fun run() {
2576         |  }
2577         |}
2578       """.trimMargin(),
2579     )
2580   }
2581 
interfaceClassToStringnull2582   @Test fun interfaceClassToString() {
2583     val type = TypeSpec.interfaceBuilder("Taco")
2584       .build()
2585     assertThat(type.toString()).isEqualTo(
2586       """
2587         |public interface Taco
2588         |
2589       """.trimMargin(),
2590     )
2591   }
2592 
annotationDeclarationToStringnull2593   @Test fun annotationDeclarationToString() {
2594     val type = TypeSpec.annotationBuilder("Taco")
2595       .build()
2596     assertThat(type.toString()).isEqualTo(
2597       """
2598         |public annotation class Taco
2599         |
2600       """.trimMargin(),
2601     )
2602   }
2603 
toStringnull2604   private fun toString(typeSpec: TypeSpec): String {
2605     return FileSpec.get(tacosPackage, typeSpec).toString()
2606   }
2607 
multilineStatementnull2608   @Test fun multilineStatement() {
2609     val taco = TypeSpec.classBuilder("Taco")
2610       .addFunction(
2611         FunSpec.builder("toString")
2612           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
2613           .returns(String::class)
2614           .addStatement(
2615             "val result = %S\n+ %S\n+ %S\n+ %S\n+ %S",
2616             "Taco(",
2617             "beef,",
2618             "lettuce,",
2619             "cheese",
2620             ")",
2621           )
2622           .addStatement("return result")
2623           .build(),
2624       )
2625       .build()
2626     assertThat(toString(taco)).isEqualTo(
2627       """
2628         |package com.squareup.tacos
2629         |
2630         |import kotlin.String
2631         |
2632         |public class Taco {
2633         |  public override fun toString(): String {
2634         |    val result = "Taco("
2635         |        + "beef,"
2636         |        + "lettuce,"
2637         |        + "cheese"
2638         |        + ")"
2639         |    return result
2640         |  }
2641         |}
2642         |
2643       """.trimMargin(),
2644     )
2645   }
2646 
multilineStatementWithAnonymousClassnull2647   @Test fun multilineStatementWithAnonymousClass() {
2648     val stringComparator = Comparator::class.parameterizedBy(String::class)
2649     val listOfString = List::class.parameterizedBy(String::class)
2650     val prefixComparator = TypeSpec.anonymousClassBuilder()
2651       .addSuperinterface(stringComparator)
2652       .addFunction(
2653         FunSpec.builder("compare")
2654           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
2655           .returns(Int::class)
2656           .addParameter("a", String::class)
2657           .addParameter("b", String::class)
2658           .addComment("Prefix the strings and compare them")
2659           .addStatement("return a.substring(0, length)\n" + ".compareTo(b.substring(0, length))")
2660           .build(),
2661       )
2662       .build()
2663     val taco = TypeSpec.classBuilder("Taco")
2664       .addFunction(
2665         FunSpec.builder("comparePrefix")
2666           .returns(stringComparator)
2667           .addParameter("length", Int::class)
2668           .addComment("Return a new comparator for the target length.")
2669           .addStatement("return %L", prefixComparator)
2670           .build(),
2671       )
2672       .addFunction(
2673         FunSpec.builder("sortPrefix")
2674           .addParameter("list", listOfString)
2675           .addParameter("length", Int::class)
2676           .addStatement("%T.sort(\nlist,\n%L)", Collections::class, prefixComparator)
2677           .build(),
2678       )
2679       .build()
2680     assertThat(toString(taco)).isEqualTo(
2681       """
2682         |package com.squareup.tacos
2683         |
2684         |import java.util.Collections
2685         |import java.util.Comparator
2686         |import kotlin.Int
2687         |import kotlin.String
2688         |import kotlin.collections.List
2689         |
2690         |public class Taco {
2691         |  public fun comparePrefix(length: Int): Comparator<String> {
2692         |    // Return a new comparator for the target length.
2693         |    return object : Comparator<String> {
2694         |      public override fun compare(a: String, b: String): Int {
2695         |        // Prefix the strings and compare them
2696         |        return a.substring(0, length)
2697         |            .compareTo(b.substring(0, length))
2698         |      }
2699         |    }
2700         |  }
2701         |
2702         |  public fun sortPrefix(list: List<String>, length: Int) {
2703         |    Collections.sort(
2704         |        list,
2705         |        object : Comparator<String> {
2706         |          public override fun compare(a: String, b: String): Int {
2707         |            // Prefix the strings and compare them
2708         |            return a.substring(0, length)
2709         |                .compareTo(b.substring(0, length))
2710         |          }
2711         |        })
2712         |  }
2713         |}
2714         |
2715       """.trimMargin(),
2716     )
2717   }
2718 
multilineStringsnull2719   @Test fun multilineStrings() {
2720     val taco = TypeSpec.classBuilder("Taco")
2721       .addProperty(
2722         PropertySpec.builder("toppings", String::class)
2723           .initializer("%S", "shell\nbeef\nlettuce\ncheese\n")
2724           .build(),
2725       )
2726       .build()
2727     assertThat(toString(taco)).isEqualTo(
2728       """
2729         |package com.squareup.tacos
2730         |
2731         |import kotlin.String
2732         |
2733         |public class Taco {
2734         |  public val toppings: String = ${"\"\"\""}
2735         |      |shell
2736         |      |beef
2737         |      |lettuce
2738         |      |cheese
2739         |      ${"\"\"\""}.trimMargin()
2740         |}
2741         |
2742       """.trimMargin(),
2743     )
2744   }
2745 
multipleAnnotationAdditionnull2746   @Test fun multipleAnnotationAddition() {
2747     val taco = TypeSpec.classBuilder("Taco")
2748       .addAnnotations(
2749         listOf(
2750           AnnotationSpec.builder(SuppressWarnings::class)
2751             .addMember("%S", "unchecked")
2752             .build(),
2753           AnnotationSpec.builder(Deprecated::class).build(),
2754         ),
2755       )
2756       .build()
2757     assertThat(toString(taco)).isEqualTo(
2758       """
2759         |package com.squareup.tacos
2760         |
2761         |import java.lang.Deprecated
2762         |import java.lang.SuppressWarnings
2763         |
2764         |@SuppressWarnings("unchecked")
2765         |@Deprecated
2766         |public class Taco
2767         |
2768       """.trimMargin(),
2769     )
2770   }
2771 
multiplePropertyAdditionnull2772   @Test fun multiplePropertyAddition() {
2773     val taco = TypeSpec.classBuilder("Taco")
2774       .addProperties(
2775         listOf(
2776           PropertySpec.builder("ANSWER", Int::class, KModifier.CONST).build(),
2777           PropertySpec.builder("price", BigDecimal::class, PRIVATE).build(),
2778         ),
2779       )
2780       .build()
2781     assertThat(toString(taco)).isEqualTo(
2782       """
2783         |package com.squareup.tacos
2784         |
2785         |import java.math.BigDecimal
2786         |import kotlin.Int
2787         |
2788         |public class Taco {
2789         |  public const val ANSWER: Int
2790         |
2791         |  private val price: BigDecimal
2792         |}
2793         |
2794       """.trimMargin(),
2795     )
2796   }
2797 
multipleFunctionAdditionnull2798   @Test fun multipleFunctionAddition() {
2799     val taco = TypeSpec.classBuilder("Taco")
2800       .addFunctions(
2801         listOf(
2802           FunSpec.builder("getAnswer")
2803             .addModifiers(PUBLIC)
2804             .returns(Int::class)
2805             .addStatement("return %L", 42)
2806             .build(),
2807           FunSpec.builder("getRandomQuantity")
2808             .addModifiers(PUBLIC)
2809             .returns(Int::class)
2810             .addKdoc("chosen by fair dice roll ;)\n")
2811             .addStatement("return %L", 4)
2812             .build(),
2813         ),
2814       )
2815       .build()
2816     assertThat(toString(taco)).isEqualTo(
2817       """
2818         |package com.squareup.tacos
2819         |
2820         |import kotlin.Int
2821         |
2822         |public class Taco {
2823         |  public fun getAnswer(): Int = 42
2824         |
2825         |  /**
2826         |   * chosen by fair dice roll ;)
2827         |   */
2828         |  public fun getRandomQuantity(): Int = 4
2829         |}
2830         |
2831       """.trimMargin(),
2832     )
2833   }
2834 
multipleSuperinterfaceAdditionnull2835   @Test fun multipleSuperinterfaceAddition() {
2836     val taco = TypeSpec.classBuilder("Taco")
2837       .addSuperinterfaces(
2838         listOf(
2839           Serializable::class.asTypeName(),
2840           EventListener::class.asTypeName(),
2841         ),
2842       )
2843       .build()
2844     assertThat(toString(taco)).isEqualTo(
2845       """
2846         |package com.squareup.tacos
2847         |
2848         |import java.io.Serializable
2849         |import java.util.EventListener
2850         |
2851         |public class Taco : Serializable, EventListener
2852         |
2853       """.trimMargin(),
2854     )
2855   }
2856 
multipleTypeVariableAdditionnull2857   @Test fun multipleTypeVariableAddition() {
2858     val location = TypeSpec.classBuilder("Location")
2859       .addTypeVariables(
2860         listOf(
2861           TypeVariableName("T"),
2862           TypeVariableName("P", Number::class),
2863         ),
2864       )
2865       .build()
2866     assertThat(toString(location)).isEqualTo(
2867       """
2868         |package com.squareup.tacos
2869         |
2870         |import kotlin.Number
2871         |
2872         |public class Location<T, P : Number>
2873         |
2874       """.trimMargin(),
2875     )
2876   }
2877 
multipleTypeAdditionnull2878   @Test fun multipleTypeAddition() {
2879     val taco = TypeSpec.classBuilder("Taco")
2880       .addTypes(
2881         listOf(
2882           TypeSpec.classBuilder("Topping").build(),
2883           TypeSpec.classBuilder("Sauce").build(),
2884         ),
2885       )
2886       .build()
2887     assertThat(toString(taco)).isEqualTo(
2888       """
2889         |package com.squareup.tacos
2890         |
2891         |public class Taco {
2892         |  public class Topping
2893         |
2894         |  public class Sauce
2895         |}
2896         |
2897       """.trimMargin(),
2898     )
2899   }
2900 
tryCatchnull2901   @Test fun tryCatch() {
2902     val taco = TypeSpec.classBuilder("Taco")
2903       .addFunction(
2904         FunSpec.builder("addTopping")
2905           .addParameter("topping", ClassName("com.squareup.tacos", "Topping"))
2906           .beginControlFlow("try")
2907           .addCode("/* do something tricky with the topping */\n")
2908           .nextControlFlow(
2909             "catch (e: %T)",
2910             ClassName("com.squareup.tacos", "IllegalToppingException"),
2911           )
2912           .endControlFlow()
2913           .build(),
2914       )
2915       .build()
2916     assertThat(toString(taco)).isEqualTo(
2917       """
2918         |package com.squareup.tacos
2919         |
2920         |public class Taco {
2921         |  public fun addTopping(topping: Topping) {
2922         |    try {
2923         |      /* do something tricky with the topping */
2924         |    } catch (e: IllegalToppingException) {
2925         |    }
2926         |  }
2927         |}
2928         |
2929       """.trimMargin(),
2930     )
2931   }
2932 
ifElsenull2933   @Test fun ifElse() {
2934     val taco = TypeSpec.classBuilder("Taco")
2935       .addFunction(
2936         FunSpec.builder("isDelicious")
2937           .addParameter("count", INT)
2938           .returns(BOOLEAN)
2939           .beginControlFlow("if (count > 0)")
2940           .addStatement("return true")
2941           .nextControlFlow("else")
2942           .addStatement("return false")
2943           .endControlFlow()
2944           .build(),
2945       )
2946       .build()
2947     assertThat(toString(taco)).isEqualTo(
2948       """
2949         |package com.squareup.tacos
2950         |
2951         |import kotlin.Boolean
2952         |import kotlin.Int
2953         |
2954         |public class Taco {
2955         |  public fun isDelicious(count: Int): Boolean {
2956         |    if (count > 0) {
2957         |      return true
2958         |    } else {
2959         |      return false
2960         |    }
2961         |  }
2962         |}
2963         |
2964       """.trimMargin(),
2965     )
2966   }
2967 
whenReturnnull2968   @Test fun whenReturn() {
2969     val taco = TypeSpec.classBuilder("Taco")
2970       .addFunction(
2971         FunSpec.builder("toppingPrice")
2972           .addParameter("topping", String::class)
2973           .returns(INT)
2974           .beginControlFlow("return when(topping)")
2975           .addStatement("%S -> 1", "beef")
2976           .addStatement("%S -> 2", "lettuce")
2977           .addStatement("%S -> 3", "cheese")
2978           .addStatement("else -> throw IllegalToppingException(topping)")
2979           .endControlFlow()
2980           .build(),
2981       )
2982       .build()
2983     assertThat(toString(taco)).isEqualTo(
2984       """
2985         |package com.squareup.tacos
2986         |
2987         |import kotlin.Int
2988         |import kotlin.String
2989         |
2990         |public class Taco {
2991         |  public fun toppingPrice(topping: String): Int = when(topping) {
2992         |    "beef" -> 1
2993         |    "lettuce" -> 2
2994         |    "cheese" -> 3
2995         |    else -> throw IllegalToppingException(topping)
2996         |  }
2997         |}
2998         |
2999       """.trimMargin(),
3000     )
3001   }
3002 
literalFromAnythingnull3003   @Test fun literalFromAnything() {
3004     val value = object : Any() {
3005       override fun toString(): String {
3006         return "foo"
3007       }
3008     }
3009     assertThat(CodeBlock.of("%L", value).toString()).isEqualTo("foo")
3010   }
3011 
nameFromCharSequencenull3012   @Test fun nameFromCharSequence() {
3013     assertThat(CodeBlock.of("%N", "text").toString()).isEqualTo("text")
3014   }
3015 
nameFromPropertynull3016   @Test fun nameFromProperty() {
3017     val property = PropertySpec.builder("property", String::class).build()
3018     assertThat(CodeBlock.of("%N", property).toString()).isEqualTo("`property`")
3019   }
3020 
nameFromParameternull3021   @Test fun nameFromParameter() {
3022     val parameter = ParameterSpec.builder("parameter", String::class).build()
3023     assertThat(CodeBlock.of("%N", parameter).toString()).isEqualTo("parameter")
3024   }
3025 
nameFromFunctionnull3026   @Test fun nameFromFunction() {
3027     val funSpec = FunSpec.builder("method")
3028       .addModifiers(KModifier.ABSTRACT)
3029       .returns(String::class)
3030       .build()
3031     assertThat(CodeBlock.of("%N", funSpec).toString()).isEqualTo("method")
3032   }
3033 
nameFromTypenull3034   @Test fun nameFromType() {
3035     val type = TypeSpec.classBuilder("Type").build()
3036     assertThat(CodeBlock.of("%N", type).toString()).isEqualTo("Type")
3037   }
3038 
nameFromUnsupportedTypenull3039   @Test fun nameFromUnsupportedType() {
3040     assertThrows<IllegalArgumentException> {
3041       CodeBlock.builder().add("%N", String::class)
3042     }.hasMessageThat().isEqualTo("expected name but was " + String::class)
3043   }
3044 
stringFromAnythingnull3045   @Test fun stringFromAnything() {
3046     val value = object : Any() {
3047       override fun toString(): String {
3048         return "foo"
3049       }
3050     }
3051     assertThat(CodeBlock.of("%S", value).toString()).isEqualTo("\"foo\"")
3052   }
3053 
stringFromNullnull3054   @Test fun stringFromNull() {
3055     assertThat(CodeBlock.of("%S", null).toString()).isEqualTo("null")
3056   }
3057 
typeFromTypeNamenull3058   @Test fun typeFromTypeName() {
3059     val typeName = String::class.asTypeName()
3060     assertThat(CodeBlock.of("%T", typeName).toString()).isEqualTo("kotlin.String")
3061   }
3062 
typeFromTypeMirrornull3063   @Test fun typeFromTypeMirror() {
3064     val mirror = getElement(String::class).asType()
3065     assertThat(CodeBlock.of("%T", mirror).toString()).isEqualTo("java.lang.String")
3066   }
3067 
typeFromTypeElementnull3068   @Test fun typeFromTypeElement() {
3069     val element = getElement(String::class)
3070     assertThat(CodeBlock.of("%T", element).toString()).isEqualTo("java.lang.String")
3071   }
3072 
typeFromReflectTypenull3073   @Test fun typeFromReflectType() {
3074     assertThat(CodeBlock.of("%T", String::class).toString()).isEqualTo("kotlin.String")
3075   }
3076 
typeFromUnsupportedTypenull3077   @Test fun typeFromUnsupportedType() {
3078     assertThrows<IllegalArgumentException> {
3079       CodeBlock.builder().add("%T", "kotlin.String")
3080     }.hasMessageThat().isEqualTo("expected type but was kotlin.String")
3081   }
3082 
tooFewArgumentsnull3083   @Test fun tooFewArguments() {
3084     assertThrows<IllegalArgumentException> {
3085       CodeBlock.builder().add("%S")
3086     }.hasMessageThat().isEqualTo("index 1 for '%S' not in range (received 0 arguments)")
3087   }
3088 
unusedArgumentsRelativenull3089   @Test fun unusedArgumentsRelative() {
3090     assertThrows<IllegalArgumentException> {
3091       CodeBlock.builder().add("%L %L", "a", "b", "c")
3092     }.hasMessageThat().isEqualTo("unused arguments: expected 2, received 3")
3093   }
3094 
unusedArgumentsIndexednull3095   @Test fun unusedArgumentsIndexed() {
3096     assertThrows<IllegalArgumentException> {
3097       CodeBlock.builder().add("%1L %2L", "a", "b", "c")
3098     }.hasMessageThat().isEqualTo("unused argument: %3")
3099 
3100     assertThrows<IllegalArgumentException> {
3101       CodeBlock.builder().add("%1L %1L %1L", "a", "b", "c")
3102     }.hasMessageThat().isEqualTo("unused arguments: %2, %3")
3103 
3104     assertThrows<IllegalArgumentException> {
3105       CodeBlock.builder().add("%3L %1L %3L %1L %3L", "a", "b", "c", "d")
3106     }.hasMessageThat().isEqualTo("unused arguments: %2, %4")
3107   }
3108 
superClassOnlyValidForClassesnull3109   @Test fun superClassOnlyValidForClasses() {
3110     assertThrows<IllegalStateException> {
3111       TypeSpec.annotationBuilder("A").superclass(Any::class.asClassName())
3112     }
3113 
3114     assertThrows<IllegalStateException> {
3115       TypeSpec.enumBuilder("E").superclass(Any::class.asClassName())
3116     }
3117 
3118     assertThrows<IllegalStateException> {
3119       TypeSpec.interfaceBuilder("I").superclass(Any::class.asClassName())
3120     }
3121   }
3122 
superClassConstructorParametersOnlyValidForClassesnull3123   @Test fun superClassConstructorParametersOnlyValidForClasses() {
3124     assertThrows<IllegalStateException> {
3125       TypeSpec.annotationBuilder("A").addSuperclassConstructorParameter("")
3126     }
3127 
3128     assertThrows<IllegalStateException> {
3129       TypeSpec.enumBuilder("E").addSuperclassConstructorParameter("")
3130     }
3131 
3132     assertThrows<IllegalStateException> {
3133       TypeSpec.interfaceBuilder("I").addSuperclassConstructorParameter("")
3134     }
3135   }
3136 
anonymousClassesCannotHaveModifiersOrTypeVariablenull3137   @Test fun anonymousClassesCannotHaveModifiersOrTypeVariable() {
3138     assertThrows<IllegalStateException> {
3139       TypeSpec.anonymousClassBuilder().addModifiers(PUBLIC)
3140     }
3141 
3142     assertThrows<IllegalStateException> {
3143       TypeSpec.anonymousClassBuilder().addTypeVariable(TypeVariableName("T")).build()
3144     }
3145 
3146     assertThrows<IllegalStateException> {
3147       TypeSpec.anonymousClassBuilder().addTypeVariables(listOf(TypeVariableName("T"))).build()
3148     }
3149   }
3150 
invalidSuperClassnull3151   @Test fun invalidSuperClass() {
3152     assertThrows<IllegalStateException> {
3153       TypeSpec.classBuilder("foo")
3154         .superclass(List::class)
3155         .superclass(Map::class)
3156     }
3157   }
3158 
staticCodeBlocknull3159   @Test fun staticCodeBlock() {
3160     val taco = TypeSpec.classBuilder("Taco")
3161       .addProperty("foo", String::class, KModifier.PRIVATE)
3162       .addProperty(
3163         PropertySpec.builder("FOO", String::class, KModifier.PRIVATE, KModifier.CONST)
3164           .initializer("%S", "FOO")
3165           .build(),
3166       )
3167       .addFunction(
3168         FunSpec.builder("toString")
3169           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
3170           .returns(String::class)
3171           .addStatement("return FOO")
3172           .build(),
3173       )
3174       .build()
3175     assertThat(toString(taco)).isEqualTo(
3176       """
3177         |package com.squareup.tacos
3178         |
3179         |import kotlin.String
3180         |
3181         |public class Taco {
3182         |  private val foo: String
3183         |
3184         |  private const val FOO: String = "FOO"
3185         |
3186         |  public override fun toString(): String = FOO
3187         |}
3188         |
3189       """.trimMargin(),
3190     )
3191   }
3192 
initializerBlockInRightPlacenull3193   @Test fun initializerBlockInRightPlace() {
3194     val taco = TypeSpec.classBuilder("Taco")
3195       .addProperty("foo", String::class, KModifier.PRIVATE)
3196       .addProperty(
3197         PropertySpec.builder("FOO", String::class, KModifier.PRIVATE, KModifier.CONST)
3198           .initializer("%S", "FOO")
3199           .build(),
3200       )
3201       .addFunction(FunSpec.constructorBuilder().build())
3202       .addFunction(
3203         FunSpec.builder("toString")
3204           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
3205           .returns(String::class)
3206           .addStatement("return FOO")
3207           .build(),
3208       )
3209       .addInitializerBlock(
3210         CodeBlock.builder()
3211           .addStatement("foo = %S", "FOO")
3212           .build(),
3213       )
3214       .build()
3215     assertThat(toString(taco)).isEqualTo(
3216       """
3217         |package com.squareup.tacos
3218         |
3219         |import kotlin.String
3220         |
3221         |public class Taco {
3222         |  private val foo: String
3223         |
3224         |  private const val FOO: String = "FOO"
3225         |
3226         |  init {
3227         |    foo = "FOO"
3228         |  }
3229         |
3230         |  public constructor()
3231         |
3232         |  public override fun toString(): String = FOO
3233         |}
3234         |
3235       """.trimMargin(),
3236     )
3237   }
3238 
initializersToBuildernull3239   @Test fun initializersToBuilder() {
3240     // Tests if toBuilder() contains instance initializers
3241     val taco = TypeSpec.classBuilder("Taco")
3242       .addProperty(PropertySpec.builder("foo", String::class, KModifier.PRIVATE).build())
3243       .addProperty(
3244         PropertySpec.builder("FOO", String::class, KModifier.PRIVATE, KModifier.CONST)
3245           .initializer("%S", "FOO")
3246           .build(),
3247       )
3248       .addFunction(FunSpec.constructorBuilder().build())
3249       .addFunction(
3250         FunSpec.builder("toString")
3251           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
3252           .returns(String::class)
3253           .addStatement("return FOO")
3254           .build(),
3255       )
3256       .build()
3257 
3258     val recreatedTaco = taco.toBuilder().build()
3259     assertThat(toString(taco)).isEqualTo(toString(recreatedTaco))
3260 
3261     val initializersAdded = taco.toBuilder()
3262       .addInitializerBlock(
3263         CodeBlock.builder()
3264           .addStatement("foo = %S", "instanceFoo")
3265           .build(),
3266       )
3267       .build()
3268 
3269     assertThat(toString(initializersAdded)).isEqualTo(
3270       """
3271         |package com.squareup.tacos
3272         |
3273         |import kotlin.String
3274         |
3275         |public class Taco {
3276         |  private val foo: String
3277         |
3278         |  private const val FOO: String = "FOO"
3279         |
3280         |  init {
3281         |    foo = "instanceFoo"
3282         |  }
3283         |
3284         |  public constructor()
3285         |
3286         |  public override fun toString(): String = FOO
3287         |}
3288         |
3289       """.trimMargin(),
3290     )
3291   }
3292 
generalToBuilderEqualityTestnull3293   @Test fun generalToBuilderEqualityTest() {
3294     val originatingElement = FakeElement()
3295     val comprehensiveTaco = TypeSpec.classBuilder("Taco")
3296       .addKdoc("SuperTaco")
3297       .addAnnotation(SuppressWarnings::class)
3298       .addModifiers(DATA)
3299       .addTypeVariable(TypeVariableName.of("State", listOf(ANY), IN).copy(reified = true))
3300       .addType(
3301         TypeSpec.companionObjectBuilder()
3302           .build(),
3303       )
3304       .addType(
3305         TypeSpec.classBuilder("InnerTaco")
3306           .addModifiers(INNER)
3307           .build(),
3308       )
3309       .primaryConstructor(
3310         FunSpec.constructorBuilder()
3311           .build(),
3312       )
3313       .superclass(ClassName("texmexfood", "TortillaBased"))
3314       .addSuperclassConstructorParameter("true")
3315       .addProperty(
3316         PropertySpec.builder("meat", ClassName("texmexfood", "Meat"))
3317           .build(),
3318       )
3319       .addFunction(
3320         FunSpec.builder("fold")
3321           .build(),
3322       )
3323       .addSuperinterface(ClassName("texmexfood", "Consumable"))
3324       .addOriginatingElement(originatingElement)
3325       .build()
3326 
3327     val newTaco = comprehensiveTaco.toBuilder().build()
3328     assertThat(newTaco).isEqualTo(comprehensiveTaco)
3329     assertThat(newTaco.originatingElements).containsExactly(originatingElement)
3330   }
3331 
generalEnumToBuilderEqualityTestnull3332   @Test fun generalEnumToBuilderEqualityTest() {
3333     val bestTexMexEnum = TypeSpec.enumBuilder("BestTexMex")
3334       .addEnumConstant("TACO")
3335       .addEnumConstant("BREAKFAST_TACO")
3336       .build()
3337 
3338     assertThat(bestTexMexEnum.toBuilder().build()).isEqualTo(bestTexMexEnum)
3339   }
3340 
generalInterfaceBuilderEqualityTestnull3341   @Test fun generalInterfaceBuilderEqualityTest() {
3342     val taco = TypeSpec.interfaceBuilder("Taco")
3343       .addProperty("isVegan", Boolean::class)
3344       .addSuperinterface(Runnable::class)
3345       .build()
3346     assertThat(taco.toBuilder().build()).isEqualTo(taco)
3347   }
3348 
generalAnnotationBuilderEqualityTestnull3349   @Test fun generalAnnotationBuilderEqualityTest() {
3350     val annotation = TypeSpec.annotationBuilder("MyAnnotation")
3351       .addModifiers(KModifier.PUBLIC)
3352       .primaryConstructor(
3353         FunSpec.constructorBuilder()
3354           .addParameter(
3355             ParameterSpec.builder("test", Int::class)
3356               .build(),
3357           )
3358           .build(),
3359       )
3360       .addProperty(
3361         PropertySpec.builder("test", Int::class)
3362           .initializer("test")
3363           .build(),
3364       )
3365       .build()
3366     assertThat(annotation.toBuilder().build()).isEqualTo(annotation)
3367   }
3368 
generalExpectClassBuilderEqualityTestnull3369   @Test fun generalExpectClassBuilderEqualityTest() {
3370     val expectSpec = TypeSpec.classBuilder("AtmoicRef")
3371       .addModifiers(KModifier.EXPECT, KModifier.INTERNAL)
3372       .primaryConstructor(
3373         FunSpec.constructorBuilder()
3374           .addParameter("value", Int::class)
3375           .build(),
3376       )
3377       .addProperty(PropertySpec.builder("value", Int::class).build())
3378       .addFunction(
3379         FunSpec.builder("get")
3380           .returns(Int::class)
3381           .build(),
3382       )
3383       .build()
3384     assertThat(expectSpec.toBuilder().build()).isEqualTo(expectSpec)
3385   }
3386 
generalObjectBuilderEqualityTestnull3387   @Test fun generalObjectBuilderEqualityTest() {
3388     val objectSpec = TypeSpec.objectBuilder("MyObject")
3389       .addModifiers(KModifier.PUBLIC)
3390       .addProperty("tacos", Int::class)
3391       .addInitializerBlock(CodeBlock.builder().build())
3392       .addFunction(
3393         FunSpec.builder("test")
3394           .addModifiers(KModifier.PUBLIC)
3395           .build(),
3396       )
3397       .build()
3398     assertThat(objectSpec.toBuilder().build()).isEqualTo(objectSpec)
3399   }
3400 
generalAnonymousClassBuilderEqualityTestnull3401   @Test fun generalAnonymousClassBuilderEqualityTest() {
3402     val anonObjectSpec = TypeSpec.anonymousClassBuilder()
3403       .addSuperinterface(Runnable::class)
3404       .addFunction(
3405         FunSpec.builder("run")
3406           .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
3407           .build(),
3408       )
3409       .build()
3410     assertThat(anonObjectSpec.toBuilder().build()).isEqualTo(anonObjectSpec)
3411   }
3412 
initializerBlockUnsupportedExceptionOnInterfacenull3413   @Test fun initializerBlockUnsupportedExceptionOnInterface() {
3414     val interfaceBuilder = TypeSpec.interfaceBuilder("Taco")
3415     assertThrows<IllegalStateException> {
3416       interfaceBuilder.addInitializerBlock(CodeBlock.builder().build())
3417     }
3418   }
3419 
initializerBlockUnsupportedExceptionOnAnnotationnull3420   @Test fun initializerBlockUnsupportedExceptionOnAnnotation() {
3421     val annotationBuilder = TypeSpec.annotationBuilder("Taco")
3422     assertThrows<IllegalStateException> {
3423       annotationBuilder.addInitializerBlock(CodeBlock.builder().build())
3424     }
3425   }
3426 
equalsAndHashCodenull3427   @Test fun equalsAndHashCode() {
3428     var a = TypeSpec.interfaceBuilder("taco").build()
3429     var b = TypeSpec.interfaceBuilder("taco").build()
3430     assertThat(a == b).isTrue()
3431     assertThat(a.hashCode()).isEqualTo(b.hashCode())
3432     a = TypeSpec.classBuilder("taco").build()
3433     b = TypeSpec.classBuilder("taco").build()
3434     assertThat(a == b).isTrue()
3435     assertThat(a.hashCode()).isEqualTo(b.hashCode())
3436     a = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build()
3437     b = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build()
3438     assertThat(a == b).isTrue()
3439     assertThat(a.hashCode()).isEqualTo(b.hashCode())
3440     a = TypeSpec.annotationBuilder("taco").build()
3441     b = TypeSpec.annotationBuilder("taco").build()
3442     assertThat(a == b).isTrue()
3443     assertThat(a.hashCode()).isEqualTo(b.hashCode())
3444   }
3445 
classNameFactoriesnull3446   @Test fun classNameFactories() {
3447     val className = ClassName("com.example", "Example")
3448     assertThat(TypeSpec.classBuilder(className).build().name).isEqualTo("Example")
3449     assertThat(TypeSpec.interfaceBuilder(className).build().name).isEqualTo("Example")
3450     assertThat(TypeSpec.enumBuilder(className).addEnumConstant("A").build().name).isEqualTo("Example")
3451     assertThat(TypeSpec.annotationBuilder(className).build().name).isEqualTo("Example")
3452   }
3453 
objectTypenull3454   @Test fun objectType() {
3455     val type = TypeSpec.objectBuilder("MyObject")
3456       .addModifiers(KModifier.PUBLIC)
3457       .addProperty("tacos", Int::class)
3458       .addInitializerBlock(CodeBlock.builder().build())
3459       .addFunction(
3460         FunSpec.builder("test")
3461           .addModifiers(KModifier.PUBLIC)
3462           .build(),
3463       )
3464       .build()
3465 
3466     assertThat(toString(type)).isEqualTo(
3467       """
3468         |package com.squareup.tacos
3469         |
3470         |import kotlin.Int
3471         |
3472         |public object MyObject {
3473         |  public val tacos: Int
3474         |
3475         |  init {
3476         |  }
3477         |
3478         |  public fun test() {
3479         |  }
3480         |}
3481         |
3482       """.trimMargin(),
3483     )
3484   }
3485 
objectClassWithSupertypenull3486   @Test fun objectClassWithSupertype() {
3487     val superclass = ClassName("com.squareup.wire", "Message")
3488     val type = TypeSpec.objectBuilder("MyObject")
3489       .addModifiers(KModifier.PUBLIC)
3490       .superclass(superclass)
3491       .addInitializerBlock(CodeBlock.builder().build())
3492       .addFunction(
3493         FunSpec.builder("test")
3494           .addModifiers(KModifier.PUBLIC)
3495           .build(),
3496       )
3497       .build()
3498 
3499     assertThat(toString(type)).isEqualTo(
3500       """
3501         |package com.squareup.tacos
3502         |
3503         |import com.squareup.wire.Message
3504         |
3505         |public object MyObject : Message() {
3506         |  init {
3507         |  }
3508         |
3509         |  public fun test() {
3510         |  }
3511         |}
3512         |
3513       """.trimMargin(),
3514     )
3515   }
3516 
companionObjectnull3517   @Test fun companionObject() {
3518     val companion = TypeSpec.companionObjectBuilder()
3519       .addProperty(
3520         PropertySpec.builder("tacos", Int::class)
3521           .initializer("%L", 42)
3522           .build(),
3523       )
3524       .addFunction(
3525         FunSpec.builder("test")
3526           .addModifiers(KModifier.PUBLIC)
3527           .build(),
3528       )
3529       .build()
3530 
3531     val type = TypeSpec.classBuilder("MyClass")
3532       .addModifiers(KModifier.PUBLIC)
3533       .addType(companion)
3534       .build()
3535 
3536     assertThat(toString(type)).isEqualTo(
3537       """
3538         |package com.squareup.tacos
3539         |
3540         |import kotlin.Int
3541         |
3542         |public class MyClass {
3543         |  public companion object {
3544         |    public val tacos: Int = 42
3545         |
3546         |    public fun test() {
3547         |    }
3548         |  }
3549         |}
3550         |
3551       """.trimMargin(),
3552     )
3553   }
3554 
companionObjectWithInitializernull3555   @Test fun companionObjectWithInitializer() {
3556     val companion = TypeSpec.companionObjectBuilder()
3557       .addProperty(
3558         PropertySpec.builder("tacos", Int::class)
3559           .mutable()
3560           .initializer("%L", 24)
3561           .build(),
3562       )
3563       .addInitializerBlock(
3564         CodeBlock.builder()
3565           .addStatement("tacos = %L", 42)
3566           .build(),
3567       )
3568       .build()
3569 
3570     val type = TypeSpec.classBuilder("MyClass")
3571       .addModifiers(KModifier.PUBLIC)
3572       .addType(companion)
3573       .build()
3574 
3575     assertThat(toString(type)).isEqualTo(
3576       """
3577         |package com.squareup.tacos
3578         |
3579         |import kotlin.Int
3580         |
3581         |public class MyClass {
3582         |  public companion object {
3583         |    public var tacos: Int = 24
3584         |
3585         |    init {
3586         |      tacos = 42
3587         |    }
3588         |  }
3589         |}
3590         |
3591       """.trimMargin(),
3592     )
3593   }
3594 
companionObjectWithNamenull3595   @Test fun companionObjectWithName() {
3596     val companion = TypeSpec.companionObjectBuilder("Factory")
3597       .addFunction(FunSpec.builder("tacos").build())
3598       .build()
3599 
3600     val type = TypeSpec.classBuilder("MyClass")
3601       .addType(companion)
3602       .build()
3603 
3604     assertThat(toString(type)).isEqualTo(
3605       """
3606         |package com.squareup.tacos
3607         |
3608         |public class MyClass {
3609         |  public companion object Factory {
3610         |    public fun tacos() {
3611         |    }
3612         |  }
3613         |}
3614         |
3615       """.trimMargin(),
3616     )
3617   }
3618 
companionObjectOnInterfacenull3619   @Test fun companionObjectOnInterface() {
3620     val companion = TypeSpec.companionObjectBuilder()
3621       .addFunction(
3622         FunSpec.builder("test")
3623           .addModifiers(KModifier.PUBLIC)
3624           .build(),
3625       )
3626       .build()
3627 
3628     val type = TypeSpec.interfaceBuilder("MyInterface")
3629       .addModifiers(KModifier.PUBLIC)
3630       .addType(companion)
3631       .build()
3632 
3633     assertThat(toString(type)).isEqualTo(
3634       """
3635         |package com.squareup.tacos
3636         |
3637         |public interface MyInterface {
3638         |  public companion object {
3639         |    public fun test() {
3640         |    }
3641         |  }
3642         |}
3643         |
3644       """.trimMargin(),
3645     )
3646   }
3647 
companionObjectOnEnumnull3648   @Test fun companionObjectOnEnum() {
3649     val companion = TypeSpec.companionObjectBuilder()
3650       .addFunction(
3651         FunSpec.builder("test")
3652           .addModifiers(KModifier.PUBLIC)
3653           .build(),
3654       )
3655       .build()
3656 
3657     val enumBuilder = TypeSpec.enumBuilder("MyEnum")
3658       .addEnumConstant("FOO")
3659       .addEnumConstant("BAR")
3660       .addModifiers(KModifier.PUBLIC)
3661       .addType(companion)
3662       .build()
3663 
3664     assertThat(toString(enumBuilder)).isEqualTo(
3665       """
3666         |package com.squareup.tacos
3667         |
3668         |public enum class MyEnum {
3669         |  FOO,
3670         |  BAR,
3671         |  ;
3672         |
3673         |  public companion object {
3674         |    public fun test() {
3675         |    }
3676         |  }
3677         |}
3678         |
3679       """.trimMargin(),
3680     )
3681   }
3682 
companionObjectOnObjectNotAllowednull3683   @Test fun companionObjectOnObjectNotAllowed() {
3684     val companion = TypeSpec.companionObjectBuilder()
3685       .addFunction(
3686         FunSpec.builder("test")
3687           .addModifiers(KModifier.PUBLIC)
3688           .build(),
3689       )
3690       .build()
3691 
3692     val objectBuilder = TypeSpec.objectBuilder("MyObject")
3693       .addModifiers(KModifier.PUBLIC)
3694       .addType(companion)
3695 
3696     assertThrows<IllegalArgumentException> {
3697       objectBuilder.build()
3698     }
3699   }
3700 
companionObjectSupernull3701   @Test fun companionObjectSuper() {
3702     val superclass = ClassName("com.squareup.wire", "Message")
3703     val companion = TypeSpec.companionObjectBuilder()
3704       .superclass(superclass)
3705       .addFunction(
3706         FunSpec.builder("test")
3707           .addModifiers(KModifier.PUBLIC)
3708           .build(),
3709       )
3710       .build()
3711 
3712     val type = TypeSpec.classBuilder("MyClass")
3713       .addModifiers(KModifier.PUBLIC)
3714       .addType(companion)
3715       .build()
3716 
3717     assertThat(toString(type)).isEqualTo(
3718       """
3719         |package com.squareup.tacos
3720         |
3721         |import com.squareup.wire.Message
3722         |
3723         |public class MyClass {
3724         |  public companion object : Message() {
3725         |    public fun test() {
3726         |    }
3727         |  }
3728         |}
3729         |
3730       """.trimMargin(),
3731     )
3732   }
3733 
propertyInPrimaryConstructornull3734   @Test fun propertyInPrimaryConstructor() {
3735     val type = TypeSpec.classBuilder("Taco")
3736       .primaryConstructor(
3737         FunSpec.constructorBuilder()
3738           .addParameter("a", Int::class)
3739           .addParameter("b", String::class)
3740           .build(),
3741       )
3742       .addProperty(
3743         PropertySpec.builder("a", Int::class)
3744           .initializer("a")
3745           .build(),
3746       )
3747       .addProperty(
3748         PropertySpec.builder("b", String::class)
3749           .initializer("b")
3750           .build(),
3751       )
3752       .build()
3753 
3754     assertThat(toString(type)).isEqualTo(
3755       """
3756         |package com.squareup.tacos
3757         |
3758         |import kotlin.Int
3759         |import kotlin.String
3760         |
3761         |public class Taco(
3762         |  public val a: Int,
3763         |  public val b: String,
3764         |)
3765         |
3766       """.trimMargin(),
3767     )
3768   }
3769 
propertyWithKdocInPrimaryConstructornull3770   @Test fun propertyWithKdocInPrimaryConstructor() {
3771     val type = TypeSpec.classBuilder("Taco")
3772       .primaryConstructor(
3773         FunSpec.constructorBuilder()
3774           .addParameter("a", Int::class)
3775           .addParameter("b", String::class)
3776           .build(),
3777       )
3778       .addProperty(
3779         PropertySpec.builder("a", Int::class)
3780           .initializer("a")
3781           .addKdoc("KDoc\n")
3782           .build(),
3783       )
3784       .addProperty(
3785         PropertySpec.builder("b", String::class)
3786           .initializer("b")
3787           .build(),
3788       )
3789       .build()
3790 
3791     assertThat(toString(type)).isEqualTo(
3792       """
3793         |package com.squareup.tacos
3794         |
3795         |import kotlin.Int
3796         |import kotlin.String
3797         |
3798         |public class Taco(
3799         |  /**
3800         |   * KDoc
3801         |   */
3802         |  public val a: Int,
3803         |  public val b: String,
3804         |)
3805         |
3806       """.trimMargin(),
3807     )
3808   }
3809 
annotatedConstructornull3810   @Test fun annotatedConstructor() {
3811     val injectAnnotation = ClassName("javax.inject", "Inject")
3812     val taco = TypeSpec.classBuilder("Taco")
3813       .primaryConstructor(
3814         FunSpec.constructorBuilder()
3815           .addAnnotation(AnnotationSpec.builder(injectAnnotation).build())
3816           .build(),
3817       )
3818       .build()
3819 
3820     assertThat(toString(taco)).isEqualTo(
3821       """
3822     |package com.squareup.tacos
3823     |
3824     |import javax.inject.Inject
3825     |
3826     |public class Taco @Inject constructor()
3827     |
3828       """.trimMargin(),
3829     )
3830   }
3831 
internalConstructornull3832   @Test fun internalConstructor() {
3833     val taco = TypeSpec.classBuilder("Taco")
3834       .primaryConstructor(
3835         FunSpec.constructorBuilder()
3836           .addModifiers(INTERNAL)
3837           .build(),
3838       )
3839       .build()
3840 
3841     assertThat(toString(taco)).isEqualTo(
3842       """
3843     |package com.squareup.tacos
3844     |
3845     |public class Taco internal constructor()
3846     |
3847       """.trimMargin(),
3848     )
3849   }
3850 
annotatedInternalConstructornull3851   @Test fun annotatedInternalConstructor() {
3852     val injectAnnotation = ClassName("javax.inject", "Inject")
3853     val taco = TypeSpec.classBuilder("Taco")
3854       .primaryConstructor(
3855         FunSpec.constructorBuilder()
3856           .addAnnotation(AnnotationSpec.builder(injectAnnotation).build())
3857           .addModifiers(INTERNAL)
3858           .build(),
3859       )
3860       .build()
3861 
3862     assertThat(toString(taco)).isEqualTo(
3863       """
3864     |package com.squareup.tacos
3865     |
3866     |import javax.inject.Inject
3867     |
3868     |public class Taco @Inject internal constructor()
3869     |
3870       """.trimMargin(),
3871     )
3872   }
3873 
multipleAnnotationsInternalConstructornull3874   @Test fun multipleAnnotationsInternalConstructor() {
3875     val injectAnnotation = ClassName("javax.inject", "Inject")
3876     val namedAnnotation = ClassName("javax.inject", "Named")
3877     val taco = TypeSpec.classBuilder("Taco")
3878       .primaryConstructor(
3879         FunSpec.constructorBuilder()
3880           .addAnnotation(AnnotationSpec.builder(injectAnnotation).build())
3881           .addAnnotation(AnnotationSpec.builder(namedAnnotation).build())
3882           .addModifiers(INTERNAL)
3883           .build(),
3884       )
3885       .build()
3886 
3887     assertThat(toString(taco)).isEqualTo(
3888       """
3889     |package com.squareup.tacos
3890     |
3891     |import javax.inject.Inject
3892     |import javax.inject.Named
3893     |
3894     |public class Taco @Inject @Named internal constructor()
3895     |
3896       """.trimMargin(),
3897     )
3898   }
3899 
importNonNullablePropertynull3900   @Test fun importNonNullableProperty() {
3901     val type = String::class.asTypeName()
3902     val taco = TypeSpec.classBuilder("Taco")
3903       .addProperty(
3904         PropertySpec.builder("taco", type.copy(nullable = false))
3905           .initializer("%S", "taco")
3906           .build(),
3907       )
3908       .addProperty(
3909         PropertySpec.builder("nullTaco", type.copy(nullable = true))
3910           .initializer("null")
3911           .build(),
3912       )
3913       .build()
3914 
3915     assertThat(toString(taco)).isEqualTo(
3916       """
3917         |package com.squareup.tacos
3918         |
3919         |import kotlin.String
3920         |
3921         |public class Taco {
3922         |  public val taco: String = "taco"
3923         |
3924         |  public val nullTaco: String? = null
3925         |}
3926         |
3927       """.trimMargin(),
3928     )
3929   }
3930 
superclassConstructorParamsnull3931   @Test fun superclassConstructorParams() {
3932     val taco = TypeSpec.classBuilder("Foo")
3933       .superclass(ClassName(tacosPackage, "Bar"))
3934       .addSuperclassConstructorParameter("%S", "foo")
3935       .addSuperclassConstructorParameter(CodeBlock.of("%L", 42))
3936       .build()
3937 
3938     assertThat(toString(taco)).isEqualTo(
3939       """
3940         |package com.squareup.tacos
3941         |
3942         |public class Foo : Bar("foo", 42)
3943         |
3944       """.trimMargin(),
3945     )
3946   }
3947 
superclassConstructorParamsForbiddenForAnnotationnull3948   @Test fun superclassConstructorParamsForbiddenForAnnotation() {
3949     assertThrows<IllegalStateException> {
3950       TypeSpec.annotationBuilder("Taco")
3951         .addSuperclassConstructorParameter("%S", "foo")
3952     }
3953   }
3954 
classExtendsNoPrimaryConstructornull3955   @Test fun classExtendsNoPrimaryConstructor() {
3956     val typeSpec = TypeSpec.classBuilder("IoException")
3957       .superclass(Exception::class)
3958       .addFunction(FunSpec.constructorBuilder().build())
3959       .build()
3960 
3961     assertThat(toString(typeSpec)).isEqualTo(
3962       """
3963       |package com.squareup.tacos
3964       |
3965       |import java.lang.Exception
3966       |
3967       |public class IoException : Exception {
3968       |  public constructor()
3969       |}
3970       |
3971       """.trimMargin(),
3972     )
3973   }
3974 
classExtendsNoPrimaryOrSecondaryConstructornull3975   @Test fun classExtendsNoPrimaryOrSecondaryConstructor() {
3976     val typeSpec = TypeSpec.classBuilder("IoException")
3977       .superclass(Exception::class)
3978       .build()
3979 
3980     assertThat(toString(typeSpec)).isEqualTo(
3981       """
3982       |package com.squareup.tacos
3983       |
3984       |import java.lang.Exception
3985       |
3986       |public class IoException : Exception()
3987       |
3988       """.trimMargin(),
3989     )
3990   }
3991 
classExtendsNoPrimaryConstructorButSuperclassParamsnull3992   @Test fun classExtendsNoPrimaryConstructorButSuperclassParams() {
3993     assertThrows<IllegalArgumentException> {
3994       TypeSpec.classBuilder("IoException")
3995         .superclass(Exception::class)
3996         .addSuperclassConstructorParameter("%S", "hey")
3997         .addFunction(FunSpec.constructorBuilder().build())
3998         .build()
3999     }.hasMessageThat().isEqualTo(
4000       "types without a primary constructor cannot specify secondary constructors and superclass constructor parameters",
4001     )
4002   }
4003 
constructorWithDefaultParamValuenull4004   @Test fun constructorWithDefaultParamValue() {
4005     val type = TypeSpec.classBuilder("Taco")
4006       .primaryConstructor(
4007         FunSpec.constructorBuilder()
4008           .addParameter(
4009             ParameterSpec.builder("a", Int::class)
4010               .defaultValue("1")
4011               .build(),
4012           )
4013           .addParameter(
4014             ParameterSpec
4015               .builder("b", String::class.asTypeName().copy(nullable = true))
4016               .defaultValue("null")
4017               .build(),
4018           )
4019           .build(),
4020       )
4021       .addProperty(
4022         PropertySpec.builder("a", Int::class)
4023           .initializer("a")
4024           .build(),
4025       )
4026       .addProperty(
4027         PropertySpec.builder("b", String::class.asTypeName().copy(nullable = true))
4028           .initializer("b")
4029           .build(),
4030       )
4031       .build()
4032 
4033     assertThat(toString(type)).isEqualTo(
4034       """
4035         |package com.squareup.tacos
4036         |
4037         |import kotlin.Int
4038         |import kotlin.String
4039         |
4040         |public class Taco(
4041         |  public val a: Int = 1,
4042         |  public val b: String? = null,
4043         |)
4044         |
4045       """.trimMargin(),
4046     )
4047   }
4048 
constructorDelegationnull4049   @Test fun constructorDelegation() {
4050     val type = TypeSpec.classBuilder("Taco")
4051       .primaryConstructor(
4052         FunSpec.constructorBuilder()
4053           .addParameter("a", String::class.asTypeName().copy(nullable = true))
4054           .addParameter("b", String::class.asTypeName().copy(nullable = true))
4055           .addParameter("c", String::class.asTypeName().copy(nullable = true))
4056           .build(),
4057       )
4058       .addProperty(
4059         PropertySpec.builder("a", String::class.asTypeName().copy(nullable = true))
4060           .initializer("a")
4061           .build(),
4062       )
4063       .addProperty(
4064         PropertySpec.builder("b", String::class.asTypeName().copy(nullable = true))
4065           .initializer("b")
4066           .build(),
4067       )
4068       .addProperty(
4069         PropertySpec.builder("c", String::class.asTypeName().copy(nullable = true))
4070           .initializer("c")
4071           .build(),
4072       )
4073       .addFunction(
4074         FunSpec.constructorBuilder()
4075           .addParameter("map", Map::class.parameterizedBy(String::class, String::class))
4076           .callThisConstructor(
4077             CodeBlock.of("map[%S]", "a"),
4078             CodeBlock.of("map[%S]", "b"),
4079             CodeBlock.of("map[%S]", "c"),
4080           )
4081           .build(),
4082       )
4083       .build()
4084 
4085     assertThat(toString(type)).isEqualTo(
4086       """
4087         |package com.squareup.tacos
4088         |
4089         |import kotlin.String
4090         |import kotlin.collections.Map
4091         |
4092         |public class Taco(
4093         |  public val a: String?,
4094         |  public val b: String?,
4095         |  public val c: String?,
4096         |) {
4097         |  public constructor(map: Map<String, String>) : this(map["a"], map["b"], map["c"])
4098         |}
4099         |
4100       """.trimMargin(),
4101     )
4102   }
4103 
internalFunForbiddenInInterfacenull4104   @Test fun internalFunForbiddenInInterface() {
4105     val type = TypeSpec.interfaceBuilder("ITaco")
4106 
4107     assertThrows<IllegalArgumentException> {
4108       type.addFunction(
4109         FunSpec.builder("eat")
4110           .addModifiers(ABSTRACT, INTERNAL)
4111           .build(),
4112       )
4113         .build()
4114     }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, INTERNAL] must contain none of [INTERNAL, PROTECTED]")
4115 
4116     assertThrows<IllegalArgumentException> {
4117       type.addFunctions(
4118         listOf(
4119           FunSpec.builder("eat")
4120             .addModifiers(ABSTRACT, INTERNAL)
4121             .build(),
4122         ),
4123       )
4124         .build()
4125     }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, INTERNAL] must contain none of [INTERNAL, PROTECTED]")
4126   }
4127 
privateAbstractFunForbiddenInInterfacenull4128   @Test fun privateAbstractFunForbiddenInInterface() {
4129     val type = TypeSpec.interfaceBuilder("ITaco")
4130 
4131     assertThrows<IllegalArgumentException> {
4132       type.addFunction(
4133         FunSpec.builder("eat")
4134           .addModifiers(ABSTRACT, PRIVATE)
4135           .build(),
4136       )
4137         .build()
4138     }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, PRIVATE] must contain none or only one of [ABSTRACT, PRIVATE]")
4139 
4140     assertThrows<IllegalArgumentException> {
4141       type.addFunctions(
4142         listOf(
4143           FunSpec.builder("eat")
4144             .addModifiers(ABSTRACT, PRIVATE)
4145             .build(),
4146         ),
4147       )
4148         .build()
4149     }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, PRIVATE] must contain none or only one of [ABSTRACT, PRIVATE]")
4150   }
4151 
internalConstructorForbiddenInAnnotationnull4152   @Test fun internalConstructorForbiddenInAnnotation() {
4153     val type = TypeSpec.annotationBuilder("Taco")
4154 
4155     assertThrows<IllegalArgumentException> {
4156       type.primaryConstructor(
4157         FunSpec.constructorBuilder()
4158           .addModifiers(INTERNAL)
4159           .build(),
4160       )
4161         .build()
4162     }.hasMessageThat().isEqualTo("modifiers [INTERNAL] must contain none of [INTERNAL, PROTECTED, PRIVATE, ABSTRACT]")
4163   }
4164 
4165   // https://github.com/square/kotlinpoet/issues/1557
memberFunForbiddenInAnnotationnull4166   @Test fun memberFunForbiddenInAnnotation() {
4167     val type = TypeSpec.annotationBuilder("Taco")
4168 
4169     assertThrows<IllegalArgumentException> {
4170       type.addFunction(
4171         FunSpec.builder("eat")
4172           .build(),
4173       )
4174         .build()
4175     }.hasMessageThat().isEqualTo("annotation class Taco cannot declare member function eat")
4176   }
4177 
4178   // https://github.com/square/kotlinpoet/issues/1557
secondaryConstructorForbiddenInAnnotationnull4179   @Test fun secondaryConstructorForbiddenInAnnotation() {
4180     val type = TypeSpec.annotationBuilder("Taco")
4181 
4182     assertThrows<IllegalArgumentException> {
4183       type.primaryConstructor(FunSpec.constructorBuilder().build())
4184         .addFunction(
4185           FunSpec.constructorBuilder()
4186             .addParameter("value", String::class)
4187             .build(),
4188         ).build()
4189     }.hasMessageThat().isEqualTo("annotation class Taco cannot declare member function constructor()")
4190   }
4191 
4192   // https://github.com/square/kotlinpoet/issues/1556
abstractFunForbiddenInObjectnull4193   @Test fun abstractFunForbiddenInObject() {
4194     val type = TypeSpec.objectBuilder("Taco")
4195 
4196     assertThrows<IllegalArgumentException> {
4197       type.addFunction(
4198         FunSpec.builder("eat")
4199           .addModifiers(ABSTRACT)
4200           .build(),
4201       )
4202         .build()
4203     }.hasMessageThat().isEqualTo("non-abstract type Taco cannot declare abstract function eat")
4204   }
4205 
classHeaderFormattingnull4206   @Test fun classHeaderFormatting() {
4207     val typeSpec = TypeSpec.classBuilder("Person")
4208       .addModifiers(DATA)
4209       .primaryConstructor(
4210         FunSpec.constructorBuilder()
4211           .addParameter("id", Int::class)
4212           .addParameter("name", String::class)
4213           .addParameter("surname", String::class)
4214           .build(),
4215       )
4216       .addProperty(
4217         PropertySpec.builder("id", Int::class, KModifier.OVERRIDE)
4218           .initializer("id")
4219           .build(),
4220       )
4221       .addProperty(
4222         PropertySpec.builder("name", String::class, KModifier.OVERRIDE)
4223           .initializer("name")
4224           .build(),
4225       )
4226       .addProperty(
4227         PropertySpec.builder("surname", String::class, KModifier.OVERRIDE)
4228           .initializer("surname")
4229           .build(),
4230       )
4231       .build()
4232 
4233     assertThat(toString(typeSpec)).isEqualTo(
4234       """
4235       |package com.squareup.tacos
4236       |
4237       |import kotlin.Int
4238       |import kotlin.String
4239       |
4240       |public data class Person(
4241       |  override val id: Int,
4242       |  override val name: String,
4243       |  override val surname: String,
4244       |)
4245       |
4246       """.trimMargin(),
4247     )
4248   }
4249 
4250   @Test
classHeaderAnnotationsnull4251   fun classHeaderAnnotations() {
4252     val idParameterSpec = ParameterSpec.builder("id", Int::class)
4253       .addAnnotation(ClassName("com.squareup.kotlinpoet", "Id"))
4254       .defaultValue("1")
4255       .build()
4256 
4257     val typeSpec = TypeSpec.classBuilder("Person")
4258       .addModifiers(DATA)
4259       .primaryConstructor(
4260         FunSpec.constructorBuilder()
4261           .addParameter(idParameterSpec)
4262           .build(),
4263       )
4264       .addProperty(
4265         PropertySpec.builder("id", Int::class)
4266           .addModifiers(PRIVATE)
4267           .initializer("id")
4268           .addAnnotation(ClassName("com.squareup.kotlinpoet", "OrderBy"))
4269           .build(),
4270       )
4271       .build()
4272 
4273     assertThat(toString(typeSpec)).isEqualTo(
4274       """
4275       |package com.squareup.tacos
4276       |
4277       |import com.squareup.kotlinpoet.Id
4278       |import com.squareup.kotlinpoet.OrderBy
4279       |import kotlin.Int
4280       |
4281       |public data class Person(
4282       |  @OrderBy
4283       |  @Id
4284       |  private val id: Int = 1,
4285       |)
4286       |
4287       """.trimMargin(),
4288     )
4289   }
4290 
literalPropertySpecnull4291   @Test fun literalPropertySpec() {
4292     val taco = TypeSpec.classBuilder("Taco")
4293       .addFunction(
4294         FunSpec.builder("shell")
4295           .addCode(
4296             CodeBlock.of(
4297               "%L",
4298               PropertySpec.builder("taco1", String::class.asTypeName())
4299                 .initializer("%S", "Taco!").build(),
4300             ),
4301           )
4302           .addCode(
4303             CodeBlock.of(
4304               "%L",
4305               PropertySpec.builder("taco2", String::class.asTypeName().copy(nullable = true))
4306                 .initializer("null")
4307                 .build(),
4308             ),
4309           )
4310           .addCode(
4311             CodeBlock.of(
4312               "%L",
4313               PropertySpec.builder("taco3", String::class.asTypeName(), KModifier.LATEINIT)
4314                 .mutable()
4315                 .build(),
4316             ),
4317           )
4318           .build(),
4319       )
4320       .build()
4321     assertThat(toString(taco)).isEqualTo(
4322       """
4323         |package com.squareup.tacos
4324         |
4325         |import kotlin.String
4326         |
4327         |public class Taco {
4328         |  public fun shell() {
4329         |    val taco1: String = "Taco!"
4330         |    val taco2: String? = null
4331         |    lateinit var taco3: String
4332         |  }
4333         |}
4334         |
4335       """.trimMargin(),
4336     )
4337   }
4338 
basicDelegateTestnull4339   @Test fun basicDelegateTest() {
4340     val type = TypeSpec.classBuilder("Guac")
4341       .primaryConstructor(
4342         FunSpec.constructorBuilder()
4343           .addParameter("somethingElse", String::class)
4344           .build(),
4345       )
4346       .addSuperinterface(
4347         Consumer::class.parameterizedBy(String::class),
4348         CodeBlock.of("({ println(it) })"),
4349       )
4350       .build()
4351 
4352     val expect = """
4353         |package com.squareup.tacos
4354         |
4355         |import java.util.function.Consumer
4356         |import kotlin.String
4357         |
4358         |public class Guac(
4359         |  somethingElse: String,
4360         |) : Consumer<String> by ({ println(it) })
4361         |
4362     """.trimMargin()
4363 
4364     assertThat(toString(type)).isEqualTo(expect)
4365   }
4366 
testDelegateOnObjectnull4367   @Test fun testDelegateOnObject() {
4368     val type = TypeSpec.objectBuilder("Guac")
4369       .addSuperinterface(
4370         Consumer::class.parameterizedBy(String::class),
4371         CodeBlock.of("({ println(it) })"),
4372       )
4373       .build()
4374 
4375     val expect = """
4376         |package com.squareup.tacos
4377         |
4378         |import java.util.function.Consumer
4379         |import kotlin.String
4380         |
4381         |public object Guac : Consumer<String> by ({ println(it) })
4382         |
4383     """.trimMargin()
4384 
4385     assertThat(toString(type)).isEqualTo(expect)
4386   }
4387 
testMultipleDelegatesnull4388   @Test fun testMultipleDelegates() {
4389     val type = TypeSpec.classBuilder("StringToInteger")
4390       .primaryConstructor(
4391         FunSpec.constructorBuilder()
4392           .build(),
4393       )
4394       .addSuperinterface(
4395         Function::class.parameterizedBy(String::class, Int::class),
4396         CodeBlock.of("Function ({ text -> text.toIntOrNull() ?: 0 })"),
4397       )
4398       .addSuperinterface(
4399         Runnable::class,
4400         CodeBlock.of("Runnable ({ %T.debug(\"Hello world\") })", Logger::class.asTypeName()),
4401       )
4402       .build()
4403 
4404     val expect = """
4405         |package com.squareup.tacos
4406         |
4407         |import java.lang.Runnable
4408         |import java.util.logging.Logger
4409         |import kotlin.Function
4410         |import kotlin.Int
4411         |import kotlin.String
4412         |
4413         |public class StringToInteger() : Function<String, Int> by Function ({ text -> text.toIntOrNull() ?:
4414         |    0 }), Runnable by Runnable ({ Logger.debug("Hello world") })
4415         |
4416     """.trimMargin()
4417 
4418     assertThat(toString(type)).isEqualTo(expect)
4419   }
4420 
testNoSuchParameterDelegatenull4421   @Test fun testNoSuchParameterDelegate() {
4422     assertThrows<IllegalArgumentException> {
4423       TypeSpec.classBuilder("Taco")
4424         .primaryConstructor(
4425           FunSpec.constructorBuilder()
4426             .addParameter("other", String::class)
4427             .build(),
4428         )
4429         .addSuperinterface(KFunction::class, "notOther")
4430         .build()
4431     }.hasMessageThat().isEqualTo("no such constructor parameter 'notOther' to delegate to for type 'Taco'")
4432   }
4433 
failAddParamDelegateWhenNullConstructornull4434   @Test fun failAddParamDelegateWhenNullConstructor() {
4435     assertThrows<IllegalArgumentException> {
4436       TypeSpec.classBuilder("Taco")
4437         .addSuperinterface(Runnable::class, "etc")
4438         .build()
4439     }.hasMessageThat().isEqualTo("delegating to constructor parameter requires not-null constructor")
4440   }
4441 
testAddedDelegateByParamNamenull4442   @Test fun testAddedDelegateByParamName() {
4443     val type = TypeSpec.classBuilder("Taco")
4444       .primaryConstructor(
4445         FunSpec.constructorBuilder()
4446           .addParameter("superString", Function::class)
4447           .build(),
4448       )
4449       .addSuperinterface(Function::class, "superString")
4450       .build()
4451 
4452     assertThat(toString(type)).isEqualTo(
4453       """
4454           |package com.squareup.tacos
4455           |
4456           |import kotlin.Function
4457           |
4458           |public class Taco(
4459           |  superString: Function,
4460           |) : Function by superString
4461           |
4462       """.trimMargin(),
4463     )
4464   }
4465 
failOnAddExistingDelegateTypenull4466   @Test fun failOnAddExistingDelegateType() {
4467     assertThrows<IllegalArgumentException> {
4468       TypeSpec.classBuilder("Taco")
4469         .primaryConstructor(
4470           FunSpec.constructorBuilder()
4471             .addParameter("superString", Function::class)
4472             .build(),
4473         )
4474         .addSuperinterface(Function::class, CodeBlock.of("{ print(Hello) }"))
4475         .addSuperinterface(Function::class, "superString")
4476         .build()
4477       fail()
4478     }.hasMessageThat().isEqualTo(
4479       "'Taco' can not delegate to kotlin.Function " +
4480         "by superString with existing declaration by { print(Hello) }",
4481     )
4482   }
4483 
testDelegateIfaceWithOtherParamTypeNamenull4484   @Test fun testDelegateIfaceWithOtherParamTypeName() {
4485     val entity = ClassName(tacosPackage, "Entity")
4486     val entityBuilder = ClassName(tacosPackage, "EntityBuilder")
4487     val type = TypeSpec.classBuilder("EntityBuilder")
4488       .primaryConstructor(
4489         FunSpec.constructorBuilder()
4490           .addParameter(
4491             ParameterSpec.builder(
4492               "argBuilder",
4493               ClassName(tacosPackage, "Payload")
4494                 .parameterizedBy(entityBuilder, entity),
4495             )
4496               .defaultValue("Payload.create()")
4497               .build(),
4498           )
4499           .build(),
4500       )
4501       .addSuperinterface(
4502         ClassName(tacosPackage, "TypeBuilder")
4503           .parameterizedBy(entityBuilder, entity),
4504         "argBuilder",
4505       )
4506       .build()
4507 
4508     assertThat(toString(type)).isEqualTo(
4509       """
4510           |package com.squareup.tacos
4511           |
4512           |public class EntityBuilder(
4513           |  argBuilder: Payload<EntityBuilder, Entity> = Payload.create(),
4514           |) : TypeBuilder<EntityBuilder, Entity> by argBuilder
4515           |
4516       """.trimMargin(),
4517     )
4518   }
4519 
externalClassFunctionHasNoBodynull4520   @Test fun externalClassFunctionHasNoBody() {
4521     val typeSpec = TypeSpec.classBuilder("Foo")
4522       .addModifiers(KModifier.EXTERNAL)
4523       .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build())
4524       .build()
4525 
4526     assertThat(toString(typeSpec)).isEqualTo(
4527       """
4528       |package com.squareup.tacos
4529       |
4530       |public external class Foo {
4531       |  public fun bar()
4532       |}
4533       |
4534       """.trimMargin(),
4535     )
4536   }
4537 
externalInterfaceWithMembersnull4538   @Test fun externalInterfaceWithMembers() {
4539     val typeSpec = TypeSpec.interfaceBuilder("Foo")
4540       .addModifiers(KModifier.EXTERNAL)
4541       .addProperty(PropertySpec.builder("baz", String::class).addModifiers(KModifier.EXTERNAL).build())
4542       .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build())
4543       .build()
4544 
4545     assertThat(toString(typeSpec)).isEqualTo(
4546       """
4547       |package com.squareup.tacos
4548       |
4549       |import kotlin.String
4550       |
4551       |public external interface Foo {
4552       |  public val baz: String
4553       |
4554       |  public fun bar()
4555       |}
4556       |
4557       """.trimMargin(),
4558     )
4559   }
4560 
externalObjectWithMembersnull4561   @Test fun externalObjectWithMembers() {
4562     val typeSpec = TypeSpec.objectBuilder("Foo")
4563       .addModifiers(KModifier.EXTERNAL)
4564       .addProperty(PropertySpec.builder("baz", String::class).addModifiers(KModifier.EXTERNAL).build())
4565       .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build())
4566       .build()
4567 
4568     assertThat(toString(typeSpec)).isEqualTo(
4569       """
4570       |package com.squareup.tacos
4571       |
4572       |import kotlin.String
4573       |
4574       |public external object Foo {
4575       |  public val baz: String
4576       |
4577       |  public fun bar()
4578       |}
4579       |
4580       """.trimMargin(),
4581     )
4582   }
4583 
externalClassWithNestedTypesnull4584   @Test fun externalClassWithNestedTypes() {
4585     val typeSpec = TypeSpec.classBuilder("Foo")
4586       .addModifiers(KModifier.EXTERNAL)
4587       .addType(
4588         TypeSpec.classBuilder("Nested1")
4589           .addModifiers(KModifier.EXTERNAL)
4590           .addType(
4591             TypeSpec.objectBuilder("Nested2")
4592               .addModifiers(KModifier.EXTERNAL)
4593               .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build())
4594               .build(),
4595           )
4596           .addFunction(FunSpec.builder("baz").addModifiers(KModifier.EXTERNAL).build())
4597           .build(),
4598       )
4599       .addType(
4600         TypeSpec.companionObjectBuilder()
4601           .addModifiers(KModifier.EXTERNAL)
4602           .addFunction(FunSpec.builder("qux").addModifiers(KModifier.EXTERNAL).build())
4603           .build(),
4604       )
4605       .build()
4606 
4607     assertThat(toString(typeSpec)).isEqualTo(
4608       """
4609       |package com.squareup.tacos
4610       |
4611       |public external class Foo {
4612       |  public class Nested1 {
4613       |    public fun baz()
4614       |
4615       |    public object Nested2 {
4616       |      public fun bar()
4617       |    }
4618       |  }
4619       |
4620       |  public companion object {
4621       |    public fun qux()
4622       |  }
4623       |}
4624       |
4625       """.trimMargin(),
4626     )
4627   }
4628 
isEnumnull4629   @Test fun isEnum() {
4630     val enum = TypeSpec.enumBuilder("Topping")
4631       .addEnumConstant("CHEESE")
4632       .build()
4633     assertThat(enum.isEnum).isTrue()
4634   }
4635 
isAnnotationnull4636   @Test fun isAnnotation() {
4637     val annotation = TypeSpec.annotationBuilder("Taco")
4638       .build()
4639     assertThat(annotation.isAnnotation).isTrue()
4640   }
4641 
escapePunctuationInTypeNamenull4642   @Test fun escapePunctuationInTypeName() {
4643     assertThat(TypeSpec.classBuilder("With-Hyphen").build().toString()).isEqualTo(
4644       """
4645       |public class `With-Hyphen`
4646       |
4647       """.trimMargin(),
4648     )
4649   }
4650 
multipleCompanionObjectsnull4651   @Test fun multipleCompanionObjects() {
4652     assertThrows<IllegalArgumentException> {
4653       TypeSpec.classBuilder("Taco")
4654         .addTypes(
4655           listOf(
4656             TypeSpec.companionObjectBuilder()
4657               .build(),
4658             TypeSpec.companionObjectBuilder()
4659               .build(),
4660           ),
4661         )
4662         .build()
4663     }
4664   }
4665 
objectKindIsCompanionnull4666   @Test fun objectKindIsCompanion() {
4667     val companionObject = TypeSpec.companionObjectBuilder()
4668       .build()
4669     assertThat(companionObject.isCompanion).isTrue()
4670   }
4671 
typeNamesCollisionnull4672   @Test fun typeNamesCollision() {
4673     val sqlTaco = ClassName("java.sql", "Taco")
4674     val source = FileSpec.builder("com.squareup.tacos", "Taco")
4675       .addType(
4676         TypeSpec.classBuilder("Taco")
4677           .addModifiers(DATA)
4678           .addProperty(
4679             PropertySpec.builder("madeFreshDatabaseDate", sqlTaco)
4680               .initializer("madeFreshDatabaseDate")
4681               .build(),
4682           )
4683           .primaryConstructor(
4684             FunSpec.constructorBuilder()
4685               .addParameter("madeFreshDatabaseDate", sqlTaco)
4686               .addParameter("fooNt", INT)
4687               .build(),
4688           )
4689           .addFunction(
4690             FunSpec.constructorBuilder()
4691               .addParameter("anotherTaco", ClassName("com.squareup.tacos", "Taco"))
4692               .callThisConstructor(CodeBlock.of("%T.defaultInstance(), 0", sqlTaco))
4693               .build(),
4694           )
4695           .build(),
4696       )
4697       .build()
4698     assertThat(source.toString()).isEqualTo(
4699       """
4700       |package com.squareup.tacos
4701       |
4702       |import kotlin.Int
4703       |
4704       |public data class Taco(
4705       |  public val madeFreshDatabaseDate: java.sql.Taco,
4706       |  fooNt: Int,
4707       |) {
4708       |  public constructor(anotherTaco: Taco) : this(java.sql.Taco.defaultInstance(), 0)
4709       |}
4710       |
4711       """.trimMargin(),
4712     )
4713   }
4714 
modifyAnnotationsnull4715   @Test fun modifyAnnotations() {
4716     val builder = TypeSpec.classBuilder("Taco")
4717       .addAnnotation(
4718         AnnotationSpec.builder(JvmName::class.asClassName())
4719           .addMember("name = %S", "jvmWord")
4720           .build(),
4721       )
4722 
4723     val javaWord = AnnotationSpec.builder(JvmName::class.asClassName())
4724       .addMember("name = %S", "javaWord")
4725       .build()
4726     builder.annotations.clear()
4727     builder.annotations.add(javaWord)
4728 
4729     assertThat(builder.build().annotations).containsExactly(javaWord)
4730   }
4731 
modifyTypeVariableNamesnull4732   @Test fun modifyTypeVariableNames() {
4733     val builder = TypeSpec.classBuilder("Taco")
4734       .addTypeVariable(TypeVariableName("V"))
4735 
4736     val tVar = TypeVariableName("T")
4737     builder.typeVariables.clear()
4738     builder.typeVariables.add(tVar)
4739 
4740     assertThat(builder.build().typeVariables).containsExactly(tVar)
4741   }
4742 
modifyFunctionsnull4743   @Test fun modifyFunctions() {
4744     val builder = TypeSpec.classBuilder("Taco")
4745       .addFunction(FunSpec.builder("topping").build())
4746 
4747     val seasoning = FunSpec.builder("seasoning").build()
4748     builder.funSpecs.clear()
4749     builder.funSpecs.add(seasoning)
4750 
4751     assertThat(builder.build().funSpecs).containsExactly(seasoning)
4752   }
4753 
modifyTypeSpecsnull4754   @Test fun modifyTypeSpecs() {
4755     val builder = TypeSpec.classBuilder("Taco")
4756       .addType(TypeSpec.classBuilder("Topping").build())
4757 
4758     val seasoning = TypeSpec.classBuilder("Seasoning").build()
4759     builder.typeSpecs.clear()
4760     builder.typeSpecs.add(seasoning)
4761 
4762     assertThat(builder.build().typeSpecs).containsExactly(seasoning)
4763   }
4764 
modifySuperinterfacesnull4765   @Test fun modifySuperinterfaces() {
4766     val builder = TypeSpec.classBuilder("Taco")
4767       .addSuperinterface(List::class)
4768 
4769     builder.superinterfaces.clear()
4770     builder.superinterfaces[Set::class.asTypeName()] = CodeBlock.EMPTY
4771 
4772     assertThat(builder.build().superinterfaces)
4773       .containsExactlyEntriesIn(mapOf(Set::class.asTypeName() to CodeBlock.EMPTY))
4774   }
4775 
modifyPropertiesnull4776   @Test fun modifyProperties() {
4777     val builder = TypeSpec.classBuilder("Taco")
4778       .addProperty(PropertySpec.builder("topping", String::class.asClassName()).build())
4779 
4780     val seasoning = PropertySpec.builder("seasoning", String::class.asClassName()).build()
4781     builder.propertySpecs.clear()
4782     builder.propertySpecs.add(seasoning)
4783 
4784     assertThat(builder.build().propertySpecs).containsExactly(seasoning)
4785   }
4786 
modifyEnumConstantsnull4787   @Test fun modifyEnumConstants() {
4788     val builder = TypeSpec.enumBuilder("Taco")
4789       .addEnumConstant("TOPPING")
4790 
4791     builder.enumConstants.clear()
4792     builder.enumConstants["SEASONING"] = TypeSpec.anonymousClassBuilder().build()
4793 
4794     assertThat(builder.build().enumConstants)
4795       .containsExactlyEntriesIn(mapOf("SEASONING" to TypeSpec.anonymousClassBuilder().build()))
4796   }
4797 
modifySuperclassConstructorParamsnull4798   @Test fun modifySuperclassConstructorParams() {
4799     val builder = TypeSpec.classBuilder("Taco")
4800       .addSuperclassConstructorParameter(CodeBlock.of("seasoning = %S", "mild"))
4801 
4802     val seasoning = CodeBlock.of("seasoning = %S", "spicy")
4803     builder.superclassConstructorParameters.clear()
4804     builder.superclassConstructorParameters.add(seasoning)
4805 
4806     assertThat(builder.build().superclassConstructorParameters).containsExactly(seasoning)
4807   }
4808 
4809   // https://github.com/square/kotlinpoet/issues/565
markerEnumnull4810   @Test fun markerEnum() {
4811     val spec = TypeSpec.enumBuilder("Topping")
4812       .build()
4813     assertThat(spec.toString()).isEqualTo(
4814       """
4815       |public enum class Topping
4816       |
4817       """.trimMargin(),
4818     )
4819   }
4820 
4821   // https://github.com/square/kotlinpoet/issues/586
classKdocWithoutTagsnull4822   @Test fun classKdocWithoutTags() {
4823     val typeSpec = TypeSpec.classBuilder("Foo")
4824       .addKdoc("blah blah")
4825       .build()
4826     assertThat(typeSpec.toString()).isEqualTo(
4827       """
4828     |/**
4829     | * blah blah
4830     | */
4831     |public class Foo
4832     |
4833       """.trimMargin(),
4834     )
4835   }
4836 
classWithPropertyKdocnull4837   @Test fun classWithPropertyKdoc() {
4838     val typeSpec = TypeSpec.classBuilder("Foo")
4839       .addProperty(
4840         PropertySpec.builder("bar", String::class)
4841           .addKdoc("The bar for your foo")
4842           .build(),
4843       )
4844       .build()
4845     assertThat(typeSpec.toString()).isEqualTo(
4846       """
4847     |public class Foo {
4848     |  /**
4849     |   * The bar for your foo
4850     |   */
4851     |  public val bar: kotlin.String
4852     |}
4853     |
4854       """.trimMargin(),
4855     )
4856   }
4857 
4858   // https://github.com/square/kotlinpoet/issues/563
kdocFormattingnull4859   @Test fun kdocFormatting() {
4860     val typeSpec = TypeSpec.classBuilder("MyType")
4861       .addKdoc("This is a thing for stuff.")
4862       .addProperty(
4863         PropertySpec.builder("first", INT)
4864           .initializer("first")
4865           .build(),
4866       )
4867       .addProperty(
4868         PropertySpec.builder("second", INT)
4869           .initializer("second")
4870           .build(),
4871       )
4872       .addProperty(
4873         PropertySpec.builder("third", INT)
4874           .initializer("third")
4875           .build(),
4876       )
4877       .primaryConstructor(
4878         FunSpec.constructorBuilder()
4879           .addKdoc("Construct a thing!")
4880           .addParameter(
4881             ParameterSpec.builder("first", INT)
4882               .addKdoc("the first thing")
4883               .build(),
4884           )
4885           .addParameter(
4886             ParameterSpec.builder("second", INT)
4887               .addKdoc("the second thing")
4888               .build(),
4889           )
4890           .addParameter(
4891             ParameterSpec.builder("third", INT)
4892               .addKdoc("the third thing")
4893               .build(),
4894           )
4895           .build(),
4896       )
4897       .build()
4898     assertThat(typeSpec.toString()).isEqualTo(
4899       """
4900       |/**
4901       | * This is a thing for stuff.
4902       | *
4903       | * @constructor Construct a thing!
4904       | * @param first the first thing
4905       | * @param second the second thing
4906       | * @param third the third thing
4907       | */
4908       |public class MyType(
4909       |  /**
4910       |   * the first thing
4911       |   */
4912       |  public val first: kotlin.Int,
4913       |  /**
4914       |   * the second thing
4915       |   */
4916       |  public val second: kotlin.Int,
4917       |  /**
4918       |   * the third thing
4919       |   */
4920       |  public val third: kotlin.Int,
4921       |)
4922       |
4923       """.trimMargin(),
4924     )
4925   }
4926 
primaryConstructorWithOneParameterKdocFormattingnull4927   @Test fun primaryConstructorWithOneParameterKdocFormatting() {
4928     val typeSpec = TypeSpec.classBuilder("MyType")
4929       .primaryConstructor(
4930         FunSpec.constructorBuilder()
4931           .addParameter(
4932             ParameterSpec.builder("first", INT)
4933               .addKdoc("the first thing")
4934               .build(),
4935           )
4936           .build(),
4937       )
4938       .build()
4939     assertThat(typeSpec.toString()).isEqualTo(
4940       """
4941       |/**
4942       | * @param first the first thing
4943       | */
4944       |public class MyType(
4945       |  first: kotlin.Int,
4946       |)
4947       |
4948       """.trimMargin(),
4949     )
4950   }
4951 
4952   // https://github.com/square/kotlinpoet/issues/594
longCommentnull4953   @Test fun longComment() {
4954     val taco = TypeSpec.classBuilder("Taco")
4955       .addFunction(
4956         FunSpec.builder("getAnswer")
4957           .returns(Int::class)
4958           .addComment(
4959             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
4960               "eiusmod tempor incididunt ut labore et dolore magna aliqua.",
4961           )
4962           .addStatement("return 42")
4963           .build(),
4964       )
4965       .build()
4966     assertThat(toString(taco)).isEqualTo(
4967       """
4968         |package com.squareup.tacos
4969         |
4970         |import kotlin.Int
4971         |
4972         |public class Taco {
4973         |  public fun getAnswer(): Int {
4974         |    // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
4975         |    return 42
4976         |  }
4977         |}
4978         |
4979       """.trimMargin(),
4980     )
4981   }
4982 
originatingElementsIncludesThoseOfNestedTypesnull4983   @Test fun originatingElementsIncludesThoseOfNestedTypes() {
4984     val outerElement = FakeElement()
4985     val innerElement = FakeElement()
4986     val outer = TypeSpec.classBuilder("Outer")
4987       .addOriginatingElement(outerElement)
4988       .addType(
4989         TypeSpec.classBuilder("Inner")
4990           .addOriginatingElement(innerElement)
4991           .build(),
4992       )
4993       .build()
4994     assertThat(outer.originatingElements).containsExactly(outerElement, innerElement)
4995   }
4996 
4997   // https://github.com/square/kotlinpoet/issues/698
escapeEnumConstantsnull4998   @Test fun escapeEnumConstants() {
4999     val enum = TypeSpec.enumBuilder("MyEnum")
5000       .addEnumConstant("test test")
5001       .addEnumConstant("0constants")
5002       .build()
5003     assertThat(enum.toString()).isEqualTo(
5004       """
5005       |public enum class MyEnum {
5006       |  `test test`,
5007       |  `0constants`,
5008       |}
5009       |
5010       """.trimMargin(),
5011     )
5012   }
5013 
initOrdering_firstnull5014   @Test fun initOrdering_first() {
5015     val type = TypeSpec.classBuilder("MyClass")
5016       .addInitializerBlock(CodeBlock.builder().build())
5017       .addProperty("tacos", Int::class)
5018       .build()
5019 
5020     //language=kotlin
5021     assertThat(toString(type)).isEqualTo(
5022       """
5023         package com.squareup.tacos
5024 
5025         import kotlin.Int
5026 
5027         public class MyClass {
5028           init {
5029           }
5030 
5031           public val tacos: Int
5032         }
5033 
5034       """.trimIndent(),
5035     )
5036   }
5037 
initOrdering_middlenull5038   @Test fun initOrdering_middle() {
5039     val type = TypeSpec.classBuilder("MyClass")
5040       .addProperty("tacos1", Int::class)
5041       .addInitializerBlock(CodeBlock.builder().build())
5042       .addProperty("tacos2", Int::class)
5043       .build()
5044 
5045     //language=kotlin
5046     assertThat(toString(type)).isEqualTo(
5047       """
5048         package com.squareup.tacos
5049 
5050         import kotlin.Int
5051 
5052         public class MyClass {
5053           public val tacos1: Int
5054 
5055           init {
5056           }
5057 
5058           public val tacos2: Int
5059         }
5060 
5061       """.trimIndent(),
5062     )
5063   }
5064 
initOrdering_lastnull5065   @Test fun initOrdering_last() {
5066     val type = TypeSpec.classBuilder("MyClass")
5067       .addProperty("tacos", Int::class)
5068       .addInitializerBlock(CodeBlock.builder().build())
5069       .build()
5070 
5071     //language=kotlin
5072     assertThat(toString(type)).isEqualTo(
5073       """
5074         package com.squareup.tacos
5075 
5076         import kotlin.Int
5077 
5078         public class MyClass {
5079           public val tacos: Int
5080 
5081           init {
5082           }
5083         }
5084 
5085       """.trimIndent(),
5086     )
5087   }
5088 
initOrdering_constructorParamsExludedAfterIndexnull5089   @Test fun initOrdering_constructorParamsExludedAfterIndex() {
5090     val type = TypeSpec.classBuilder("MyClass")
5091       .primaryConstructor(
5092         FunSpec.constructorBuilder()
5093           .addParameter("tacos1", Int::class)
5094           .addParameter("tacos2", Int::class)
5095           .build(),
5096       )
5097       .addProperty(
5098         PropertySpec.builder("tacos1", Int::class)
5099           .initializer("tacos1")
5100           .build(),
5101       )
5102       .addInitializerBlock(CodeBlock.builder().build())
5103       .addProperty(
5104         PropertySpec.builder("tacos2", Int::class)
5105           .initializer("tacos2")
5106           .build(),
5107       )
5108       .build()
5109 
5110     //language=kotlin
5111     assertThat(toString(type)).isEqualTo(
5112       """
5113         package com.squareup.tacos
5114 
5115         import kotlin.Int
5116 
5117         public class MyClass(
5118           public val tacos1: Int,
5119           tacos2: Int,
5120         ) {
5121           init {
5122           }
5123 
5124           public val tacos2: Int = tacos2
5125         }
5126 
5127       """.trimIndent(),
5128     )
5129   }
5130 
5131   // https://github.com/square/kotlinpoet/issues/843
kdocWithParametersWithoutClassKdocnull5132   @Test fun kdocWithParametersWithoutClassKdoc() {
5133     val taco = TypeSpec.classBuilder("Taco")
5134       .primaryConstructor(
5135         FunSpec.constructorBuilder()
5136           .addParameter(
5137             ParameterSpec.builder("mild", Boolean::class)
5138               .addKdoc(CodeBlock.of("%L", "Whether the taco is mild (ew) or crunchy (ye).\n"))
5139               .build(),
5140           )
5141           .build(),
5142       )
5143       .addProperty(
5144         PropertySpec.builder("mild", Boolean::class)
5145           .addKdoc("No one likes mild tacos.")
5146           .initializer("mild")
5147           .build(),
5148       )
5149       .build()
5150     assertThat(toString(taco)).isEqualTo(
5151       """
5152         |package com.squareup.tacos
5153         |
5154         |import kotlin.Boolean
5155         |
5156         |/**
5157         | * @param mild Whether the taco is mild (ew) or crunchy (ye).
5158         | */
5159         |public class Taco(
5160         |  /**
5161         |   * No one likes mild tacos.
5162         |   */
5163         |  public val mild: Boolean,
5164         |)
5165         |
5166       """.trimMargin(),
5167     )
5168   }
5169 
5170   // https://github.com/square/kotlinpoet/issues/848
escapeEnumConstantNamesnull5171   @Test fun escapeEnumConstantNames() {
5172     val enum = TypeSpec
5173       .enumBuilder("MyEnum")
5174       .addEnumConstant("object")
5175       .build()
5176     assertThat(toString(enum)).isEqualTo(
5177       """
5178       |package com.squareup.tacos
5179       |
5180       |public enum class MyEnum {
5181       |  `object`,
5182       |}
5183       |
5184       """.trimMargin(),
5185     )
5186   }
5187 
5188   // https://youtrack.jetbrains.com/issue/KT-52315
escapeHeaderAndImplAsEnumConstantNamesnull5189   @Test fun escapeHeaderAndImplAsEnumConstantNames() {
5190     val primaryConstructor = FunSpec.constructorBuilder()
5191       .addParameter("int", Int::class)
5192       .build()
5193     val enum = TypeSpec
5194       .enumBuilder("MyEnum")
5195       .primaryConstructor(primaryConstructor)
5196       .addEnumConstant(
5197         "header",
5198         TypeSpec.anonymousClassBuilder()
5199           .addSuperclassConstructorParameter("%L", 1)
5200           .build(),
5201       )
5202       .addEnumConstant(
5203         "impl",
5204         TypeSpec.anonymousClassBuilder()
5205           .addSuperclassConstructorParameter("%L", 2)
5206           .build(),
5207       )
5208       .build()
5209     assertThat(toString(enum)).isEqualTo(
5210       """
5211       |package com.squareup.tacos
5212       |
5213       |import kotlin.Int
5214       |
5215       |public enum class MyEnum(
5216       |  int: Int,
5217       |) {
5218       |  `header`(1),
5219       |  `impl`(2),
5220       |}
5221       |
5222       """.trimMargin(),
5223     )
5224   }
5225 
escapeClassNamesnull5226   @Test fun escapeClassNames() {
5227     val type = TypeSpec.classBuilder("fun").build()
5228     assertThat(type.toString()).isEqualTo(
5229       """
5230       |public class `fun`
5231       |
5232       """.trimMargin(),
5233     )
5234   }
5235 
escapeInnerClassNamenull5236   @Test fun escapeInnerClassName() {
5237     val tacoType = ClassName("com.squareup.tacos", "Taco", "object")
5238     val funSpec = FunSpec.builder("printTaco")
5239       .addParameter("taco", tacoType)
5240       .addStatement("print(taco)")
5241       .build()
5242     assertThat(funSpec.toString()).isEqualTo(
5243       """
5244       |public fun printTaco(taco: com.squareup.tacos.Taco.`object`) {
5245       |  print(taco)
5246       |}
5247       |
5248       """.trimMargin(),
5249     )
5250   }
5251 
escapeAllowedCharactersnull5252   @Test fun escapeAllowedCharacters() {
5253     val typeSpec = TypeSpec.classBuilder("A\$B")
5254       .build()
5255     assertThat(typeSpec.toString()).isEqualTo("public class `A\$B`\n")
5256   }
5257 
5258   // https://github.com/square/kotlinpoet/issues/1011
abstractInterfaceMembersnull5259   @Test fun abstractInterfaceMembers() {
5260     val file = FileSpec.builder("com.squareup.tacos", "Taco")
5261       .addType(
5262         TypeSpec.interfaceBuilder("Taco")
5263           .addProperty("foo", String::class, ABSTRACT)
5264           .addProperty(
5265             PropertySpec.builder("fooWithDefault", String::class)
5266               .initializer("%S", "defaultValue")
5267               .build(),
5268           )
5269           .addFunction(
5270             FunSpec.builder("bar")
5271               .addModifiers(ABSTRACT)
5272               .returns(String::class)
5273               .build(),
5274           )
5275           .addFunction(
5276             FunSpec.builder("barWithDefault")
5277               .build(),
5278           )
5279           .build(),
5280       )
5281       .build()
5282     // language=kotlin
5283     assertThat(file.toString()).isEqualTo(
5284       """
5285       package com.squareup.tacos
5286 
5287       import kotlin.String
5288 
5289       public interface Taco {
5290         public val foo: String
5291 
5292         public val fooWithDefault: String = "defaultValue"
5293 
5294         public fun bar(): String
5295 
5296         public fun barWithDefault() {
5297         }
5298       }
5299 
5300       """.trimIndent(),
5301     )
5302   }
5303 
emptyConstructorGeneratednull5304   @Test fun emptyConstructorGenerated() {
5305     val taco = TypeSpec.classBuilder("Taco")
5306       .primaryConstructor(FunSpec.constructorBuilder().build())
5307       .build()
5308     val file = FileSpec.builder("com.squareup.tacos", "Taco")
5309       .addType(taco)
5310       .build()
5311     assertThat(file.toString()).isEqualTo(
5312       """
5313       package com.squareup.tacos
5314 
5315       public class Taco()
5316 
5317       """.trimIndent(),
5318     )
5319   }
5320 
5321   // Regression test for https://github.com/square/kotlinpoet/issues/1176
templates in class delegation blocks should be imported toonull5322   @Test fun `templates in class delegation blocks should be imported too`() {
5323     val taco = TypeSpec.classBuilder("TacoShim")
5324       .addSuperinterface(
5325         ClassName("test", "Taco"),
5326         CodeBlock.of("%T", ClassName("test", "RealTaco")),
5327       )
5328       .build()
5329     val file = FileSpec.builder("com.squareup.tacos", "Tacos")
5330       .addType(taco)
5331       .build()
5332     assertThat(file.toString()).isEqualTo(
5333       """
5334       package com.squareup.tacos
5335 
5336       import test.RealTaco
5337       import test.Taco
5338 
5339       public class TacoShim : Taco by RealTaco
5340 
5341       """.trimIndent(),
5342     )
5343   }
5344 
5345   // https://github.com/square/kotlinpoet/issues/1183
forbidden enum constant namesnull5346   @Test fun `forbidden enum constant names`() {
5347     var exception = assertFailsWith<IllegalArgumentException> {
5348       TypeSpec.enumBuilder("Topping")
5349         .addEnumConstant("name")
5350     }
5351     assertThat(exception.message).isEqualTo(
5352       "constant with name \"name\" conflicts with a supertype member with the same name",
5353     )
5354 
5355     @Suppress("RemoveExplicitTypeArguments")
5356     exception = assertFailsWith<IllegalArgumentException> {
5357       TypeSpec.enumBuilder("Topping")
5358         .addEnumConstant("ordinal")
5359     }
5360     assertThat(exception.message).isEqualTo(
5361       "constant with name \"ordinal\" conflicts with a supertype member with the same name",
5362     )
5363   }
5364 
5365   // https://github.com/square/kotlinpoet/issues/1183
forbidden enum property namesnull5366   @Test fun `forbidden enum property names`() {
5367     var exception = assertFailsWith<IllegalArgumentException> {
5368       TypeSpec.enumBuilder("Topping")
5369         .addProperty("name", String::class)
5370     }
5371     assertThat(exception.message).isEqualTo(
5372       "name is a final supertype member and can't be redeclared or overridden",
5373     )
5374 
5375     @Suppress("RemoveExplicitTypeArguments")
5376     exception = assertFailsWith<IllegalArgumentException> {
5377       TypeSpec.enumBuilder("Topping")
5378         .addProperty("ordinal", String::class)
5379     }
5380     assertThat(exception.message).isEqualTo(
5381       "ordinal is a final supertype member and can't be redeclared or overridden",
5382     )
5383   }
5384 
5385   // https://github.com/square/kotlinpoet/issues/1234
enum constants are resolvednull5386   @Test fun `enum constants are resolved`() {
5387     val file = FileSpec.builder("com.example", "test")
5388       .addType(
5389         TypeSpec.enumBuilder("Foo")
5390           .addProperty(
5391             PropertySpec.builder("rawValue", String::class)
5392               .initializer("%S", "")
5393               .build(),
5394           )
5395           .addEnumConstant("String")
5396           .build(),
5397       )
5398       .build()
5399 
5400     assertThat(file.toString()).isEqualTo(
5401       """
5402     package com.example
5403 
5404     public enum class Foo {
5405       String,
5406       ;
5407 
5408       public val rawValue: kotlin.String = ""
5409     }
5410 
5411       """.trimIndent(),
5412     )
5413   }
5414 
5415   // https://github.com/square/kotlinpoet/issues/1035
dataClassWithKeywordPropertynull5416   @Test fun dataClassWithKeywordProperty() {
5417     val parameter = ParameterSpec.builder("data", STRING).build()
5418     val typeSpec = TypeSpec.classBuilder("Example")
5419       .addModifiers(DATA)
5420       .primaryConstructor(
5421         FunSpec.constructorBuilder()
5422           .addParameter(parameter)
5423           .build(),
5424       )
5425       .addProperty(
5426         PropertySpec.builder(parameter.name, STRING)
5427           .initializer("%N", parameter)
5428           .build(),
5429       )
5430       .build()
5431     assertThat(typeSpec.toString()).isEqualTo(
5432       """
5433       public data class Example(
5434         public val `data`: kotlin.String,
5435       )
5436 
5437       """.trimIndent(),
5438     )
5439   }
5440 
5441   // https://github.com/square/kotlinpoet/issues/1548
overrideInternalAbstractFunctionVisibilitynull5442   @Test fun overrideInternalAbstractFunctionVisibility() {
5443     val baseClass = TypeSpec.classBuilder("Base")
5444       .addModifiers(PUBLIC, ABSTRACT)
5445       .addFunction(
5446         FunSpec.builder("foo")
5447           .addModifiers(INTERNAL, ABSTRACT)
5448           .build(),
5449       )
5450       .build()
5451     assertThat(baseClass.toString()).isEqualTo(
5452       """
5453       |public abstract class Base {
5454       |  internal abstract fun foo()
5455       |}
5456       |
5457       """.trimMargin(),
5458     )
5459     val bassClassName = ClassName("", "Base")
5460     val exampleClass = TypeSpec.classBuilder("Example")
5461       .addModifiers(PUBLIC)
5462       .superclass(bassClassName)
5463       .addFunction(
5464         FunSpec.builder("foo")
5465           .addModifiers(KModifier.OVERRIDE)
5466           .build(),
5467       )
5468       .build()
5469     assertThat(exampleClass.toString()).isEqualTo(
5470       """
5471       |public class Example : Base() {
5472       |  override fun foo() {
5473       |  }
5474       |}
5475       |
5476       """.trimMargin(),
5477     )
5478     val example2Class = TypeSpec.classBuilder("Example2")
5479       .addModifiers(PUBLIC)
5480       .superclass(bassClassName)
5481       .addFunction(
5482         FunSpec.builder("foo")
5483           .addModifiers(PUBLIC, KModifier.OVERRIDE)
5484           .build(),
5485       )
5486       .build()
5487     // Don't omit the public modifier here,
5488     // as we're explicitly increasing the visibility of this method in the subclass.
5489     assertThat(example2Class.toString()).isEqualTo(
5490       """
5491       |public class Example2 : Base() {
5492       |  public override fun foo() {
5493       |  }
5494       |}
5495       |
5496       """.trimMargin(),
5497     )
5498     val example3Class = TypeSpec.classBuilder("Example3")
5499       .addModifiers(INTERNAL)
5500       .superclass(bassClassName)
5501       .addFunction(
5502         FunSpec.builder("foo")
5503           .addModifiers(PUBLIC, KModifier.OVERRIDE)
5504           .build(),
5505       )
5506       .build()
5507     // Don't omit the public modifier here,
5508     // as we're explicitly increasing the visibility of this method in the subclass.
5509     assertThat(example3Class.toString()).isEqualTo(
5510       """
5511       |internal class Example3 : Base() {
5512       |  public override fun foo() {
5513       |  }
5514       |}
5515       |
5516       """.trimMargin(),
5517     )
5518   }
5519 
contextReceivernull5520   @Test fun contextReceiver() {
5521     val typeSpec = TypeSpec.classBuilder("Example")
5522       .contextReceivers(STRING)
5523       .build()
5524 
5525     assertThat(typeSpec.toString()).isEqualTo(
5526       """
5527       |context(kotlin.String)
5528       |public class Example
5529       |
5530       """.trimMargin(),
5531     )
5532   }
5533 
contextReceiver_mustBeClassnull5534   @Test fun contextReceiver_mustBeClass() {
5535     val t = assertFailsWith<IllegalStateException> {
5536       TypeSpec.interfaceBuilder("Example")
5537         .contextReceivers(STRING)
5538     }
5539     assertThat(t).hasMessageThat().contains("contextReceivers can only be applied on simple classes")
5540   }
5541 
valWithContextReceiverWithoutGetternull5542   @Test fun valWithContextReceiverWithoutGetter() {
5543     assertThrows<IllegalArgumentException> {
5544       TypeSpec.classBuilder("Example")
5545         .addProperty(
5546           PropertySpec.builder("foo", STRING)
5547             .mutable(false)
5548             .contextReceivers(INT)
5549             .build(),
5550         )
5551         .build()
5552     }.hasMessageThat()
5553       .isEqualTo("non-abstract properties with context receivers require a get()")
5554   }
5555 
varWithContextReceiverWithoutAccessorsnull5556   @Test fun varWithContextReceiverWithoutAccessors() {
5557     assertThrows<IllegalArgumentException> {
5558       TypeSpec.classBuilder("Example")
5559         .addProperty(
5560           PropertySpec.builder("foo", STRING)
5561             .mutable()
5562             .contextReceivers(INT)
5563             .getter(
5564               FunSpec.getterBuilder()
5565                 .build(),
5566             )
5567             .build(),
5568         ).build()
5569     }.hasMessageThat()
5570       .isEqualTo("non-abstract mutable properties with context receivers require a set()")
5571 
5572     assertThrows<IllegalArgumentException> {
5573       TypeSpec.classBuilder("Example")
5574         .addProperty(
5575           PropertySpec.builder("foo", STRING)
5576             .mutable()
5577             .contextReceivers(INT)
5578             .setter(
5579               FunSpec.setterBuilder()
5580                 .build(),
5581             )
5582             .build(),
5583         ).build()
5584     }.hasMessageThat()
5585       .isEqualTo("non-abstract properties with context receivers require a get()")
5586   }
5587 
5588   // https://github.com/square/kotlinpoet/issues/1525
propertyWithContextReceiverInInterfacenull5589   @Test fun propertyWithContextReceiverInInterface() {
5590     val typeSpec = TypeSpec.interfaceBuilder("Bar")
5591       .addProperty(
5592         PropertySpec.builder("foo", Int::class)
5593           .contextReceivers(STRING)
5594           .build(),
5595       )
5596       .addProperty(
5597         PropertySpec.builder("bar", Int::class)
5598           .contextReceivers(STRING)
5599           .mutable(true)
5600           .build(),
5601       )
5602       .build()
5603 
5604     assertThat(typeSpec.toString()).isEqualTo(
5605       """
5606       |public interface Bar {
5607       |  context(kotlin.String)
5608       |  public val foo: kotlin.Int
5609       |
5610       |  context(kotlin.String)
5611       |  public var bar: kotlin.Int
5612       |}
5613       |
5614       """.trimMargin(),
5615     )
5616   }
5617 
nonAbstractPropertyWithContextReceiverInAbstractClassnull5618   @Test fun nonAbstractPropertyWithContextReceiverInAbstractClass() {
5619     assertThrows<IllegalArgumentException> {
5620       TypeSpec.classBuilder("Bar")
5621         .addModifiers(ABSTRACT)
5622         .addProperty(
5623           PropertySpec.builder("foo", Int::class)
5624             .contextReceivers(STRING)
5625             .build(),
5626         )
5627         .build()
5628     }.hasMessageThat().isEqualTo("non-abstract properties with context receivers require a get()")
5629   }
5630 
abstractPropertyWithContextReceiverInAbstractClassnull5631   @Test fun abstractPropertyWithContextReceiverInAbstractClass() {
5632     val typeSpec = TypeSpec.classBuilder("Bar")
5633       .addModifiers(ABSTRACT)
5634       .addProperty(
5635         PropertySpec.builder("foo", Int::class)
5636           .contextReceivers(STRING)
5637           .addModifiers(ABSTRACT)
5638           .build(),
5639       )
5640       .build()
5641 
5642     assertThat(typeSpec.toString()).isEqualTo(
5643       """
5644       |public abstract class Bar {
5645       |  context(kotlin.String)
5646       |  public abstract val foo: kotlin.Int
5647       |}
5648       |
5649       """.trimMargin(),
5650     )
5651   }
5652 
abstractPropertyInNonAbstractClassnull5653   @Test fun abstractPropertyInNonAbstractClass() {
5654     assertThrows<IllegalArgumentException> {
5655       TypeSpec.classBuilder("Bar")
5656         .addProperty(
5657           PropertySpec.builder("foo", Int::class)
5658             .addModifiers(ABSTRACT)
5659             .build(),
5660         )
5661         .build()
5662     }.hasMessageThat().isEqualTo("non-abstract type Bar cannot declare abstract property foo")
5663   }
5664 
abstractPropertyInObjectnull5665   @Test fun abstractPropertyInObject() {
5666     assertThrows<IllegalArgumentException> {
5667       TypeSpec.objectBuilder("Bar")
5668         .addProperty(
5669           PropertySpec.builder("foo", Int::class)
5670             .addModifiers(ABSTRACT)
5671             .build(),
5672         )
5673         .build()
5674     }.hasMessageThat().isEqualTo("non-abstract type Bar cannot declare abstract property foo")
5675   }
5676 
abstractPropertyInEnumnull5677   @Test fun abstractPropertyInEnum() {
5678     val typeSpec = TypeSpec.enumBuilder("Bar")
5679       .addProperty(
5680         PropertySpec.builder("foo", Int::class)
5681           .addModifiers(ABSTRACT)
5682           .build(),
5683       )
5684       .build()
5685 
5686     assertThat(typeSpec.toString()).isEqualTo(
5687       """
5688       |public enum class Bar {
5689       |  ;
5690       |  public abstract val foo: kotlin.Int
5691       |}
5692       |
5693       """.trimMargin(),
5694     )
5695   }
5696 
abstractPropertyInSealedClassnull5697   @Test fun abstractPropertyInSealedClass() {
5698     val typeSpec = TypeSpec.classBuilder("Bar")
5699       .addModifiers(SEALED)
5700       .addProperty(
5701         PropertySpec.builder("foo", Int::class)
5702           .addModifiers(ABSTRACT)
5703           .build(),
5704       )
5705       .build()
5706 
5707     assertThat(typeSpec.toString()).isEqualTo(
5708       """
5709       |public sealed class Bar {
5710       |  public abstract val foo: kotlin.Int
5711       |}
5712       |
5713       """.trimMargin(),
5714     )
5715   }
5716 
classKdocnull5717   @Test fun classKdoc() {
5718     val type = TypeSpec.classBuilder("MyClass")
5719       .addKdoc("This is my class")
5720       .primaryConstructor(
5721         FunSpec.constructorBuilder()
5722           .build(),
5723       )
5724       .build()
5725 
5726     //language=kotlin
5727     assertThat(type.toString()).isEqualTo(
5728       """
5729       /**
5730        * This is my class
5731        */
5732       public class MyClass()
5733 
5734       """.trimIndent(),
5735     )
5736   }
5737 
5738   // https://github.com/square/kotlinpoet/issues/1630
primaryConstructorKDocnull5739   @Test fun primaryConstructorKDoc() {
5740     val type = TypeSpec.classBuilder("MyClass")
5741       .addKdoc("This is my class")
5742       .primaryConstructor(
5743         FunSpec.constructorBuilder()
5744           .addKdoc("This is my constructor")
5745           .build(),
5746       )
5747       .build()
5748 
5749     //language=kotlin
5750     assertThat(type.toString()).isEqualTo(
5751       """
5752       /**
5753        * This is my class
5754        *
5755        * @constructor This is my constructor
5756        */
5757       public class MyClass()
5758 
5759       """.trimIndent(),
5760     )
5761   }
5762 
5763   // https://github.com/square/kotlinpoet/issues/1818
primaryConstructorCanNotDelegatenull5764   @Test fun primaryConstructorCanNotDelegate() {
5765     assertThrows<IllegalArgumentException> {
5766       TypeSpec.classBuilder("Child")
5767         .superclass(ClassName("com.squareup", "Parent"))
5768         .primaryConstructor(
5769           FunSpec.constructorBuilder()
5770             .callSuperConstructor(CodeBlock.of("%L", "param"))
5771             .addParameter(
5772               name = "param",
5773               type = ClassName("com.squareup", "Param"),
5774             )
5775             .build(),
5776         )
5777         .build()
5778     }.hasMessageThat().isEqualTo("primary constructor can't delegate to other constructors")
5779   }
5780 
5781   companion object {
5782     private const val donutsPackage = "com.squareup.donuts"
5783   }
5784 }
5785