Upgrade kotlinpoet to 1.16.0 am: af7bd99cda

Original change: https://android-review.googlesource.com/c/platform/external/kotlinpoet/+/2967683

Change-Id: I5922082e30e41d299676b15095b223a761ca1b40
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0bd44d1..187d0ac 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,24 +13,33 @@
 
 jobs:
   jvm:
-    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        os:
+          - macos-latest
+          - ubuntu-latest
+          - windows-latest
+
+    runs-on: ${{ matrix.os }}
 
     steps:
       - name: Checkout
         uses: actions/checkout@v4
 
-      - name: Validate Gradle Wrapper
-        uses: gradle/wrapper-validation-action@v1
-
       - name: Configure JDK
-        uses: actions/setup-java@v3
+        uses: actions/setup-java@v4
         with:
           distribution: 'zulu'
           java-version: 19
 
-      - name: Test
+      - name: Full build
+        if: matrix.os == 'ubuntu-latest'
         run: ./gradlew build
 
+      - name: KotlinPoet check
+        if: "matrix.os != 'ubuntu-latest'"
+        run: ./gradlew :kotlinpoet:check
+
   build-docs:
     runs-on: ubuntu-latest
     if: github.repository == 'square/kotlinpoet'
@@ -40,7 +49,7 @@
         uses: actions/checkout@v4
 
       - name: Configure JDK
-        uses: actions/setup-java@v3
+        uses: actions/setup-java@v4
         with:
           distribution: 'zulu'
           java-version: 19
@@ -49,7 +58,7 @@
         run: ./gradlew dokkaHtml
 
       - name: Set up Python
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: 3.8
 
diff --git a/.github/workflows/gradle-wrapper.yaml b/.github/workflows/gradle-wrapper.yaml
new file mode 100644
index 0000000..d82e291
--- /dev/null
+++ b/.github/workflows/gradle-wrapper.yaml
@@ -0,0 +1,15 @@
+name: gradle-wrapper
+
+on:
+  pull_request:
+    paths:
+      - 'gradlew'
+      - 'gradlew.bat'
+      - 'gradle/wrapper/**'
+
+jobs:
+  validate:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: gradle/wrapper-validation-action@v1
diff --git a/.github/workflows/mkdocs-requirements.txt b/.github/workflows/mkdocs-requirements.txt
index 213346e..5f787ff 100644
--- a/.github/workflows/mkdocs-requirements.txt
+++ b/.github/workflows/mkdocs-requirements.txt
@@ -1,18 +1,18 @@
 click==8.1.7
 future==0.18.3
-Jinja2==3.1.2
+Jinja2==3.1.3
 livereload==2.6.3
 lunr==0.7.0.post1
 MarkupSafe==2.1.3
 mkdocs==1.5.3
-mkdocs-macros-plugin==1.0.4
-mkdocs-material==9.4.5
-mkdocs-material-extensions==1.2
-Pygments==2.16.1
-pymdown-extensions==10.3
+mkdocs-macros-plugin==1.0.5
+mkdocs-material==9.5.4
+mkdocs-material-extensions==1.3.1
+Pygments==2.17.2
+pymdown-extensions==10.7
 python-dateutil==2.8.2
 PyYAML==6.0.1
 repackage==0.7.3
 six==1.16.0
-termcolor==2.3.0
-tornado==6.3.3
+termcolor==2.4.0
+tornado==6.4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cb8950a..1a2f3f9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,7 +20,7 @@
         uses: actions/checkout@v4
 
       - name: Configure JDK
-        uses: actions/setup-java@v3
+        uses: actions/setup-java@v4
         with:
           distribution: 'zulu'
           java-version: 19
@@ -36,7 +36,7 @@
         run: ./gradlew dokkaHtml
 
       - name: Set up Python
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: 3.8
 
diff --git a/.gitignore b/.gitignore
index 75e7808..9310aea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@
 
 # Mkdocs files
 docs/1.x/*
+site
 
 obj
 
diff --git a/Android.bp b/Android.bp
index 87c062c..1505c4a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,12 +15,14 @@
 
 java_library_host {
     name: "kotlinpoet",
-    srcs: ["kotlinpoet/src/main/**/*.kt"],
+    srcs: [
+        "kotlinpoet/src/commonMain/**/*.kt",
+    ],
     kotlincflags: ["-Xjvm-default=all"],
     static_libs: [
         "kotlin-stdlib-jdk8",
         "kotlin-reflect",
-    ]
+    ],
 }
 
 java_library_host {
@@ -30,6 +32,6 @@
     static_libs: [
         "javapoet",
         "kotlin-stdlib-jdk8",
-        "kotlinpoet"
-    ]
+        "kotlinpoet",
+    ],
 }
diff --git a/METADATA b/METADATA
index fbd5831..127fc45 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
 # This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update kotlinpoet
+# Usage: tools/external_updater/updater.sh update external/kotlinpoet
 # For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
 
 name: "kotlinpoet"
 description: "A Kotlin API for generating .kt source files"
 third_party {
-  url {
-    type: HOMEPAGE
-    value: "https://square.github.io/kotlinpoet/"
-  }
-  url {
-    type: GIT
-    value: "https://github.com/square/kotlinpoet.git"
-  }
-  version: "562b5c4a5297e85ae2727fc9f6c4e96e770d7420"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2023
-    month: 10
-    day: 10
+    year: 2024
+    month: 2
+    day: 15
+  }
+  homepage: "https://square.github.io/kotlinpoet/"
+  identifier {
+    type: "Git"
+    value: "https://github.com/square/kotlinpoet.git"
+    version: "1.16.0"
   }
 }
diff --git a/RELEASING.md b/RELEASING.md
index 418d952..7c064c7 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -10,4 +10,6 @@
  7. `git push && git push --tags`.
 
 This will trigger a GitHub Action workflow which will create a GitHub release and upload the
-release artifacts to Maven Central.
+release artifacts to [Maven Central][maven-central].
+
+ [maven-central]: https://repo.maven.apache.org/maven2/com/squareup/kotlinpoet/
diff --git a/build.gradle.kts b/build.gradle.kts
index 3522375..16fa872 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -17,10 +17,12 @@
 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
 import org.jetbrains.dokka.gradle.DokkaTask
 import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
+  alias(libs.plugins.kotlin.multiplatform) apply false
   alias(libs.plugins.kotlin.jvm) apply false
   alias(libs.plugins.ksp) apply false
   alias(libs.plugins.dokka) apply false
@@ -53,12 +55,18 @@
     options.release.set(8)
   }
 
-  apply(plugin = "org.jetbrains.kotlin.jvm")
   if ("test" !in name && buildFile.exists()) {
     apply(plugin = "org.jetbrains.dokka")
     apply(plugin = "com.vanniktech.maven.publish")
-    configure<KotlinProjectExtension> {
-      explicitApi()
+    pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
+      configure<KotlinMultiplatformExtension> {
+        explicitApi()
+      }
+    }
+    pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
+      configure<KotlinProjectExtension> {
+        explicitApi()
+      }
     }
     afterEvaluate {
       tasks.named<DokkaTask>("dokkaHtml") {
@@ -70,7 +78,6 @@
       }
     }
   }
-
   apply(plugin = "com.diffplug.spotless")
   configure<SpotlessExtension> {
     kotlin {
@@ -98,7 +105,7 @@
         | * See the License for the specific language governing permissions and
         | * limitations under the License.
         | */
-        """.trimMargin()
+        """.trimMargin(),
       )
     }
   }
@@ -106,32 +113,44 @@
   // Only enable the extra toolchain tests on CI. Otherwise local development is broken on Apple Silicon macs
   // because there are no matching toolchains for several older JDK versions.
   if ("CI" in System.getenv()) {
-    // Copied from https://github.com/square/retrofit/blob/master/retrofit/build.gradle#L28.
-    // Create a test task for each supported JDK. We check every "LTS" + current version.
-    val versionsToTest = listOf(8, 11, 17, 19)
-    for (majorVersion in versionsToTest) {
-      val jdkTest = tasks.register<Test>("testJdk$majorVersion") {
-        val javaToolchains = project.extensions.getByType(JavaToolchainService::class)
-        javaLauncher.set(javaToolchains.launcherFor {
-          languageVersion.set(JavaLanguageVersion.of(majorVersion))
-          vendor.set(JvmVendorSpec.AZUL)
-        })
+    fun Project.setupCheckTask(testTaskName: String) {
+      // Copied from https://github.com/square/retrofit/blob/master/retrofit/build.gradle#L28.
+      // Create a test task for each supported JDK. We check every "LTS" + current version.
+      val versionsToTest = listOf(8, 11, 17, 19)
+      for (majorVersion in versionsToTest) {
+        val jdkTest = tasks.register<Test>("testJdk$majorVersion") {
+          val javaToolchains = project.extensions.getByType(JavaToolchainService::class)
+          javaLauncher.set(
+            javaToolchains.launcherFor {
+              languageVersion.set(JavaLanguageVersion.of(majorVersion))
+              vendor.set(JvmVendorSpec.AZUL)
+            },
+          )
 
-        description = "Runs the test suite on JDK $majorVersion"
-        group = LifecycleBasePlugin.VERIFICATION_GROUP
+          description = "Runs the test suite on JDK $majorVersion"
+          group = LifecycleBasePlugin.VERIFICATION_GROUP
 
-        // Copy inputs from normal Test task.
-        val testTask = tasks.getByName<Test>("test")
-        classpath = testTask.classpath
-        testClassesDirs = testTask.testClassesDirs
+          // Copy inputs from normal Test task.
+          val testTask =
+            tasks.getByName<Test>(testTaskName)
 
-        testLogging {
-          exceptionFormat = TestExceptionFormat.FULL
+          classpath = testTask.classpath
+          testClassesDirs = testTask.testClassesDirs
+
+          testLogging {
+            exceptionFormat = TestExceptionFormat.FULL
+          }
+        }
+        tasks.named("check").configure {
+          dependsOn(jdkTest)
         }
       }
-      tasks.named("check").configure {
-        dependsOn(jdkTest)
-      }
+    }
+    pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
+      setupCheckTask("jvmTest")
+    }
+    pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
+      setupCheckTask("test")
     }
   }
 }
@@ -140,6 +159,6 @@
   nonPublicMarkers += "com.squareup.kotlinpoet.ExperimentalKotlinPoetApi"
   ignoredProjects += listOf(
     "interop", // Empty middle package
-    "test-processor" // Test only
+    "test-processor", // Test only
   )
 }
diff --git a/docs/annotations.md b/docs/annotations.md
new file mode 100644
index 0000000..47a2cd9
--- /dev/null
+++ b/docs/annotations.md
@@ -0,0 +1,118 @@
+Annotations
+===========
+
+Simple annotations are easy:
+
+```kotlin
+val test = FunSpec.builder("test string equality")
+  .addAnnotation(Test::class)
+  .addStatement("assertThat(%1S).isEqualTo(%1S)", "foo")
+  .build()
+```
+
+Which generates this function with an `@Test` annotation:
+
+```kotlin
+@Test
+fun `test string equality`() {
+  assertThat("foo").isEqualTo("foo")
+}
+```
+
+Use `AnnotationSpec.builder()` to set properties on annotations:
+
+```kotlin
+val logRecord = FunSpec.builder("recordEvent")
+  .addModifiers(KModifier.ABSTRACT)
+  .addAnnotation(
+    AnnotationSpec.builder(Headers::class)
+      .addMember("accept = %S", "application/json; charset=utf-8")
+      .addMember("userAgent = %S", "Square Cash")
+      .build()
+  )
+  .addParameter("logRecord", LogRecord::class)
+  .returns(LogReceipt::class)
+  .build()
+```
+
+Which generates this annotation with `accept` and `userAgent` properties:
+
+```kotlin
+@Headers(
+  accept = "application/json; charset=utf-8",
+  userAgent = "Square Cash"
+)
+abstract fun recordEvent(logRecord: LogRecord): LogReceipt
+```
+
+When you get fancy, annotation values can be annotations themselves. Use `%L` for embedded
+annotations:
+
+```kotlin
+val headerList = ClassName("", "HeaderList")
+val header = ClassName("", "Header")
+val logRecord = FunSpec.builder("recordEvent")
+  .addModifiers(KModifier.ABSTRACT)
+  .addAnnotation(
+    AnnotationSpec.builder(headerList)
+      .addMember(
+        "[\n⇥%L,\n%L⇤\n]",
+        AnnotationSpec.builder(header)
+          .addMember("name = %S", "Accept")
+          .addMember("value = %S", "application/json; charset=utf-8")
+          .build(),
+        AnnotationSpec.builder(header)
+          .addMember("name = %S", "User-Agent")
+          .addMember("value = %S", "Square Cash")
+          .build()
+      )
+      .build()
+  )
+  .addParameter("logRecord", logRecordName)
+  .returns(logReceipt)
+  .build()
+```
+
+Which generates this:
+
+```kotlin
+@HeaderList(
+  [
+    Header(name = "Accept", value = "application/json; charset=utf-8"),
+    Header(name = "User-Agent", value = "Square Cash")
+  ]
+)
+abstract fun recordEvent(logRecord: LogRecord): LogReceipt
+```
+
+KotlinPoet supports use-site targets for annotations:
+
+```kotlin
+val utils = FileSpec.builder("com.example", "Utils")
+  .addAnnotation(
+    AnnotationSpec.builder(JvmName::class)
+      .useSiteTarget(UseSiteTarget.FILE)
+      .build()
+  )
+  .addFunction(
+    FunSpec.builder("abs")
+      .receiver(Int::class)
+      .returns(Int::class)
+      .addStatement("return if (this < 0) -this else this")
+      .build()
+  )
+  .build()
+```
+
+Will output this:
+
+```kotlin
+@file:JvmName
+
+package com.example
+
+import kotlin.Int
+import kotlin.jvm.JvmName
+
+fun Int.abs(): Int = if (this < 0) -this else this
+```
diff --git a/docs/anonymous-inner-classes.md b/docs/anonymous-inner-classes.md
new file mode 100644
index 0000000..a2f35da
--- /dev/null
+++ b/docs/anonymous-inner-classes.md
@@ -0,0 +1,44 @@
+Anonymous Inner Classes
+=======================
+
+In the enum code, we used `TypeSpec.anonymousClassBuilder()`. Anonymous inner classes can also be
+used in code blocks. They are values that can be referenced with `%L`:
+
+```kotlin
+val comparator = TypeSpec.anonymousClassBuilder()
+  .addSuperinterface(Comparator::class.parameterizedBy(String::class))
+  .addFunction(
+    FunSpec.builder("compare")
+      .addModifiers(KModifier.OVERRIDE)
+      .addParameter("a", String::class)
+      .addParameter("b", String::class)
+      .returns(Int::class)
+      .addStatement("return %N.length - %N.length", "a", "b")
+      .build()
+  )
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .addFunction(
+    FunSpec.builder("sortByLength")
+      .addParameter("strings", List::class.parameterizedBy(String::class))
+      .addStatement("%N.sortedWith(%L)", "strings", comparator)
+      .build()
+  )
+  .build()
+```
+
+This generates a method that contains a class that contains a method:
+
+```kotlin
+class HelloWorld {
+  fun sortByLength(strings: List<String>) {
+    strings.sortedWith(object : Comparator<String> {
+      override fun compare(a: String, b: String): Int = a.length - b.length
+    })
+  }
+}
+```
+
+One particularly tricky part of defining anonymous inner classes is the arguments to the superclass
+constructor. To pass them use `TypeSpec.Builder`'s `addSuperclassConstructorParameter()` method.
diff --git a/docs/callable-references.md b/docs/callable-references.md
new file mode 100644
index 0000000..a7bbdc8
--- /dev/null
+++ b/docs/callable-references.md
@@ -0,0 +1,45 @@
+Callable References
+===================
+
+[Callable references][callable-references] to constructors, functions, and properties may be emitted
+via:
+
+- `ClassName.constructorReference()` for constructors
+- `MemberName.reference()` for functions and properties
+
+For example,
+
+```kotlin
+val helloClass = ClassName("com.example.hello", "Hello")
+val worldFunction: MemberName = helloClass.member("world")
+val byeProperty: MemberName = helloClass.nestedClass("World").member("bye")
+
+val factoriesFun = FunSpec.builder("factories")
+  .addStatement("val hello = %L", helloClass.constructorReference())
+  .addStatement("val world = %L", worldFunction.reference())
+  .addStatement("val bye = %L", byeProperty.reference())
+  .build()
+
+FileSpec.builder("com.example", "HelloWorld")
+  .addFunction(factoriesFun)
+  .build()
+```
+
+would generate:
+
+```kotlin
+package com.example
+
+import com.example.hello.Hello
+
+fun factories() {
+  val hello = ::Hello
+  val world = Hello::world
+  val bye = Hello.World::bye
+}
+```
+
+Top-level classes and members with conflicting names may require aliased imports, as with
+[member names](m-for-members.md).
+
+ [callable-references]: https://kotlinlang.org/docs/reference/reflection.html#callable-references
diff --git a/docs/changelog.md b/docs/changelog.md
index 108d8e0..8f971fe 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -3,6 +3,71 @@
 
 ## Unreleased
 
+## Version 1.16.0
+
+Thanks to [@drawers][drawers], [@rickclephas][rickclephas] for contributing to this release.
+
+_2024-01-18_
+
+ * New: Kotlin 1.9.22.
+ * New: KSP 1.9.22-1.0.16.
+ * New: Add `NameAllocator` API to control keyword pre-allocation (#1803).
+ * Fix: Fix issue with missing `suspend` modifier in `KSTypeReference.toTypeName` (#1793).
+ * Fix: Honour same-package import aliases (#1794).
+ * Fix: Always include parameter docs in the type header (#1800).
+
+## Version 1.15.3
+
+Thanks to [@gabrielittner][gabrielittner] for contributing to this release.
+
+_2023-12-04_
+
+ * Fix: Fix nullability of lambdas in `KSTypeReference.toTypeName` (#1756).
+
+## Version 1.15.2
+
+Thanks to [@evant][evant] for contributing to this release.
+
+_2023-11-30_
+
+ * New: Kotlin 1.9.21.
+ * New: KSP 1.9.21-1.0.15.
+ * New: KSP: more accurately represent function types (#1742).
+
+## Version 1.15.1
+
+_2023-11-19_
+
+ * Fix: Fix a regression introduced by #1637, where a superfluous newline is added to a type's KDoc
+ if it has a primary constructor with no docs (#1727).
+
+## Version 1.15.0
+
+_2023-11-18_
+
+Thanks to [@drawers][drawers], [@fejesjoco][fejesjoco], [@takahirom][takahirom],
+[@martinbonnin][martinbonnin], [@mcarleio][mcarleio] for contributing to this release.
+
+In this release the `:kotlinpoet` module has been converted to a Kotlin Multiplatform module
+(#1654), though for now it only supports the JVM target. **Important**: unless you're building
+with Gradle, you will now need to depend on the `kotlinpoet-jvm` artifact, instead of `kotlinpoet` -
+see [Downloads](index.md#download) for instructions.
+
+ * New: Kotlin 1.9.20.
+ * New: KSP 1.9.20-1.0.14.
+ * New: Extract `TypeSpecHolder` interface for constructs that can hold a TypeSpec and their builders (#1723).
+ * New: Expose relative path from `FileSpec` (#1720).
+ * New: Return the generated path from `FileSpec.writeTo()`. (#1514).
+ * New: Remove default compatibility from unstable types (#1662).
+ * New: Deprecate `TypeSpec.expectClassBuilder()` and `TypeSpec.valueClassBuilder()` (#1589).
+ * New: Add option to convert `KSAnnotation` to `AnnotationSpec` while omitting default values (#1538).
+ * New: Add `FileSpec.builder` convenience for `MemberName` (#1585).
+ * Fix: Set `DecimalFormatSymbols.minusSign` for consistency across locales (#1658).
+ * Fix: Fix link to incremental KSP in KDoc (#1638).
+ * Fix: Emit primary constructor KDoc (#1637).
+ * Change: kotlinx-metadata 0.7.0. This is a breaking change for users of the `:kotlinpoet-metadata`
+   module, as most `Flags`-API extensions have been removed in favor of the now-available first-party versions.
+
 ## Version 1.14.2
 
 _2023-05-30_
@@ -716,3 +781,7 @@
  [Squiry]: https://github.com/Squiry
  [Omico]: https://github.com/Omico
  [RBusarow]: https://github.com/RBusarow
+ [fejesjoco]: https://github.com/fejesjoco
+ [takahirom]: https://github.com/takahirom
+ [mcarleio]: https://github.com/mcarleio
+ [gabrielittner]: https://github.com/gabrielittner
diff --git a/docs/code-block-format-strings.md b/docs/code-block-format-strings.md
new file mode 100644
index 0000000..86547b1
--- /dev/null
+++ b/docs/code-block-format-strings.md
@@ -0,0 +1,36 @@
+Code Block Format Strings
+=========================
+
+Code blocks may specify the values for their placeholders in a few ways. Only one style may be used
+for each operation on a code block.
+
+## Relative Arguments
+
+Pass an argument value for each placeholder in the format string to `CodeBlock.add()`. In each
+example, we generate code to say "I ate 3 tacos"
+
+```kotlin
+CodeBlock.builder().add("I ate %L %L", 3, "tacos")
+```
+
+## Positional Arguments
+
+Place an integer index (1-based) before the placeholder in the format string to specify which
+argument to use.
+
+```kotlin
+CodeBlock.builder().add("I ate %2L %1L", "tacos", 3)
+```
+
+## Named Arguments
+
+Use the syntax `%argumentName:X` where `X` is the format character and call `CodeBlock.addNamed()`
+with a map containing all argument keys in the format string. Argument names use characters in
+`a-z`, `A-Z`, `0-9`, and `_`, and must start with a lowercase character.
+
+```kotlin
+val map = LinkedHashMap<String, Any>()
+map += "food" to "tacos"
+map += "count" to 3
+CodeBlock.builder().addNamed("I ate %count:L %food:L", map)
+```
diff --git a/docs/code-control-flow.md b/docs/code-control-flow.md
new file mode 100644
index 0000000..f2a12f6
--- /dev/null
+++ b/docs/code-control-flow.md
@@ -0,0 +1,76 @@
+Code & Control Flow
+===================
+
+Most of KotlinPoet's API uses immutable Kotlin objects. There's also builders, method chaining
+and varargs to make the API friendly. KotlinPoet offers models for Kotlin files (`FileSpec`),
+classes, interfaces & objects (`TypeSpec`), type aliases (`TypeAliasSpec`),
+properties (`PropertySpec`), functions & constructors (`FunSpec`), parameters (`ParameterSpec`) and
+annotations (`AnnotationSpec`).
+
+But the _body_ of methods and constructors is not modeled. There's no expression class, no
+statement class or syntax tree nodes. Instead, KotlinPoet uses strings for code blocks, and you can
+take advantage of Kotlin's multiline strings to make this look nice:
+
+```kotlin
+val main = FunSpec.builder("main")
+  .addCode("""
+    |var total = 0
+    |for (i in 0..<10) {
+    |    total += i
+    |}
+    |""".trimMargin())
+  .build()
+```
+
+Which generates this:
+
+```kotlin
+fun main() {
+  var total = 0
+  for (i in 0..<10) {
+    total += i
+  }
+}
+```
+
+There are additional APIs to assist with newlines, braces and indentation:
+
+```kotlin
+val main = FunSpec.builder("main")
+  .addStatement("var total = 0")
+  .beginControlFlow("for (i in 0..<10)")
+  .addStatement("total += i")
+  .endControlFlow()
+  .build()
+```
+
+This example is lame because the generated code is constant! Suppose instead of just adding 0 to 10,
+we want to make the operation and range configurable. Here's a method that generates a method:
+
+```kotlin
+private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
+  return FunSpec.builder(name)
+    .returns(Int::class)
+    .addStatement("var result = 1")
+    .beginControlFlow("for (i in $from..<$to)")
+    .addStatement("result = result $op i")
+    .endControlFlow()
+    .addStatement("return result")
+    .build()
+}
+```
+
+And here's what we get when we call `computeRange("multiply10to20", 10, 20, "*")`:
+
+```kotlin
+fun multiply10to20(): kotlin.Int {
+  var result = 1
+  for (i in 10..<20) {
+    result = result * i
+  }
+  return result
+}
+```
+
+Methods generating methods! And since KotlinPoet generates source instead of bytecode, you can
+read through it to make sure it's right.
diff --git a/docs/constructors.md b/docs/constructors.md
new file mode 100644
index 0000000..65eef43
--- /dev/null
+++ b/docs/constructors.md
@@ -0,0 +1,80 @@
+Constructors
+============
+
+`FunSpec` is a slight misnomer; it can also be used for constructors:
+
+```kotlin
+val flux = FunSpec.constructorBuilder()
+  .addParameter("greeting", String::class)
+  .addStatement("this.%N = %N", "greeting", "greeting")
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .addProperty("greeting", String::class, KModifier.PRIVATE)
+  .addFunction(flux)
+  .build()
+```
+
+Which generates this:
+
+```kotlin
+class HelloWorld {
+  private val greeting: String
+
+  constructor(greeting: String) {
+    this.greeting = greeting
+  }
+}
+```
+
+For the most part, constructors work just like methods. When emitting code, KotlinPoet will place
+constructors before methods in the output file.
+
+Often times you'll need to generate the primary constructor for a class:
+
+```kotlin
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .primaryConstructor(flux)
+  .addProperty("greeting", String::class, KModifier.PRIVATE)
+  .build()
+```
+
+This code, however, generates the following:
+
+```kotlin
+class HelloWorld(greeting: String) {
+  private val greeting: String
+
+  init {
+    this.greeting = greeting
+  }
+}
+```
+
+By default, KotlinPoet won't merge primary constructor parameters and properties, even if they share
+the same name. To achieve the effect, you have to tell KotlinPoet that the property is initialized
+via the constructor parameter:
+
+```kotlin
+val flux = FunSpec.constructorBuilder()
+  .addParameter("greeting", String::class)
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .primaryConstructor(flux)
+  .addProperty(
+    PropertySpec.builder("greeting", String::class)
+      .initializer("greeting")
+      .addModifiers(KModifier.PRIVATE)
+      .build()
+  )
+  .build()
+```
+
+Now we're getting the following output:
+
+```kotlin
+class HelloWorld(private val greeting: String)
+```
+
+Notice that KotlinPoet omits `{}` for classes with empty bodies.
diff --git a/docs/enums.md b/docs/enums.md
new file mode 100644
index 0000000..229f98c
--- /dev/null
+++ b/docs/enums.md
@@ -0,0 +1,78 @@
+Enums
+=====
+
+Use `enumBuilder` to create the enum type, and `addEnumConstant()` for each value:
+
+```kotlin
+val helloWorld = TypeSpec.enumBuilder("Roshambo")
+  .addEnumConstant("ROCK")
+  .addEnumConstant("SCISSORS")
+  .addEnumConstant("PAPER")
+  .build()
+```
+
+To generate this:
+
+```kotlin
+enum class Roshambo {
+  ROCK,
+
+  SCISSORS,
+
+  PAPER
+}
+```
+
+Fancy enums are supported, where the enum values override methods or call a superclass constructor.
+Here's a comprehensive example:
+
+```kotlin
+val helloWorld = TypeSpec.enumBuilder("Roshambo")
+  .primaryConstructor(
+    FunSpec.constructorBuilder()
+      .addParameter("handsign", String::class)
+      .build()
+  )
+  .addEnumConstant(
+    "ROCK", TypeSpec.anonymousClassBuilder()
+      .addSuperclassConstructorParameter("%S", "fist")
+      .addFunction(
+        FunSpec.builder("toString")
+          .addModifiers(KModifier.OVERRIDE)
+          .addStatement("return %S", "avalanche!")
+          .returns(String::class)
+          .build()
+      )
+      .build()
+  )
+  .addEnumConstant(
+    "SCISSORS", TypeSpec.anonymousClassBuilder()
+      .addSuperclassConstructorParameter("%S", "peace")
+      .build()
+  )
+  .addEnumConstant(
+    "PAPER", TypeSpec.anonymousClassBuilder()
+      .addSuperclassConstructorParameter("%S", "flat")
+      .build()
+  )
+  .addProperty(
+    PropertySpec.builder("handsign", String::class, KModifier.PRIVATE)
+      .initializer("handsign")
+      .build()
+  )
+  .build()
+```
+
+Which generates this:
+
+```kotlin
+enum class Roshambo(private val handsign: String) {
+  ROCK("fist") {
+    override fun toString(): String = "avalanche!"
+  },
+
+  SCISSORS("peace"),
+
+  PAPER("flat");
+}
+```
diff --git a/docs/functions.md b/docs/functions.md
new file mode 100644
index 0000000..f24d19d
--- /dev/null
+++ b/docs/functions.md
@@ -0,0 +1,160 @@
+Functions
+=========
+
+All of the above functions have a code body. Use `KModifier.ABSTRACT` to get a function without any
+body. This is only legal if it is enclosed by an abstract class or an interface.
+
+```kotlin
+val flux = FunSpec.builder("flux")
+  .addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .addModifiers(KModifier.ABSTRACT)
+  .addFunction(flux)
+  .build()
+```
+
+Which generates this:
+
+```kotlin
+abstract class HelloWorld {
+  protected abstract fun flux()
+}
+```
+
+The other modifiers work where permitted.
+
+Methods also have parameters, varargs, KDoc, annotations, type variables, return type and receiver
+type for extension functions. All of these are configured with `FunSpec.Builder`.
+
+## Extension functions
+
+Extension functions can be generated by specifying a `receiver`.
+
+```kotlin
+val square = FunSpec.builder("square")
+  .receiver(Int::class)
+  .returns(Int::class)
+  .addStatement("var s = this * this")
+  .addStatement("return s")
+  .build()
+```
+
+Which outputs:
+
+```kotlin
+fun Int.square(): Int {
+  val s = this * this
+  return s
+}
+```
+
+## Single-expression functions
+
+KotlinPoet can recognize single-expression functions and print them out properly. It treats
+each function with a body that starts with `return` as a single-expression function:
+
+```kotlin
+val abs = FunSpec.builder("abs")
+  .addParameter("x", Int::class)
+  .returns(Int::class)
+  .addStatement("return if (x < 0) -x else x")
+  .build()
+```
+
+Which outputs:
+
+```kotlin
+fun abs(x: Int): Int = if (x < 0) -x else x
+```
+
+## Default function arguments
+
+Consider the example below.
+Function argument `b` has a default value of 0 to avoid overloading this function.
+
+```kotlin
+fun add(a: Int, b: Int = 0) {
+  print("a + b = ${a + b}")
+}
+```
+
+Use the `defaultValue()` builder function to declare default value for a function argument.
+
+```kotlin
+FunSpec.builder("add")
+  .addParameter("a", Int::class)
+  .addParameter(
+    ParameterSpec.builder("b", Int::class)
+      .defaultValue("%L", 0)
+      .build()
+  )
+  .addStatement("print(\"a + b = ${a + b}\")")
+  .build()
+```
+
+## Spaces wrap by default!
+
+In order to provide meaningful formatting, KotlinPoet would replace spaces, found in blocks of code,
+with new line symbols, in cases when the line of code exceeds the length limit. Let's take this
+function for example:
+
+```kotlin
+val funSpec = FunSpec.builder("foo")
+  .addStatement("return (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }")
+  .build()
+```
+
+Depending on where it's found in the file, it may end up being printed out like this:
+
+```kotlin
+fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also
+{ string -> println(string) }
+```
+
+Unfortunately this code is broken: the compiler expects `also` and `{` to be on the same line.
+KotlinPoet is unable to understand the context of the expression and fix the formatting for you, but
+there's a trick you can use to declare a non-breaking space - use the `·` symbol where you would
+otherwise use a space. Let's apply this to our example:
+
+```kotlin
+val funSpec = FunSpec.builder("foo")
+  .addStatement("return (100..10000).map·{ number -> number * number }.map·{ number -> number.toString() }.also·{ string -> println(string) }")
+  .build()
+```
+
+This will now produce the following result:
+
+```kotlin
+fun foo() = (100..10000).map { number -> number * number }.map { number ->
+  number.toString()
+}.also { string -> println(string) }
+```
+
+The code is now correct and will compile properly. It still doesn't look perfect - you can play with
+replacing other spaces in the code block with `·` symbols to achieve better formatting.
+
+Another common use case where you'd want to ensure spaces don't wrap is when emitting string
+literals:
+
+```kotlin
+CodeBlock.of("""println("Class: $className")""")
+```
+
+If `$className` is long, KotlinPoet may wrap the space that precedes it, resulting in broken output:
+
+```kotlin
+println("Class:
+very.long.class.name.Here")
+```
+
+KotlinPoet doesn't know that `"Class: $className"` is, in fact, a string literal, and that the space
+inside of it should never be wrapped. To make sure this case is handled correctly, use the `%S`
+modifier (as described in [%S for Strings](s-for-strings.md)):
+
+```kotlin
+CodeBlock.of("""println(%S)""", "Class: $className")
+```
+
+Now the library knows it's dealing with a string literal and can use appropriate line-wrapping rules.
diff --git a/docs/index.md b/docs/index.md
index 683f40e..27db6f3 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -7,7 +7,7 @@
 with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate
 the need to write boilerplate while also keeping a single source of truth for the metadata.
 
-### Example
+## Example
 
 Here's a `HelloWorld` file:
 
@@ -62,1470 +62,8 @@
 
 **Note:** In order to maximize portability, KotlinPoet generates code with explicit visibility
 modifiers. This ensures compatibility with both standard Kotlin projects as well as projects
-using [explicit API mode](https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors).
-Examples in this file omit those modifiers for brevity.
-
-### Code & Control Flow
-
-Most of KotlinPoet's API uses immutable Kotlin objects. There's also builders, method chaining
-and varargs to make the API friendly. KotlinPoet offers models for Kotlin files (`FileSpec`),
-classes, interfaces & objects (`TypeSpec`), type aliases (`TypeAliasSpec`),
-properties (`PropertySpec`), functions & constructors (`FunSpec`), parameters (`ParameterSpec`) and
-annotations (`AnnotationSpec`).
-
-But the _body_ of methods and constructors is not modeled. There's no expression class, no
-statement class or syntax tree nodes. Instead, KotlinPoet uses strings for code blocks, and you can
-take advantage of Kotlin's multiline strings to make this look nice:
-
-```kotlin
-val main = FunSpec.builder("main")
-  .addCode("""
-    |var total = 0
-    |for (i in 0..<10) {
-    |    total += i
-    |}
-    |""".trimMargin())
-  .build()
-```
-
-Which generates this:
-
-```kotlin
-fun main() {
-  var total = 0
-  for (i in 0..<10) {
-    total += i
-  }
-}
-```
-
-There are additional APIs to assist with newlines, braces and indentation:
-
-```kotlin
-val main = FunSpec.builder("main")
-  .addStatement("var total = 0")
-  .beginControlFlow("for (i in 0..<10)")
-  .addStatement("total += i")
-  .endControlFlow()
-  .build()
-```
-
-This example is lame because the generated code is constant! Suppose instead of just adding 0 to 10,
-we want to make the operation and range configurable. Here's a method that generates a method:
-
-```kotlin
-private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
-  return FunSpec.builder(name)
-    .returns(Int::class)
-    .addStatement("var result = 1")
-    .beginControlFlow("for (i in $from..<$to)")
-    .addStatement("result = result $op i")
-    .endControlFlow()
-    .addStatement("return result")
-    .build()
-}
-```
-
-And here's what we get when we call `computeRange("multiply10to20", 10, 20, "*")`:
-
-```kotlin
-fun multiply10to20(): kotlin.Int {
-  var result = 1
-  for (i in 10..<20) {
-    result = result * i
-  }
-  return result
-}
-```
-
-Methods generating methods! And since KotlinPoet generates source instead of bytecode, you can
-read through it to make sure it's right.
-
-### %S for Strings
-
-When emitting code that includes string literals, we can use **`%S`** to emit a **string**, complete
-with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which
-returns its own name:
-
-```kotlin
-fun main(args: Array<String>) {
-  val helloWorld = TypeSpec.classBuilder("HelloWorld")
-    .addFunction(whatsMyNameYo("slimShady"))
-    .addFunction(whatsMyNameYo("eminem"))
-    .addFunction(whatsMyNameYo("marshallMathers"))
-    .build()
-
-  val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
-    .addType(helloWorld)
-    .build()
-
-  kotlinFile.writeTo(System.out)
-}
-
-private fun whatsMyNameYo(name: String): FunSpec {
-  return FunSpec.builder(name)
-    .returns(String::class)
-    .addStatement("return %S", name)
-    .build()
-}
-```
-
-In this case, using `%S` gives us quotation marks:
-
-```kotlin
-class HelloWorld {
-  fun slimShady(): String = "slimShady"
-
-  fun eminem(): String = "eminem"
-
-  fun marshallMathers(): String = "marshallMathers"
-}
-```
-
-### %P for String Templates
-
-`%S` also handles the escaping of dollar signs (`$`), to avoid inadvertent creation of string
-templates, which may fail to compile in generated code:
-
-```kotlin
-val stringWithADollar = "Your total is " + "$" + "50"
-val funSpec = FunSpec.builder("printTotal")
-  .returns(String::class)
-  .addStatement("return %S", stringWithADollar)
-  .build()
-```
-
-produces:
-
-```kotlin
-fun printTotal(): String = "Your total is ${'$'}50"
-```
-
-If you need to generate string templates, use `%P`, which doesn't escape dollars:
-
-```kotlin
-val amount = 50
-val stringWithADollar = "Your total is " + "$" + "amount"
-val funSpec = FunSpec.builder("printTotal")
-  .returns(String::class)
-  .addStatement("return %P", stringWithADollar)
-  .build()
-```
-
-produces:
-
-```kotlin
-fun printTotal(): String = "Your total is $amount"
-```
-
-You can also use `CodeBlock`s as arguments to `%P`, which is handy when you need to reference
-importable types or members inside the string template:
-
-```kotlin
-val file = FileSpec.builder("com.example", "Digits")
-  .addFunction(
-    FunSpec.builder("print")
-      .addParameter("digits", IntArray::class)
-      .addStatement("println(%P)", buildCodeBlock {
-        val contentToString = MemberName("kotlin.collections", "contentToString")
-        add("These are the digits: \${digits.%M()}", contentToString)
-      })
-      .build()
-  )
-  .build()
-println(file)
-```
-
-The snippet above will produce the following output, handling the imports properly:
-
-```kotlin
-package com.example
-
-import kotlin.IntArray
-import kotlin.collections.contentToString
-
-fun print(digits: IntArray) {
-  println("""These are the digits: ${digits.contentToString()}""")
-}
-```
-
-### %T for Types
-
-KotlinPoet has rich built-in support for types, including automatic generation of `import`
-statements. Just use **`%T`** to reference **types**:
-
-```kotlin
-val today = FunSpec.builder("today")
-  .returns(Date::class)
-  .addStatement("return %T()", Date::class)
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .addFunction(today)
-  .build()
-
-val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
-  .addType(helloWorld)
-  .build()
-
-kotlinFile.writeTo(System.out)
-```
-
-That generates the following `.kt` file, complete with the necessary `import`:
-
-```kotlin
-package com.example.helloworld
-
-import java.util.Date
-
-class HelloWorld {
-  fun today(): Date = Date()
-}
-```
-
-We passed `Date::class` to reference a class that just-so-happens to be available when we're
-generating code. This doesn't need to be the case. Here's a similar example, but this one
-references a class that doesn't exist (yet):
-
-```kotlin
-val hoverboard = ClassName("com.mattel", "Hoverboard")
-
-val tomorrow = FunSpec.builder("tomorrow")
-  .returns(hoverboard)
-  .addStatement("return %T()", hoverboard)
-  .build()
-```
-
-And that not-yet-existent class is imported as well:
-
-```kotlin
-package com.example.helloworld
-
-import com.mattel.Hoverboard
-
-class HelloWorld {
-  fun tomorrow(): Hoverboard = Hoverboard()
-}
-```
-
-The `ClassName` type is very important, and you'll need it frequently when you're using KotlinPoet.
-It can identify any _declared_ class. Declared types are just the beginning of Kotlin's rich type
-system: we also have arrays, parameterized types, wildcard types, lambda types and type variables.
-KotlinPoet has classes for building each of these:
-
-```kotlin
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
-import com.squareup.kotlinpoet.STAR
-
-val hoverboard = ClassName("com.mattel", "Hoverboard")
-val list = ClassName("kotlin.collections", "List")
-val arrayList = ClassName("kotlin.collections", "ArrayList")
-val listOfHoverboards = list.parameterizedBy(hoverboard)
-val arrayListOfHoverboards = arrayList.parameterizedBy(hoverboard)
-
-val thing = ClassName("com.misc", "Thing")
-val array = ClassName("kotlin", "Array")
-val producerArrayOfThings = array.parameterizedBy(WildcardTypeName.producerOf(thing))
-
-val beyond = FunSpec.builder("beyond")
-  .returns(listOfHoverboards)
-  .addStatement("val result = %T()", arrayListOfHoverboards)
-  .addStatement("result += %T()", hoverboard)
-  .addStatement("result += %T()", hoverboard)
-  .addStatement("result += %T()", hoverboard)
-  .addStatement("return result")
-  .build()
-
-val printThings = FunSpec.builder("printThings")
-  .addParameter("things", producerArrayOfThings)
-  .addStatement("println(things)")
-  .build()
-
-val printKClass = FunSpec.builder("printKClass")
-  .addParameter("kClass", KClass::class.asClassName().parameterizedBy(STAR))
-  .addStatement("println(kClass)")
-  .build()
-```
-
-The `STAR` is represented as `*` in KotlinPoet. You can find more in the [KDoc][kdoc].
-
-KotlinPoet will decompose each type and import its components where possible.
-
-```kotlin
-package com.example.helloworld
-
-import com.mattel.Hoverboard
-import com.misc.Thing
-import kotlin.Array
-import kotlin.collections.ArrayList
-import kotlin.collections.List
-import kotlin.reflect.KClass
-
-class HelloWorld {
-  fun beyond(): List<Hoverboard> {
-    val result = ArrayList<Hoverboard>()
-    result += Hoverboard()
-    result += Hoverboard()
-    result += Hoverboard()
-    return result
-  }
-
-  fun printThings(things: Array<out Thing>) {
-    println(things)
-  }
-
-  fun printKClass(kClass: KClass<*>) {
-    println(kClass)
-  }
-}
-```
-
-#### Nullable Types
-
-KotlinPoet supports nullable types. To convert a `TypeName` into its nullable counterpart, use the
-`copy()` method with `nullable` parameter set to `true`:
-
-```kotlin
-val java = PropertySpec.builder("java", String::class.asTypeName().copy(nullable = true))
-  .mutable()
-  .addModifiers(KModifier.PRIVATE)
-  .initializer("null")
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .addProperty(java)
-  .addProperty("kotlin", String::class, KModifier.PRIVATE)
-  .build()
-```
-
-generates:
-
-```kotlin
-class HelloWorld {
-  private var java: String? = null
-
-  private val kotlin: String
-}
-```
-
-### %M for Members
-
-Similar to types, KotlinPoet has a special placeholder for **members** (functions and properties),
-which comes handy when your code needs to access top-level members and members declared inside
-objects. Use **`%M`** to reference members, pass an instance of `MemberName` as the argument for the
-placeholder, and KotlinPoet will handle imports automatically:
-
-```kotlin
-val createTaco = MemberName("com.squareup.tacos", "createTaco")
-val isVegan = MemberName("com.squareup.tacos", "isVegan")
-val file = FileSpec.builder("com.squareup.example", "TacoTest")
-  .addFunction(
-    FunSpec.builder("main")
-      .addStatement("val taco = %M()", createTaco)
-      .addStatement("println(taco.%M)", isVegan)
-      .build()
-  )
-  .build()
-println(file)
-```
-
-The code above generates the following file:
-
-```kotlin
-package com.squareup.example
-
-import com.squareup.tacos.createTaco
-import com.squareup.tacos.isVegan
-
-fun main() {
-  val taco = createTaco()
-  println(taco.isVegan)
-}
-```
-
-As you can see, it's also possible to use `%M` to reference extension functions and properties. You
-just need to make sure the member can be imported without simple name collisions, otherwise
-importing will fail and the code generator output will not pass compilation. There's a way to work
-around such cases though - use `FileSpec.addAliasedImport()` to create an alias for a clashing
-`MemberName`:
-
-```kotlin
-val createTaco = MemberName("com.squareup.tacos", "createTaco")
-val createCake = MemberName("com.squareup.cakes", "createCake")
-val isTacoVegan = MemberName("com.squareup.tacos", "isVegan")
-val isCakeVegan = MemberName("com.squareup.cakes", "isVegan")
-val file = FileSpec.builder("com.squareup.example", "Test")
-  .addAliasedImport(isTacoVegan, "isTacoVegan")
-  .addAliasedImport(isCakeVegan, "isCakeVegan")
-  .addFunction(
-    FunSpec.builder("main")
-      .addStatement("val taco = %M()", createTaco)
-      .addStatement("val cake = %M()", createCake)
-      .addStatement("println(taco.%M)", isTacoVegan)
-      .addStatement("println(cake.%M)", isCakeVegan)
-      .build()
-  )
-  .build()
-println(file)
-```
-
-KotlinPoet will produce an aliased import for `com.squareup.tacos2.isVegan`:
-
-```kotlin
-package com.squareup.example
-
-import com.squareup.cakes.createCake
-import com.squareup.tacos.createTaco
-import com.squareup.cakes.isVegan as isCakeVegan
-import com.squareup.tacos.isVegan as isTacoVegan
-
-fun main() {
-  val taco = createTaco()
-  val cake = createCake()
-  println(taco.isTacoVegan)
-  println(cake.isCakeVegan)
-}
-```
-
-#### MemberName and operators
-
-MemberName also supports operators, you can use `MemberName(String, KOperator)`
-or `MemberName(ClassName, KOperator)` to import and reference operators.
-
-```kotlin
-val taco = ClassName("com.squareup.tacos", "Taco")
-val meat = ClassName("com.squareup.tacos.ingredient", "Meat")
-val iterator = MemberName("com.squareup.tacos.internal", KOperator.ITERATOR)
-val minusAssign = MemberName("com.squareup.tacos.internal", KOperator.MINUS_ASSIGN)
-val file = FileSpec.builder("com.example", "Test")
-  .addFunction(
-    FunSpec.builder("makeTacoHealthy")
-      .addParameter("taco", taco)
-      .beginControlFlow("for (ingredient %M taco)", iterator)
-      .addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign)
-      .endControlFlow()
-      .addStatement("return taco")
-      .build()
-  )
-  .build()
-println(file)
-```
-
-KotlinPoet will import the extension operator functions and emit the operator.
-
-```kotlin
-package com.example
-
-import com.squareup.tacos.Taco
-import com.squareup.tacos.ingredient.Meat
-import com.squareup.tacos.internal.iterator
-import com.squareup.tacos.internal.minusAssign
-
-fun makeTacoHealthy(taco: Taco) {
-  for (ingredient in taco) {
-    if (ingredient is Meat) taco -= ingredient
-  }
-  return taco
-}
-
-```
-
-### %N for Names
-
-Generated code is often self-referential. Use **`%N`** to refer to another generated declaration by
-its name. Here's a method that calls another:
-
-```kotlin
-fun byteToHex(b: Int): String {
-  val result = CharArray(2)
-  result[0] = hexDigit((b ushr 4) and 0xf)
-  result[1] = hexDigit(b and 0xf)
-  return String(result)
-}
-
-fun hexDigit(i: Int): Char {
-  return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()
-}
-```
-
-When generating the code above, we pass the `hexDigit()` method as an argument to the `byteToHex()`
-method using `%N`:
-
-```kotlin
-val hexDigit = FunSpec.builder("hexDigit")
-  .addParameter("i", Int::class)
-  .returns(Char::class)
-  .addStatement("return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()")
-  .build()
-
-val byteToHex = FunSpec.builder("byteToHex")
-  .addParameter("b", Int::class)
-  .returns(String::class)
-  .addStatement("val result = CharArray(2)")
-  .addStatement("result[0] = %N((b ushr 4) and 0xf)", hexDigit)
-  .addStatement("result[1] = %N(b and 0xf)", hexDigit)
-  .addStatement("return String(result)")
-  .build()
-```
-
-Another handy feature that `%N` provides is automatically escaping names that contain illegal
-identifier characters with double ticks. Suppose your code creates a `MemberName` with a Kotlin
-keyword as the simple name:
-
-```kotlin
-val taco = ClassName("com.squareup.tacos", "Taco")
-val packager = ClassName("com.squareup.tacos", "TacoPackager")
-val file = FileSpec.builder("com.example", "Test")
-  .addFunction(
-    FunSpec.builder("packageTacos")
-      .addParameter("tacos", LIST.parameterizedBy(taco))
-      .addParameter("packager", packager)
-      .addStatement("packager.%N(tacos)", packager.member("package"))
-      .build()
-  )
-  .build()
-```
-
-`%N` will escape the name for you, ensuring that the output will pass compilation:
-
-```kotlin
-package com.example
-
-import com.squareup.tacos.Taco
-import com.squareup.tacos.TacoPackager
-import kotlin.collections.List
-
-fun packageTacos(tacos: List<Taco>, packager: TacoPackager) {
-  packager.`package`(tacos)
-}
-```
-
-### %L for Literals
-
-Although Kotlin's string templates usually work well in cases when you want to include literals into
-generated code, KotlinPoet offers additional syntax inspired-by but incompatible-with
-[`String.format()`][formatter]. It accepts **`%L`** to emit a **literal** value in the output. This
-works just like `Formatter`'s `%s`:
-
-```kotlin
-private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
-  return FunSpec.builder(name)
-    .returns(Int::class)
-    .addStatement("var result = 0")
-    .beginControlFlow("for (i in %L..<%L)", from, to)
-    .addStatement("result = result %L i", op)
-    .endControlFlow()
-    .addStatement("return result")
-    .build()
-}
-```
-
-Literals are emitted directly to the output code with no escaping. Arguments for literals may be
-strings, primitives, and a few KotlinPoet types described below.
-
-### Code block format strings
-
-Code blocks may specify the values for their placeholders in a few ways. Only one style may be used
-for each operation on a code block.
-
-#### Relative Arguments
-
-Pass an argument value for each placeholder in the format string to `CodeBlock.add()`. In each
-example, we generate code to say "I ate 3 tacos"
-
-```kotlin
-CodeBlock.builder().add("I ate %L %L", 3, "tacos")
-```
-
-#### Positional Arguments
-
-Place an integer index (1-based) before the placeholder in the format string to specify which
-argument to use.
-
-```kotlin
-CodeBlock.builder().add("I ate %2L %1L", "tacos", 3)
-```
-
-#### Named Arguments
-
-Use the syntax `%argumentName:X` where `X` is the format character and call `CodeBlock.addNamed()`
-with a map containing all argument keys in the format string. Argument names use characters in
-`a-z`, `A-Z`, `0-9`, and `_`, and must start with a lowercase character.
-
-```kotlin
-val map = LinkedHashMap<String, Any>()
-map += "food" to "tacos"
-map += "count" to 3
-CodeBlock.builder().addNamed("I ate %count:L %food:L", map)
-```
-
-### Functions
-
-All of the above functions have a code body. Use `KModifier.ABSTRACT` to get a function without any
-body. This is only legal if it is enclosed by an abstract class or an interface.
-
-```kotlin
-val flux = FunSpec.builder("flux")
-  .addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .addModifiers(KModifier.ABSTRACT)
-  .addFunction(flux)
-  .build()
-```
-
-Which generates this:
-
-```kotlin
-abstract class HelloWorld {
-  protected abstract fun flux()
-}
-```
-
-The other modifiers work where permitted.
-
-Methods also have parameters, varargs, KDoc, annotations, type variables, return type and receiver
-type for extension functions. All of these are configured with `FunSpec.Builder`.
-
-#### Extension functions
-
-Extension functions can be generated by specifying a `receiver`.
-
-```kotlin
-val square = FunSpec.builder("square")
-  .receiver(Int::class)
-  .returns(Int::class)
-  .addStatement("var s = this * this")
-  .addStatement("return s")
-  .build()
-```
-
-Which outputs:
-
-```kotlin
-fun Int.square(): Int {
-  val s = this * this
-  return s
-}
-```
-
-#### Single-expression functions
-
-KotlinPoet can recognize single-expression functions and print them out properly. It treats
-each function with a body that starts with `return` as a single-expression function:
-
-```kotlin
-val abs = FunSpec.builder("abs")
-  .addParameter("x", Int::class)
-  .returns(Int::class)
-  .addStatement("return if (x < 0) -x else x")
-  .build()
-```
-
-Which outputs:
-
-```kotlin
-fun abs(x: Int): Int = if (x < 0) -x else x
-```
-
-#### Default function arguments
-
-Consider the example below.
-Function argument `b` has a default value of 0 to avoid overloading this function.
-
-```kotlin
-fun add(a: Int, b: Int = 0) {
-  print("a + b = ${a + b}")
-}
-```
-
-Use the `defaultValue()` builder function to declare default value for a function argument.
-
-```kotlin
-FunSpec.builder("add")
-  .addParameter("a", Int::class)
-  .addParameter(
-    ParameterSpec.builder("b", Int::class)
-      .defaultValue("%L", 0)
-      .build()
-  )
-  .addStatement("print(\"a + b = ${a + b}\")")
-  .build()
-```
-
-#### Spaces wrap by default!
-
-In order to provide meaningful formatting, KotlinPoet would replace spaces, found in blocks of code,
-with new line symbols, in cases when the line of code exceeds the length limit. Let's take this
-function for example:
-
-```kotlin
-val funSpec = FunSpec.builder("foo")
-  .addStatement("return (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }")
-  .build()
-```
-
-Depending on where it's found in the file, it may end up being printed out like this:
-
-```kotlin
-fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also
-{ string -> println(string) }
-```
-
-Unfortunately this code is broken: the compiler expects `also` and `{` to be on the same line.
-KotlinPoet is unable to understand the context of the expression and fix the formatting for you, but
-there's a trick you can use to declare a non-breaking space - use the `·` symbol where you would
-otherwise use a space. Let's apply this to our example:
-
-```kotlin
-val funSpec = FunSpec.builder("foo")
-  .addStatement("return (100..10000).map·{ number -> number * number }.map·{ number -> number.toString() }.also·{ string -> println(string) }")
-  .build()
-```
-
-This will now produce the following result:
-
-```kotlin
-fun foo() = (100..10000).map { number -> number * number }.map { number ->
-  number.toString()
-}.also { string -> println(string) }
-```
-
-The code is now correct and will compile properly. It still doesn't look perfect - you can play with
-replacing other spaces in the code block with `·` symbols to achieve better formatting.
-
-Another common use case where you'd want to ensure spaces don't wrap is when emitting string literals:
-
-```kotlin
-CodeBlock.of("""println("Class: $className")""")
-```
-
-If `$className` is long, KotlinPoet may wrap the space that precedes it, resulting in broken output:
-
-```kotlin
-println("Class:
-very.long.class.name.Here")
-```
-
-KotlinPoet doesn't know that `"Class: $className"` is, in fact, a string literal, and that the space inside of it
-should never be wrapped. To make sure this case is handled correctly, use the `%S` modifier (as described in
-[%S for Strings](#s-for-strings)):
-
-```kotlin
-CodeBlock.of("""println(%S)""", "Class: $className")
-```
-
-Now the library knows it's dealing with a string literal and can use appropriate line-wrapping rules.
-
-### Constructors
-
-`FunSpec` is a slight misnomer; it can also be used for constructors:
-
-```kotlin
-val flux = FunSpec.constructorBuilder()
-  .addParameter("greeting", String::class)
-  .addStatement("this.%N = %N", "greeting", "greeting")
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .addProperty("greeting", String::class, KModifier.PRIVATE)
-  .addFunction(flux)
-  .build()
-```
-
-Which generates this:
-
-```kotlin
-class HelloWorld {
-  private val greeting: String
-
-  constructor(greeting: String) {
-    this.greeting = greeting
-  }
-}
-```
-
-For the most part, constructors work just like methods. When emitting code, KotlinPoet will place
-constructors before methods in the output file.
-
-Often times you'll need to generate the primary constructor for a class:
-
-```kotlin
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .primaryConstructor(flux)
-  .addProperty("greeting", String::class, KModifier.PRIVATE)
-  .build()
-```
-
-This code, however, generates the following:
-
-```kotlin
-class HelloWorld(greeting: String) {
-  private val greeting: String
-
-  init {
-    this.greeting = greeting
-  }
-}
-```
-
-By default, KotlinPoet won't merge primary constructor parameters and properties, even if they share
-the same name. To achieve the effect, you have to tell KotlinPoet that the property is initialized
-via the constructor parameter:
-
-```kotlin
-val flux = FunSpec.constructorBuilder()
-  .addParameter("greeting", String::class)
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .primaryConstructor(flux)
-  .addProperty(
-    PropertySpec.builder("greeting", String::class)
-      .initializer("greeting")
-      .addModifiers(KModifier.PRIVATE)
-      .build()
-  )
-  .build()
-```
-
-Now we're getting the following output:
-
-```kotlin
-class HelloWorld(private val greeting: String)
-```
-
-Notice that KotlinPoet omits `{}` for classes with empty bodies.
-
-### Parameters
-
-Declare parameters on methods and constructors with either `ParameterSpec.builder()` or
-`FunSpec`'s convenient `addParameter()` API:
-
-```kotlin
-val android = ParameterSpec.builder("android", String::class)
-  .defaultValue("\"pie\"")
-  .build()
-
-val welcomeOverlords = FunSpec.builder("welcomeOverlords")
-  .addParameter(android)
-  .addParameter("robot", String::class)
-  .build()
-```
-
-The code above generates:
-
-```kotlin
-fun welcomeOverlords(android: String = "pie", robot: String) {
-}
-```
-
-The extended `Builder` form is necessary when the parameter has annotations (such as `@Inject`).
-
-### Properties
-
-Like parameters, properties can be created either with builders or by using convenient helper
-methods:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
-  .addModifiers(KModifier.PRIVATE)
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .addProperty(android)
-  .addProperty("robot", String::class, KModifier.PRIVATE)
-  .build()
-```
-
-Which generates:
-
-```kotlin
-class HelloWorld {
-  private val android: String
-
-  private val robot: String
-}
-```
-
-The extended `Builder` form is necessary when a field has KDoc, annotations, or a field
-initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code
-blocks above:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
-  .addModifiers(KModifier.PRIVATE)
-  .initializer("%S + %L", "Oreo v.", 8.1)
-  .build()
-```
-
-Which generates:
-
-```kotlin
-private val android: String = "Oreo v." + 8.1
-```
-
-By default `PropertySpec.Builder` produces `val` properties. Use `mutable()` if you need a
-`var`:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
-  .mutable()
-  .addModifiers(KModifier.PRIVATE)
-  .initializer("%S + %L", "Oreo v.", 8.1)
-  .build()
-```
-
-#### Inline properties
-
-The way KotlinPoet models inline properties deserves special mention. The following snippet of code:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
-  .mutable()
-  .addModifiers(KModifier.INLINE)
-  .build()
-```
-
-will produce an error:
-
-```
-java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on
-properties. You should mark either the getter, the setter, or both inline.
-```
-
-Indeed, a property marked with `inline` should have at least one accessor which will be inlined by
-the compiler. Let's add a getter to this property:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
-  .mutable()
-  .getter(
-    FunSpec.getterBuilder()
-      .addModifiers(KModifier.INLINE)
-      .addStatement("return %S", "foo")
-      .build()
-  )
-  .build()
-```
-
-The result is the following:
-
-```kotlin
-var android: kotlin.String
-  inline get() = "foo"
-```
-
-Now, what if we wanted to add a non-inline setter to the property above? We can do so without
-modifying any of the code we wrote previously:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
-  .mutable()
-  .getter(
-    FunSpec.getterBuilder()
-      .addModifiers(KModifier.INLINE)
-      .addStatement("return %S", "foo")
-      .build()
-  )
-  .setter(
-    FunSpec.setterBuilder()
-      .addParameter("value", String::class)
-      .build()
-  )
-  .build()
-```
-
-We get the expected result:
-
-```kotlin
-var android: kotlin.String
-  inline get() = "foo"
-  set(`value`) {
-  }
-```
-
-Finally, if we go back and add `KModifier.INLINE` to the setter, KotlinPoet can wrap it nicely and
-produce the following result:
-
-```kotlin
-inline var android: kotlin.String
-  get() = "foo"
-  set(`value`) {
-  }
-```
-
-Removing the modifier from either the getter or the setter will unwrap the expression back.
-
-If, on the other hand, KotlinPoet had allowed marking a property `inline` directly, the programmer
-would have had to manually add/remove the modifier whenever the state of the accessors changes in
-order to get correct and compilable output. We're solving this problem by making accessors the
-source of truth for the `inline` modifier.
-
-### Interfaces
-
-KotlinPoet has no trouble with interfaces. Note that interface methods must always be `ABSTRACT`.
-The modifier is necessary when defining the interface:
-
-```kotlin
-val helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
-  .addProperty("buzz", String::class)
-  .addFunction(
-    FunSpec.builder("beep")
-      .addModifiers(KModifier.ABSTRACT)
-      .build()
-  )
-  .build()
-```
-
-But these modifiers are omitted when the code is generated. These are the default so we don't need
-to include them for `kotlinc`'s benefit!
-
-```kotlin
-interface HelloWorld {
-  val buzz: String
-
-  fun beep()
-}
-```
-
-Kotlin 1.4 adds support for functional interfaces via `fun interface` syntax. To create this in
-KotlinPoet, use `TypeSpec.funInterfaceBuilder()`.
-
-```kotlin
-val helloWorld = TypeSpec.funInterfaceBuilder("HelloWorld")
-  .addFunction(
-    FunSpec.builder("beep")
-      .addModifiers(KModifier.ABSTRACT)
-      .build()
-  )
-  .build()
-
-// Generates...
-fun interface HelloWorld {
-  fun beep()
-}
-```
-
-### Objects
-
-KotlinPoet supports objects:
-
-```kotlin
-val helloWorld = TypeSpec.objectBuilder("HelloWorld")
-  .addProperty(
-    PropertySpec.builder("buzz", String::class)
-      .initializer("%S", "buzz")
-      .build()
-  )
-  .addFunction(
-    FunSpec.builder("beep")
-      .addStatement("println(%S)", "Beep!")
-      .build()
-  )
-  .build()
-```
-
-Similarly, you can create companion objects and add them to classes using `addType()`:
-
-```kotlin
-val companion = TypeSpec.companionObjectBuilder()
-  .addProperty(
-    PropertySpec.builder("buzz", String::class)
-      .initializer("%S", "buzz")
-      .build()
-  )
-  .addFunction(
-    FunSpec.builder("beep")
-      .addStatement("println(%S)", "Beep!")
-      .build()
-  )
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .addType(companion)
-  .build()
-```
-
-You can provide an optional name for a companion object.
-
-### Enums
-
-Use `enumBuilder` to create the enum type, and `addEnumConstant()` for each value:
-
-```kotlin
-val helloWorld = TypeSpec.enumBuilder("Roshambo")
-  .addEnumConstant("ROCK")
-  .addEnumConstant("SCISSORS")
-  .addEnumConstant("PAPER")
-  .build()
-```
-
-To generate this:
-
-```kotlin
-enum class Roshambo {
-  ROCK,
-
-  SCISSORS,
-
-  PAPER
-}
-```
-
-Fancy enums are supported, where the enum values override methods or call a superclass constructor.
-Here's a comprehensive example:
-
-```kotlin
-val helloWorld = TypeSpec.enumBuilder("Roshambo")
-  .primaryConstructor(
-    FunSpec.constructorBuilder()
-      .addParameter("handsign", String::class)
-      .build()
-  )
-  .addEnumConstant(
-    "ROCK", TypeSpec.anonymousClassBuilder()
-      .addSuperclassConstructorParameter("%S", "fist")
-      .addFunction(
-        FunSpec.builder("toString")
-          .addModifiers(KModifier.OVERRIDE)
-          .addStatement("return %S", "avalanche!")
-          .returns(String::class)
-          .build()
-      )
-      .build()
-  )
-  .addEnumConstant(
-    "SCISSORS", TypeSpec.anonymousClassBuilder()
-      .addSuperclassConstructorParameter("%S", "peace")
-      .build()
-  )
-  .addEnumConstant(
-    "PAPER", TypeSpec.anonymousClassBuilder()
-      .addSuperclassConstructorParameter("%S", "flat")
-      .build()
-  )
-  .addProperty(
-    PropertySpec.builder("handsign", String::class, KModifier.PRIVATE)
-      .initializer("handsign")
-      .build()
-  )
-  .build()
-```
-
-Which generates this:
-
-```kotlin
-enum class Roshambo(private val handsign: String) {
-  ROCK("fist") {
-    override fun toString(): String = "avalanche!"
-  },
-
-  SCISSORS("peace"),
-
-  PAPER("flat");
-}
-```
-
-### Anonymous Inner Classes
-
-In the enum code, we used `TypeSpec.anonymousClassBuilder()`. Anonymous inner classes can also be
-used in code blocks. They are values that can be referenced with `%L`:
-
-```kotlin
-val comparator = TypeSpec.anonymousClassBuilder()
-  .addSuperinterface(Comparator::class.parameterizedBy(String::class))
-  .addFunction(
-    FunSpec.builder("compare")
-      .addModifiers(KModifier.OVERRIDE)
-      .addParameter("a", String::class)
-      .addParameter("b", String::class)
-      .returns(Int::class)
-      .addStatement("return %N.length - %N.length", "a", "b")
-      .build()
-  )
-  .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
-  .addFunction(
-    FunSpec.builder("sortByLength")
-      .addParameter("strings", List::class.parameterizedBy(String::class))
-      .addStatement("%N.sortedWith(%L)", "strings", comparator)
-      .build()
-  )
-  .build()
-```
-
-This generates a method that contains a class that contains a method:
-
-```kotlin
-class HelloWorld {
-  fun sortByLength(strings: List<String>) {
-    strings.sortedWith(object : Comparator<String> {
-      override fun compare(a: String, b: String): Int = a.length - b.length
-    })
-  }
-}
-```
-
-One particularly tricky part of defining anonymous inner classes is the arguments to the superclass
-constructor. To pass them use `TypeSpec.Builder`'s `addSuperclassConstructorParameter()` method.
-
-### Annotations
-
-Simple annotations are easy:
-
-```kotlin
-val test = FunSpec.builder("test string equality")
-  .addAnnotation(Test::class)
-  .addStatement("assertThat(%1S).isEqualTo(%1S)", "foo")
-  .build()
-```
-
-Which generates this function with an `@Test` annotation:
-
-```kotlin
-@Test
-fun `test string equality`() {
-  assertThat("foo").isEqualTo("foo")
-}
-```
-
-Use `AnnotationSpec.builder()` to set properties on annotations:
-
-```kotlin
-val logRecord = FunSpec.builder("recordEvent")
-  .addModifiers(KModifier.ABSTRACT)
-  .addAnnotation(
-    AnnotationSpec.builder(Headers::class)
-      .addMember("accept = %S", "application/json; charset=utf-8")
-      .addMember("userAgent = %S", "Square Cash")
-      .build()
-  )
-  .addParameter("logRecord", LogRecord::class)
-  .returns(LogReceipt::class)
-  .build()
-```
-
-Which generates this annotation with `accept` and `userAgent` properties:
-
-```kotlin
-@Headers(
-  accept = "application/json; charset=utf-8",
-  userAgent = "Square Cash"
-)
-abstract fun recordEvent(logRecord: LogRecord): LogReceipt
-```
-
-When you get fancy, annotation values can be annotations themselves. Use `%L` for embedded
-annotations:
-
-```kotlin
-val headerList = ClassName("", "HeaderList")
-val header = ClassName("", "Header")
-val logRecord = FunSpec.builder("recordEvent")
-  .addModifiers(KModifier.ABSTRACT)
-  .addAnnotation(
-    AnnotationSpec.builder(headerList)
-      .addMember(
-        "[\n⇥%L,\n%L⇤\n]",
-        AnnotationSpec.builder(header)
-          .addMember("name = %S", "Accept")
-          .addMember("value = %S", "application/json; charset=utf-8")
-          .build(),
-        AnnotationSpec.builder(header)
-          .addMember("name = %S", "User-Agent")
-          .addMember("value = %S", "Square Cash")
-          .build()
-      )
-      .build()
-  )
-  .addParameter("logRecord", logRecordName)
-  .returns(logReceipt)
-  .build()
-```
-
-Which generates this:
-
-```kotlin
-@HeaderList(
-  [
-    Header(name = "Accept", value = "application/json; charset=utf-8"),
-    Header(name = "User-Agent", value = "Square Cash")
-  ]
-)
-abstract fun recordEvent(logRecord: LogRecord): LogReceipt
-```
-
-KotlinPoet supports use-site targets for annotations:
-
-```kotlin
-val utils = FileSpec.builder("com.example", "Utils")
-  .addAnnotation(
-    AnnotationSpec.builder(JvmName::class)
-      .useSiteTarget(UseSiteTarget.FILE)
-      .build()
-  )
-  .addFunction(
-    FunSpec.builder("abs")
-      .receiver(Int::class)
-      .returns(Int::class)
-      .addStatement("return if (this < 0) -this else this")
-      .build()
-  )
-  .build()
-```
-
-Will output this:
-
-```kotlin
-@file:JvmName
-
-package com.example
-
-import kotlin.Int
-import kotlin.jvm.JvmName
-
-fun Int.abs(): Int = if (this < 0) -this else this
-```
-
-### Type Aliases
-
-KotlinPoet provides API for creating Type Aliases, which supports simple class names, parameterized
-types and lambdas:
-
-```kotlin
-val k = TypeVariableName("K")
-val t = TypeVariableName("T")
-
-val fileTable = Map::class.asClassName()
-  .parameterizedBy(k, Set::class.parameterizedBy(File::class))
-
-val predicate = LambdaTypeName.get(
-  parameters = arrayOf(t),
-  returnType = Boolean::class.asClassName()
-)
-val helloWorld = FileSpec.builder("com.example", "HelloWorld")
-  .addTypeAlias(TypeAliasSpec.builder("Word", String::class).build())
-  .addTypeAlias(
-    TypeAliasSpec.builder("FileTable", fileTable)
-      .addTypeVariable(k)
-      .build()
-  )
-  .addTypeAlias(
-    TypeAliasSpec.builder("Predicate", predicate)
-      .addTypeVariable(t)
-      .build()
-  )
-  .build()
-```
-
-Which generates the following:
-
-```kotlin
-package com.example
-
-import java.io.File
-import kotlin.Boolean
-import kotlin.String
-import kotlin.collections.Map
-import kotlin.collections.Set
-
-typealias Word = String
-
-typealias FileTable<K> = Map<K, Set<File>>
-
-typealias Predicate<T> = (T) -> Boolean
-```
-
-### Callable References
-
-[Callable references](https://kotlinlang.org/docs/reference/reflection.html#callable-references) to
-constructors, functions, and properties may be emitted via:
-
-- `ClassName.constructorReference()` for constructors
-- `MemberName.reference()` for functions and properties
-
-For example,
-
-```kotlin
-val helloClass = ClassName("com.example.hello", "Hello")
-val worldFunction: MemberName = helloClass.member("world")
-val byeProperty: MemberName = helloClass.nestedClass("World").member("bye")
-
-val factoriesFun = FunSpec.builder("factories")
-  .addStatement("val hello = %L", helloClass.constructorReference())
-  .addStatement("val world = %L", worldFunction.reference())
-  .addStatement("val bye = %L", byeProperty.reference())
-  .build()
-
-FileSpec.builder("com.example", "HelloWorld")
-  .addFunction(factoriesFun)
-  .build()
-```
-
-would generate:
-
-```kotlin
-package com.example
-
-import com.example.hello.Hello
-
-fun factories() {
-  val hello = ::Hello
-  val world = Hello::world
-  val bye = Hello.World::bye
-}
-```
-
-Top-level classes and members with conflicting names may require aliased imports, as with
-[member names](#m-for-members).
-
-kotlin-reflect
---------
-
-To generate source code from
-any [`KType`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-type/), including
-information that's not accessible to the builtin reflection APIs, KotlinPoet depends
-on [kotlin-reflect](https://kotlinlang.org/docs/reflection.html#jvm-dependency). `kotlin-reflect`
-can read the metadata of your classes and access this extra information. KotlinPoet can for an
-example, read the type parameters and
-their [variance](https://kotlinlang.org/docs/generics.html#variance) from a generic `KType` and
-generate appropriate source code.
-
-`kotlin-reflect` is a relatively big dependency though and in some cases it is desirable to remove
-it from the final executable to save some space and/or simplify the proguard/R8 setup (for example
-for a Gradle plugin that generates Kotlin code). It is possible to do so and still use most of the
-KotlinPoet APIs:
-
-```kotlin
-dependencies {
-  implementation("com.squareup:kotlinpoet:<version>") {
-    exclude(module = "kotlin-reflect")
-  }
-}
-```
-
-The main APIs that require `kotlin-reflect`
-are [`KType.asTypeName()`](https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/as-type-name.html)
-and [`typeNameOf<T>()`](https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/type-name-of.html).
-If you're calling one of these without `kotlin-reflect` in the classpath and the type is generic
-or has annotations you will get a crash.
-
-You can replace it with code that passes type parameters or annotations explicitly and doesn't
-need `kotlin-reflect`. For example:
-
-```kotlin
-// Replace
-// kotlin-reflect needed
-val typeName = typeNameOf<List<Int?>>()
-
-// With
-// kotlin-reflect not needed
-val typeName =
-  List::class.asClassName().parameterizedBy(Int::class.asClassName().copy(nullable = true))
-```
+using [explicit API mode][explicit-api]. Examples in the documentation omit those modifiers for
+brevity.
 
 Download
 --------
@@ -1537,7 +75,7 @@
 ```xml
 <dependency>
   <groupId>com.squareup</groupId>
-  <artifactId>kotlinpoet</artifactId>
+  <artifactId>kotlinpoet-jvm</artifactId>
   <version>[version]</version>
 </dependency>
 ```
@@ -1550,7 +88,6 @@
 
 Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap].
 
-
 License
 -------
 
@@ -1568,10 +105,9 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 
-
- [dl]: https://search.maven.org/remote_content?g=com.squareup&a=kotlinpoet&v=LATEST
- [version-shield]: https://img.shields.io/maven-central/v/com.squareup/kotlinpoet
- [snap]: https://s01.oss.sonatype.org/content/repositories/snapshots/com/squareup/kotlinpoet/
  [kdoc]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/
  [javapoet]: https://github.com/square/javapoet/
- [formatter]: https://developer.android.com/reference/java/util/Formatter.html
+ [explicit-api]: https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors
+ [version-shield]: https://img.shields.io/maven-central/v/com.squareup/kotlinpoet
+ [dl]: https://search.maven.org/remote_content?g=com.squareup&a=kotlinpoet-jvm&v=LATEST
+ [snap]: https://s01.oss.sonatype.org/content/repositories/snapshots/com/squareup/kotlinpoet/
diff --git a/docs/interfaces.md b/docs/interfaces.md
new file mode 100644
index 0000000..52682b4
--- /dev/null
+++ b/docs/interfaces.md
@@ -0,0 +1,45 @@
+Interfaces
+==========
+
+KotlinPoet has no trouble with interfaces. Note that interface methods must always be `ABSTRACT`.
+The modifier is necessary when defining the interface:
+
+```kotlin
+val helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
+  .addProperty("buzz", String::class)
+  .addFunction(
+    FunSpec.builder("beep")
+      .addModifiers(KModifier.ABSTRACT)
+      .build()
+  )
+  .build()
+```
+
+But these modifiers are omitted when the code is generated. These are the default so we don't need
+to include them for `kotlinc`'s benefit!
+
+```kotlin
+interface HelloWorld {
+  val buzz: String
+
+  fun beep()
+}
+```
+
+Kotlin 1.4 adds support for functional interfaces via `fun interface` syntax. To create this in
+KotlinPoet, use `TypeSpec.funInterfaceBuilder()`.
+
+```kotlin
+val helloWorld = TypeSpec.funInterfaceBuilder("HelloWorld")
+  .addFunction(
+    FunSpec.builder("beep")
+      .addModifiers(KModifier.ABSTRACT)
+      .build()
+  )
+  .build()
+
+// Generates...
+fun interface HelloWorld {
+  fun beep()
+}
+```
diff --git a/docs/kotlin-reflect.md b/docs/kotlin-reflect.md
new file mode 100644
index 0000000..0bda13a
--- /dev/null
+++ b/docs/kotlin-reflect.md
@@ -0,0 +1,45 @@
+kotlin-reflect
+==============
+
+To generate source code from any [`KType`][k-type], including information that's not accessible to
+the builtin reflection APIs, KotlinPoet depends on [kotlin-reflect][kotlin-reflect]. `kotlin-reflect`
+can read the metadata of your classes and access this extra information. KotlinPoet can for an
+example, read the type parameters and their [variance][variance] from a generic `KType` and
+generate appropriate source code.
+
+`kotlin-reflect` is a relatively big dependency though and in some cases it is desirable to remove
+it from the final executable to save some space and/or simplify the proguard/R8 setup (for example
+for a Gradle plugin that generates Kotlin code). It is possible to do so and still use most of the
+KotlinPoet APIs:
+
+```kotlin
+dependencies {
+  implementation("com.squareup:kotlinpoet:<version>") {
+    exclude(module = "kotlin-reflect")
+  }
+}
+```
+
+The main APIs that require `kotlin-reflect` are [`KType.asTypeName()`][as-type-name] and
+[`typeNameOf<T>()`][type-name-of]. If you're calling one of these without `kotlin-reflect` in the
+classpath and the type is generic or has annotations you will get a crash.
+
+You can replace it with code that passes type parameters or annotations explicitly and doesn't
+need `kotlin-reflect`. For example:
+
+```kotlin
+// Replace
+// kotlin-reflect needed
+val typeName = typeNameOf<List<Int?>>()
+
+// With
+// kotlin-reflect not needed
+val typeName =
+  List::class.asClassName().parameterizedBy(Int::class.asClassName().copy(nullable = true))
+```
+
+ [k-type]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-type/
+ [kotlin-reflect]: https://kotlinlang.org/docs/reflection.html#jvm-dependency
+ [variance]: https://kotlinlang.org/docs/generics.html#variance
+ [as-type-name]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/as-type-name.html
+ [type-name-of]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/type-name-of.html
diff --git a/docs/l-for-literals.md b/docs/l-for-literals.md
new file mode 100644
index 0000000..82a0d0a
--- /dev/null
+++ b/docs/l-for-literals.md
@@ -0,0 +1,25 @@
+%L for Literals
+===============
+
+Although Kotlin's string templates usually work well in cases when you want to include literals into
+generated code, KotlinPoet offers additional syntax inspired-by but incompatible-with
+[`String.format()`][formatter]. It accepts **`%L`** to emit a **literal** value in the output. This
+works just like `Formatter`'s `%s`:
+
+```kotlin
+private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
+  return FunSpec.builder(name)
+    .returns(Int::class)
+    .addStatement("var result = 0")
+    .beginControlFlow("for (i in %L..<%L)", from, to)
+    .addStatement("result = result %L i", op)
+    .endControlFlow()
+    .addStatement("return result")
+    .build()
+}
+```
+
+Literals are emitted directly to the output code with no escaping. Arguments for literals may be
+strings, primitives, and a few KotlinPoet types described below.
+
+ [formatter]: https://developer.android.com/reference/java/util/Formatter.html
diff --git a/docs/m-for-members.md b/docs/m-for-members.md
new file mode 100644
index 0000000..0a683c7
--- /dev/null
+++ b/docs/m-for-members.md
@@ -0,0 +1,122 @@
+%M for Members
+==============
+
+Similar to types, KotlinPoet has a special placeholder for **members** (functions and properties),
+which comes handy when your code needs to access top-level members and members declared inside
+objects. Use **`%M`** to reference members, pass an instance of `MemberName` as the argument for the
+placeholder, and KotlinPoet will handle imports automatically:
+
+```kotlin
+val createTaco = MemberName("com.squareup.tacos", "createTaco")
+val isVegan = MemberName("com.squareup.tacos", "isVegan")
+val file = FileSpec.builder("com.squareup.example", "TacoTest")
+  .addFunction(
+    FunSpec.builder("main")
+      .addStatement("val taco = %M()", createTaco)
+      .addStatement("println(taco.%M)", isVegan)
+      .build()
+  )
+  .build()
+println(file)
+```
+
+The code above generates the following file:
+
+```kotlin
+package com.squareup.example
+
+import com.squareup.tacos.createTaco
+import com.squareup.tacos.isVegan
+
+fun main() {
+  val taco = createTaco()
+  println(taco.isVegan)
+}
+```
+
+As you can see, it's also possible to use `%M` to reference extension functions and properties. You
+just need to make sure the member can be imported without simple name collisions, otherwise
+importing will fail and the code generator output will not pass compilation. There's a way to work
+around such cases though - use `FileSpec.addAliasedImport()` to create an alias for a clashing
+`MemberName`:
+
+```kotlin
+val createTaco = MemberName("com.squareup.tacos", "createTaco")
+val createCake = MemberName("com.squareup.cakes", "createCake")
+val isTacoVegan = MemberName("com.squareup.tacos", "isVegan")
+val isCakeVegan = MemberName("com.squareup.cakes", "isVegan")
+val file = FileSpec.builder("com.squareup.example", "Test")
+  .addAliasedImport(isTacoVegan, "isTacoVegan")
+  .addAliasedImport(isCakeVegan, "isCakeVegan")
+  .addFunction(
+    FunSpec.builder("main")
+      .addStatement("val taco = %M()", createTaco)
+      .addStatement("val cake = %M()", createCake)
+      .addStatement("println(taco.%M)", isTacoVegan)
+      .addStatement("println(cake.%M)", isCakeVegan)
+      .build()
+  )
+  .build()
+println(file)
+```
+
+KotlinPoet will produce an aliased import for `com.squareup.tacos2.isVegan`:
+
+```kotlin
+package com.squareup.example
+
+import com.squareup.cakes.createCake
+import com.squareup.tacos.createTaco
+import com.squareup.cakes.isVegan as isCakeVegan
+import com.squareup.tacos.isVegan as isTacoVegan
+
+fun main() {
+  val taco = createTaco()
+  val cake = createCake()
+  println(taco.isTacoVegan)
+  println(cake.isCakeVegan)
+}
+```
+
+## MemberName and operators
+
+MemberName also supports operators, you can use `MemberName(String, KOperator)`
+or `MemberName(ClassName, KOperator)` to import and reference operators.
+
+```kotlin
+val taco = ClassName("com.squareup.tacos", "Taco")
+val meat = ClassName("com.squareup.tacos.ingredient", "Meat")
+val iterator = MemberName("com.squareup.tacos.internal", KOperator.ITERATOR)
+val minusAssign = MemberName("com.squareup.tacos.internal", KOperator.MINUS_ASSIGN)
+val file = FileSpec.builder("com.example", "Test")
+  .addFunction(
+    FunSpec.builder("makeTacoHealthy")
+      .addParameter("taco", taco)
+      .beginControlFlow("for (ingredient %M taco)", iterator)
+      .addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign)
+      .endControlFlow()
+      .addStatement("return taco")
+      .build()
+  )
+  .build()
+println(file)
+```
+
+KotlinPoet will import the extension operator functions and emit the operator.
+
+```kotlin
+package com.example
+
+import com.squareup.tacos.Taco
+import com.squareup.tacos.ingredient.Meat
+import com.squareup.tacos.internal.iterator
+import com.squareup.tacos.internal.minusAssign
+
+fun makeTacoHealthy(taco: Taco) {
+  for (ingredient in taco) {
+    if (ingredient is Meat) taco -= ingredient
+  }
+  return taco
+}
+
+```
diff --git a/docs/n-for-names.md b/docs/n-for-names.md
new file mode 100644
index 0000000..cc3783a
--- /dev/null
+++ b/docs/n-for-names.md
@@ -0,0 +1,70 @@
+%N for Names
+============
+
+Generated code is often self-referential. Use **`%N`** to refer to another generated declaration by
+its name. Here's a method that calls another:
+
+```kotlin
+fun byteToHex(b: Int): String {
+  val result = CharArray(2)
+  result[0] = hexDigit((b ushr 4) and 0xf)
+  result[1] = hexDigit(b and 0xf)
+  return String(result)
+}
+
+fun hexDigit(i: Int): Char {
+  return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()
+}
+```
+
+When generating the code above, we pass the `hexDigit()` method as an argument to the `byteToHex()`
+method using `%N`:
+
+```kotlin
+val hexDigit = FunSpec.builder("hexDigit")
+  .addParameter("i", Int::class)
+  .returns(Char::class)
+  .addStatement("return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()")
+  .build()
+
+val byteToHex = FunSpec.builder("byteToHex")
+  .addParameter("b", Int::class)
+  .returns(String::class)
+  .addStatement("val result = CharArray(2)")
+  .addStatement("result[0] = %N((b ushr 4) and 0xf)", hexDigit)
+  .addStatement("result[1] = %N(b and 0xf)", hexDigit)
+  .addStatement("return String(result)")
+  .build()
+```
+
+Another handy feature that `%N` provides is automatically escaping names that contain illegal
+identifier characters with double ticks. Suppose your code creates a `MemberName` with a Kotlin
+keyword as the simple name:
+
+```kotlin
+val taco = ClassName("com.squareup.tacos", "Taco")
+val packager = ClassName("com.squareup.tacos", "TacoPackager")
+val file = FileSpec.builder("com.example", "Test")
+  .addFunction(
+    FunSpec.builder("packageTacos")
+      .addParameter("tacos", LIST.parameterizedBy(taco))
+      .addParameter("packager", packager)
+      .addStatement("packager.%N(tacos)", packager.member("package"))
+      .build()
+  )
+  .build()
+```
+
+`%N` will escape the name for you, ensuring that the output will pass compilation:
+
+```kotlin
+package com.example
+
+import com.squareup.tacos.Taco
+import com.squareup.tacos.TacoPackager
+import kotlin.collections.List
+
+fun packageTacos(tacos: List<Taco>, packager: TacoPackager) {
+  packager.`package`(tacos)
+}
+```
diff --git a/docs/objects.md b/docs/objects.md
new file mode 100644
index 0000000..75d9aab
--- /dev/null
+++ b/docs/objects.md
@@ -0,0 +1,42 @@
+Objects
+=======
+
+KotlinPoet supports objects:
+
+```kotlin
+val helloWorld = TypeSpec.objectBuilder("HelloWorld")
+  .addProperty(
+    PropertySpec.builder("buzz", String::class)
+      .initializer("%S", "buzz")
+      .build()
+  )
+  .addFunction(
+    FunSpec.builder("beep")
+      .addStatement("println(%S)", "Beep!")
+      .build()
+  )
+  .build()
+```
+
+Similarly, you can create companion objects and add them to classes using `addType()`:
+
+```kotlin
+val companion = TypeSpec.companionObjectBuilder()
+  .addProperty(
+    PropertySpec.builder("buzz", String::class)
+      .initializer("%S", "buzz")
+      .build()
+  )
+  .addFunction(
+    FunSpec.builder("beep")
+      .addStatement("println(%S)", "Beep!")
+      .build()
+  )
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .addType(companion)
+  .build()
+```
+
+You can provide an optional name for a companion object.
diff --git a/docs/p-for-string-templates.md b/docs/p-for-string-templates.md
new file mode 100644
index 0000000..6b27e6a
--- /dev/null
+++ b/docs/p-for-string-templates.md
@@ -0,0 +1,67 @@
+%P for String Templates
+=======================
+
+`%S` also handles the escaping of dollar signs (`$`), to avoid inadvertent creation of string
+templates, which may fail to compile in generated code:
+
+```kotlin
+val stringWithADollar = "Your total is " + "$" + "50"
+val funSpec = FunSpec.builder("printTotal")
+  .returns(String::class)
+  .addStatement("return %S", stringWithADollar)
+  .build()
+```
+
+produces:
+
+```kotlin
+fun printTotal(): String = "Your total is ${'$'}50"
+```
+
+If you need to generate string templates, use `%P`, which doesn't escape dollars:
+
+```kotlin
+val amount = 50
+val stringWithADollar = "Your total is " + "$" + "amount"
+val funSpec = FunSpec.builder("printTotal")
+  .returns(String::class)
+  .addStatement("return %P", stringWithADollar)
+  .build()
+```
+
+produces:
+
+```kotlin
+fun printTotal(): String = "Your total is $amount"
+```
+
+You can also use `CodeBlock`s as arguments to `%P`, which is handy when you need to reference
+importable types or members inside the string template:
+
+```kotlin
+val file = FileSpec.builder("com.example", "Digits")
+  .addFunction(
+    FunSpec.builder("print")
+      .addParameter("digits", IntArray::class)
+      .addStatement("println(%P)", buildCodeBlock {
+        val contentToString = MemberName("kotlin.collections", "contentToString")
+        add("These are the digits: \${digits.%M()}", contentToString)
+      })
+      .build()
+  )
+  .build()
+println(file)
+```
+
+The snippet above will produce the following output, handling the imports properly:
+
+```kotlin
+package com.example
+
+import kotlin.IntArray
+import kotlin.collections.contentToString
+
+fun print(digits: IntArray) {
+  println("""These are the digits: ${digits.contentToString()}""")
+}
+```
diff --git a/docs/parameters.md b/docs/parameters.md
new file mode 100644
index 0000000..d816508
--- /dev/null
+++ b/docs/parameters.md
@@ -0,0 +1,25 @@
+Parameters
+==========
+
+Declare parameters on methods and constructors with either `ParameterSpec.builder()` or
+`FunSpec`'s convenient `addParameter()` API:
+
+```kotlin
+val android = ParameterSpec.builder("android", String::class)
+  .defaultValue("\"pie\"")
+  .build()
+
+val welcomeOverlords = FunSpec.builder("welcomeOverlords")
+  .addParameter(android)
+  .addParameter("robot", String::class)
+  .build()
+```
+
+The code above generates:
+
+```kotlin
+fun welcomeOverlords(android: String = "pie", robot: String) {
+}
+```
+
+The extended `Builder` form is necessary when the parameter has annotations (such as `@Inject`).
diff --git a/docs/properties.md b/docs/properties.md
new file mode 100644
index 0000000..cb878ab
--- /dev/null
+++ b/docs/properties.md
@@ -0,0 +1,142 @@
+Properties
+==========
+
+Like parameters, properties can be created either with builders or by using convenient helper
+methods:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+  .addModifiers(KModifier.PRIVATE)
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .addProperty(android)
+  .addProperty("robot", String::class, KModifier.PRIVATE)
+  .build()
+```
+
+Which generates:
+
+```kotlin
+class HelloWorld {
+  private val android: String
+
+  private val robot: String
+}
+```
+
+The extended `Builder` form is necessary when a field has KDoc, annotations, or a field
+initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code
+blocks above:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+  .addModifiers(KModifier.PRIVATE)
+  .initializer("%S + %L", "Oreo v.", 8.1)
+  .build()
+```
+
+Which generates:
+
+```kotlin
+private val android: String = "Oreo v." + 8.1
+```
+
+By default `PropertySpec.Builder` produces `val` properties. Use `mutable()` if you need a
+`var`:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+  .mutable()
+  .addModifiers(KModifier.PRIVATE)
+  .initializer("%S + %L", "Oreo v.", 8.1)
+  .build()
+```
+
+## Inline properties
+
+The way KotlinPoet models inline properties deserves special mention. The following snippet of code:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+  .mutable()
+  .addModifiers(KModifier.INLINE)
+  .build()
+```
+
+will produce an error:
+
+```
+java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on
+properties. You should mark either the getter, the setter, or both inline.
+```
+
+Indeed, a property marked with `inline` should have at least one accessor which will be inlined by
+the compiler. Let's add a getter to this property:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+  .mutable()
+  .getter(
+    FunSpec.getterBuilder()
+      .addModifiers(KModifier.INLINE)
+      .addStatement("return %S", "foo")
+      .build()
+  )
+  .build()
+```
+
+The result is the following:
+
+```kotlin
+var android: kotlin.String
+  inline get() = "foo"
+```
+
+Now, what if we wanted to add a non-inline setter to the property above? We can do so without
+modifying any of the code we wrote previously:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+  .mutable()
+  .getter(
+    FunSpec.getterBuilder()
+      .addModifiers(KModifier.INLINE)
+      .addStatement("return %S", "foo")
+      .build()
+  )
+  .setter(
+    FunSpec.setterBuilder()
+      .addParameter("value", String::class)
+      .build()
+  )
+  .build()
+```
+
+We get the expected result:
+
+```kotlin
+var android: kotlin.String
+  inline get() = "foo"
+  set(`value`) {
+  }
+```
+
+Finally, if we go back and add `KModifier.INLINE` to the setter, KotlinPoet can wrap it nicely and
+produce the following result:
+
+```kotlin
+inline var android: kotlin.String
+  get() = "foo"
+  set(`value`) {
+  }
+```
+
+Removing the modifier from either the getter or the setter will unwrap the expression back.
+
+If, on the other hand, KotlinPoet had allowed marking a property `inline` directly, the programmer
+would have had to manually add/remove the modifier whenever the state of the accessors changes in
+order to get correct and compilable output. We're solving this problem by making accessors the
+source of truth for the `inline` modifier.
+
+ [formatter]: https://developer.android.com/reference/java/util/Formatter.html
diff --git a/docs/s-for-strings.md b/docs/s-for-strings.md
new file mode 100644
index 0000000..d14d7e7
--- /dev/null
+++ b/docs/s-for-strings.md
@@ -0,0 +1,41 @@
+%S for Strings
+==============
+
+When emitting code that includes string literals, we can use **`%S`** to emit a **string**, complete
+with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which
+returns its own name:
+
+```kotlin
+fun main(args: Array<String>) {
+  val helloWorld = TypeSpec.classBuilder("HelloWorld")
+    .addFunction(whatsMyNameYo("slimShady"))
+    .addFunction(whatsMyNameYo("eminem"))
+    .addFunction(whatsMyNameYo("marshallMathers"))
+    .build()
+
+  val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
+    .addType(helloWorld)
+    .build()
+
+  kotlinFile.writeTo(System.out)
+}
+
+private fun whatsMyNameYo(name: String): FunSpec {
+  return FunSpec.builder(name)
+    .returns(String::class)
+    .addStatement("return %S", name)
+    .build()
+}
+```
+
+In this case, using `%S` gives us quotation marks:
+
+```kotlin
+class HelloWorld {
+  fun slimShady(): String = "slimShady"
+
+  fun eminem(): String = "eminem"
+
+  fun marshallMathers(): String = "marshallMathers"
+}
+```
diff --git a/docs/t-for-types.md b/docs/t-for-types.md
new file mode 100644
index 0000000..d0bfa4a
--- /dev/null
+++ b/docs/t-for-types.md
@@ -0,0 +1,161 @@
+%T for Types
+============
+
+KotlinPoet has rich built-in support for types, including automatic generation of `import`
+statements. Just use **`%T`** to reference **types**:
+
+```kotlin
+val today = FunSpec.builder("today")
+  .returns(Date::class)
+  .addStatement("return %T()", Date::class)
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .addFunction(today)
+  .build()
+
+val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
+  .addType(helloWorld)
+  .build()
+
+kotlinFile.writeTo(System.out)
+```
+
+That generates the following `.kt` file, complete with the necessary `import`:
+
+```kotlin
+package com.example.helloworld
+
+import java.util.Date
+
+class HelloWorld {
+  fun today(): Date = Date()
+}
+```
+
+We passed `Date::class` to reference a class that just-so-happens to be available when we're
+generating code. This doesn't need to be the case. Here's a similar example, but this one
+references a class that doesn't exist (yet):
+
+```kotlin
+val hoverboard = ClassName("com.mattel", "Hoverboard")
+
+val tomorrow = FunSpec.builder("tomorrow")
+  .returns(hoverboard)
+  .addStatement("return %T()", hoverboard)
+  .build()
+```
+
+And that not-yet-existent class is imported as well:
+
+```kotlin
+package com.example.helloworld
+
+import com.mattel.Hoverboard
+
+class HelloWorld {
+  fun tomorrow(): Hoverboard = Hoverboard()
+}
+```
+
+The `ClassName` type is very important, and you'll need it frequently when you're using KotlinPoet.
+It can identify any _declared_ class. Declared types are just the beginning of Kotlin's rich type
+system: we also have arrays, parameterized types, wildcard types, lambda types and type variables.
+KotlinPoet has classes for building each of these:
+
+```kotlin
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.STAR
+
+val hoverboard = ClassName("com.mattel", "Hoverboard")
+val list = ClassName("kotlin.collections", "List")
+val arrayList = ClassName("kotlin.collections", "ArrayList")
+val listOfHoverboards = list.parameterizedBy(hoverboard)
+val arrayListOfHoverboards = arrayList.parameterizedBy(hoverboard)
+
+val thing = ClassName("com.misc", "Thing")
+val array = ClassName("kotlin", "Array")
+val producerArrayOfThings = array.parameterizedBy(WildcardTypeName.producerOf(thing))
+
+val beyond = FunSpec.builder("beyond")
+  .returns(listOfHoverboards)
+  .addStatement("val result = %T()", arrayListOfHoverboards)
+  .addStatement("result += %T()", hoverboard)
+  .addStatement("result += %T()", hoverboard)
+  .addStatement("result += %T()", hoverboard)
+  .addStatement("return result")
+  .build()
+
+val printThings = FunSpec.builder("printThings")
+  .addParameter("things", producerArrayOfThings)
+  .addStatement("println(things)")
+  .build()
+
+val printKClass = FunSpec.builder("printKClass")
+  .addParameter("kClass", KClass::class.asClassName().parameterizedBy(STAR))
+  .addStatement("println(kClass)")
+  .build()
+```
+
+The `STAR` is represented as `*` in KotlinPoet. You can find more in the [KDoc][kdoc].
+
+KotlinPoet will decompose each type and import its components where possible.
+
+```kotlin
+package com.example.helloworld
+
+import com.mattel.Hoverboard
+import com.misc.Thing
+import kotlin.Array
+import kotlin.collections.ArrayList
+import kotlin.collections.List
+import kotlin.reflect.KClass
+
+class HelloWorld {
+  fun beyond(): List<Hoverboard> {
+    val result = ArrayList<Hoverboard>()
+    result += Hoverboard()
+    result += Hoverboard()
+    result += Hoverboard()
+    return result
+  }
+
+  fun printThings(things: Array<out Thing>) {
+    println(things)
+  }
+
+  fun printKClass(kClass: KClass<*>) {
+    println(kClass)
+  }
+}
+```
+
+## Nullable Types
+
+KotlinPoet supports nullable types. To convert a `TypeName` into its nullable counterpart, use the
+`copy()` method with `nullable` parameter set to `true`:
+
+```kotlin
+val java = PropertySpec.builder("java", String::class.asTypeName().copy(nullable = true))
+  .mutable()
+  .addModifiers(KModifier.PRIVATE)
+  .initializer("null")
+  .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+  .addProperty(java)
+  .addProperty("kotlin", String::class, KModifier.PRIVATE)
+  .build()
+```
+
+generates:
+
+```kotlin
+class HelloWorld {
+  private var java: String? = null
+
+  private val kotlin: String
+}
+```
+
+ [kdoc]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/
diff --git a/docs/type-aliases.md b/docs/type-aliases.md
new file mode 100644
index 0000000..c4aa2a7
--- /dev/null
+++ b/docs/type-aliases.md
@@ -0,0 +1,49 @@
+Type Aliases
+============
+
+KotlinPoet provides API for creating Type Aliases, which supports simple class names, parameterized
+types and lambdas:
+
+```kotlin
+val k = TypeVariableName("K")
+val t = TypeVariableName("T")
+
+val fileTable = Map::class.asClassName()
+  .parameterizedBy(k, Set::class.parameterizedBy(File::class))
+
+val predicate = LambdaTypeName.get(
+  parameters = arrayOf(t),
+  returnType = Boolean::class.asClassName()
+)
+val helloWorld = FileSpec.builder("com.example", "HelloWorld")
+  .addTypeAlias(TypeAliasSpec.builder("Word", String::class).build())
+  .addTypeAlias(
+    TypeAliasSpec.builder("FileTable", fileTable)
+      .addTypeVariable(k)
+      .build()
+  )
+  .addTypeAlias(
+    TypeAliasSpec.builder("Predicate", predicate)
+      .addTypeVariable(t)
+      .build()
+  )
+  .build()
+```
+
+Which generates the following:
+
+```kotlin
+package com.example
+
+import java.io.File
+import kotlin.Boolean
+import kotlin.String
+import kotlin.collections.Map
+import kotlin.collections.Set
+
+typealias Word = String
+
+typealias FileTable<K> = Map<K, Set<File>>
+
+typealias Predicate<T> = (T) -> Boolean
+```
diff --git a/gradle.properties b/gradle.properties
index dc6f725..5ee29f6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
 org.gradle.jvmargs='-Dfile.encoding=UTF-8'
 
 GROUP=com.squareup
-VERSION_NAME=1.15.0-SNAPSHOT
+VERSION_NAME=1.16.0
 
 POM_URL=https://github.com/square/kotlinpoet
 POM_SCM_URL=https://github.com/square/kotlinpoet
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6bba5eb..1f39d95 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -13,22 +13,23 @@
 # limitations under the License.
 
 [versions]
-kotlin = "1.9.10"
-kct = "0.3.2"
-ksp = "1.9.10-1.0.13"
+kotlin = "1.9.22"
+kct = "0.4.0"
+ksp = "1.9.22-1.0.16"
 ktlint = "0.48.2"
 
 [plugins]
+kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
 kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
-dokka = { id = "org.jetbrains.dokka", version = "1.9.0" }
+dokka = { id = "org.jetbrains.dokka", version = "1.9.10" }
 ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
-spotless = { id = "com.diffplug.spotless", version = "6.22.0" }
-mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.25.3" }
+spotless = { id = "com.diffplug.spotless", version = "6.24.0" }
+mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.27.0" }
 kotlinBinaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.13.2" }
 
 [libraries]
 autoCommon = { module = "com.google.auto:auto-common", version = "1.2.2" }
-guava = { module = "com.google.guava:guava", version = "32.1.2-jre" }
+guava = { module = "com.google.guava:guava", version = "33.0.0-jre" }
 javapoet = "com.squareup:javapoet:1.13.0"
 
 autoService = "com.google.auto.service:auto-service-annotations:1.1.1"
@@ -38,12 +39,12 @@
 kotlin-annotationProcessingEmbeddable = { module = "org.jetbrains.kotlin:kotlin-annotation-processing-embeddable", version.ref = "kotlin" }
 kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
 kotlin-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
-kotlin-metadata = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.7.0" }
+kotlin-metadata = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.8.0" }
 
 ksp = { module = "com.google.devtools.ksp:symbol-processing", version.ref = "ksp" }
 ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
 
-truth = { module = "com.google.truth:truth", version = "1.1.5" }
+truth = { module = "com.google.truth:truth", version = "1.2.0" }
 compileTesting = { module = "com.google.testing.compile:compile-testing", version = "0.21.0" }
 jimfs = { module = "com.google.jimfs:jimfs", version = "1.3.0" }
 ecj = { module = "org.eclipse.jdt.core.compiler:ecj", version = "4.6.1" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7f93135..d64cd49 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8838ba9..e6aba25 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
 networkTimeout=10000
 validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
diff --git a/interop/javapoet/build.gradle.kts b/interop/javapoet/build.gradle.kts
index 734d934..8fd35cb 100644
--- a/interop/javapoet/build.gradle.kts
+++ b/interop/javapoet/build.gradle.kts
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+plugins {
+  kotlin("jvm")
+}
 
 tasks.jar {
   manifest {
diff --git a/interop/kotlinx-metadata/build.gradle.kts b/interop/kotlinx-metadata/build.gradle.kts
index a828789..6ac626e 100644
--- a/interop/kotlinx-metadata/build.gradle.kts
+++ b/interop/kotlinx-metadata/build.gradle.kts
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+plugins {
+  kotlin("jvm")
+}
 
 tasks.jar {
   manifest {
diff --git a/interop/ksp/build.gradle.kts b/interop/ksp/build.gradle.kts
index 3d615a3..782d151 100644
--- a/interop/ksp/build.gradle.kts
+++ b/interop/ksp/build.gradle.kts
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+plugins {
+  kotlin("jvm")
+}
 
 tasks.jar {
   manifest {
diff --git a/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt b/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt
index 8d07220..3c57aad 100644
--- a/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt
+++ b/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt
@@ -15,6 +15,7 @@
  */
 package com.squareup.kotlinpoet.ksp
 
+import com.google.devtools.ksp.symbol.KSCallableReference
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSType
 import com.google.devtools.ksp.symbol.KSTypeAlias
@@ -27,6 +28,8 @@
 import com.google.devtools.ksp.symbol.Variance.INVARIANT
 import com.squareup.kotlinpoet.ClassName
 import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.LambdaTypeName
+import com.squareup.kotlinpoet.ParameterSpec
 import com.squareup.kotlinpoet.STAR
 import com.squareup.kotlinpoet.TypeName
 import com.squareup.kotlinpoet.TypeVariableName
@@ -179,5 +182,15 @@
 public fun KSTypeReference.toTypeName(
   typeParamResolver: TypeParameterResolver = TypeParameterResolver.EMPTY,
 ): TypeName {
-  return resolve().toTypeName(typeParamResolver, element?.typeArguments.orEmpty())
+  val type = resolve()
+  return when (val elem = element) {
+    is KSCallableReference -> {
+      LambdaTypeName.get(
+        receiver = elem.receiverType?.toTypeName(typeParamResolver),
+        parameters = elem.functionParameters.map { ParameterSpec.unnamed(it.type.toTypeName(typeParamResolver)) },
+        returnType = elem.returnType.toTypeName(typeParamResolver),
+      ).copy(nullable = type.isMarkedNullable, suspending = type.isSuspendFunctionType)
+    }
+    else -> type.toTypeName(typeParamResolver, element?.typeArguments.orEmpty())
+  }
 }
diff --git a/interop/ksp/test-processor/build.gradle.kts b/interop/ksp/test-processor/build.gradle.kts
index f0a4d71..d2e3fb7 100644
--- a/interop/ksp/test-processor/build.gradle.kts
+++ b/interop/ksp/test-processor/build.gradle.kts
@@ -15,6 +15,7 @@
  */
 plugins {
   id("com.google.devtools.ksp")
+  kotlin("jvm")
 }
 
 tasks.compileTestKotlin {
diff --git a/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt b/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt
index 82064c8..fb0a5f0 100644
--- a/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt
+++ b/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt
@@ -171,7 +171,14 @@
           )
           .addParameters(
             function.parameters.map { parameter ->
-              val parameterType = parameter.type.toValidatedTypeName(functionTypeParams).let {
+              // Function references can't be obtained from a resolved KSType because it resolves to FunctionN<> which
+              // loses the necessary context, skip validation in these cases as we know they won't match.
+              val typeName = if (parameter.type.resolve().run { isFunctionType || isSuspendFunctionType }) {
+                parameter.type.toTypeName(functionTypeParams)
+              } else {
+                parameter.type.toValidatedTypeName(functionTypeParams)
+              }
+              val parameterType = typeName.let {
                 if (unwrapTypeAliases) {
                   it.unwrapTypeAlias()
                 } else {
diff --git a/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt b/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt
index 1aaa1ed..06bf588 100644
--- a/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt
+++ b/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt
@@ -129,7 +129,12 @@
              suspend fun functionD(
                param1: () -> String,
                param2: (String) -> String,
-               param3: String.() -> String
+               param3: String.() -> String,
+               param4: Function0<String>,
+               param5: Function1<String, String>,
+               param6: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit,
+               param7: ((String) -> String)?,
+               param8: suspend () -> String,
              ) {
              }
 
@@ -188,6 +193,7 @@
       import kotlin.Int
       import kotlin.IntArray
       import kotlin.String
+      import kotlin.Unit
       import kotlin.collections.List
       import kotlin.collections.Map
       import kotlin.collections.MutableList
@@ -261,9 +267,39 @@
         }
 
         public suspend fun functionD(
-          param1: Function0<String>,
-          param2: Function1<String, String>,
-          param3: Function1<String, String>,
+          param1: () -> String,
+          param2: (String) -> String,
+          param3: String.() -> String,
+          param4: Function0<String>,
+          param5: Function1<String, String>,
+          param6: (
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+            Int,
+          ) -> Unit,
+          param7: ((String) -> String)?,
+          param8: suspend () -> String,
         ) {
         }
 
diff --git a/kotlinpoet/api/kotlinpoet.api b/kotlinpoet/api/kotlinpoet.api
index 114bd19..4e92873 100644
--- a/kotlinpoet/api/kotlinpoet.api
+++ b/kotlinpoet/api/kotlinpoet.api
@@ -181,7 +181,7 @@
 public abstract interface annotation class com/squareup/kotlinpoet/ExperimentalKotlinPoetApi : java/lang/annotation/Annotation {
 }
 
-public final class com/squareup/kotlinpoet/FileSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/Taggable {
+public final class com/squareup/kotlinpoet/FileSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/Taggable, com/squareup/kotlinpoet/TypeSpecHolder {
 	public static final field Companion Lcom/squareup/kotlinpoet/FileSpec$Companion;
 	public static final fun builder (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
 	public static final fun builder (Lcom/squareup/kotlinpoet/MemberName;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -195,7 +195,9 @@
 	public final fun getMembers ()Ljava/util/List;
 	public final fun getName ()Ljava/lang/String;
 	public final fun getPackageName ()Ljava/lang/String;
+	public final fun getRelativePath ()Ljava/lang/String;
 	public fun getTags ()Ljava/util/Map;
+	public fun getTypeSpecs ()Ljava/util/List;
 	public fun hashCode ()I
 	public final fun isScript ()Z
 	public static final fun scriptBuilder (Ljava/lang/String;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -207,13 +209,15 @@
 	public static synthetic fun toBuilder$default (Lcom/squareup/kotlinpoet/FileSpec;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
 	public final fun toJavaFileObject ()Ljavax/tools/JavaFileObject;
 	public fun toString ()Ljava/lang/String;
-	public final fun writeTo (Ljava/io/File;)V
+	public final fun writeTo (Ljava/io/File;)Ljava/io/File;
+	public final synthetic fun writeTo (Ljava/io/File;)V
 	public final fun writeTo (Ljava/lang/Appendable;)V
-	public final fun writeTo (Ljava/nio/file/Path;)V
+	public final fun writeTo (Ljava/nio/file/Path;)Ljava/nio/file/Path;
+	public final synthetic fun writeTo (Ljava/nio/file/Path;)V
 	public final fun writeTo (Ljavax/annotation/processing/Filer;)V
 }
 
-public final class com/squareup/kotlinpoet/FileSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/Taggable$Builder {
+public final class com/squareup/kotlinpoet/FileSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/Taggable$Builder, com/squareup/kotlinpoet/TypeSpecHolder$Builder {
 	public final fun addAliasedImport (Lcom/squareup/kotlinpoet/ClassName;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
 	public final fun addAliasedImport (Lcom/squareup/kotlinpoet/ClassName;Ljava/lang/String;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
 	public final fun addAliasedImport (Lcom/squareup/kotlinpoet/MemberName;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -251,8 +255,11 @@
 	public final fun addNamedCode (Ljava/lang/String;Ljava/util/Map;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
 	public final fun addProperty (Lcom/squareup/kotlinpoet/PropertySpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
 	public final fun addStatement (Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
-	public final fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+	public fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+	public synthetic fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
 	public final fun addTypeAlias (Lcom/squareup/kotlinpoet/TypeAliasSpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+	public fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+	public synthetic fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
 	public final fun beginControlFlow (Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
 	public final fun build ()Lcom/squareup/kotlinpoet/FileSpec;
 	public final fun clearBody ()Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -554,6 +561,7 @@
 
 public final class com/squareup/kotlinpoet/NameAllocator {
 	public fun <init> ()V
+	public fun <init> (Z)V
 	public final fun copy ()Lcom/squareup/kotlinpoet/NameAllocator;
 	public final fun get (Ljava/lang/Object;)Ljava/lang/String;
 	public final fun newName (Ljava/lang/String;)Ljava/lang/String;
@@ -925,7 +933,7 @@
 	public static final fun get (Lkotlin/reflect/KClass;)Lcom/squareup/kotlinpoet/ClassName;
 }
 
-public final class com/squareup/kotlinpoet/TypeSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable {
+public final class com/squareup/kotlinpoet/TypeSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable, com/squareup/kotlinpoet/TypeSpecHolder {
 	public static final field Companion Lcom/squareup/kotlinpoet/TypeSpec$Companion;
 	public static final fun annotationBuilder (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
 	public static final fun annotationBuilder (Ljava/lang/String;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
@@ -959,7 +967,7 @@
 	public final fun getSuperclassConstructorParameters ()Ljava/util/List;
 	public final fun getSuperinterfaces ()Ljava/util/Map;
 	public fun getTags ()Ljava/util/Map;
-	public final fun getTypeSpecs ()Ljava/util/List;
+	public fun getTypeSpecs ()Ljava/util/List;
 	public final fun getTypeVariables ()Ljava/util/List;
 	public fun hashCode ()I
 	public static final fun interfaceBuilder (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
@@ -981,7 +989,7 @@
 	public static final fun valueClassBuilder (Ljava/lang/String;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
 }
 
-public final class com/squareup/kotlinpoet/TypeSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/ContextReceivable$Builder, com/squareup/kotlinpoet/Documentable$Builder, com/squareup/kotlinpoet/OriginatingElementsHolder$Builder, com/squareup/kotlinpoet/Taggable$Builder {
+public final class com/squareup/kotlinpoet/TypeSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/ContextReceivable$Builder, com/squareup/kotlinpoet/Documentable$Builder, com/squareup/kotlinpoet/OriginatingElementsHolder$Builder, com/squareup/kotlinpoet/Taggable$Builder, com/squareup/kotlinpoet/TypeSpecHolder$Builder {
 	public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
 	public fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
 	public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
@@ -1023,10 +1031,12 @@
 	public static synthetic fun addSuperinterface$default (Lcom/squareup/kotlinpoet/TypeSpec$Builder;Ljava/lang/reflect/Type;Lcom/squareup/kotlinpoet/CodeBlock;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
 	public static synthetic fun addSuperinterface$default (Lcom/squareup/kotlinpoet/TypeSpec$Builder;Lkotlin/reflect/KClass;Lcom/squareup/kotlinpoet/CodeBlock;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
 	public final fun addSuperinterfaces (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
-	public final fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+	public fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+	public synthetic fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
 	public final fun addTypeVariable (Lcom/squareup/kotlinpoet/TypeVariableName;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
 	public final fun addTypeVariables (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
-	public final fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+	public fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+	public synthetic fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
 	public final fun build ()Lcom/squareup/kotlinpoet/TypeSpec;
 	public synthetic fun contextReceivers (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/ContextReceivable$Builder;
 	public final fun getAnnotationSpecs ()Ljava/util/List;
@@ -1081,6 +1091,15 @@
 	public static fun values ()[Lcom/squareup/kotlinpoet/TypeSpec$Kind;
 }
 
+public abstract interface class com/squareup/kotlinpoet/TypeSpecHolder {
+	public abstract fun getTypeSpecs ()Ljava/util/List;
+}
+
+public abstract interface class com/squareup/kotlinpoet/TypeSpecHolder$Builder {
+	public abstract fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
+	public fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
+}
+
 public final class com/squareup/kotlinpoet/TypeVariableName : com/squareup/kotlinpoet/TypeName {
 	public static final field Companion Lcom/squareup/kotlinpoet/TypeVariableName$Companion;
 	public final fun copy (ZLjava/util/List;Ljava/util/List;ZLjava/util/Map;)Lcom/squareup/kotlinpoet/TypeVariableName;
diff --git a/kotlinpoet/build.gradle.kts b/kotlinpoet/build.gradle.kts
index 9627b60..48669b3 100644
--- a/kotlinpoet/build.gradle.kts
+++ b/kotlinpoet/build.gradle.kts
@@ -13,40 +13,59 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-tasks.jar {
-  manifest {
-    attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet")
-  }
-}
-
-tasks.compileTestKotlin {
-  compilerOptions {
-    freeCompilerArgs.add("-opt-in=com.squareup.kotlinpoet.DelicateKotlinPoetApi")
-  }
+plugins {
+  kotlin("multiplatform")
 }
 
 spotless {
   kotlin {
     targetExclude(
       // Non-Square licensed files
-      "src/main/java/com/squareup/kotlinpoet/ClassName.kt",
-      "src/test/java/com/squareup/kotlinpoet/AbstractTypesTest.kt",
-      "src/test/java/com/squareup/kotlinpoet/ClassNameTest.kt",
-      "src/test/java/com/squareup/kotlinpoet/TypesEclipseTest.kt",
-      "src/test/java/com/squareup/kotlinpoet/TypesTest.kt",
+      "src/commonMain/kotlin/com/squareup/kotlinpoet/ClassName.kt",
+      "src/commonTest/kotlin/com/squareup/kotlinpoet/AbstractTypesTest.kt",
+      "src/commonTest/kotlin/com/squareup/kotlinpoet/ClassNameTest.kt",
+      "src/commonTest/kotlin/com/squareup/kotlinpoet/TypesEclipseTest.kt",
+      "src/commonTest/kotlin/com/squareup/kotlinpoet/TypesTest.kt",
     )
   }
 }
 
-dependencies {
-  implementation(libs.kotlin.reflect)
-  testImplementation(libs.kotlin.junit)
-  testImplementation(libs.truth)
-  testImplementation(libs.guava)
-  testImplementation(libs.compileTesting)
-  testImplementation(libs.jimfs)
-  testImplementation(libs.ecj)
-  testImplementation(libs.kotlinCompileTesting)
-  testImplementation(libs.kotlin.annotationProcessingEmbeddable)
-  testImplementation(libs.kotlin.compilerEmbeddable)
+kotlin {
+  jvm {
+    withJava()
+  }
+
+  sourceSets {
+    val commonMain by getting {
+      dependencies {
+        implementation(libs.kotlin.reflect)
+      }
+    }
+    val commonTest by getting {
+      dependencies {
+        implementation(libs.kotlin.junit)
+        implementation(libs.truth)
+        implementation(libs.guava)
+        implementation(libs.compileTesting)
+        implementation(libs.jimfs)
+        implementation(libs.ecj)
+        implementation(libs.kotlinCompileTesting)
+        implementation(libs.kotlin.annotationProcessingEmbeddable)
+        implementation(libs.kotlin.compilerEmbeddable)
+      }
+    }
+  }
 }
+
+tasks.withType(org.gradle.jvm.tasks.Jar::class.java) {
+  manifest {
+    attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet")
+  }
+}
+
+tasks.named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileTestKotlinJvm") {
+  compilerOptions {
+    freeCompilerArgs.add("-opt-in=com.squareup.kotlinpoet.DelicateKotlinPoetApi")
+  }
+}
+
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Annotatable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Annotatable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Annotatable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Annotatable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/AnnotationSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/AnnotationSpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/AnnotationSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/AnnotationSpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ClassName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ClassName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeBlock.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeBlock.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeBlock.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeBlock.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt
similarity index 98%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt
index ff1de01..d4489f2 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt
@@ -445,8 +445,8 @@
       return className.canonicalName
     }
 
-    // If the class is in the same package, we're done.
-    if (packageName == className.packageName) {
+    // If the class is in the same package and there's no import alias for that class, we're done.
+    if (packageName == className.packageName && imports[className.canonicalName]?.alias == null) {
       referencedNames.add(className.topLevelClassName().simpleName)
       return className.simpleNames.joinToString(".")
     }
@@ -722,11 +722,11 @@
       importsCollector.close()
 
       return CodeWriter(
-        out,
-        indent,
-        memberImports + generatedImports.filterKeys { it !in memberImports },
-        suggestedTypeImports,
-        suggestedMemberImports,
+        out = out,
+        indent = indent,
+        imports = memberImports + generatedImports.filterKeys { it !in memberImports },
+        importedTypes = suggestedTypeImports,
+        importedMembers = suggestedMemberImports,
       )
     }
 
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ContextReceivable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ContextReceivable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ContextReceivable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ContextReceivable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Documentable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Documentable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Documentable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Documentable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Dynamic.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Dynamic.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/FileSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FileSpec.kt
similarity index 89%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/FileSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FileSpec.kt
index ef3f032..288848c 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/FileSpec.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FileSpec.kt
@@ -20,16 +20,19 @@
 import java.io.File
 import java.io.IOException
 import java.io.InputStream
-import java.io.OutputStreamWriter
 import java.net.URI
 import java.nio.charset.StandardCharsets.UTF_8
-import java.nio.file.Files
 import java.nio.file.Path
 import javax.annotation.processing.Filer
 import javax.tools.JavaFileObject
 import javax.tools.JavaFileObject.Kind
 import javax.tools.SimpleJavaFileObject
 import javax.tools.StandardLocation
+import kotlin.DeprecationLevel.HIDDEN
+import kotlin.io.path.createDirectories
+import kotlin.io.path.isDirectory
+import kotlin.io.path.notExists
+import kotlin.io.path.outputStream
 import kotlin.reflect.KClass
 
 /**
@@ -46,8 +49,9 @@
 public class FileSpec private constructor(
   builder: Builder,
   private val tagMap: TagMap = builder.buildTagMap(),
-) : Taggable by tagMap, Annotatable {
+) : Taggable by tagMap, Annotatable, TypeSpecHolder {
   override val annotations: List<AnnotationSpec> = builder.annotations.toImmutableList()
+  override val typeSpecs: List<TypeSpec> = builder.members.filterIsInstance<TypeSpec>().toImmutableList()
   public val comment: CodeBlock = builder.comment.build()
   public val packageName: String = builder.packageName
   public val name: String = builder.name
@@ -59,6 +63,20 @@
   private val indent = builder.indent
   private val extension = if (isScript) "kts" else "kt"
 
+  /**
+   * The relative path of the file which would be produced by a call to [writeTo].
+   * This value always uses unix-style path separators (`/`).
+   */
+  public val relativePath: String = buildString {
+    for (packageComponent in packageName.split('.').dropLastWhile { it.isEmpty() }) {
+      append(packageComponent)
+      append('/')
+    }
+    append(name)
+    append('.')
+    append(extension)
+  }
+
   @Throws(IOException::class)
   public fun writeTo(out: Appendable) {
     val codeWriter = CodeWriter.withCollectedImports(
@@ -71,28 +89,39 @@
     codeWriter.close()
   }
 
-  /** Writes this to `directory` as UTF-8 using the standard directory structure.  */
-  @Throws(IOException::class)
-  public fun writeTo(directory: Path) {
-    require(Files.notExists(directory) || Files.isDirectory(directory)) {
-      "path $directory exists but is not a directory."
-    }
-    var outputDirectory = directory
-    if (packageName.isNotEmpty()) {
-      for (packageComponent in packageName.split('.').dropLastWhile { it.isEmpty() }) {
-        outputDirectory = outputDirectory.resolve(packageComponent)
-      }
-    }
-
-    Files.createDirectories(outputDirectory)
-
-    val outputPath = outputDirectory.resolve("$name.$extension")
-    OutputStreamWriter(Files.newOutputStream(outputPath), UTF_8).use { writer -> writeTo(writer) }
+  @Deprecated("", level = HIDDEN)
+  @JvmName("writeTo") // For binary compatibility.
+  public fun oldWriteTo(directory: Path) {
+    writeTo(directory)
   }
 
-  /** Writes this to `directory` as UTF-8 using the standard directory structure.  */
+  /**
+   * Writes this to [directory] as UTF-8 using the standard directory structure
+   * and returns the newly output path.
+   */
   @Throws(IOException::class)
-  public fun writeTo(directory: File): Unit = writeTo(directory.toPath())
+  public fun writeTo(directory: Path): Path {
+    require(directory.notExists() || directory.isDirectory()) {
+      "path $directory exists but is not a directory."
+    }
+    val outputPath = directory.resolve(relativePath)
+    outputPath.parent.createDirectories()
+    outputPath.outputStream().bufferedWriter().use(::writeTo)
+    return outputPath
+  }
+
+  @Deprecated("", level = HIDDEN)
+  @JvmName("writeTo") // For binary compatibility.
+  public fun oldWriteTo(directory: File) {
+    writeTo(directory)
+  }
+
+  /**
+   * Writes this to [directory] as UTF-8 using the standard directory structure
+   * and returns the newly output file.
+   */
+  @Throws(IOException::class)
+  public fun writeTo(directory: File): File = writeTo(directory.toPath()).toFile()
 
   /** Writes this to `filer`.  */
   @Throws(IOException::class)
@@ -191,13 +220,7 @@
   override fun toString(): String = buildString { writeTo(this) }
 
   public fun toJavaFileObject(): JavaFileObject {
-    val uri = URI.create(
-      if (packageName.isEmpty()) {
-        name
-      } else {
-        packageName.replace('.', '/') + '/' + name
-      } + ".$extension",
-    )
+    val uri = URI.create(relativePath)
     return object : SimpleJavaFileObject(uri, Kind.SOURCE) {
       private val lastModified = System.currentTimeMillis()
       override fun getCharContent(ignoreEncodingErrors: Boolean): String {
@@ -230,7 +253,7 @@
     public val packageName: String,
     public val name: String,
     public val isScript: Boolean,
-  ) : Taggable.Builder<Builder>, Annotatable.Builder<Builder> {
+  ) : Taggable.Builder<Builder>, Annotatable.Builder<Builder>, TypeSpecHolder.Builder<Builder> {
     override val annotations: MutableList<AnnotationSpec> = mutableListOf()
     internal val comment = CodeBlock.builder()
     internal val memberImports = sortedSetOf<Import>()
@@ -275,7 +298,7 @@
       comment.clear()
     }
 
-    public fun addType(typeSpec: TypeSpec): Builder = apply {
+    override fun addType(typeSpec: TypeSpec): Builder = apply {
       if (isScript) {
         body.add("%L", typeSpec)
       } else {
@@ -283,6 +306,11 @@
       }
     }
 
+    //region Overrides for binary compatibility
+    @Suppress("RedundantOverride")
+    override fun addTypes(typeSpecs: Iterable<TypeSpec>): Builder = super.addTypes(typeSpecs)
+    //endregion
+
     public fun addFunction(funSpec: FunSpec): Builder = apply {
       require(!funSpec.isConstructor && !funSpec.isAccessor) {
         "cannot add ${funSpec.name} to file $name"
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/FunSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FunSpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FunSpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Import.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Import.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Import.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Import.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/KModifier.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KModifier.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/KModifier.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KModifier.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/KOperator.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KOperator.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/KOperator.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KOperator.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LambdaTypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LambdaTypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LineWrapper.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LineWrapper.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/MemberName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/MemberName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/MemberName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/MemberName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/NameAllocator.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/NameAllocator.kt
similarity index 77%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/NameAllocator.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/NameAllocator.kt
index cda1004..a6c570b 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/NameAllocator.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/NameAllocator.kt
@@ -77,7 +77,39 @@
   private val allocatedNames: MutableSet<String>,
   private val tagToName: MutableMap<Any, String>,
 ) {
-  public constructor() : this(mutableSetOf(), mutableMapOf())
+  public constructor() : this(preallocateKeywords = true)
+
+  /**
+   * @param preallocateKeywords If true, all Kotlin keywords will be preallocated. Requested names which
+   * collide with keywords will be suffixed with underscores to avoid being used as identifiers:
+   *
+   * ```kotlin
+   * val nameAllocator = NameAllocator(preallocateKeywords = true)
+   * println(nameAllocator.newName("when")) // prints "when_"
+   * ```
+   *
+   * If false, keywords will not get any special treatment:
+   *
+   * ```kotlin
+   * val nameAllocator = NameAllocator(preallocateKeywords = false)
+   * println(nameAllocator.newName("when")) // prints "when"
+   * ```
+   *
+   * Note that you can use the `%N` placeholder when emitting a name produced by [NameAllocator] to
+   * ensure it's properly escaped for use as an identifier:
+   *
+   * ```kotlin
+   * val nameAllocator = NameAllocator(preallocateKeywords = false)
+   * println(CodeBlock.of("%N", nameAllocator.newName("when"))) // prints "`when`"
+   * ```
+   *
+   * The default behaviour of [NameAllocator] is to preallocate keywords - this is the behaviour you'll
+   * get when using the no-arg constructor.
+   */
+  public constructor(preallocateKeywords: Boolean) : this(
+    allocatedNames = if (preallocateKeywords) KEYWORDS.toMutableSet() else mutableSetOf(),
+    tagToName = mutableMapOf(),
+  )
 
   /**
    * Return a new name using `suggestion` that will not be a Java identifier or clash with other
@@ -89,7 +121,7 @@
     tag: Any = UUID.randomUUID().toString(),
   ): String {
     var result = toJavaIdentifier(suggestion)
-    while (result.isKeyword || !allocatedNames.add(result)) {
+    while (!allocatedNames.add(result)) {
       result += "_"
     }
 
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/OriginatingElementsHolder.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/OriginatingElementsHolder.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/OriginatingElementsHolder.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/OriginatingElementsHolder.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterSpec.kt
similarity index 98%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterSpec.kt
index 15cf4bf..91dc8b0 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterSpec.kt
@@ -54,10 +54,8 @@
   internal fun emit(
     codeWriter: CodeWriter,
     includeType: Boolean = true,
-    emitKdoc: Boolean = false,
     inlineAnnotations: Boolean = true,
   ) {
-    if (emitKdoc) codeWriter.emitKdoc(kdoc.ensureEndsWithNewLine())
     codeWriter.emitAnnotations(annotations, inlineAnnotations)
     codeWriter.emitModifiers(modifiers)
     if (name.isNotEmpty()) codeWriter.emitCode("%N", this)
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterizedTypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterizedTypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/PropertySpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/PropertySpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/PropertySpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/PropertySpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Taggable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Taggable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Taggable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Taggable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeAliasSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeAliasSpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeAliasSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeAliasSpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpec.kt
similarity index 95%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpec.kt
index 5f52e4f..af0613b 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpec.kt
@@ -47,7 +47,8 @@
   OriginatingElementsHolder by delegateOriginatingElements,
   ContextReceivable by contextReceivers,
   Annotatable,
-  Documentable {
+  Documentable,
+  TypeSpecHolder {
   public val kind: Kind = builder.kind
   public val name: String? = builder.name
   override val kdoc: CodeBlock = builder.kdoc.build()
@@ -76,7 +77,7 @@
   public val initializerBlock: CodeBlock = builder.initializerBlock.build()
   public val initializerIndex: Int = builder.initializerIndex
   public val funSpecs: List<FunSpec> = builder.funSpecs.toImmutableList()
-  public val typeSpecs: List<TypeSpec> = builder.typeSpecs.toImmutableList()
+  public override val typeSpecs: List<TypeSpec> = builder.typeSpecs.toImmutableList()
   internal val nestedTypesSimpleNames = typeSpecs.map { it.name }.toImmutableSet()
 
   @Deprecated("Use annotations property", ReplaceWith("annotations"), ERROR)
@@ -208,7 +209,7 @@
               )
               param.emitDefaultValue(codeWriter)
             } else {
-              param.emit(codeWriter, emitKdoc = true, inlineAnnotations = false)
+              param.emit(codeWriter, inlineAnnotations = false)
             }
           }
 
@@ -373,31 +374,25 @@
   /**
    * Returns KDoc comments including those of the primary constructor and its parameters.
    *
-   * If the primary constructor parameter is not mapped to a property, or if the property doesn't
-   * have its own KDoc - the parameter's KDoc will be attached to the parameter. Otherwise, if both
-   * the parameter and the property have KDoc - the property's KDoc will be attached to the
-   * property/parameter, and the parameter's KDoc will be printed in the type header.
+   * Parameters' KDocs, if present, will always be printed in the type header.
    */
   private fun kdocWithConstructorDocs(): CodeBlock {
-    if (primaryConstructor == null) {
-      return kdoc.ensureEndsWithNewLine()
-    }
-    val constructorProperties = constructorProperties()
-    val parametersWithKdoc = primaryConstructor.parameters.filter { parameter ->
-      val propertyKdoc = constructorProperties[parameter.name]?.kdoc ?: CodeBlock.EMPTY
-      return@filter parameter.kdoc.isNotEmpty() && propertyKdoc.isNotEmpty() &&
-        parameter.kdoc != propertyKdoc
-    }
-    return with(kdoc.ensureEndsWithNewLine().toBuilder()) {
-      if (kdoc.isNotEmpty()) add("\n")
-      if (primaryConstructor.kdoc.isNotEmpty()) {
-        add("@constructor %L", primaryConstructor.kdoc.ensureEndsWithNewLine())
+    val classKdoc = kdoc.ensureEndsWithNewLine()
+    val constructorKdoc = buildCodeBlock {
+      if (primaryConstructor != null) {
+        if (primaryConstructor.kdoc.isNotEmpty()) {
+          add("@constructor %L", primaryConstructor.kdoc.ensureEndsWithNewLine())
+        }
+        primaryConstructor.parameters.forEach { parameter ->
+          if (parameter.kdoc.isNotEmpty()) {
+            add("@param %L %L", parameter.name, parameter.kdoc.ensureEndsWithNewLine())
+          }
+        }
       }
-      parametersWithKdoc.forEach { parameter ->
-        add("@param %L %L", parameter.name, parameter.kdoc.ensureEndsWithNewLine())
-      }
-      build()
     }
+    return listOf(classKdoc, constructorKdoc)
+      .filter(CodeBlock::isNotEmpty)
+      .joinToCode(separator = "\n")
   }
 
   private val hasInitializer: Boolean get() = initializerIndex != -1 && initializerBlock.isNotEmpty()
@@ -475,7 +470,8 @@
     OriginatingElementsHolder.Builder<Builder>,
     ContextReceivable.Builder<Builder>,
     Annotatable.Builder<Builder>,
-    Documentable.Builder<Builder> {
+    Documentable.Builder<Builder>,
+    TypeSpecHolder.Builder<Builder> {
     internal var primaryConstructor: FunSpec? = null
     internal var superclass: TypeName = ANY
     internal val initializerBlock = CodeBlock.builder()
@@ -725,11 +721,7 @@
       funSpecs += funSpec
     }
 
-    public fun addTypes(typeSpecs: Iterable<TypeSpec>): Builder = apply {
-      this.typeSpecs += typeSpecs
-    }
-
-    public fun addType(typeSpec: TypeSpec): Builder = apply {
+    override fun addType(typeSpec: TypeSpec): Builder = apply {
       typeSpecs += typeSpec
     }
 
@@ -764,6 +756,9 @@
 
     @Suppress("RedundantOverride")
     override fun addKdoc(block: CodeBlock): Builder = super.addKdoc(block)
+
+    @Suppress("RedundantOverride")
+    override fun addTypes(typeSpecs: Iterable<TypeSpec>): Builder = super.addTypes(typeSpecs)
     //endregion
 
     public fun build(): TypeSpec {
diff --git a/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpecHolder.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpecHolder.kt
new file mode 100644
index 0000000..aad67e6
--- /dev/null
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpecHolder.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.squareup.kotlinpoet
+
+/** A spec which can contain other [TypeSpec] */
+public interface TypeSpecHolder {
+  public val typeSpecs: List<TypeSpec>
+
+  public interface Builder<out T : Builder<T>> {
+
+    public fun addType(typeSpec: TypeSpec): T
+
+    @Suppress("UNCHECKED_CAST")
+    public fun addTypes(typeSpecs: Iterable<TypeSpec>): T = apply {
+      for (typeSpec in typeSpecs) addType(typeSpec)
+    } as T
+  }
+}
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeVariableName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeVariableName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Util.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Util.kt
similarity index 99%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Util.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Util.kt
index 16a443d..c226f39 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Util.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Util.kt
@@ -169,7 +169,7 @@
 internal val String.isIdentifier get() = IDENTIFIER_REGEX.matches(this)
 
 // https://kotlinlang.org/docs/reference/keyword-reference.html
-private val KEYWORDS = setOf(
+internal val KEYWORDS = setOf(
   // Hard keywords
   "as",
   "break",
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/WildcardTypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/WildcardTypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/tags/TypeAliasTag.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/tags/TypeAliasTag.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/tags/TypeAliasTag.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/tags/TypeAliasTag.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AbstractTypesTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AbstractTypesTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AbstractTypesTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AbstractTypesTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotationSpecTest.kt
similarity index 91%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotationSpecTest.kt
index fff366f..b35e65f 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotationSpecTest.kt
@@ -345,32 +345,6 @@
     )
   }
 
-  @Test fun getOnValueArrayTypeMirrorShouldNameValueArg() {
-    val myClazz = compilation.elements
-      .getTypeElement(JavaClassWithArrayValueAnnotation::class.java.canonicalName)
-    val classBuilder = TypeSpec.classBuilder("Result")
-
-    myClazz.annotationMirrors.map { AnnotationSpec.get(it) }
-      .forEach {
-        classBuilder.addAnnotation(it)
-      }
-
-    assertThat(toString(classBuilder.build())).isEqualTo(
-      """
-            |package com.squareup.tacos
-            |
-            |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
-            |import java.lang.Boolean
-            |import java.lang.Object
-            |
-            |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
-            |        Boolean::class))
-            |public class Result
-            |
-      """.trimMargin(),
-    )
-  }
-
   @Test fun getOnVarargMirrorShouldNameValueArg() {
     val myClazz = compilation.elements
       .getTypeElement(KotlinClassWithVarargAnnotation::class.java.canonicalName)
@@ -399,28 +373,6 @@
     )
   }
 
-  @Test fun getOnValueArrayTypeAnnotationShouldNameValueArg() {
-    val annotation = JavaClassWithArrayValueAnnotation::class.java.getAnnotation(
-      JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue::class.java,
-    )
-    val classBuilder = TypeSpec.classBuilder("Result")
-      .addAnnotation(AnnotationSpec.get(annotation))
-
-    assertThat(toString(classBuilder.build()).trim()).isEqualTo(
-      """
-        |package com.squareup.tacos
-        |
-        |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
-        |import java.lang.Boolean
-        |import java.lang.Object
-        |
-        |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
-        |        Boolean::class))
-        |public class Result
-      """.trimMargin(),
-    )
-  }
-
   @Test fun getOnVarargAnnotationShouldNameValueArg() {
     val annotation = KotlinClassWithVarargAnnotation::class.java
       .getAnnotation(AnnotationWithArrayValue::class.java)
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AssertThrows.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AssertThrows.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AssertThrows.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AssertThrows.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/Cased/Weird/Sup.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/Cased/Weird/Sup.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/Cased/Weird/Sup.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/Cased/Weird/Sup.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ClassNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ClassNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ClassNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ClassNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/CodeBlockTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CodeBlockTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/CodeBlockTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CodeBlockTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/CrossplatformTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CrossplatformTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/CrossplatformTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CrossplatformTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileReadingTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileReadingTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileReadingTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileReadingTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileSpecTest.kt
similarity index 97%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileSpecTest.kt
index c363f59..a855cdd 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileSpecTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileSpecTest.kt
@@ -500,6 +500,37 @@
     )
   }
 
+  // https://github.com/square/kotlinpoet/issues/1696
+  @Test fun aliasedImportInSamePackage() {
+    val packageName = "com.mypackage"
+    val className = ClassName(packageName, "StringKey")
+    val source = FileSpec.builder(packageName, "K")
+      .addAliasedImport(className, "S")
+      .addType(
+        TypeSpec
+          .objectBuilder("K")
+          .addProperty(
+            PropertySpec.builder("test", className)
+              .initializer("%T(%L)", className, 0)
+              .build(),
+          )
+          .build(),
+      )
+      .build()
+    assertThat(source.toString()).isEqualTo(
+      """
+      |package com.mypackage
+      |
+      |import com.mypackage.StringKey as S
+      |
+      |public object K {
+      |  public val test: S = S(0)
+      |}
+      |
+      """.trimMargin(),
+    )
+  }
+
   @Test fun conflictingParentName() {
     val source = FileSpec.builder("com.squareup.tacos", "A")
       .addType(
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileWritingTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileWritingTest.kt
similarity index 84%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileWritingTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileWritingTest.kt
index 83574f3..0e79ac7 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileWritingTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileWritingTest.kt
@@ -69,37 +69,46 @@
 
   @Test fun pathDefaultPackage() {
     val type = TypeSpec.classBuilder("Test").build()
-    FileSpec.get("", type).writeTo(fsRoot)
+    val fileSpec = FileSpec.get("", type)
+    assertThat(fileSpec.relativePath).isEqualTo("Test.kt")
 
-    val testPath = fsRoot.resolve("Test.kt")
+    val testPath = fileSpec.writeTo(fsRoot)
+    assertThat(testPath).isEqualTo(fsRoot.resolve("Test.kt"))
     assertThat(Files.exists(testPath)).isTrue()
   }
 
   @Test fun pathDefaultPackageWithSubdirectory() {
     val type = TypeSpec.classBuilder("Test").build()
-    FileSpec.get("", type).writeTo(fsRoot.resolve("sub"))
+    val testPath = FileSpec.get("", type).writeTo(fsRoot.resolve("sub"))
 
-    val testPath = fsRoot.resolve("sub/Test.kt")
+    assertThat(testPath).isEqualTo(fsRoot.resolve("sub/Test.kt"))
     assertThat(Files.exists(testPath)).isTrue()
   }
 
   @Test fun fileDefaultPackage() {
     val type = TypeSpec.classBuilder("Test").build()
-    FileSpec.get("", type).writeTo(tmp.root)
+    val testFile = FileSpec.get("", type).writeTo(tmp.root)
 
-    val testFile = File(tmp.root, "Test.kt")
+    assertThat(testFile).isEqualTo(File(tmp.root, "Test.kt"))
     assertThat(testFile.exists()).isTrue()
   }
 
   @Test fun pathNestedClasses() {
     val type = TypeSpec.classBuilder("Test").build()
-    FileSpec.get("foo", type).writeTo(fsRoot)
-    FileSpec.get("foo.bar", type).writeTo(fsRoot)
-    FileSpec.get("foo.bar.baz", type).writeTo(fsRoot)
+    val fooSpec = FileSpec.get("foo", type)
+    val barSpec = FileSpec.get("foo.bar", type)
+    val bazSpec = FileSpec.get("foo.bar.baz", type)
+    assertThat(fooSpec.relativePath).isEqualTo("foo/Test.kt")
+    assertThat(barSpec.relativePath).isEqualTo("foo/bar/Test.kt")
+    assertThat(bazSpec.relativePath).isEqualTo("foo/bar/baz/Test.kt")
 
-    val fooPath = fsRoot.resolve(fs.getPath("foo", "Test.kt"))
-    val barPath = fsRoot.resolve(fs.getPath("foo", "bar", "Test.kt"))
-    val bazPath = fsRoot.resolve(fs.getPath("foo", "bar", "baz", "Test.kt"))
+    val fooPath = fooSpec.writeTo(fsRoot)
+    val barPath = barSpec.writeTo(fsRoot)
+    val bazPath = bazSpec.writeTo(fsRoot)
+
+    assertThat(fooPath).isEqualTo(fsRoot.resolve(fs.getPath("foo", "Test.kt")))
+    assertThat(barPath).isEqualTo(fsRoot.resolve(fs.getPath("foo", "bar", "Test.kt")))
+    assertThat(bazPath).isEqualTo(fsRoot.resolve(fs.getPath("foo", "bar", "baz", "Test.kt")))
     assertThat(Files.exists(fooPath)).isTrue()
     assertThat(Files.exists(barPath)).isTrue()
     assertThat(Files.exists(bazPath)).isTrue()
@@ -107,16 +116,16 @@
 
   @Test fun fileNestedClasses() {
     val type = TypeSpec.classBuilder("Test").build()
-    FileSpec.get("foo", type).writeTo(tmp.root)
-    FileSpec.get("foo.bar", type).writeTo(tmp.root)
-    FileSpec.get("foo.bar.baz", type).writeTo(tmp.root)
+    val fooFile = FileSpec.get("foo", type).writeTo(tmp.root)
+    val barFile = FileSpec.get("foo.bar", type).writeTo(tmp.root)
+    val bazFile = FileSpec.get("foo.bar.baz", type).writeTo(tmp.root)
 
     val fooDir = File(tmp.root, "foo")
-    val fooFile = File(fooDir, "Test.kt")
+    assertThat(fooFile).isEqualTo(File(fooDir, "Test.kt"))
     val barDir = File(fooDir, "bar")
-    val barFile = File(barDir, "Test.kt")
+    assertThat(barFile).isEqualTo(File(barDir, "Test.kt"))
     val bazDir = File(barDir, "baz")
-    val bazFile = File(bazDir, "Test.kt")
+    assertThat(bazFile).isEqualTo(File(bazDir, "Test.kt"))
     assertThat(fooFile.exists()).isTrue()
     assertThat(barFile.exists()).isTrue()
     assertThat(bazFile.exists()).isTrue()
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FunSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FunSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/KotlinPoetTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/KotlinPoetTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/LambdaTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LambdaTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/LambdaTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LambdaTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrapperTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrapperTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrapperTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrapperTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrappingTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrappingTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrappingTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrappingTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/MemberNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/MemberNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/NameAllocatorTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/NameAllocatorTest.kt
similarity index 93%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/NameAllocatorTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/NameAllocatorTest.kt
index 28c7d34..a61877c 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/NameAllocatorTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/NameAllocatorTest.kt
@@ -70,6 +70,12 @@
     assertThat(nameAllocator[1]).isEqualTo("when_")
   }
 
+  @Test fun kotlinKeywordNotPreAllocated() {
+    val nameAllocator = NameAllocator(preallocateKeywords = false)
+    assertThat(nameAllocator.newName("when", 1)).isEqualTo("when")
+    assertThat(nameAllocator[1]).isEqualTo("when")
+  }
+
   @Test fun tagReuseForbidden() {
     val nameAllocator = NameAllocator()
     nameAllocator.newName("foo", 1)
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/PropertySpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/PropertySpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/PropertySpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/PropertySpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/StringsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/StringsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/StringsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/StringsTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TaggableTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TaggableTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TaggableTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TaggableTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TestFiler.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TestFiler.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TestFiler.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TestFiler.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeAliasSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeAliasSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeAliasSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeAliasSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameKotlinTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeNameKotlinTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameKotlinTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeNameKotlinTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeSpecTest.kt
similarity index 99%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeSpecTest.kt
index ebac635..94732b6 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeSpecTest.kt
@@ -1975,14 +1975,12 @@
         | * [random][java.util.Random] tex-mex stuff we could find in the pantry
         | * and some [kotlin.String] cheese.
         | *
+        | * @param temperature Taco temperature. Can be as cold as the famous ice tacos from
+        | * the Andes, or hot with lava-like cheese from the depths of
+        | * the Ninth Circle.
         | * @param mild Whether the taco is mild (ew) or crunchy (ye).
         | */
         |public class Taco(
-        |  /**
-        |   * Taco temperature. Can be as cold as the famous ice tacos from
-        |   * the Andes, or hot with lava-like cheese from the depths of
-        |   * the Ninth Circle.
-        |   */
         |  temperature: Double,
         |  /**
         |   * True for a soft flour tortilla; false for a crunchy corn tortilla.
@@ -4805,6 +4803,9 @@
       | * This is a thing for stuff.
       | *
       | * @constructor Construct a thing!
+      | * @param first the first thing
+      | * @param second the second thing
+      | * @param third the third thing
       | */
       |public class MyType(
       |  /**
@@ -4839,10 +4840,10 @@
       .build()
     assertThat(typeSpec.toString()).isEqualTo(
       """
+      |/**
+      | * @param first the first thing
+      | */
       |public class MyType(
-      |  /**
-      |   * the first thing
-      |   */
       |  first: kotlin.Int,
       |)
       |
@@ -5615,6 +5616,27 @@
     )
   }
 
+  @Test fun classKdoc() {
+    val type = TypeSpec.classBuilder("MyClass")
+      .addKdoc("This is my class")
+      .primaryConstructor(
+        FunSpec.constructorBuilder()
+          .build(),
+      )
+      .build()
+
+    //language=kotlin
+    assertThat(type.toString()).isEqualTo(
+      """
+      /**
+       * This is my class
+       */
+      public class MyClass()
+
+      """.trimIndent(),
+    )
+  }
+
   // https://github.com/square/kotlinpoet/issues/1630
   @Test fun primaryConstructorKDoc() {
     val type = TypeSpec.classBuilder("MyClass")
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeVariableNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeVariableNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesEclipseTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesEclipseTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesEclipseTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesEclipseTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/UtilTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/UtilTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/UtilTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/UtilTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ValueTypeSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ValueTypeSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ValueTypeSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ValueTypeSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/WildcardTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/WildcardTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/WildcardTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/WildcardTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
diff --git a/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaAnnotationSpecTest.kt b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaAnnotationSpecTest.kt
new file mode 100644
index 0000000..4b26596
--- /dev/null
+++ b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaAnnotationSpecTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.squareup.kotlinpoet
+
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.compile.CompilationRule
+import kotlin.test.Test
+import org.junit.Rule
+
+class JavaAnnotationSpecTest {
+
+  @Rule @JvmField
+  val compilation = CompilationRule()
+
+  @Test fun getOnValueArrayTypeMirrorShouldNameValueArg() {
+    val myClazz = compilation.elements
+      .getTypeElement(JavaClassWithArrayValueAnnotation::class.java.canonicalName)
+    val classBuilder = TypeSpec.classBuilder("Result")
+
+    myClazz.annotationMirrors.map { AnnotationSpec.get(it) }
+      .forEach {
+        classBuilder.addAnnotation(it)
+      }
+
+    assertThat(toString(classBuilder.build())).isEqualTo(
+      """
+            |package com.squareup.tacos
+            |
+            |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
+            |import java.lang.Boolean
+            |import java.lang.Object
+            |
+            |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
+            |        Boolean::class))
+            |public class Result
+            |
+      """.trimMargin(),
+    )
+  }
+
+  @Test fun getOnValueArrayTypeAnnotationShouldNameValueArg() {
+    val annotation = JavaClassWithArrayValueAnnotation::class.java.getAnnotation(
+      JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue::class.java,
+    )
+    val classBuilder = TypeSpec.classBuilder("Result")
+      .addAnnotation(AnnotationSpec.get(annotation))
+
+    assertThat(toString(classBuilder.build()).trim()).isEqualTo(
+      """
+        |package com.squareup.tacos
+        |
+        |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
+        |import java.lang.Boolean
+        |import java.lang.Object
+        |
+        |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
+        |        Boolean::class))
+        |public class Result
+      """.trimMargin(),
+    )
+  }
+
+  private fun toString(typeSpec: TypeSpec) =
+    FileSpec.get("com.squareup.tacos", typeSpec).toString()
+}
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java
rename to kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameTest.java b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/TypeNameTest.java
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameTest.java
rename to kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/TypeNameTest.java
diff --git a/mkdocs.yml b/mkdocs.yml
index 74caf79..abd937b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -55,10 +55,30 @@
 
 nav:
   - 'Overview':
-    - 'KotlinPoet': index.md
-    - 'Interop - JavaPoet': interop-javapoet.md
-    - 'Interop - kotlinx-metadata': interop-kotlinx-metadata.md
-    - 'Interop - KSP': interop-ksp.md
+    - 'Code & Control Flow': code-control-flow.md
+    - 'Format Specifiers':
+      - '%S for Strings': s-for-strings.md
+      - '%P for String Templates': p-for-string-templates.md
+      - '%T for Types': t-for-types.md
+      - '%M for Members': m-for-members.md
+      - '%N for Names': n-for-names.md
+      - '%L for Literals': l-for-literals.md
+      - 'Code Block Format Strings': code-block-format-strings.md
+    - 'Functions': functions.md
+    - 'Constructors': constructors.md
+    - 'Parameters': parameters.md
+    - 'Properties': properties.md
+    - 'Interfaces': interfaces.md
+    - 'Objects': objects.md
+    - 'Enums': enums.md
+    - 'Anonymous Inner Classes': anonymous-inner-classes.md
+    - 'Annotations': annotations.md
+    - 'Type Aliases': type-aliases.md
+    - 'Callable References': callable-references.md
+    - 'kotlin-reflect': kotlin-reflect.md
+  - 'Interop - JavaPoet': interop-javapoet.md
+  - 'Interop - kotlinx-metadata': interop-kotlinx-metadata.md
+  - 'Interop - KSP': interop-ksp.md
   - 'API':
     - 'kotlinpoet': 1.x/kotlinpoet/index.html
     - 'interop-javapoet': 1.x/interop-javapoet/index.html
diff --git a/renovate.json b/renovate.json
index 47fc816..d77f3a6 100644
--- a/renovate.json
+++ b/renovate.json
@@ -3,6 +3,7 @@
   "extends": [
     "config:base"
   ],
+  "semanticCommits": "disabled",
   "packageRules": [
     {
       "matchManagers": ["pip_requirements"],
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 465f238..c9b262e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -21,7 +21,7 @@
 }
 
 plugins {
-  id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0")
+  id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0")
 }
 
 include(