├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md ├── renovate.json5 └── workflows │ ├── .java-version │ ├── build.yml │ ├── gradle-wrapper.yaml │ ├── mkdocs-requirements.txt │ └── release.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── RELEASING.md ├── build.gradle.kts ├── docs ├── annotations.md ├── anonymous-inner-classes.md ├── callable-references.md ├── changelog.md ├── code-block-format-strings.md ├── code-control-flow.md ├── constructors.md ├── context-parameters.md ├── contributing.md ├── css │ └── app.css ├── enums.md ├── functions.md ├── images │ └── icon-square.png ├── index.md ├── interfaces.md ├── interop-javapoet.md ├── interop-kotlin-metadata.md ├── interop-ksp.md ├── kotlin-reflect.md ├── l-for-literals.md ├── m-for-members.md ├── n-for-names.md ├── objects.md ├── p-for-string-templates.md ├── parameters.md ├── properties.md ├── s-for-strings.md ├── t-for-types.md └── type-aliases.md ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── interop ├── javapoet │ ├── api │ │ └── javapoet.api │ ├── build.gradle.kts │ ├── gradle.properties │ └── src │ │ ├── main │ │ └── kotlin │ │ │ └── com │ │ │ └── squareup │ │ │ └── kotlinpoet │ │ │ └── javapoet │ │ │ ├── K2jInterop.kt │ │ │ ├── KotlinPoetJavaPoetPreview.kt │ │ │ ├── PoetInterop.kt │ │ │ ├── j2kInterop.kt │ │ │ └── typeAliases.kt │ │ └── test │ │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ └── javapoet │ │ └── PoetInteropTest.kt ├── kotlin-metadata │ ├── api │ │ └── kotlin-metadata.api │ ├── build.gradle.kts │ ├── gradle.properties │ └── src │ │ ├── main │ │ └── kotlin │ │ │ └── com │ │ │ └── squareup │ │ │ └── kotlinpoet │ │ │ └── metadata │ │ │ ├── KotlinPoetMetadata.kt │ │ │ ├── classinspectors │ │ │ ├── ClassInspectorUtil.kt │ │ │ ├── ElementsClassInspector.kt │ │ │ ├── JvmDescriptorUtils.kt │ │ │ ├── Optional.kt │ │ │ └── ReflectiveClassInspector.kt │ │ │ ├── specs │ │ │ ├── ClassInspector.kt │ │ │ ├── ConstructorData.kt │ │ │ ├── ContainerData.kt │ │ │ ├── FieldData.kt │ │ │ ├── JvmFieldModifier.kt │ │ │ ├── JvmMethodModifier.kt │ │ │ ├── JvmModifier.kt │ │ │ ├── KmTypes.kt │ │ │ ├── KotlinPoetMetadataSpecs.kt │ │ │ ├── MethodData.kt │ │ │ ├── PropertyData.kt │ │ │ └── kmAnnotations.kt │ │ │ └── util.kt │ │ └── test │ │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ └── metadata │ │ └── specs │ │ ├── FacadeFile.kt │ │ ├── FacadeFileTest.kt │ │ ├── JvmNameWithKtFacadeFile.kt │ │ ├── KmAnnotationsTest.kt │ │ ├── KotlinPoetMetadataSpecsTest.kt │ │ ├── MultiClassInspectorTest.kt │ │ ├── NoJvmNameFacadeFile.kt │ │ ├── ReflectiveClassInspectorTest.kt │ │ └── classinspectors │ │ └── ClassInspectorUtilTest.kt └── ksp │ ├── api │ └── ksp.api │ ├── build.gradle.kts │ ├── gradle.properties │ ├── src │ └── main │ │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ └── ksp │ │ ├── Annotations.kt │ │ ├── KsClassDeclarations.kt │ │ ├── KsTypes.kt │ │ ├── Modifiers.kt │ │ ├── OriginatingKSFiles.kt │ │ ├── TypeParameterResolver.kt │ │ ├── Visibilities.kt │ │ └── utils.kt │ └── test-processor │ ├── build.gradle.kts │ └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ └── ksp │ │ └── test │ │ └── processor │ │ ├── TestProcessor.kt │ │ ├── TestProcessorProvider.kt │ │ ├── TypeAliasUnwrapping.kt │ │ └── exampleAnnotations.kt │ └── test │ └── kotlin │ └── com │ └── squareup │ └── kotlinpoet │ └── ksp │ └── test │ └── processor │ ├── KsTypesTest.kt │ └── TestProcessorTest.kt ├── kotlin-js-store └── yarn.lock ├── kotlinpoet ├── api │ └── kotlinpoet.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ ├── CodePoint.kt │ │ ├── DelicateKotlinPoetApi.kt │ │ ├── ExperimentalKotlinPoetApi.kt │ │ ├── Import.kt │ │ ├── KModifier.kt │ │ ├── KOperator.kt │ │ ├── LineWrapper.kt │ │ ├── NameAllocator.kt │ │ └── Util.kt │ ├── jsMain │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ ├── CodePoint.js.kt │ │ └── Util.js.kt │ ├── jvmMain │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ ├── Annotatable.kt │ │ ├── AnnotationSpec.kt │ │ ├── ClassName.kt │ │ ├── CodeBlock.kt │ │ ├── CodePoint.jvm.kt │ │ ├── CodeWriter.kt │ │ ├── ContextParameter.kt │ │ ├── ContextReceivable.kt │ │ ├── Documentable.kt │ │ ├── Dynamic.kt │ │ ├── FileSpec.kt │ │ ├── FunSpec.kt │ │ ├── LambdaTypeName.kt │ │ ├── MemberName.kt │ │ ├── MemberSpecHolder.kt │ │ ├── OriginatingElementsHolder.kt │ │ ├── ParameterSpec.kt │ │ ├── ParameterizedTypeName.kt │ │ ├── PropertySpec.kt │ │ ├── Taggable.kt │ │ ├── TypeAliasSpec.kt │ │ ├── TypeName.kt │ │ ├── TypeSpec.kt │ │ ├── TypeSpecHolder.kt │ │ ├── TypeVariableName.kt │ │ ├── Util.jvm.kt │ │ ├── WildcardTypeName.kt │ │ ├── jvm │ │ └── JvmAnnotations.kt │ │ └── tags │ │ └── TypeAliasTag.kt │ ├── jvmTest │ ├── java │ │ └── com │ │ │ └── squareup │ │ │ └── kotlinpoet │ │ │ ├── JavaAnnotationSpecTest.kt │ │ │ ├── JavaClassWithArrayValueAnnotation.java │ │ │ └── TypeNameTest.java │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ ├── AbstractTypesTest.kt │ │ ├── AnnotatedTypeNameTest.kt │ │ ├── AnnotationSpecTest.kt │ │ ├── AssertThrows.kt │ │ ├── Cased │ │ └── Weird │ │ │ └── Sup.kt │ │ ├── ClassNameTest.kt │ │ ├── CodeBlockTest.kt │ │ ├── CrossplatformTest.kt │ │ ├── DelegatedConstructorCallTest.kt │ │ ├── ExpectDeclarationsTest.kt │ │ ├── ExternalDeclarationsTest.kt │ │ ├── FileReadingTest.kt │ │ ├── FileSpecTest.kt │ │ ├── FileWritingTest.kt │ │ ├── FunSpecTest.kt │ │ ├── KotlinPoetTest.kt │ │ ├── LambdaTypeNameTest.kt │ │ ├── LineWrapperTest.kt │ │ ├── LineWrappingTest.kt │ │ ├── MemberNameTest.kt │ │ ├── NameAllocatorTest.kt │ │ ├── ParameterSpecTest.kt │ │ ├── ParameterizedTypeNameTest.kt │ │ ├── PropertySpecTest.kt │ │ ├── StringsTest.kt │ │ ├── TaggableTest.kt │ │ ├── TestFiler.kt │ │ ├── TypeAliasSpecTest.kt │ │ ├── TypeNameKotlinTest.kt │ │ ├── TypeSpecTest.kt │ │ ├── TypeVariableNameTest.kt │ │ ├── TypesEclipseTest.kt │ │ ├── TypesTest.kt │ │ ├── UtilTest.kt │ │ ├── ValueTypeSpecTest.kt │ │ ├── WildcardTypeNameTest.kt │ │ └── jvm │ │ └── JvmAnnotationsTest.kt │ ├── nonJvmMain │ └── kotlin │ │ └── com │ │ └── squareup │ │ └── kotlinpoet │ │ ├── CodePoint.nonJvm.kt │ │ └── Util.nonJvm.kt │ └── wasmJsMain │ └── kotlin │ └── com │ └── squareup │ └── kotlinpoet │ ├── CodePoint.wasmJs.kt │ └── Util.wasmJs.kt ├── mkdocs.yml └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.{kt,kts}] 10 | ij_kotlin_imports_layout = * 11 | ij_kotlin_allow_trailing_comma = true 12 | ij_kotlin_allow_trailing_comma_on_call_site = true 13 | ij_kotlin_name_count_to_use_star_import = 99999 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.bat text eol=crlf 4 | *.jar binary -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | A sample Github project that reproduces the problem is ideal. Alternatively, please provide a failing unit test that triggers the bug. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | - [ ] `docs/changelog.md` has been updated if applicable. 3 | - Changes not visible to library consumers, such as build script, documentation, or test code updates, don't need to 4 | be added to the changelog. 5 | - [ ] [CLA](https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1) signed. 6 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | extends: [ 4 | 'config:recommended', 5 | ], 6 | semanticCommits: 'disabled', 7 | packageRules: [ 8 | { 9 | matchManagers: [ 10 | 'pip_requirements', 11 | ], 12 | automerge: true, 13 | }, 14 | { 15 | groupName: 'Kotlin and KSP', 16 | matchPackageNames: [ 17 | '/^org\\.jetbrains\\.kotlin:(?:[\\w-]+)$/', 18 | '/^com\\.google\\.devtools\\.ksp:(?:[\\w-]+)$/', 19 | ], 20 | }, 21 | { 22 | matchManagers: [ 23 | 'pip_requirements', 24 | ], 25 | groupName: 'MkDocs', 26 | matchPackageNames: [ 27 | 'mkdocs{/,}**', 28 | ], 29 | } 30 | ], 31 | ignorePresets: [ 32 | // Ensure we get the latest version and are not pinned to old versions. 33 | 'workarounds:javaLTSVersions', 34 | ], 35 | customManagers: [ 36 | // Update .java-version file with the latest JDK version. 37 | { 38 | customType: 'regex', 39 | fileMatch: [ 40 | '\\.java-version$', 41 | ], 42 | matchStrings: [ 43 | '(?.*)\\n', 44 | ], 45 | datasourceTemplate: 'java-version', 46 | depNameTemplate: 'java', 47 | // Only write the major version. 48 | extractVersionTemplate: '^(?\\d+)', 49 | }, 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/.java-version: -------------------------------------------------------------------------------- 1 | 24 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - main 7 | tags-ignore: 8 | - '**' 9 | pull_request: 10 | 11 | env: 12 | GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false" 13 | 14 | jobs: 15 | jvm: 16 | strategy: 17 | matrix: 18 | os: 19 | - macos-latest 20 | - ubuntu-latest 21 | - windows-latest 22 | 23 | runs-on: ${{ matrix.os }} 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | 29 | - uses: actions/setup-java@v4 30 | with: 31 | distribution: 'zulu' 32 | java-version-file: .github/workflows/.java-version 33 | 34 | - name: Full build 35 | if: matrix.os == 'ubuntu-latest' 36 | run: ./gradlew build 37 | 38 | - name: KotlinPoet check 39 | if: "matrix.os != 'ubuntu-latest'" 40 | run: ./gradlew :kotlinpoet:check 41 | 42 | build-docs: 43 | runs-on: ubuntu-latest 44 | if: github.repository == 'square/kotlinpoet' 45 | 46 | steps: 47 | - name: Checkout 48 | uses: actions/checkout@v4 49 | 50 | - uses: actions/setup-java@v4 51 | with: 52 | distribution: 'zulu' 53 | java-version-file: .github/workflows/.java-version 54 | 55 | - name: Prep docs 56 | run: ./gradlew dokkaGeneratePublicationHtml 57 | 58 | - name: Set up Python 59 | uses: actions/setup-python@v5 60 | with: 61 | python-version: 3.13 62 | 63 | - name: Build mkdocs 64 | run: | 65 | pip3 install -r .github/workflows/mkdocs-requirements.txt 66 | mkdocs build 67 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper.yaml: -------------------------------------------------------------------------------- 1 | name: gradle-wrapper 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'gradlew' 7 | - 'gradlew.bat' 8 | - 'gradle/wrapper/**' 9 | 10 | jobs: 11 | validate: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: gradle/gradle-build-action@v3 16 | -------------------------------------------------------------------------------- /.github/workflows/mkdocs-requirements.txt: -------------------------------------------------------------------------------- 1 | click==8.2.1 2 | future==1.0.0 3 | Jinja2==3.1.6 4 | livereload==2.7.1 5 | lunr==0.8.0 6 | MarkupSafe==3.0.2 7 | mkdocs==1.6.1 8 | mkdocs-macros-plugin==1.3.7 9 | mkdocs-material==9.6.14 10 | mkdocs-material-extensions==1.3.1 11 | Pygments==2.19.1 12 | pymdown-extensions==10.15 13 | python-dateutil==2.9.0.post0 14 | PyYAML==6.0.2 15 | repackage==0.7.3 16 | six==1.17.0 17 | termcolor==3.1.0 18 | tornado==6.5.1 19 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - '**' 9 | 10 | env: 11 | GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false" 12 | 13 | jobs: 14 | publish: 15 | runs-on: ubuntu-latest 16 | if: github.repository == 'square/kotlinpoet' 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - uses: actions/setup-java@v4 23 | with: 24 | distribution: 'zulu' 25 | java-version-file: .github/workflows/.java-version 26 | 27 | - name: Upload Artifacts 28 | run: ./gradlew publish 29 | env: 30 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USERNAME_COM_SQUAREUP }} 31 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD_COM_SQUAREUP }} 32 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SECRET_KEY }} 33 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SECRET_PASSPHRASE }} 34 | 35 | - name: Prep docs 36 | run: ./gradlew dokkaGeneratePublicationHtml 37 | 38 | - name: Set up Python 39 | uses: actions/setup-python@v5 40 | with: 41 | python-version: 3.13 42 | 43 | - name: Build mkdocs 44 | run: | 45 | pip3 install -r .github/workflows/mkdocs-requirements.txt 46 | mkdocs build 47 | 48 | - name: Deploy 🚀 49 | if: success() 50 | uses: JamesIves/github-pages-deploy-action@releases/v3 51 | with: 52 | GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }} 53 | BRANCH: gh-pages # The branch the action should deploy to. 54 | FOLDER: site # The folder the action should deploy. 55 | SINGLE_COMMIT: true 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .gradle 3 | .kotlin 4 | .project 5 | .settings 6 | eclipsebin 7 | local.properties 8 | 9 | bin 10 | gen 11 | build 12 | out 13 | lib 14 | reports 15 | 16 | .idea 17 | *.iml 18 | classes 19 | 20 | # Mkdocs files 21 | docs/1.x/* 22 | site 23 | 24 | obj 25 | 26 | .DS_Store 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | KotlinPoet 2 | ========== 3 | 4 | `KotlinPoet` is a Kotlin and Java API for generating `.kt` source files. 5 | 6 | ### [square.github.io/kotlinpoet](https://square.github.io/kotlinpoet) 7 | 8 | License 9 | ------- 10 | 11 | Copyright 2017 Square, Inc. 12 | 13 | Licensed under the Apache License, Version 2.0 (the "License"); 14 | you may not use this file except in compliance with the License. 15 | You may obtain a copy of the License at 16 | 17 | https://www.apache.org/licenses/LICENSE-2.0 18 | 19 | Unless required by applicable law or agreed to in writing, software 20 | distributed under the License is distributed on an "AS IS" BASIS, 21 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | See the License for the specific language governing permissions and 23 | limitations under the License. 24 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Releasing 2 | ========= 3 | 4 | 1. Change the version in `gradle.properties` to a non-SNAPSHOT version. 5 | 2. Update `docs/changelog.md` for the impending release. 6 | 3. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version) 7 | 4. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version). 8 | 5. Update `gradle.properties` to the next SNAPSHOT version. 9 | 6. `git commit -am "Prepare next development version."`. 10 | 7. `git push && git push --tags`. 11 | 12 | This will trigger a GitHub Action workflow which will create a GitHub release and upload the 13 | release artifacts to [Maven Central][maven-central]. 14 | 15 | [maven-central]: https://repo.maven.apache.org/maven2/com/squareup/kotlinpoet/ 16 | -------------------------------------------------------------------------------- /docs/annotations.md: -------------------------------------------------------------------------------- 1 | Annotations 2 | =========== 3 | 4 | Simple annotations are easy: 5 | 6 | ```kotlin 7 | val test = FunSpec.builder("test string equality") 8 | .addAnnotation(Test::class) 9 | .addStatement("assertThat(%1S).isEqualTo(%1S)", "foo") 10 | .build() 11 | ``` 12 | 13 | Which generates this function with an `@Test` annotation: 14 | 15 | ```kotlin 16 | @Test 17 | fun `test string equality`() { 18 | assertThat("foo").isEqualTo("foo") 19 | } 20 | ``` 21 | 22 | Use `AnnotationSpec.builder()` to set properties on annotations: 23 | 24 | ```kotlin 25 | val logRecord = FunSpec.builder("recordEvent") 26 | .addModifiers(KModifier.ABSTRACT) 27 | .addAnnotation( 28 | AnnotationSpec.builder(Headers::class) 29 | .addMember("accept = %S", "application/json; charset=utf-8") 30 | .addMember("userAgent = %S", "Square Cash") 31 | .build() 32 | ) 33 | .addParameter("logRecord", LogRecord::class) 34 | .returns(LogReceipt::class) 35 | .build() 36 | ``` 37 | 38 | Which generates this annotation with `accept` and `userAgent` properties: 39 | 40 | ```kotlin 41 | @Headers( 42 | accept = "application/json; charset=utf-8", 43 | userAgent = "Square Cash" 44 | ) 45 | abstract fun recordEvent(logRecord: LogRecord): LogReceipt 46 | ``` 47 | 48 | When you get fancy, annotation values can be annotations themselves. Use `%L` for embedded 49 | annotations: 50 | 51 | ```kotlin 52 | val headerList = ClassName("", "HeaderList") 53 | val header = ClassName("", "Header") 54 | val logRecord = FunSpec.builder("recordEvent") 55 | .addModifiers(KModifier.ABSTRACT) 56 | .addAnnotation( 57 | AnnotationSpec.builder(headerList) 58 | .addMember( 59 | "[\n⇥%L,\n%L⇤\n]", 60 | AnnotationSpec.builder(header) 61 | .addMember("name = %S", "Accept") 62 | .addMember("value = %S", "application/json; charset=utf-8") 63 | .build(), 64 | AnnotationSpec.builder(header) 65 | .addMember("name = %S", "User-Agent") 66 | .addMember("value = %S", "Square Cash") 67 | .build() 68 | ) 69 | .build() 70 | ) 71 | .addParameter("logRecord", logRecordName) 72 | .returns(logReceipt) 73 | .build() 74 | ``` 75 | 76 | Which generates this: 77 | 78 | ```kotlin 79 | @HeaderList( 80 | [ 81 | Header(name = "Accept", value = "application/json; charset=utf-8"), 82 | Header(name = "User-Agent", value = "Square Cash") 83 | ] 84 | ) 85 | abstract fun recordEvent(logRecord: LogRecord): LogReceipt 86 | ``` 87 | 88 | KotlinPoet supports use-site targets for annotations: 89 | 90 | ```kotlin 91 | val utils = FileSpec.builder("com.example", "Utils") 92 | .addAnnotation( 93 | AnnotationSpec.builder(JvmName::class) 94 | .useSiteTarget(UseSiteTarget.FILE) 95 | .build() 96 | ) 97 | .addFunction( 98 | FunSpec.builder("abs") 99 | .receiver(Int::class) 100 | .returns(Int::class) 101 | .addStatement("return if (this < 0) -this else this") 102 | .build() 103 | ) 104 | .build() 105 | ``` 106 | 107 | Will output this: 108 | 109 | ```kotlin 110 | @file:JvmName 111 | 112 | package com.example 113 | 114 | import kotlin.Int 115 | import kotlin.jvm.JvmName 116 | 117 | fun Int.abs(): Int = if (this < 0) -this else this 118 | ``` 119 | -------------------------------------------------------------------------------- /docs/anonymous-inner-classes.md: -------------------------------------------------------------------------------- 1 | Anonymous Inner Classes 2 | ======================= 3 | 4 | In the enum code, we used `TypeSpec.anonymousClassBuilder()`. Anonymous inner classes can also be 5 | used in code blocks. They are values that can be referenced with `%L`: 6 | 7 | ```kotlin 8 | val comparator = TypeSpec.anonymousClassBuilder() 9 | .addSuperinterface(Comparator::class.parameterizedBy(String::class)) 10 | .addFunction( 11 | FunSpec.builder("compare") 12 | .addModifiers(KModifier.OVERRIDE) 13 | .addParameter("a", String::class) 14 | .addParameter("b", String::class) 15 | .returns(Int::class) 16 | .addStatement("return %N.length - %N.length", "a", "b") 17 | .build() 18 | ) 19 | .build() 20 | 21 | val helloWorld = TypeSpec.classBuilder("HelloWorld") 22 | .addFunction( 23 | FunSpec.builder("sortByLength") 24 | .addParameter("strings", List::class.parameterizedBy(String::class)) 25 | .addStatement("%N.sortedWith(%L)", "strings", comparator) 26 | .build() 27 | ) 28 | .build() 29 | ``` 30 | 31 | This generates a method that contains a class that contains a method: 32 | 33 | ```kotlin 34 | class HelloWorld { 35 | fun sortByLength(strings: List) { 36 | strings.sortedWith(object : Comparator { 37 | override fun compare(a: String, b: String): Int = a.length - b.length 38 | }) 39 | } 40 | } 41 | ``` 42 | 43 | One particularly tricky part of defining anonymous inner classes is the arguments to the superclass 44 | constructor. To pass them use `TypeSpec.Builder`'s `addSuperclassConstructorParameter()` method. 45 | -------------------------------------------------------------------------------- /docs/callable-references.md: -------------------------------------------------------------------------------- 1 | Callable References 2 | =================== 3 | 4 | [Callable references][callable-references] to constructors, functions, and properties may be emitted 5 | via: 6 | 7 | - `ClassName.constructorReference()` for constructors 8 | - `MemberName.reference()` for functions and properties 9 | 10 | For example, 11 | 12 | ```kotlin 13 | val helloClass = ClassName("com.example.hello", "Hello") 14 | val worldFunction: MemberName = helloClass.member("world") 15 | val byeProperty: MemberName = helloClass.nestedClass("World").member("bye") 16 | 17 | val factoriesFun = FunSpec.builder("factories") 18 | .addStatement("val hello = %L", helloClass.constructorReference()) 19 | .addStatement("val world = %L", worldFunction.reference()) 20 | .addStatement("val bye = %L", byeProperty.reference()) 21 | .build() 22 | 23 | FileSpec.builder("com.example", "HelloWorld") 24 | .addFunction(factoriesFun) 25 | .build() 26 | ``` 27 | 28 | would generate: 29 | 30 | ```kotlin 31 | package com.example 32 | 33 | import com.example.hello.Hello 34 | 35 | fun factories() { 36 | val hello = ::Hello 37 | val world = Hello::world 38 | val bye = Hello.World::bye 39 | } 40 | ``` 41 | 42 | Top-level classes and members with conflicting names may require aliased imports, as with 43 | [member names](m-for-members.md). 44 | 45 | [callable-references]: https://kotlinlang.org/docs/reference/reflection.html#callable-references 46 | -------------------------------------------------------------------------------- /docs/code-block-format-strings.md: -------------------------------------------------------------------------------- 1 | Code Block Format Strings 2 | ========================= 3 | 4 | Code blocks may specify the values for their placeholders in a few ways. Only one style may be used 5 | for each operation on a code block. 6 | 7 | ## Relative Arguments 8 | 9 | Pass an argument value for each placeholder in the format string to `CodeBlock.add()`. In each 10 | example, we generate code to say "I ate 3 tacos" 11 | 12 | ```kotlin 13 | CodeBlock.builder().add("I ate %L %L", 3, "tacos") 14 | ``` 15 | 16 | ## Positional Arguments 17 | 18 | Place an integer index (1-based) before the placeholder in the format string to specify which 19 | argument to use. 20 | 21 | ```kotlin 22 | CodeBlock.builder().add("I ate %2L %1L", "tacos", 3) 23 | ``` 24 | 25 | ## Named Arguments 26 | 27 | Use the syntax `%argumentName:X` where `X` is the format character and call `CodeBlock.addNamed()` 28 | with a map containing all argument keys in the format string. Argument names use characters in 29 | `a-z`, `A-Z`, `0-9`, and `_`, and must start with a lowercase character. 30 | 31 | ```kotlin 32 | val map = LinkedHashMap() 33 | map += "food" to "tacos" 34 | map += "count" to 3 35 | CodeBlock.builder().addNamed("I ate %count:L %food:L", map) 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/code-control-flow.md: -------------------------------------------------------------------------------- 1 | Code & Control Flow 2 | =================== 3 | 4 | Most of KotlinPoet's API uses immutable Kotlin objects. There's also builders, method chaining 5 | and varargs to make the API friendly. KotlinPoet offers models for Kotlin files (`FileSpec`), 6 | classes, interfaces & objects (`TypeSpec`), type aliases (`TypeAliasSpec`), 7 | properties (`PropertySpec`), functions & constructors (`FunSpec`), parameters (`ParameterSpec`) and 8 | annotations (`AnnotationSpec`). 9 | 10 | But the _body_ of methods and constructors is not modeled. There's no expression class, no 11 | statement class or syntax tree nodes. Instead, KotlinPoet uses strings for code blocks, and you can 12 | take advantage of Kotlin's multiline strings to make this look nice: 13 | 14 | ```kotlin 15 | val main = FunSpec.builder("main") 16 | .addCode(""" 17 | |var total = 0 18 | |for (i in 0..<10) { 19 | | total += i 20 | |} 21 | |""".trimMargin()) 22 | .build() 23 | ``` 24 | 25 | Which generates this: 26 | 27 | ```kotlin 28 | fun main() { 29 | var total = 0 30 | for (i in 0..<10) { 31 | total += i 32 | } 33 | } 34 | ``` 35 | 36 | There are additional APIs to assist with newlines, braces and indentation: 37 | 38 | ```kotlin 39 | val main = FunSpec.builder("main") 40 | .addStatement("var total = 0") 41 | .beginControlFlow("for (i in 0..<10)") 42 | .addStatement("total += i") 43 | .endControlFlow() 44 | .build() 45 | ``` 46 | 47 | This example is lame because the generated code is constant! Suppose instead of just adding 0 to 10, 48 | we want to make the operation and range configurable. Here's a method that generates a method: 49 | 50 | ```kotlin 51 | private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec { 52 | return FunSpec.builder(name) 53 | .returns(Int::class) 54 | .addStatement("var result = 1") 55 | .beginControlFlow("for (i in $from..<$to)") 56 | .addStatement("result = result $op i") 57 | .endControlFlow() 58 | .addStatement("return result") 59 | .build() 60 | } 61 | ``` 62 | 63 | And here's what we get when we call `computeRange("multiply10to20", 10, 20, "*")`: 64 | 65 | ```kotlin 66 | fun multiply10to20(): kotlin.Int { 67 | var result = 1 68 | for (i in 10..<20) { 69 | result = result * i 70 | } 71 | return result 72 | } 73 | ``` 74 | 75 | Methods generating methods! And since KotlinPoet generates source instead of bytecode, you can 76 | read through it to make sure it's right. 77 | -------------------------------------------------------------------------------- /docs/constructors.md: -------------------------------------------------------------------------------- 1 | Constructors 2 | ============ 3 | 4 | `FunSpec` is a slight misnomer; it can also be used for constructors: 5 | 6 | ```kotlin 7 | val flux = FunSpec.constructorBuilder() 8 | .addParameter("greeting", String::class) 9 | .addStatement("this.%N = %N", "greeting", "greeting") 10 | .build() 11 | 12 | val helloWorld = TypeSpec.classBuilder("HelloWorld") 13 | .addProperty("greeting", String::class, KModifier.PRIVATE) 14 | .addFunction(flux) 15 | .build() 16 | ``` 17 | 18 | Which generates this: 19 | 20 | ```kotlin 21 | class HelloWorld { 22 | private val greeting: String 23 | 24 | constructor(greeting: String) { 25 | this.greeting = greeting 26 | } 27 | } 28 | ``` 29 | 30 | For the most part, constructors work just like methods. When emitting code, KotlinPoet will place 31 | constructors before methods in the output file. 32 | 33 | Often times you'll need to generate the primary constructor for a class: 34 | 35 | ```kotlin 36 | val helloWorld = TypeSpec.classBuilder("HelloWorld") 37 | .primaryConstructor(flux) 38 | .addProperty("greeting", String::class, KModifier.PRIVATE) 39 | .build() 40 | ``` 41 | 42 | This code, however, generates the following: 43 | 44 | ```kotlin 45 | class HelloWorld(greeting: String) { 46 | private val greeting: String 47 | 48 | init { 49 | this.greeting = greeting 50 | } 51 | } 52 | ``` 53 | 54 | By default, KotlinPoet won't merge primary constructor parameters and properties, even if they share 55 | the same name. To achieve the effect, you have to tell KotlinPoet that the property is initialized 56 | via the constructor parameter: 57 | 58 | ```kotlin 59 | val flux = FunSpec.constructorBuilder() 60 | .addParameter("greeting", String::class) 61 | .build() 62 | 63 | val helloWorld = TypeSpec.classBuilder("HelloWorld") 64 | .primaryConstructor(flux) 65 | .addProperty( 66 | PropertySpec.builder("greeting", String::class) 67 | .initializer("greeting") 68 | .addModifiers(KModifier.PRIVATE) 69 | .build() 70 | ) 71 | .build() 72 | ``` 73 | 74 | Now we're getting the following output: 75 | 76 | ```kotlin 77 | class HelloWorld(private val greeting: String) 78 | ``` 79 | 80 | Notice that KotlinPoet omits `{}` for classes with empty bodies. 81 | -------------------------------------------------------------------------------- /docs/context-parameters.md: -------------------------------------------------------------------------------- 1 | Context Parameters 2 | ================== 3 | 4 | KotlinPoet supports Kotlin's context parameters feature, which allows you to add context parameters to functions and properties. Context parameters are added using the `contextParameter()` method: 5 | 6 | ```kotlin 7 | val greet = FunSpec.builder("greet") 8 | .contextParameter("user", String::class) 9 | .addStatement("println(\"Hello, ${'$'}user!\")") 10 | .build() 11 | ``` 12 | 13 | The code above generates: 14 | 15 | ```kotlin 16 | context(user: kotlin.String) 17 | public fun greet() { 18 | println("Hello, $user!") 19 | } 20 | ``` 21 | 22 | You can add multiple context parameters: 23 | 24 | ```kotlin 25 | val loggerType = ClassName("java.util.logging", "Logger") 26 | val configType = ClassName("com.example", "Config") 27 | 28 | val logger = ContextParameter("logger", loggerType) 29 | val config = ContextParameter("config", configType) 30 | 31 | val processData = FunSpec.builder("processData") 32 | .contextParameter(logger) 33 | .contextParameter(config) 34 | .addStatement("%N.info(\"Processing with config: ${'$'}%N\")", logger, config) 35 | .build() 36 | ``` 37 | 38 | This generates: 39 | 40 | ```kotlin 41 | context(logger: com.example.Logger, config: com.example.Config) 42 | public fun processData() { 43 | logger.info("Processing with config: $config") 44 | } 45 | ``` 46 | 47 | **Note:** context receivers and context parameters cannot be used together. 48 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | If you would like to contribute code you can do so through GitHub by forking 5 | the repository and sending a pull request. 6 | 7 | When submitting code, please make every effort to follow existing conventions 8 | and style in order to keep the code as readable as possible. Please also make 9 | sure your code compiles by running `./gradlew clean build`. 10 | 11 | When creating a pull request, please add a row in the [changelog][2] with the 12 | patch description and PR # to `Unreleased` section. 13 | 14 | Before your code can be accepted into the project you must also sign the 15 | [Individual Contributor License Agreement (CLA)][1]. 16 | 17 | 18 | [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 19 | [2]: https://github.com/square/kotlinpoet/blob/main/docs/changelog.md 20 | -------------------------------------------------------------------------------- /docs/css/app.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: cash-market; 3 | src: url("https://cash-f.squarecdn.com/static/fonts/cash-market/v2/CashMarket-Regular.woff2") format("woff2"); 4 | font-weight: 400; 5 | font-style: normal 6 | } 7 | 8 | @font-face { 9 | font-family: cash-market; 10 | src: url("https://cash-f.squarecdn.com/static/fonts/cash-market/v2/CashMarket-Medium.woff2") format("woff2"); 11 | font-weight: 500; 12 | font-style: normal 13 | } 14 | 15 | @font-face { 16 | font-family: cash-market; 17 | src: url("https://cash-f.squarecdn.com/static/fonts/cash-market/v2/CashMarket-Bold.woff2") format("woff2"); 18 | font-weight: 700; 19 | font-style: normal 20 | } 21 | 22 | body, input { 23 | font-family: cash-market,"Helvetica Neue",helvetica,sans-serif; 24 | } 25 | 26 | .md-typeset h1, .md-typeset h2, .md-typeset h3, .md-typeset h4 { 27 | font-family: cash-market,"Helvetica Neue",helvetica,sans-serif; 28 | line-height: normal; 29 | font-weight: bold; 30 | } 31 | -------------------------------------------------------------------------------- /docs/enums.md: -------------------------------------------------------------------------------- 1 | Enums 2 | ===== 3 | 4 | Use `enumBuilder` to create the enum type, and `addEnumConstant()` for each value: 5 | 6 | ```kotlin 7 | val helloWorld = TypeSpec.enumBuilder("Roshambo") 8 | .addEnumConstant("ROCK") 9 | .addEnumConstant("SCISSORS") 10 | .addEnumConstant("PAPER") 11 | .build() 12 | ``` 13 | 14 | To generate this: 15 | 16 | ```kotlin 17 | enum class Roshambo { 18 | ROCK, 19 | 20 | SCISSORS, 21 | 22 | PAPER 23 | } 24 | ``` 25 | 26 | Fancy enums are supported, where the enum values override methods or call a superclass constructor. 27 | Here's a comprehensive example: 28 | 29 | ```kotlin 30 | val helloWorld = TypeSpec.enumBuilder("Roshambo") 31 | .primaryConstructor( 32 | FunSpec.constructorBuilder() 33 | .addParameter("handsign", String::class) 34 | .build() 35 | ) 36 | .addEnumConstant( 37 | "ROCK", TypeSpec.anonymousClassBuilder() 38 | .addSuperclassConstructorParameter("%S", "fist") 39 | .addFunction( 40 | FunSpec.builder("toString") 41 | .addModifiers(KModifier.OVERRIDE) 42 | .addStatement("return %S", "avalanche!") 43 | .returns(String::class) 44 | .build() 45 | ) 46 | .build() 47 | ) 48 | .addEnumConstant( 49 | "SCISSORS", TypeSpec.anonymousClassBuilder() 50 | .addSuperclassConstructorParameter("%S", "peace") 51 | .build() 52 | ) 53 | .addEnumConstant( 54 | "PAPER", TypeSpec.anonymousClassBuilder() 55 | .addSuperclassConstructorParameter("%S", "flat") 56 | .build() 57 | ) 58 | .addProperty( 59 | PropertySpec.builder("handsign", String::class, KModifier.PRIVATE) 60 | .initializer("handsign") 61 | .build() 62 | ) 63 | .build() 64 | ``` 65 | 66 | Which generates this: 67 | 68 | ```kotlin 69 | enum class Roshambo(private val handsign: String) { 70 | ROCK("fist") { 71 | override fun toString(): String = "avalanche!" 72 | }, 73 | 74 | SCISSORS("peace"), 75 | 76 | PAPER("flat"); 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /docs/images/icon-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/kotlinpoet/98c73d8e286fe7bd5ab9d7d7bd25814f243f99b1/docs/images/icon-square.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | KotlinPoet 2 | ========== 3 | 4 | `KotlinPoet` is a Kotlin and Java API for generating `.kt` source files. 5 | 6 | Source file generation can be useful when doing things such as annotation processing or interacting 7 | with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate 8 | the need to write boilerplate while also keeping a single source of truth for the metadata. 9 | 10 | ## Example 11 | 12 | Here's a `HelloWorld` file: 13 | 14 | ```kotlin 15 | class Greeter(val name: String) { 16 | fun greet() { 17 | println("""Hello, $name""") 18 | } 19 | } 20 | 21 | fun main(vararg args: String) { 22 | Greeter(args[0]).greet() 23 | } 24 | ``` 25 | 26 | And this is the code to generate it with KotlinPoet: 27 | 28 | ```kotlin 29 | val greeterClass = ClassName("", "Greeter") 30 | val file = FileSpec.builder("", "HelloWorld") 31 | .addType( 32 | TypeSpec.classBuilder("Greeter") 33 | .primaryConstructor( 34 | FunSpec.constructorBuilder() 35 | .addParameter("name", String::class) 36 | .build() 37 | ) 38 | .addProperty( 39 | PropertySpec.builder("name", String::class) 40 | .initializer("name") 41 | .build() 42 | ) 43 | .addFunction( 44 | FunSpec.builder("greet") 45 | .addStatement("println(%P)", "Hello, \$name") 46 | .build() 47 | ) 48 | .build() 49 | ) 50 | .addFunction( 51 | FunSpec.builder("main") 52 | .addParameter("args", String::class, VARARG) 53 | .addStatement("%T(args[0]).greet()", greeterClass) 54 | .build() 55 | ) 56 | .build() 57 | 58 | file.writeTo(System.out) 59 | ``` 60 | 61 | The [KDoc][kdoc] catalogs the complete KotlinPoet API, which is inspired by [JavaPoet][javapoet]. 62 | 63 | **Note:** In order to maximize portability, KotlinPoet generates code with explicit visibility 64 | modifiers. This ensures compatibility with both standard Kotlin projects as well as projects 65 | using [explicit API mode][explicit-api]. Examples in the documentation omit those modifiers for 66 | brevity. 67 | 68 | Download 69 | -------- 70 | 71 | ![Maven Central][version-shield] 72 | 73 | Download [the latest .jar][dl] or depend via Maven: 74 | 75 | ```xml 76 | 77 | com.squareup 78 | kotlinpoet-jvm 79 | [version] 80 | 81 | ``` 82 | 83 | or Gradle: 84 | 85 | ```groovy 86 | implementation("com.squareup:kotlinpoet:[version]") 87 | ``` 88 | 89 | Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. 90 | 91 | License 92 | ------- 93 | 94 | Copyright 2017 Square, Inc. 95 | 96 | Licensed under the Apache License, Version 2.0 (the "License"); 97 | you may not use this file except in compliance with the License. 98 | You may obtain a copy of the License at 99 | 100 | https://www.apache.org/licenses/LICENSE-2.0 101 | 102 | Unless required by applicable law or agreed to in writing, software 103 | distributed under the License is distributed on an "AS IS" BASIS, 104 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 105 | See the License for the specific language governing permissions and 106 | limitations under the License. 107 | 108 | [kdoc]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/ 109 | [javapoet]: https://github.com/square/javapoet/ 110 | [explicit-api]: https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors 111 | [version-shield]: https://img.shields.io/maven-central/v/com.squareup/kotlinpoet 112 | [dl]: https://search.maven.org/remote_content?g=com.squareup&a=kotlinpoet-jvm&v=LATEST 113 | [snap]: https://s01.oss.sonatype.org/content/repositories/snapshots/com/squareup/kotlinpoet/ 114 | -------------------------------------------------------------------------------- /docs/interfaces.md: -------------------------------------------------------------------------------- 1 | Interfaces 2 | ========== 3 | 4 | KotlinPoet has no trouble with interfaces. Note that interface methods must always be `ABSTRACT`. 5 | The modifier is necessary when defining the interface: 6 | 7 | ```kotlin 8 | val helloWorld = TypeSpec.interfaceBuilder("HelloWorld") 9 | .addProperty("buzz", String::class) 10 | .addFunction( 11 | FunSpec.builder("beep") 12 | .addModifiers(KModifier.ABSTRACT) 13 | .build() 14 | ) 15 | .build() 16 | ``` 17 | 18 | But these modifiers are omitted when the code is generated. These are the default so we don't need 19 | to include them for `kotlinc`'s benefit! 20 | 21 | ```kotlin 22 | interface HelloWorld { 23 | val buzz: String 24 | 25 | fun beep() 26 | } 27 | ``` 28 | 29 | Kotlin 1.4 adds support for functional interfaces via `fun interface` syntax. To create this in 30 | KotlinPoet, use `TypeSpec.funInterfaceBuilder()`. 31 | 32 | ```kotlin 33 | val helloWorld = TypeSpec.funInterfaceBuilder("HelloWorld") 34 | .addFunction( 35 | FunSpec.builder("beep") 36 | .addModifiers(KModifier.ABSTRACT) 37 | .build() 38 | ) 39 | .build() 40 | 41 | // Generates... 42 | fun interface HelloWorld { 43 | fun beep() 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/interop-javapoet.md: -------------------------------------------------------------------------------- 1 | JavaPoet Extensions for KotlinPoet 2 | ================================== 3 | 4 | `interop:javapoet` is an interop API for converting [JavaPoet](https://github.com/square/javapoet) 5 | types to KotlinPoet types. This is particularly useful for projects that support code gen in 6 | multiple languages and want to easily be able to jump between. 7 | 8 | Note that this API is currently in preview and subject to API changes. Usage of them requires opting 9 | in to the `@KotlinPoetJavaPoetPreview` annotation. 10 | 11 | ### Examples 12 | 13 | **Typealiases for common conflicting type names** 14 | 15 | ```kotlin 16 | // Points to com.squareup.kotlinpoet.TypeName 17 | KTypeName 18 | // Points to com.squareup.javapoet.TypeName 19 | JTypeName 20 | ``` 21 | 22 | **Convert between a `JTypeName` and `KTypeName`** 23 | 24 | Most usages of these can run through the `toKTypeName()` and `toJTypeName()` extensions. 25 | 26 | ```kotlin 27 | val jType = JTypeName.get("com.example", "Taco") 28 | 29 | // Returns a KotlinPoet `ClassName` of value `com.example.Taco` 30 | val kType = jType.toKTypeName() 31 | 32 | // Returns a JavaPoet `ClassName` of value `com.example.Taco` 33 | val jType2 = kType.toJTypeName() 34 | ``` 35 | 36 | ### Intrinsics 37 | 38 | Kotlin supports a number of intrinsic types that live in the `kotlin` package, such as primitives, 39 | `List`, `String`, `IntArray`, etc. Where possible, interop will best-effort attempt to convert to 40 | the idiomatic Kotlin type when converting from the Java type. 41 | 42 | ### Lossy Conversions 43 | 44 | Kotlin has more expressive types in some regards. These cannot be simply expressed in JavaPoet and 45 | are subject to lossy conversions. 46 | 47 | Examples include: 48 | 49 | - Nullability 50 | - Nullable types in Kotlin will appear as normal types in JavaPoet. 51 | - Collection mutability 52 | - Immutable Kotlin collections will convert to their standard (mutable) Java analogs. 53 | - Java collections will convert to _immutable_ Kotlin analogs, erring on the side of safety in generated public APIs 54 | - Unsigned types 55 | -------------------------------------------------------------------------------- /docs/interop-kotlin-metadata.md: -------------------------------------------------------------------------------- 1 | KotlinPoet-metadata 2 | =================== 3 | 4 | `interop:kotlin-metadata` is an API for working with Kotlin `@Metadata` annotations. Its API 5 | sits atop [kotlin-metadata](https://github.com/JetBrains/kotlin/tree/master/libraries/kotlinx-metadata/jvm), 6 | offering extensions for its types + JVM metadata information. This can be used to read 7 | Kotlin language semantics off of `Class` or `TypeElement` `@Metadata` annotations. 8 | 9 | ### Example 10 | 11 | ```kotlin 12 | data class Taco(val seasoning: String, val soft: Boolean) { 13 | fun prepare() { 14 | 15 | } 16 | } 17 | 18 | val kmClass = Taco::class.toKmClass() 19 | 20 | // Now you can access misc information about Taco from a Kotlin lens 21 | println(kmClass.name) 22 | kmClass.properties.forEach { println(it.name) } 23 | kmClass.functions.forEach { println(it.name) } 24 | ``` 25 | 26 | ### Flags 27 | 28 | There are a number of boolean flags available to types as well under `Flags.kt`. These read the 29 | underlying kotlin-metadata `Flags` property. 30 | 31 | Using the Taco example above, we can glean certain information: 32 | 33 | ```kotlin 34 | println("Is class? ${kmClass.isClass}") 35 | println("Is data class? ${kmClass.isData}") 36 | ``` 37 | 38 | ### Interop with KotlinPoet 39 | 40 | `interop:kotlin-metadata` offers an API for converting core kotlin-metadata `Km` types to 41 | KotlinPoet source representations of their APIs. This includes full type resolution, signatures, 42 | enclosed elements, and general stub source representations of the underlying API. 43 | 44 | ### Example 45 | 46 | ```kotlin 47 | data class Taco(val seasoning: String, val soft: Boolean) { 48 | fun prepare() { 49 | } 50 | } 51 | 52 | val typeSpec = Taco::class.toTypeSpec() 53 | 54 | // Or FileSpec 55 | val fileSpec = Taco::class.toFileSpec() 56 | ``` 57 | 58 | ### Source representation 59 | 60 | The generated representations are a _best effort_ representation of the underlying source code. 61 | This means that synthetic elements will be excluded from generation. Kotlin-specific language 62 | features like lambdas or delegation will be coerced to their idiomatic source form. 63 | 64 | To aid with this, `toTypeSpec()` and `toFileSpec()` accept optional `ClassInspector` instances 65 | to assist in parsing/understanding the underlying JVM code. This is important for things like 66 | annotations, companion objects, certain JVM modifiers, overrides, and more. While it is optional, 67 | represented sources can be incomplete without this information available. Reflective and javax 68 | `Elements` implementations are available under the 69 | `com.squareup.kotlinpoet.metadata.classinspectors` package. 70 | 71 | Generated sources are solely _stub_ implementations, meaning implementation details of elements 72 | like functions, property getters, and delegated properties are simply stubbed with `TODO()` 73 | placeholders. 74 | 75 | ### Known limitations 76 | 77 | - Only `KotlinClassMetadata.Class` and `KotlinClassMetadata.FileFacade` are supported for now. No support for `SyntheticClass`, `MultiFileClassFacade`, or `MultiFileClassPart` 78 | - `@JvmOverloads` annotations are only supported with `ElementsClassInspector` and not reflection. 79 | - Non-const literal values are only supported with `ElementsClassInspector` and not reflection. 80 | - ClassInspector data sourced from `synthetic` constructs are only supported with 81 | `ReflectiveClassInspector` and not elements. This is because the javax Elements API does not model 82 | synthetic constructs. This can yield some missing information, like static companion object properties 83 | or `property:` site target annotations. 84 | - Annotations annotated with `AnnotationRetention.SOURCE` are not parsable in reflection nor javax elements. 85 | -------------------------------------------------------------------------------- /docs/kotlin-reflect.md: -------------------------------------------------------------------------------- 1 | kotlin-reflect 2 | ============== 3 | 4 | To generate source code from any [`KType`][k-type], including information that's not accessible to 5 | the builtin reflection APIs, KotlinPoet depends on [kotlin-reflect][kotlin-reflect]. `kotlin-reflect` 6 | can read the metadata of your classes and access this extra information. KotlinPoet can for an 7 | example, read the type parameters and their [variance][variance] from a generic `KType` and 8 | generate appropriate source code. 9 | 10 | `kotlin-reflect` is a relatively big dependency though and in some cases it is desirable to remove 11 | it from the final executable to save some space and/or simplify the proguard/R8 setup (for example 12 | for a Gradle plugin that generates Kotlin code). It is possible to do so and still use most of the 13 | KotlinPoet APIs: 14 | 15 | ```kotlin 16 | dependencies { 17 | implementation("com.squareup:kotlinpoet:") { 18 | exclude(module = "kotlin-reflect") 19 | } 20 | } 21 | ``` 22 | 23 | The main APIs that require `kotlin-reflect` are [`KType.asTypeName()`][as-type-name] and 24 | [`typeNameOf()`][type-name-of]. If you're calling one of these without `kotlin-reflect` in the 25 | classpath and the type is generic or has annotations you will get a crash. 26 | 27 | You can replace it with code that passes type parameters or annotations explicitly and doesn't 28 | need `kotlin-reflect`. For example: 29 | 30 | ```kotlin 31 | // Replace 32 | // kotlin-reflect needed 33 | val typeName = typeNameOf>() 34 | 35 | // With 36 | // kotlin-reflect not needed 37 | val typeName = 38 | List::class.asClassName().parameterizedBy(Int::class.asClassName().copy(nullable = true)) 39 | ``` 40 | 41 | [k-type]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-type/ 42 | [kotlin-reflect]: https://kotlinlang.org/docs/reflection.html#jvm-dependency 43 | [variance]: https://kotlinlang.org/docs/generics.html#variance 44 | [as-type-name]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/as-type-name.html 45 | [type-name-of]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/type-name-of.html 46 | -------------------------------------------------------------------------------- /docs/l-for-literals.md: -------------------------------------------------------------------------------- 1 | %L for Literals 2 | =============== 3 | 4 | Although Kotlin's string templates usually work well in cases when you want to include literals into 5 | generated code, KotlinPoet offers additional syntax inspired-by but incompatible-with 6 | [`String.format()`][formatter]. It accepts **`%L`** to emit a **literal** value in the output. This 7 | works just like `Formatter`'s `%s`: 8 | 9 | ```kotlin 10 | private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec { 11 | return FunSpec.builder(name) 12 | .returns(Int::class) 13 | .addStatement("var result = 0") 14 | .beginControlFlow("for (i in %L..<%L)", from, to) 15 | .addStatement("result = result %L i", op) 16 | .endControlFlow() 17 | .addStatement("return result") 18 | .build() 19 | } 20 | ``` 21 | 22 | Literals are emitted directly to the output code with no escaping. Arguments for literals may be 23 | strings, primitives, and a few KotlinPoet types described below. 24 | 25 | [formatter]: https://developer.android.com/reference/java/util/Formatter.html 26 | -------------------------------------------------------------------------------- /docs/m-for-members.md: -------------------------------------------------------------------------------- 1 | %M for Members 2 | ============== 3 | 4 | Similar to types, KotlinPoet has a special placeholder for **members** (functions and properties), 5 | which comes handy when your code needs to access top-level members and members declared inside 6 | objects. Use **`%M`** to reference members, pass an instance of `MemberName` as the argument for the 7 | placeholder, and KotlinPoet will handle imports automatically: 8 | 9 | ```kotlin 10 | val createTaco = MemberName("com.squareup.tacos", "createTaco") 11 | val isVegan = MemberName("com.squareup.tacos", "isVegan") 12 | val file = FileSpec.builder("com.squareup.example", "TacoTest") 13 | .addFunction( 14 | FunSpec.builder("main") 15 | .addStatement("val taco = %M()", createTaco) 16 | .addStatement("println(taco.%M)", isVegan) 17 | .build() 18 | ) 19 | .build() 20 | println(file) 21 | ``` 22 | 23 | The code above generates the following file: 24 | 25 | ```kotlin 26 | package com.squareup.example 27 | 28 | import com.squareup.tacos.createTaco 29 | import com.squareup.tacos.isVegan 30 | 31 | fun main() { 32 | val taco = createTaco() 33 | println(taco.isVegan) 34 | } 35 | ``` 36 | 37 | As you can see, it's also possible to use `%M` to reference extension functions and properties. You 38 | just need to make sure the member can be imported without simple name collisions, otherwise 39 | importing will fail and the code generator output will not pass compilation. There's a way to work 40 | around such cases though - use `FileSpec.addAliasedImport()` to create an alias for a clashing 41 | `MemberName`: 42 | 43 | ```kotlin 44 | val createTaco = MemberName("com.squareup.tacos", "createTaco") 45 | val createCake = MemberName("com.squareup.cakes", "createCake") 46 | val isTacoVegan = MemberName("com.squareup.tacos", "isVegan") 47 | val isCakeVegan = MemberName("com.squareup.cakes", "isVegan") 48 | val file = FileSpec.builder("com.squareup.example", "Test") 49 | .addAliasedImport(isTacoVegan, "isTacoVegan") 50 | .addAliasedImport(isCakeVegan, "isCakeVegan") 51 | .addFunction( 52 | FunSpec.builder("main") 53 | .addStatement("val taco = %M()", createTaco) 54 | .addStatement("val cake = %M()", createCake) 55 | .addStatement("println(taco.%M)", isTacoVegan) 56 | .addStatement("println(cake.%M)", isCakeVegan) 57 | .build() 58 | ) 59 | .build() 60 | println(file) 61 | ``` 62 | 63 | KotlinPoet will produce an aliased import for `com.squareup.tacos2.isVegan`: 64 | 65 | ```kotlin 66 | package com.squareup.example 67 | 68 | import com.squareup.cakes.createCake 69 | import com.squareup.tacos.createTaco 70 | import com.squareup.cakes.isVegan as isCakeVegan 71 | import com.squareup.tacos.isVegan as isTacoVegan 72 | 73 | fun main() { 74 | val taco = createTaco() 75 | val cake = createCake() 76 | println(taco.isTacoVegan) 77 | println(cake.isCakeVegan) 78 | } 79 | ``` 80 | 81 | ## MemberName and operators 82 | 83 | MemberName also supports operators, you can use `MemberName(String, KOperator)` 84 | or `MemberName(ClassName, KOperator)` to import and reference operators. 85 | 86 | ```kotlin 87 | val taco = ClassName("com.squareup.tacos", "Taco") 88 | val meat = ClassName("com.squareup.tacos.ingredient", "Meat") 89 | val iterator = MemberName("com.squareup.tacos.internal", KOperator.ITERATOR) 90 | val minusAssign = MemberName("com.squareup.tacos.internal", KOperator.MINUS_ASSIGN) 91 | val file = FileSpec.builder("com.example", "Test") 92 | .addFunction( 93 | FunSpec.builder("makeTacoHealthy") 94 | .addParameter("taco", taco) 95 | .beginControlFlow("for (ingredient %M taco)", iterator) 96 | .addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign) 97 | .endControlFlow() 98 | .addStatement("return taco") 99 | .build() 100 | ) 101 | .build() 102 | println(file) 103 | ``` 104 | 105 | KotlinPoet will import the extension operator functions and emit the operator. 106 | 107 | ```kotlin 108 | package com.example 109 | 110 | import com.squareup.tacos.Taco 111 | import com.squareup.tacos.ingredient.Meat 112 | import com.squareup.tacos.internal.iterator 113 | import com.squareup.tacos.internal.minusAssign 114 | 115 | fun makeTacoHealthy(taco: Taco) { 116 | for (ingredient in taco) { 117 | if (ingredient is Meat) taco -= ingredient 118 | } 119 | return taco 120 | } 121 | 122 | ``` 123 | -------------------------------------------------------------------------------- /docs/n-for-names.md: -------------------------------------------------------------------------------- 1 | %N for Names 2 | ============ 3 | 4 | Generated code is often self-referential. Use **`%N`** to refer to another generated declaration by 5 | its name. Here's a method that calls another: 6 | 7 | ```kotlin 8 | fun byteToHex(b: Int): String { 9 | val result = CharArray(2) 10 | result[0] = hexDigit((b ushr 4) and 0xf) 11 | result[1] = hexDigit(b and 0xf) 12 | return String(result) 13 | } 14 | 15 | fun hexDigit(i: Int): Char { 16 | return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar() 17 | } 18 | ``` 19 | 20 | When generating the code above, we pass the `hexDigit()` method as an argument to the `byteToHex()` 21 | method using `%N`: 22 | 23 | ```kotlin 24 | val hexDigit = FunSpec.builder("hexDigit") 25 | .addParameter("i", Int::class) 26 | .returns(Char::class) 27 | .addStatement("return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()") 28 | .build() 29 | 30 | val byteToHex = FunSpec.builder("byteToHex") 31 | .addParameter("b", Int::class) 32 | .returns(String::class) 33 | .addStatement("val result = CharArray(2)") 34 | .addStatement("result[0] = %N((b ushr 4) and 0xf)", hexDigit) 35 | .addStatement("result[1] = %N(b and 0xf)", hexDigit) 36 | .addStatement("return String(result)") 37 | .build() 38 | ``` 39 | 40 | Another handy feature that `%N` provides is automatically escaping names that contain illegal 41 | identifier characters with double ticks. Suppose your code creates a `MemberName` with a Kotlin 42 | keyword as the simple name: 43 | 44 | ```kotlin 45 | val taco = ClassName("com.squareup.tacos", "Taco") 46 | val packager = ClassName("com.squareup.tacos", "TacoPackager") 47 | val file = FileSpec.builder("com.example", "Test") 48 | .addFunction( 49 | FunSpec.builder("packageTacos") 50 | .addParameter("tacos", LIST.parameterizedBy(taco)) 51 | .addParameter("packager", packager) 52 | .addStatement("packager.%N(tacos)", packager.member("package")) 53 | .build() 54 | ) 55 | .build() 56 | ``` 57 | 58 | `%N` will escape the name for you, ensuring that the output will pass compilation: 59 | 60 | ```kotlin 61 | package com.example 62 | 63 | import com.squareup.tacos.Taco 64 | import com.squareup.tacos.TacoPackager 65 | import kotlin.collections.List 66 | 67 | fun packageTacos(tacos: List, packager: TacoPackager) { 68 | packager.`package`(tacos) 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /docs/objects.md: -------------------------------------------------------------------------------- 1 | Objects 2 | ======= 3 | 4 | KotlinPoet supports objects: 5 | 6 | ```kotlin 7 | val helloWorld = TypeSpec.objectBuilder("HelloWorld") 8 | .addProperty( 9 | PropertySpec.builder("buzz", String::class) 10 | .initializer("%S", "buzz") 11 | .build() 12 | ) 13 | .addFunction( 14 | FunSpec.builder("beep") 15 | .addStatement("println(%S)", "Beep!") 16 | .build() 17 | ) 18 | .build() 19 | ``` 20 | 21 | Similarly, you can create companion objects and add them to classes using `addType()`: 22 | 23 | ```kotlin 24 | val companion = TypeSpec.companionObjectBuilder() 25 | .addProperty( 26 | PropertySpec.builder("buzz", String::class) 27 | .initializer("%S", "buzz") 28 | .build() 29 | ) 30 | .addFunction( 31 | FunSpec.builder("beep") 32 | .addStatement("println(%S)", "Beep!") 33 | .build() 34 | ) 35 | .build() 36 | 37 | val helloWorld = TypeSpec.classBuilder("HelloWorld") 38 | .addType(companion) 39 | .build() 40 | ``` 41 | 42 | You can provide an optional name for a companion object. 43 | -------------------------------------------------------------------------------- /docs/p-for-string-templates.md: -------------------------------------------------------------------------------- 1 | %P for String Templates 2 | ======================= 3 | 4 | `%S` also handles the escaping of dollar signs (`$`), to avoid inadvertent creation of string 5 | templates, which may fail to compile in generated code: 6 | 7 | ```kotlin 8 | val stringWithADollar = "Your total is " + "$" + "50" 9 | val funSpec = FunSpec.builder("printTotal") 10 | .returns(String::class) 11 | .addStatement("return %S", stringWithADollar) 12 | .build() 13 | ``` 14 | 15 | produces: 16 | 17 | ```kotlin 18 | fun printTotal(): String = "Your total is ${'$'}50" 19 | ``` 20 | 21 | If you need to generate string templates, use `%P`, which doesn't escape dollars: 22 | 23 | ```kotlin 24 | val amount = 50 25 | val stringWithADollar = "Your total is " + "$" + "amount" 26 | val funSpec = FunSpec.builder("printTotal") 27 | .returns(String::class) 28 | .addStatement("return %P", stringWithADollar) 29 | .build() 30 | ``` 31 | 32 | produces: 33 | 34 | ```kotlin 35 | fun printTotal(): String = "Your total is $amount" 36 | ``` 37 | 38 | You can also use `CodeBlock`s as arguments to `%P`, which is handy when you need to reference 39 | importable types or members inside the string template: 40 | 41 | ```kotlin 42 | val file = FileSpec.builder("com.example", "Digits") 43 | .addFunction( 44 | FunSpec.builder("print") 45 | .addParameter("digits", IntArray::class) 46 | .addStatement("println(%P)", buildCodeBlock { 47 | val contentToString = MemberName("kotlin.collections", "contentToString") 48 | add("These are the digits: \${digits.%M()}", contentToString) 49 | }) 50 | .build() 51 | ) 52 | .build() 53 | println(file) 54 | ``` 55 | 56 | The snippet above will produce the following output, handling the imports properly: 57 | 58 | ```kotlin 59 | package com.example 60 | 61 | import kotlin.IntArray 62 | import kotlin.collections.contentToString 63 | 64 | fun print(digits: IntArray) { 65 | println("""These are the digits: ${digits.contentToString()}""") 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/parameters.md: -------------------------------------------------------------------------------- 1 | Parameters 2 | ========== 3 | 4 | Declare parameters on methods and constructors with either `ParameterSpec.builder()` or 5 | `FunSpec`'s convenient `addParameter()` API: 6 | 7 | ```kotlin 8 | val android = ParameterSpec.builder("android", String::class) 9 | .defaultValue("\"pie\"") 10 | .build() 11 | 12 | val welcomeOverlords = FunSpec.builder("welcomeOverlords") 13 | .addParameter(android) 14 | .addParameter("robot", String::class) 15 | .build() 16 | ``` 17 | 18 | The code above generates: 19 | 20 | ```kotlin 21 | fun welcomeOverlords(android: String = "pie", robot: String) { 22 | } 23 | ``` 24 | 25 | The extended `Builder` form is necessary when the parameter has annotations (such as `@Inject`). 26 | -------------------------------------------------------------------------------- /docs/properties.md: -------------------------------------------------------------------------------- 1 | Properties 2 | ========== 3 | 4 | Like parameters, properties can be created either with builders or by using convenient helper 5 | methods: 6 | 7 | ```kotlin 8 | val android = PropertySpec.builder("android", String::class) 9 | .addModifiers(KModifier.PRIVATE) 10 | .build() 11 | 12 | val helloWorld = TypeSpec.classBuilder("HelloWorld") 13 | .addProperty(android) 14 | .addProperty("robot", String::class, KModifier.PRIVATE) 15 | .build() 16 | ``` 17 | 18 | Which generates: 19 | 20 | ```kotlin 21 | class HelloWorld { 22 | private val android: String 23 | 24 | private val robot: String 25 | } 26 | ``` 27 | 28 | The extended `Builder` form is necessary when a field has KDoc, annotations, or a field 29 | initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code 30 | blocks above: 31 | 32 | ```kotlin 33 | val android = PropertySpec.builder("android", String::class) 34 | .addModifiers(KModifier.PRIVATE) 35 | .initializer("%S + %L", "Oreo v.", 8.1) 36 | .build() 37 | ``` 38 | 39 | Which generates: 40 | 41 | ```kotlin 42 | private val android: String = "Oreo v." + 8.1 43 | ``` 44 | 45 | By default `PropertySpec.Builder` produces `val` properties. Use `mutable()` if you need a 46 | `var`: 47 | 48 | ```kotlin 49 | val android = PropertySpec.builder("android", String::class) 50 | .mutable() 51 | .addModifiers(KModifier.PRIVATE) 52 | .initializer("%S + %L", "Oreo v.", 8.1) 53 | .build() 54 | ``` 55 | 56 | ## Inline properties 57 | 58 | The way KotlinPoet models inline properties deserves special mention. The following snippet of code: 59 | 60 | ```kotlin 61 | val android = PropertySpec.builder("android", String::class) 62 | .mutable() 63 | .addModifiers(KModifier.INLINE) 64 | .build() 65 | ``` 66 | 67 | will produce an error: 68 | 69 | ``` 70 | java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on 71 | properties. You should mark either the getter, the setter, or both inline. 72 | ``` 73 | 74 | Indeed, a property marked with `inline` should have at least one accessor which will be inlined by 75 | the compiler. Let's add a getter to this property: 76 | 77 | ```kotlin 78 | val android = PropertySpec.builder("android", String::class) 79 | .mutable() 80 | .getter( 81 | FunSpec.getterBuilder() 82 | .addModifiers(KModifier.INLINE) 83 | .addStatement("return %S", "foo") 84 | .build() 85 | ) 86 | .build() 87 | ``` 88 | 89 | The result is the following: 90 | 91 | ```kotlin 92 | var android: kotlin.String 93 | inline get() = "foo" 94 | ``` 95 | 96 | Now, what if we wanted to add a non-inline setter to the property above? We can do so without 97 | modifying any of the code we wrote previously: 98 | 99 | ```kotlin 100 | val android = PropertySpec.builder("android", String::class) 101 | .mutable() 102 | .getter( 103 | FunSpec.getterBuilder() 104 | .addModifiers(KModifier.INLINE) 105 | .addStatement("return %S", "foo") 106 | .build() 107 | ) 108 | .setter( 109 | FunSpec.setterBuilder() 110 | .addParameter("value", String::class) 111 | .build() 112 | ) 113 | .build() 114 | ``` 115 | 116 | We get the expected result: 117 | 118 | ```kotlin 119 | var android: kotlin.String 120 | inline get() = "foo" 121 | set(`value`) { 122 | } 123 | ``` 124 | 125 | Finally, if we go back and add `KModifier.INLINE` to the setter, KotlinPoet can wrap it nicely and 126 | produce the following result: 127 | 128 | ```kotlin 129 | inline var android: kotlin.String 130 | get() = "foo" 131 | set(`value`) { 132 | } 133 | ``` 134 | 135 | Removing the modifier from either the getter or the setter will unwrap the expression back. 136 | 137 | If, on the other hand, KotlinPoet had allowed marking a property `inline` directly, the programmer 138 | would have had to manually add/remove the modifier whenever the state of the accessors changes in 139 | order to get correct and compilable output. We're solving this problem by making accessors the 140 | source of truth for the `inline` modifier. 141 | 142 | [formatter]: https://developer.android.com/reference/java/util/Formatter.html 143 | -------------------------------------------------------------------------------- /docs/s-for-strings.md: -------------------------------------------------------------------------------- 1 | %S for Strings 2 | ============== 3 | 4 | When emitting code that includes string literals, we can use **`%S`** to emit a **string**, complete 5 | with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which 6 | returns its own name: 7 | 8 | ```kotlin 9 | fun main(args: Array) { 10 | val helloWorld = TypeSpec.classBuilder("HelloWorld") 11 | .addFunction(whatsMyNameYo("slimShady")) 12 | .addFunction(whatsMyNameYo("eminem")) 13 | .addFunction(whatsMyNameYo("marshallMathers")) 14 | .build() 15 | 16 | val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld") 17 | .addType(helloWorld) 18 | .build() 19 | 20 | kotlinFile.writeTo(System.out) 21 | } 22 | 23 | private fun whatsMyNameYo(name: String): FunSpec { 24 | return FunSpec.builder(name) 25 | .returns(String::class) 26 | .addStatement("return %S", name) 27 | .build() 28 | } 29 | ``` 30 | 31 | In this case, using `%S` gives us quotation marks: 32 | 33 | ```kotlin 34 | class HelloWorld { 35 | fun slimShady(): String = "slimShady" 36 | 37 | fun eminem(): String = "eminem" 38 | 39 | fun marshallMathers(): String = "marshallMathers" 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/type-aliases.md: -------------------------------------------------------------------------------- 1 | Type Aliases 2 | ============ 3 | 4 | KotlinPoet provides API for creating Type Aliases, which supports simple class names, parameterized 5 | types and lambdas: 6 | 7 | ```kotlin 8 | val k = TypeVariableName("K") 9 | val t = TypeVariableName("T") 10 | 11 | val fileTable = Map::class.asClassName() 12 | .parameterizedBy(k, Set::class.parameterizedBy(File::class)) 13 | 14 | val predicate = LambdaTypeName.get( 15 | parameters = arrayOf(t), 16 | returnType = Boolean::class.asClassName() 17 | ) 18 | val helloWorld = FileSpec.builder("com.example", "HelloWorld") 19 | .addTypeAlias(TypeAliasSpec.builder("Word", String::class).build()) 20 | .addTypeAlias( 21 | TypeAliasSpec.builder("FileTable", fileTable) 22 | .addTypeVariable(k) 23 | .build() 24 | ) 25 | .addTypeAlias( 26 | TypeAliasSpec.builder("Predicate", predicate) 27 | .addTypeVariable(t) 28 | .build() 29 | ) 30 | .build() 31 | ``` 32 | 33 | Which generates the following: 34 | 35 | ```kotlin 36 | package com.example 37 | 38 | import java.io.File 39 | import kotlin.Boolean 40 | import kotlin.String 41 | import kotlin.collections.Map 42 | import kotlin.collections.Set 43 | 44 | typealias Word = String 45 | 46 | typealias FileTable = Map> 47 | 48 | typealias Predicate = (T) -> Boolean 49 | ``` 50 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs='-Dfile.encoding=UTF-8' 2 | 3 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled 4 | org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true 5 | 6 | GROUP=com.squareup 7 | VERSION_NAME=2.3.0-SNAPSHOT 8 | 9 | POM_URL=https://github.com/square/kotlinpoet 10 | POM_SCM_URL=https://github.com/square/kotlinpoet 11 | POM_SCM_CONNECTION=scm:git:https://github.com/square/kotlinpoet.git 12 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/square/kotlinpoet.git 13 | 14 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 15 | POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt 16 | POM_LICENCE_DIST=repo 17 | 18 | POM_DEVELOPER_ID=square 19 | POM_DEVELOPER_NAME=Square, Inc. 20 | 21 | SONATYPE_HOST=S01 22 | RELEASE_SIGNING_ENABLED=true 23 | SONATYPE_AUTOMATIC_RELEASE=true 24 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Square, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [versions] 16 | kotlin = "2.1.21" 17 | kct = "0.7.1" 18 | ksp = "2.1.21-2.0.1" 19 | ktlint = "0.48.2" 20 | 21 | [plugins] 22 | kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } 23 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 24 | dokka = { id = "org.jetbrains.dokka", version = "2.0.0" } 25 | ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } 26 | spotless = { id = "com.diffplug.spotless", version = "7.0.4" } 27 | mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.32.0" } 28 | kotlinBinaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.17.0" } 29 | 30 | [libraries] 31 | autoCommon = { module = "com.google.auto:auto-common", version = "1.2.2" } 32 | javapoet = "com.squareup:javapoet:1.13.0" 33 | 34 | autoService = "com.google.auto.service:auto-service-annotations:1.1.1" 35 | autoService-ksp = "dev.zacsweers.autoservice:auto-service-ksp:1.2.0" 36 | 37 | kotlin-compilerEmbeddable = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" } 38 | kotlin-annotationProcessingEmbeddable = { module = "org.jetbrains.kotlin:kotlin-annotation-processing-embeddable", version.ref = "kotlin" } 39 | kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } 40 | kotlin-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } 41 | kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" } 42 | 43 | ksp = { module = "com.google.devtools.ksp:symbol-processing", version.ref = "ksp" } 44 | ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } 45 | ksp-aaEmbeddable = { module = "com.google.devtools.ksp:symbol-processing-aa-embeddable", version.ref = "ksp" } 46 | 47 | truth = { module = "com.google.truth:truth", version = "1.4.4" } 48 | compileTesting = { module = "com.google.testing.compile:compile-testing", version = "0.21.0" } 49 | jimfs = { module = "com.google.jimfs:jimfs", version = "1.3.0" } 50 | ecj = { module = "org.eclipse.jdt.core.compiler:ecj", version = "4.6.1" } 51 | kotlinCompileTesting = { module = "dev.zacsweers.kctfork:core", version.ref = "kct" } 52 | kotlinCompileTesting-ksp = { module = "dev.zacsweers.kctfork:ksp", version.ref = "kct" } 53 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/kotlinpoet/98c73d8e286fe7bd5ab9d7d7bd25814f243f99b1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=d7042b3c11565c192041fc8c4703f541b888286404b4f267138c1d094d8ecdca 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-all.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /interop/javapoet/api/javapoet.api: -------------------------------------------------------------------------------- 1 | public final class com/squareup/kotlinpoet/javapoet/J2kInteropKt { 2 | public static final fun toKClassName (Lcom/squareup/javapoet/ClassName;)Lcom/squareup/kotlinpoet/ClassName; 3 | public static final fun toKParameterizedTypeName (Lcom/squareup/javapoet/ParameterizedTypeName;)Lcom/squareup/kotlinpoet/ParameterizedTypeName; 4 | public static final fun toKTypeName (Lcom/squareup/javapoet/TypeName;)Lcom/squareup/kotlinpoet/TypeName; 5 | public static final fun toKTypeVariableName (Lcom/squareup/javapoet/TypeVariableName;)Lcom/squareup/kotlinpoet/TypeVariableName; 6 | public static final fun toKWildcardTypeName (Lcom/squareup/javapoet/WildcardTypeName;)Lcom/squareup/kotlinpoet/WildcardTypeName; 7 | } 8 | 9 | public final class com/squareup/kotlinpoet/javapoet/K2jInteropKt { 10 | public static final fun toJClassName (Lcom/squareup/kotlinpoet/ClassName;Z)Lcom/squareup/javapoet/TypeName; 11 | public static synthetic fun toJClassName$default (Lcom/squareup/kotlinpoet/ClassName;ZILjava/lang/Object;)Lcom/squareup/javapoet/TypeName; 12 | public static final fun toJParameterizedOrArrayTypeName (Lcom/squareup/kotlinpoet/ParameterizedTypeName;)Lcom/squareup/javapoet/TypeName; 13 | public static final fun toJParameterizedTypeName (Lcom/squareup/kotlinpoet/ParameterizedTypeName;)Lcom/squareup/javapoet/ParameterizedTypeName; 14 | public static final fun toJTypeName (Lcom/squareup/kotlinpoet/TypeName;Z)Lcom/squareup/javapoet/TypeName; 15 | public static synthetic fun toJTypeName$default (Lcom/squareup/kotlinpoet/TypeName;ZILjava/lang/Object;)Lcom/squareup/javapoet/TypeName; 16 | public static final fun toJTypeVariableName (Lcom/squareup/kotlinpoet/TypeVariableName;)Lcom/squareup/javapoet/TypeVariableName; 17 | public static final fun toJWildcardTypeName (Lcom/squareup/kotlinpoet/WildcardTypeName;)Lcom/squareup/javapoet/WildcardTypeName; 18 | } 19 | 20 | public abstract interface annotation class com/squareup/kotlinpoet/javapoet/KotlinPoetJavaPoetPreview : java/lang/annotation/Annotation { 21 | } 22 | 23 | -------------------------------------------------------------------------------- /interop/javapoet/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | plugins { 17 | kotlin("jvm") 18 | } 19 | 20 | tasks.jar { 21 | manifest { 22 | attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet.javapoet") 23 | } 24 | } 25 | 26 | dependencies { 27 | api(projects.kotlinpoet) 28 | api(libs.javapoet) 29 | testImplementation(libs.kotlin.junit) 30 | testImplementation(libs.truth) 31 | } 32 | -------------------------------------------------------------------------------- /interop/javapoet/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=kotlinpoet-javapoet 2 | POM_NAME=KotlinPoet (JavaPoet Interop) 3 | POM_DESCRIPTION=Extensions for interop with JavaPoet. 4 | POM_PACKAGING=jar 5 | -------------------------------------------------------------------------------- /interop/javapoet/src/main/kotlin/com/squareup/kotlinpoet/javapoet/KotlinPoetJavaPoetPreview.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.javapoet 17 | 18 | import kotlin.annotation.AnnotationTarget.CLASS 19 | import kotlin.annotation.AnnotationTarget.FUNCTION 20 | import kotlin.annotation.AnnotationTarget.PROPERTY 21 | import kotlin.annotation.AnnotationTarget.TYPEALIAS 22 | 23 | /** 24 | * Indicates that a given API is part of the experimental KotlinPoet JavaPoet support and is 25 | * subject to API changes. 26 | */ 27 | @RequiresOptIn 28 | @Retention(AnnotationRetention.BINARY) 29 | @Target(CLASS, FUNCTION, PROPERTY, TYPEALIAS) 30 | public annotation class KotlinPoetJavaPoetPreview 31 | -------------------------------------------------------------------------------- /interop/javapoet/src/main/kotlin/com/squareup/kotlinpoet/javapoet/PoetInterop.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.javapoet 17 | 18 | /** Various JavaPoet and KotlinPoet representations of some common types. */ 19 | @OptIn(KotlinPoetJavaPoetPreview::class) 20 | internal object PoetInterop { 21 | internal val CN_JAVA_CHAR_SEQUENCE = JClassName.get("java.lang", "CharSequence") 22 | internal val CN_JAVA_STRING = JClassName.get("java.lang", "String") 23 | internal val CN_JAVA_LIST = JClassName.get("java.util", "List") 24 | internal val CN_JAVA_SET = JClassName.get("java.util", "Set") 25 | internal val CN_JAVA_MAP = JClassName.get("java.util", "Map") 26 | internal val CN_JAVA_ENUM = JClassName.get("java.lang", "Enum") 27 | } 28 | -------------------------------------------------------------------------------- /interop/javapoet/src/main/kotlin/com/squareup/kotlinpoet/javapoet/typeAliases.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.javapoet 17 | 18 | /* 19 | * Useful typealiases for colliding names 20 | */ 21 | 22 | @KotlinPoetJavaPoetPreview 23 | public typealias KTypeName = com.squareup.kotlinpoet.TypeName 24 | 25 | @KotlinPoetJavaPoetPreview 26 | public typealias KClassName = com.squareup.kotlinpoet.ClassName 27 | 28 | @KotlinPoetJavaPoetPreview 29 | public typealias KTypeVariableName = com.squareup.kotlinpoet.TypeVariableName 30 | 31 | @KotlinPoetJavaPoetPreview 32 | public typealias KParameterizedTypeName = com.squareup.kotlinpoet.ParameterizedTypeName 33 | 34 | @KotlinPoetJavaPoetPreview 35 | public typealias KWildcardTypeName = com.squareup.kotlinpoet.WildcardTypeName 36 | 37 | @KotlinPoetJavaPoetPreview 38 | public typealias KTypeSpec = com.squareup.kotlinpoet.TypeSpec 39 | 40 | @KotlinPoetJavaPoetPreview 41 | public typealias KAnnotationSpec = com.squareup.kotlinpoet.AnnotationSpec 42 | 43 | @KotlinPoetJavaPoetPreview 44 | public typealias JTypeName = com.squareup.javapoet.TypeName 45 | 46 | @KotlinPoetJavaPoetPreview 47 | public typealias JClassName = com.squareup.javapoet.ClassName 48 | 49 | @KotlinPoetJavaPoetPreview 50 | public typealias JTypeVariableName = com.squareup.javapoet.TypeVariableName 51 | 52 | @KotlinPoetJavaPoetPreview 53 | public typealias JParameterizedTypeName = com.squareup.javapoet.ParameterizedTypeName 54 | 55 | @KotlinPoetJavaPoetPreview 56 | public typealias JWildcardTypeName = com.squareup.javapoet.WildcardTypeName 57 | 58 | @KotlinPoetJavaPoetPreview 59 | public typealias JTypeSpec = com.squareup.javapoet.TypeSpec 60 | 61 | @KotlinPoetJavaPoetPreview 62 | public typealias JAnnotationSpec = com.squareup.javapoet.AnnotationSpec 63 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | plugins { 17 | kotlin("jvm") 18 | } 19 | 20 | tasks.jar { 21 | manifest { 22 | attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet.metadata") 23 | } 24 | } 25 | 26 | tasks.compileTestKotlin { 27 | compilerOptions { 28 | freeCompilerArgs.addAll("-Xjvm-default=all") 29 | optIn.add("org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi") 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation(libs.autoCommon) 35 | api(libs.kotlin.metadata) 36 | api(projects.kotlinpoet) 37 | 38 | testImplementation(libs.kotlin.junit) 39 | testImplementation(libs.truth) 40 | testImplementation(libs.compileTesting) 41 | testImplementation(libs.kotlinCompileTesting) 42 | testImplementation(libs.kotlin.annotationProcessingEmbeddable) 43 | testImplementation(libs.kotlin.compilerEmbeddable) 44 | } 45 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=kotlinpoet-metadata 2 | POM_NAME=KotlinPoet (Kotlin Metadata Extensions) 3 | POM_DESCRIPTION=Extensions for reading Kotlin metadata from Metadata annotations. 4 | POM_PACKAGING=jar 5 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/classinspectors/Optional.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.classinspectors 17 | 18 | /** 19 | * Simple `Optional` implementation for use in collections that don't allow `null` values. 20 | */ 21 | internal data class Optional(val nullableValue: T?) 22 | 23 | internal fun T?.toOptional(): Optional = Optional( 24 | this, 25 | ) 26 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/ConstructorData.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.TypeName 20 | import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil 21 | 22 | /** 23 | * Represents relevant information on a constructor used for [ClassInspector]. Should only be 24 | * associated with constructors of a [ClassData]. 25 | * 26 | * @param annotations declared annotations on this constructor. 27 | * @property parameterAnnotations a mapping of parameter indices to annotations on them. 28 | * @property isSynthetic indicates if this constructor is synthetic or not. 29 | * @property jvmModifiers set of [JvmMethodModifiers][JvmMethodModifier] on this constructor. 30 | * @property exceptions list of exceptions thrown by this constructor. 31 | */ 32 | public data class ConstructorData( 33 | private val annotations: List, 34 | val parameterAnnotations: Map>, 35 | val isSynthetic: Boolean, 36 | val jvmModifiers: Set, 37 | val exceptions: List, 38 | ) { 39 | 40 | /** 41 | * A collection of all annotations on this constructor, including any derived from [jvmModifiers], 42 | * [isSynthetic], and [exceptions]. 43 | */ 44 | val allAnnotations: Collection = ClassInspectorUtil.createAnnotations { 45 | addAll(annotations) 46 | if (isSynthetic) { 47 | add(ClassInspectorUtil.JVM_SYNTHETIC_SPEC) 48 | } 49 | addAll(jvmModifiers.mapNotNull { it.annotationSpec() }) 50 | exceptions.takeIf { it.isNotEmpty() } 51 | ?.let { 52 | add(ClassInspectorUtil.createThrowsSpec(it)) 53 | } 54 | } 55 | 56 | public companion object { 57 | public val EMPTY: ConstructorData = ConstructorData( 58 | annotations = emptyList(), 59 | parameterAnnotations = emptyMap(), 60 | isSynthetic = false, 61 | jvmModifiers = emptySet(), 62 | exceptions = emptyList(), 63 | ) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/ContainerData.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.ClassName 20 | import kotlin.metadata.KmClass 21 | import kotlin.metadata.KmConstructor 22 | import kotlin.metadata.KmDeclarationContainer 23 | import kotlin.metadata.KmFunction 24 | import kotlin.metadata.KmPackage 25 | import kotlin.metadata.KmProperty 26 | 27 | /** 28 | * Represents relevant information on a declaration container used for [ClassInspector]. Can only 29 | * ever be applied on a Kotlin type (i.e. is annotated with [Metadata]). 30 | * 31 | * @property declarationContainer the [KmDeclarationContainer] as parsed from the class's 32 | * [@Metadata][Metadata] annotation. 33 | * @property annotations declared annotations on this class. 34 | * @property properties the mapping of [declarationContainer]'s properties to parsed [PropertyData]. 35 | * @property methods the mapping of [declarationContainer]'s methods to parsed [MethodData]. 36 | */ 37 | public interface ContainerData { 38 | public val declarationContainer: KmDeclarationContainer 39 | public val annotations: Collection 40 | public val properties: Map 41 | public val methods: Map 42 | } 43 | 44 | /** 45 | * Represents relevant information on a Kotlin class used for [ClassInspector]. Can only ever be 46 | * applied on a class and not file facades. 47 | * 48 | * @property declarationContainer the [KmClass] as parsed from the class's 49 | * [@Metadata][Metadata] annotation. 50 | * @property className the KotlinPoet [ClassName] of the class. 51 | * @property constructors the mapping of [declarationContainer]'s constructors to parsed 52 | * [ConstructorData]. 53 | */ 54 | public data class ClassData( 55 | override val declarationContainer: KmClass, 56 | val className: ClassName, 57 | override val annotations: Collection, 58 | override val properties: Map, 59 | val constructors: Map, 60 | override val methods: Map, 61 | ) : ContainerData 62 | 63 | /** 64 | * Represents relevant information on a file facade used for [ClassInspector]. 65 | * 66 | * @property declarationContainer the [KmClass] as parsed from the class's 67 | * [@Metadata][Metadata] annotation. 68 | * @property className the KotlinPoet [ClassName] of the underlying facade class in JVM. 69 | * @property jvmName the `@JvmName` of the class or null if it does not have a custom name. 70 | * Default will try to infer from the [className]. 71 | */ 72 | public data class FileData( 73 | override val declarationContainer: KmPackage, 74 | override val annotations: Collection, 75 | override val properties: Map, 76 | override val methods: Map, 77 | val className: ClassName, 78 | val jvmName: String? = 79 | if (!className.simpleName.endsWith("Kt")) className.simpleName else null, 80 | ) : ContainerData { 81 | 82 | /** 83 | * The file name of the container, defaults to [className]'s simple name + "Kt". If a [jvmName] is 84 | * specified, it will always defer to that. 85 | */ 86 | val fileName: String = jvmName ?: className.simpleName.removeSuffix("Kt") 87 | } 88 | 89 | /** 90 | * Represents relevant information on a Kotlin enum entry. 91 | * 92 | * @property declarationContainer the [KmClass] as parsed from the entry's 93 | * [@Metadata][Metadata] annotation. 94 | * @property annotations the annotations for the entry 95 | */ 96 | public data class EnumEntryData( 97 | val declarationContainer: KmClass?, 98 | val annotations: Collection, 99 | ) 100 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/FieldData.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.FIELD 20 | import com.squareup.kotlinpoet.CodeBlock 21 | import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil 22 | 23 | /** 24 | * Represents relevant information on a field used for [ClassInspector]. Should only be 25 | * associated with a [PropertyData]. 26 | * 27 | * @param annotations declared annotations on this field. 28 | * @property isSynthetic indicates if this field is synthetic or not. 29 | * @property jvmModifiers set of [JvmMethodModifiers][JvmMethodModifier] on this field. 30 | * @property constant the constant value of this field, if available. Note that this is does not 31 | * strictly imply that the associated property is `const`. 32 | */ 33 | public data class FieldData( 34 | private val annotations: List, 35 | val isSynthetic: Boolean, 36 | val jvmModifiers: Set, 37 | val constant: CodeBlock?, 38 | ) { 39 | 40 | /** 41 | * A collection of all annotations on this method, including any derived from [jvmModifiers] 42 | * and [isSynthetic]. 43 | */ 44 | val allAnnotations: Collection = ClassInspectorUtil.createAnnotations( 45 | FIELD, 46 | ) { 47 | addAll(annotations) 48 | if (isSynthetic) { 49 | add(ClassInspectorUtil.JVM_SYNTHETIC_SPEC) 50 | } 51 | addAll(jvmModifiers.mapNotNull(JvmFieldModifier::annotationSpec)) 52 | } 53 | 54 | public companion object { 55 | public val SYNTHETIC: FieldData = FieldData( 56 | annotations = emptyList(), 57 | isSynthetic = true, 58 | jvmModifiers = emptySet(), 59 | constant = null, 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/JvmFieldModifier.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.asClassName 20 | 21 | /** Modifiers that are annotations in Kotlin but modifier keywords in bytecode. */ 22 | public enum class JvmFieldModifier : JvmModifier { 23 | STATIC { 24 | override fun annotationSpec(): AnnotationSpec = AnnotationSpec.builder( 25 | JvmStatic::class.asClassName(), 26 | ).build() 27 | }, 28 | TRANSIENT { 29 | override fun annotationSpec(): AnnotationSpec = AnnotationSpec.builder( 30 | Transient::class.asClassName(), 31 | ).build() 32 | }, 33 | VOLATILE { 34 | override fun annotationSpec(): AnnotationSpec = AnnotationSpec.builder( 35 | Volatile::class.asClassName(), 36 | ).build() 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/JvmMethodModifier.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.asClassName 20 | 21 | /** Modifiers that are annotations or implicit in Kotlin but modifier keywords in bytecode. */ 22 | public enum class JvmMethodModifier : JvmModifier { 23 | STATIC { 24 | override fun annotationSpec(): AnnotationSpec = AnnotationSpec.builder( 25 | JvmStatic::class.asClassName(), 26 | ).build() 27 | }, 28 | SYNCHRONIZED { 29 | override fun annotationSpec(): AnnotationSpec = AnnotationSpec.builder( 30 | Synchronized::class.asClassName(), 31 | ).build() 32 | }, 33 | DEFAULT, 34 | } 35 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/JvmModifier.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | 20 | /** 21 | * Represents a JVM modifier that is represented as an annotation in Kotlin but as a modifier in 22 | * bytecode. Examples include annotations such as [@JvmStatic][JvmStatic] or 23 | * [@JvmSynthetic][JvmSynthetic]. 24 | * 25 | * This API is considered read-only and should not be implemented outside of KotlinPoet. 26 | */ 27 | public interface JvmModifier { 28 | public fun annotationSpec(): AnnotationSpec? { 29 | return null 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/MethodData.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget 20 | import com.squareup.kotlinpoet.TypeName 21 | import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil 22 | 23 | /** 24 | * Represents relevant information on a method used for [ClassInspector]. Should only be 25 | * associated with methods of a [ClassData] or [PropertyData]. 26 | * 27 | * @param annotations declared annotations on this method. 28 | * @property parameterAnnotations a mapping of parameter indices to annotations on them. 29 | * @property isSynthetic indicates if this method is synthetic or not. 30 | * @property jvmModifiers set of [JvmMethodModifiers][JvmMethodModifier] on this method. 31 | * @property isOverride indicates if this method overrides one in a supertype. 32 | * @property exceptions list of exceptions thrown by this method. 33 | */ 34 | public data class MethodData( 35 | private val annotations: List, 36 | val parameterAnnotations: Map>, 37 | val isSynthetic: Boolean, 38 | val jvmModifiers: Set, 39 | val isOverride: Boolean, 40 | val exceptions: List, 41 | ) { 42 | 43 | /** 44 | * A collection of all annotations on this method, including any derived from [jvmModifiers], 45 | * [isSynthetic], and [exceptions]. 46 | * 47 | * @param useSiteTarget an optional [UseSiteTarget] that all annotations on this method should 48 | * use. 49 | * @param containsReifiedTypeParameter an optional boolean indicating if any type parameters on 50 | * this function are `reified`, which are implicitly synthetic. 51 | */ 52 | public fun allAnnotations( 53 | useSiteTarget: UseSiteTarget? = null, 54 | containsReifiedTypeParameter: Boolean = false, 55 | ): Collection { 56 | return ClassInspectorUtil.createAnnotations( 57 | useSiteTarget, 58 | ) { 59 | addAll(annotations) 60 | if (isSynthetic && !containsReifiedTypeParameter) { 61 | add(ClassInspectorUtil.JVM_SYNTHETIC_SPEC) 62 | } 63 | addAll(jvmModifiers.mapNotNull(JvmMethodModifier::annotationSpec)) 64 | exceptions.takeIf { it.isNotEmpty() } 65 | ?.let { 66 | add(ClassInspectorUtil.createThrowsSpec(it, useSiteTarget)) 67 | } 68 | } 69 | } 70 | 71 | public companion object { 72 | public val SYNTHETIC: MethodData = MethodData( 73 | annotations = emptyList(), 74 | parameterAnnotations = emptyMap(), 75 | isSynthetic = true, 76 | jvmModifiers = emptySet(), 77 | isOverride = false, 78 | exceptions = emptyList(), 79 | ) 80 | public val EMPTY: MethodData = MethodData( 81 | annotations = emptyList(), 82 | parameterAnnotations = emptyMap(), 83 | isSynthetic = false, 84 | jvmModifiers = emptySet(), 85 | isOverride = false, 86 | exceptions = emptyList(), 87 | ) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/PropertyData.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.GET 20 | import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.SET 21 | import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil 22 | 23 | /** 24 | * Represents relevant information on a property used for [ClassInspector]. Should only be 25 | * associated with properties of a [ClassData]. 26 | * 27 | * @param annotations declared annotations on this property. 28 | * @property fieldData associated [FieldData] with this property, if any. 29 | * @property getterData associated getter (as [MethodData]) with this property, if any. 30 | * @property setterData associated setter (as [MethodData]) with this property, if any. 31 | * @property isJvmField indicates if this property should be treated as a jvm field. 32 | */ 33 | public data class PropertyData( 34 | private val annotations: List, 35 | val fieldData: FieldData?, 36 | val getterData: MethodData?, 37 | val setterData: MethodData?, 38 | val isJvmField: Boolean, 39 | ) { 40 | /** Indicates if this property overrides another from a supertype. */ 41 | val isOverride: Boolean = (getterData?.isOverride ?: false) || (setterData?.isOverride ?: false) 42 | 43 | /** 44 | * A collection of all annotations on this property including declared ones and any derived from 45 | * [fieldData], [getterData], [setterData], and [isJvmField]. 46 | */ 47 | val allAnnotations: Collection = ClassInspectorUtil.createAnnotations { 48 | // Don't add annotations that are already defined on the parent 49 | val higherScopedAnnotations = annotations.associateBy { it.typeName } 50 | val fieldAnnotations = fieldData?.allAnnotations.orEmpty() 51 | .filterNot { it.typeName in higherScopedAnnotations } 52 | .associateByTo(LinkedHashMap()) { it.typeName } 53 | val getterAnnotations = getterData?.allAnnotations(GET).orEmpty() 54 | .filterNot { it.typeName in higherScopedAnnotations } 55 | .associateByTo(LinkedHashMap()) { it.typeName } 56 | 57 | val finalTopAnnotations = annotations.toMutableList() 58 | 59 | // If this is a val, and annotation is on both getter and field, we can move it to just the 60 | // regular annotations 61 | if (setterData == null && !isJvmField) { 62 | val sharedAnnotations = getterAnnotations.keys.intersect(fieldAnnotations.keys) 63 | for (sharedAnnotation in sharedAnnotations) { 64 | // Add it to the top-level annotations without a site-target 65 | finalTopAnnotations += getterAnnotations.getValue(sharedAnnotation).toBuilder() 66 | .useSiteTarget(null) 67 | .build() 68 | 69 | // Remove from field and getter 70 | fieldAnnotations.remove(sharedAnnotation) 71 | getterAnnotations.remove(sharedAnnotation) 72 | } 73 | } 74 | 75 | addAll(finalTopAnnotations) 76 | addAll(fieldAnnotations.values) 77 | addAll(getterAnnotations.values) 78 | addAll( 79 | setterData?.allAnnotations(SET).orEmpty() 80 | .filterNot { it.typeName in higherScopedAnnotations }, 81 | ) 82 | if (isJvmField) { 83 | add(ClassInspectorUtil.JVM_FIELD_SPEC) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/kmAnnotations.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.ARRAY 19 | import com.squareup.kotlinpoet.AnnotationSpec 20 | import com.squareup.kotlinpoet.CodeBlock 21 | import com.squareup.kotlinpoet.buildCodeBlock 22 | import com.squareup.kotlinpoet.joinToCode 23 | import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil.createClassName 24 | import com.squareup.kotlinpoet.tag 25 | import kotlin.metadata.KmAnnotation 26 | import kotlin.metadata.KmAnnotationArgument 27 | import kotlin.metadata.KmAnnotationArgument.AnnotationValue 28 | import kotlin.metadata.KmAnnotationArgument.ArrayValue 29 | import kotlin.metadata.KmAnnotationArgument.BooleanValue 30 | import kotlin.metadata.KmAnnotationArgument.ByteValue 31 | import kotlin.metadata.KmAnnotationArgument.CharValue 32 | import kotlin.metadata.KmAnnotationArgument.DoubleValue 33 | import kotlin.metadata.KmAnnotationArgument.EnumValue 34 | import kotlin.metadata.KmAnnotationArgument.FloatValue 35 | import kotlin.metadata.KmAnnotationArgument.IntValue 36 | import kotlin.metadata.KmAnnotationArgument.KClassValue 37 | import kotlin.metadata.KmAnnotationArgument.LongValue 38 | import kotlin.metadata.KmAnnotationArgument.ShortValue 39 | import kotlin.metadata.KmAnnotationArgument.StringValue 40 | import kotlin.metadata.KmAnnotationArgument.UByteValue 41 | import kotlin.metadata.KmAnnotationArgument.UIntValue 42 | import kotlin.metadata.KmAnnotationArgument.ULongValue 43 | import kotlin.metadata.KmAnnotationArgument.UShortValue 44 | 45 | internal fun KmAnnotation.toAnnotationSpec(): AnnotationSpec { 46 | val cn = createClassName(className) 47 | return AnnotationSpec.builder(cn) 48 | .apply { 49 | arguments.forEach { (name, arg) -> 50 | addMember("%L = %L", name, arg.toCodeBlock()) 51 | } 52 | } 53 | .tag(this) 54 | .build() 55 | } 56 | 57 | internal fun KmAnnotationArgument.toCodeBlock(): CodeBlock { 58 | return when (this) { 59 | is ByteValue -> CodeBlock.of("%L", value) 60 | is CharValue -> CodeBlock.of("'%L'", value) 61 | is ShortValue -> CodeBlock.of("%L", value) 62 | is IntValue -> CodeBlock.of("%L", value) 63 | is LongValue -> CodeBlock.of("%LL", value) 64 | is FloatValue -> CodeBlock.of("%LF", value) 65 | is DoubleValue -> CodeBlock.of("%L", value) 66 | is BooleanValue -> CodeBlock.of("%L", value) 67 | is UByteValue -> CodeBlock.of("%Lu", value) 68 | is UShortValue -> CodeBlock.of("%Lu", value) 69 | is UIntValue -> CodeBlock.of("%Lu", value) 70 | is ULongValue -> CodeBlock.of("%Lu", value) 71 | is StringValue -> CodeBlock.of("%S", value) 72 | is KClassValue -> CodeBlock.of("%T::class", createClassName(className)) 73 | is EnumValue -> CodeBlock.of("%T.%L", createClassName(enumClassName), enumEntryName) 74 | is AnnotationValue -> CodeBlock.of("%L", annotation.toAnnotationSpec()) 75 | is ArrayValue -> elements.map { it.toCodeBlock() }.joinToCode(", ", "[", "]") 76 | is KmAnnotationArgument.ArrayKClassValue -> buildCodeBlock { 77 | repeat(arrayDimensionCount) { add("%T<", ARRAY) } 78 | add("%T::class", createClassName(className)) 79 | repeat(arrayDimensionCount) { add(">") } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/main/kotlin/com/squareup/kotlinpoet/metadata/util.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata 17 | 18 | import com.squareup.kotlinpoet.KModifier 19 | import kotlin.metadata.ClassKind 20 | import kotlin.metadata.KmClass 21 | import kotlin.metadata.KmConstructor 22 | import kotlin.metadata.KmProperty 23 | import kotlin.metadata.MemberKind 24 | import kotlin.metadata.Modality 25 | import kotlin.metadata.Visibility 26 | import kotlin.metadata.isSecondary 27 | import kotlin.metadata.isVar 28 | import kotlin.metadata.kind 29 | 30 | internal val KmClass.isObject: Boolean 31 | get() = kind == ClassKind.OBJECT 32 | 33 | internal val KmClass.isCompanionObject: Boolean 34 | get() = kind == ClassKind.COMPANION_OBJECT 35 | 36 | internal val KmClass.isClass: Boolean 37 | get() = kind == ClassKind.CLASS 38 | 39 | internal val KmClass.isAnnotation: Boolean 40 | get() = kind == ClassKind.ANNOTATION_CLASS 41 | 42 | internal val KmClass.isEnum: Boolean 43 | get() = kind == ClassKind.ENUM_CLASS 44 | 45 | internal val KmClass.isInterface: Boolean 46 | get() = kind == ClassKind.INTERFACE 47 | 48 | internal val KmConstructor.isPrimary: Boolean 49 | get() = !isSecondary 50 | 51 | internal val KmProperty.isVal: Boolean 52 | get() = !isVar 53 | 54 | internal fun Modality.toKModifier(): KModifier = when (this) { 55 | Modality.FINAL -> KModifier.FINAL 56 | Modality.OPEN -> KModifier.OPEN 57 | Modality.ABSTRACT -> KModifier.ABSTRACT 58 | Modality.SEALED -> KModifier.SEALED 59 | } 60 | 61 | internal fun Visibility.toKModifier(): KModifier = when (this) { 62 | Visibility.INTERNAL -> KModifier.INTERNAL 63 | Visibility.PRIVATE -> KModifier.PRIVATE 64 | Visibility.PROTECTED -> KModifier.PROTECTED 65 | Visibility.PUBLIC -> KModifier.PUBLIC 66 | Visibility.PRIVATE_TO_THIS, 67 | Visibility.LOCAL, 68 | -> { 69 | // Default to public 70 | KModifier.PUBLIC 71 | } 72 | } 73 | 74 | internal val MemberKind.isDeclaration: Boolean get() = this == MemberKind.DECLARATION 75 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/test/kotlin/com/squareup/kotlinpoet/metadata/specs/FacadeFile.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("FacadeFile") 17 | @file:FileAnnotation("file annotations!") 18 | @file:Suppress("unused", "UNUSED_PARAMETER") 19 | 20 | package com.squareup.kotlinpoet.metadata.specs 21 | 22 | import kotlin.annotation.AnnotationTarget.FILE 23 | 24 | @Target(FILE) 25 | annotation class FileAnnotation(val value: String) 26 | 27 | @JvmName("jvmStaticFunction") 28 | fun jvmNameFunction() { 29 | } 30 | 31 | fun regularFun() { 32 | } 33 | 34 | @Synchronized 35 | fun synchronizedFun() { 36 | } 37 | 38 | @JvmOverloads 39 | fun jvmOverloads( 40 | param1: String, 41 | optionalParam2: String = "", 42 | nullableParam3: String? = null, 43 | ) { 44 | } 45 | 46 | val BOOL_PROP = false 47 | val BINARY_PROP = 0b00001011 48 | val INT_PROP = 1 49 | val UNDERSCORES_PROP = 1_000_000 50 | val HEX_PROP = 0x0F 51 | val UNDERSCORES_HEX_PROP = 0xFF_EC_DE_5E 52 | val LONG_PROP = 1L 53 | val FLOAT_PROP = 1.0f 54 | val DOUBLE_PROP = 1.0 55 | val STRING_PROP = "prop" 56 | var VAR_BOOL_PROP = false 57 | var VAR_BINARY_PROP = 0b00001011 58 | var VAR_INT_PROP = 1 59 | var VAR_UNDERSCORES_PROP = 1_000_000 60 | var VAR_HEX_PROP = 0x0F 61 | var VAR_UNDERSCORES_HEX_PROP = 0xFF_EC_DE_5E 62 | var VAR_LONG_PROP = 1L 63 | var VAR_FLOAT_PROP = 1.0f 64 | var VAR_DOUBLE_PROP = 1.0 65 | var VAR_STRING_PROP = "prop" 66 | 67 | const val CONST_BOOL_PROP = false 68 | const val CONST_BINARY_PROP = 0b00001011 69 | const val CONST_INT_PROP = 1 70 | const val CONST_UNDERSCORES_PROP = 1_000_000 71 | const val CONST_HEX_PROP = 0x0F 72 | const val CONST_UNDERSCORES_HEX_PROP = 0xFF_EC_DE_5E 73 | const val CONST_LONG_PROP = 1L 74 | const val CONST_FLOAT_PROP = 1.0f 75 | const val CONST_DOUBLE_PROP = 1.0 76 | const val CONST_STRING_PROP = "prop" 77 | 78 | @JvmField 79 | @JvmSynthetic 80 | val syntheticFieldProperty: kotlin.String? = null 81 | 82 | @field:JvmSynthetic 83 | val syntheticProperty: kotlin.String? = null 84 | 85 | @get:JvmSynthetic 86 | val syntheticPropertyGet: kotlin.String? = null 87 | 88 | @get:JvmSynthetic 89 | @set:JvmSynthetic 90 | var syntheticPropertyGetAndSet: kotlin.String? = null 91 | 92 | @set:JvmSynthetic 93 | var syntheticPropertySet: kotlin.String? = null 94 | 95 | typealias FacadeTypeAliasName = String 96 | typealias FacadeGenericTypeAlias = List 97 | typealias FacadeNestedTypeAlias = List 98 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/test/kotlin/com/squareup/kotlinpoet/metadata/specs/JvmNameWithKtFacadeFile.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("JvmNameKt") 17 | 18 | package com.squareup.kotlinpoet.metadata.specs 19 | 20 | val prop2: String = "" 21 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/test/kotlin/com/squareup/kotlinpoet/metadata/specs/NoJvmNameFacadeFile.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | val prop: String = "" 19 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/test/kotlin/com/squareup/kotlinpoet/metadata/specs/ReflectiveClassInspectorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs 17 | 18 | import com.squareup.kotlinpoet.ClassName 19 | import com.squareup.kotlinpoet.asClassName 20 | import com.squareup.kotlinpoet.metadata.classinspectors.ReflectiveClassInspector 21 | import com.tschuchort.compiletesting.KotlinCompilation 22 | import com.tschuchort.compiletesting.SourceFile 23 | import kotlin.test.assertEquals 24 | import kotlin.test.assertNotNull 25 | import org.junit.Test 26 | 27 | /** 28 | * Class to test the new functionality of Issue#1036. 29 | * @see issue 30 | * @author oberstrike 31 | */ 32 | class ReflectiveClassInspectorTest { 33 | 34 | data class Person(val name: String) 35 | 36 | /** 37 | * Tests if the [ReflectiveClassInspector] can be created without a 38 | * custom ClassLoader and still works. 39 | */ 40 | @Test 41 | fun standardClassLoaderTest() { 42 | val classInspector = ReflectiveClassInspector.create(lenient = false) 43 | val className = Person::class.asClassName() 44 | val declarationContainer = classInspector.declarationContainerFor(className) 45 | assertNotNull(declarationContainer) 46 | } 47 | 48 | /** 49 | * Tests if the [ReflectiveClassInspector] can be created with a 50 | * custom ClassLoader. 51 | */ 52 | @Test 53 | fun useACustomClassLoaderTest() { 54 | val testClass = "Person" 55 | val testPropertyName = "name" 56 | val testPropertyType = "String" 57 | val testPackageName = "com.test" 58 | val testClassName = ClassName(testPackageName, testClass) 59 | val testKtFileName = "KClass.kt" 60 | 61 | val kotlinSource = SourceFile.kotlin( 62 | testKtFileName, 63 | """ 64 | package $testPackageName 65 | data class $testClass(val $testPropertyName: $testPropertyType) 66 | """.trimIndent(), 67 | ) 68 | 69 | val result = KotlinCompilation().apply { 70 | sources = listOf(kotlinSource) 71 | }.compile() 72 | 73 | assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode) 74 | val classLoader = result.classLoader 75 | val classInspector = ReflectiveClassInspector.create(lenient = false, classLoader) 76 | 77 | val declarationContainer = classInspector.declarationContainerFor(testClassName) 78 | 79 | val properties = declarationContainer.properties 80 | assertEquals(1, properties.size) 81 | 82 | val testProperty = properties.findLast { it.name == testPropertyName } 83 | assertNotNull(testProperty) 84 | 85 | val returnType = testProperty.returnType 86 | assertNotNull(returnType) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /interop/kotlin-metadata/src/test/kotlin/com/squareup/kotlinpoet/metadata/specs/classinspectors/ClassInspectorUtilTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.metadata.specs.classinspectors 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import com.squareup.kotlinpoet.AnnotationSpec 20 | import com.squareup.kotlinpoet.ClassName 21 | import com.squareup.kotlinpoet.asClassName 22 | import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil 23 | import kotlin.test.Test 24 | 25 | class ClassInspectorUtilTest { 26 | 27 | @Test fun createClassName_simple() { 28 | assertThat(ClassInspectorUtil.createClassName("some/path/Foo")) 29 | .isEqualTo(ClassName("some.path", "Foo")) 30 | } 31 | 32 | @Test fun createClassName_nested() { 33 | assertThat(ClassInspectorUtil.createClassName("some/path/Foo.Nested")) 34 | .isEqualTo(ClassName("some.path", "Foo", "Nested")) 35 | } 36 | 37 | @Test fun createClassName_simple_dollarNameStart() { 38 | assertThat(ClassInspectorUtil.createClassName("some/path/Foo$")) 39 | .isEqualTo(ClassName("some.path", "Foo$")) 40 | } 41 | 42 | @Test fun createClassName_simple_dollarNameMiddle() { 43 | assertThat(ClassInspectorUtil.createClassName("some/path/Fo${'$'}o")) 44 | .isEqualTo(ClassName("some.path", "Fo${'$'}o")) 45 | } 46 | 47 | @Test fun createClassName_simple_dollarNameEnd() { 48 | assertThat(ClassInspectorUtil.createClassName("some/path/Nested.${'$'}Foo")) 49 | .isEqualTo(ClassName("some.path", "Nested", "${'$'}Foo")) 50 | } 51 | 52 | @Test fun createClassName_nested_dollarNameStart() { 53 | assertThat(ClassInspectorUtil.createClassName("some/path/Nested.Foo$")) 54 | .isEqualTo(ClassName("some.path", "Nested", "Foo$")) 55 | } 56 | 57 | @Test fun createClassName_nested_dollarNameMiddle() { 58 | assertThat(ClassInspectorUtil.createClassName("some/path/Nested.Fo${'$'}o")) 59 | .isEqualTo(ClassName("some.path", "Nested", "Fo${'$'}o")) 60 | } 61 | 62 | @Test fun createClassName_nested_dollarNameEnd() { 63 | assertThat(ClassInspectorUtil.createClassName("some/path/Nested.${'$'}Foo")) 64 | .isEqualTo(ClassName("some.path", "Nested", "${'$'}Foo")) 65 | } 66 | 67 | @Test fun createClassName_noPackageName() { 68 | assertThat(ClassInspectorUtil.createClassName("ClassWithNoPackage")) 69 | .isEqualTo(ClassName("", "ClassWithNoPackage")) 70 | } 71 | 72 | // Regression test for avoiding https://github.com/square/kotlinpoet/issues/795 73 | @Test fun createClassName_noEmptyNames() { 74 | val noPackage = ClassInspectorUtil.createClassName("ClassWithNoPackage") 75 | assertThat(noPackage.simpleNames.any { it.isEmpty() }).isFalse() 76 | 77 | val withPackage = ClassInspectorUtil.createClassName("path/to/ClassWithNoPackage") 78 | assertThat(withPackage.simpleNames.any { it.isEmpty() }).isFalse() 79 | } 80 | 81 | @Test fun createClassName_packageWithCaps() { 82 | assertThat(ClassInspectorUtil.createClassName("some/Path/Foo.Nested")) 83 | .isEqualTo(ClassName("some.Path", "Foo", "Nested")) 84 | } 85 | 86 | @Test fun throwsSpec_normal() { 87 | assertThat(ClassInspectorUtil.createThrowsSpec(listOf(Exception::class.asClassName()))) 88 | .isEqualTo( 89 | AnnotationSpec.builder(Throws::class.asClassName()) 90 | .addMember("exceptionClasses = [%T::class]", Exception::class.asClassName()) 91 | .build(), 92 | ) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /interop/ksp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | plugins { 17 | kotlin("jvm") 18 | } 19 | 20 | tasks.jar { 21 | manifest { 22 | attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet.ksp") 23 | } 24 | } 25 | 26 | dependencies { 27 | api(projects.kotlinpoet) 28 | compileOnly(libs.ksp.api) 29 | testImplementation(libs.kotlin.junit) 30 | testImplementation(libs.truth) 31 | } 32 | -------------------------------------------------------------------------------- /interop/ksp/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=kotlinpoet-ksp 2 | POM_NAME=KotlinPoet (KSP Interop) 3 | POM_DESCRIPTION=Extensions for interop with KSP (Kotlin Symbol Processing). 4 | POM_PACKAGING=jar 5 | -------------------------------------------------------------------------------- /interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsClassDeclarations.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp 17 | 18 | import com.google.devtools.ksp.symbol.KSClassDeclaration 19 | import com.google.devtools.ksp.symbol.KSTypeAlias 20 | import com.squareup.kotlinpoet.ClassName 21 | 22 | /** Returns the [ClassName] representation of this [KSClassDeclaration]. */ 23 | public fun KSClassDeclaration.toClassName(): ClassName { 24 | return toClassNameInternal() 25 | } 26 | 27 | /** Returns the [ClassName] representation of this [KSTypeAlias]. */ 28 | public fun KSTypeAlias.toClassName(): ClassName { 29 | return toClassNameInternal() 30 | } 31 | -------------------------------------------------------------------------------- /interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/Modifiers.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp 17 | 18 | import com.google.devtools.ksp.symbol.Modifier 19 | import com.google.devtools.ksp.symbol.Modifier.ABSTRACT 20 | import com.google.devtools.ksp.symbol.Modifier.ACTUAL 21 | import com.google.devtools.ksp.symbol.Modifier.ANNOTATION 22 | import com.google.devtools.ksp.symbol.Modifier.CROSSINLINE 23 | import com.google.devtools.ksp.symbol.Modifier.DATA 24 | import com.google.devtools.ksp.symbol.Modifier.ENUM 25 | import com.google.devtools.ksp.symbol.Modifier.EXPECT 26 | import com.google.devtools.ksp.symbol.Modifier.EXTERNAL 27 | import com.google.devtools.ksp.symbol.Modifier.FINAL 28 | import com.google.devtools.ksp.symbol.Modifier.FUN 29 | import com.google.devtools.ksp.symbol.Modifier.IN 30 | import com.google.devtools.ksp.symbol.Modifier.INFIX 31 | import com.google.devtools.ksp.symbol.Modifier.INLINE 32 | import com.google.devtools.ksp.symbol.Modifier.INNER 33 | import com.google.devtools.ksp.symbol.Modifier.INTERNAL 34 | import com.google.devtools.ksp.symbol.Modifier.LATEINIT 35 | import com.google.devtools.ksp.symbol.Modifier.NOINLINE 36 | import com.google.devtools.ksp.symbol.Modifier.OPEN 37 | import com.google.devtools.ksp.symbol.Modifier.OPERATOR 38 | import com.google.devtools.ksp.symbol.Modifier.OUT 39 | import com.google.devtools.ksp.symbol.Modifier.OVERRIDE 40 | import com.google.devtools.ksp.symbol.Modifier.PRIVATE 41 | import com.google.devtools.ksp.symbol.Modifier.PROTECTED 42 | import com.google.devtools.ksp.symbol.Modifier.PUBLIC 43 | import com.google.devtools.ksp.symbol.Modifier.REIFIED 44 | import com.google.devtools.ksp.symbol.Modifier.SEALED 45 | import com.google.devtools.ksp.symbol.Modifier.SUSPEND 46 | import com.google.devtools.ksp.symbol.Modifier.TAILREC 47 | import com.google.devtools.ksp.symbol.Modifier.VALUE 48 | import com.google.devtools.ksp.symbol.Modifier.VARARG 49 | import com.squareup.kotlinpoet.KModifier 50 | 51 | /** 52 | * Returns the [KModifier] representation of this [Modifier] or null if this is a Java-only 53 | * modifier (i.e. prefixed with `JAVA_`), which do not have obvious [KModifier] analogues. 54 | */ 55 | public fun Modifier.toKModifier(): KModifier? { 56 | return when (this) { 57 | PUBLIC -> KModifier.PUBLIC 58 | PRIVATE -> KModifier.PRIVATE 59 | INTERNAL -> KModifier.INTERNAL 60 | PROTECTED -> KModifier.PROTECTED 61 | IN -> KModifier.IN 62 | OUT -> KModifier.OUT 63 | OVERRIDE -> KModifier.OVERRIDE 64 | LATEINIT -> KModifier.LATEINIT 65 | ENUM -> KModifier.ENUM 66 | SEALED -> KModifier.SEALED 67 | ANNOTATION -> KModifier.ANNOTATION 68 | DATA -> KModifier.DATA 69 | INNER -> KModifier.INNER 70 | FUN -> KModifier.FUN 71 | VALUE -> KModifier.VALUE 72 | SUSPEND -> KModifier.SUSPEND 73 | TAILREC -> KModifier.TAILREC 74 | OPERATOR -> KModifier.OPERATOR 75 | INFIX -> KModifier.INFIX 76 | INLINE -> KModifier.INLINE 77 | EXTERNAL -> KModifier.EXTERNAL 78 | ABSTRACT -> KModifier.ABSTRACT 79 | FINAL -> KModifier.FINAL 80 | OPEN -> KModifier.OPEN 81 | VARARG -> KModifier.VARARG 82 | NOINLINE -> KModifier.NOINLINE 83 | CROSSINLINE -> KModifier.CROSSINLINE 84 | REIFIED -> KModifier.REIFIED 85 | EXPECT -> KModifier.EXPECT 86 | ACTUAL -> KModifier.ACTUAL 87 | else -> null 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/TypeParameterResolver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp 17 | 18 | import com.google.devtools.ksp.symbol.KSType 19 | import com.google.devtools.ksp.symbol.KSTypeParameter 20 | import com.squareup.kotlinpoet.TypeVariableName 21 | 22 | /** 23 | * A resolver for enclosing declarations' type parameters. Parent declarations can be anything with 24 | * generics that child nodes declare as defined by [KSType.arguments]. 25 | * 26 | * This is important for resolving inherited generics on child declarations, as KSP interop 27 | * otherwise can't resolve them. 28 | * 29 | * In general, you want to retrieve an instance of this via [toTypeParameterResolver]. 30 | * 31 | * @see toTypeParameterResolver 32 | */ 33 | public interface TypeParameterResolver { 34 | public val parametersMap: Map 35 | public operator fun get(index: String): TypeVariableName 36 | 37 | public companion object { 38 | /** 39 | * An empty instance of [TypeParameterResolver], only should be used if enclosing declarations 40 | * are known to not have arguments, such as top-level classes. 41 | */ 42 | public val EMPTY: TypeParameterResolver = object : TypeParameterResolver { 43 | override val parametersMap: Map = emptyMap() 44 | 45 | override fun get(index: String): TypeVariableName = throw NoSuchElementException("No TypeParameter found for index $index") 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * Returns a [TypeParameterResolver] for this list of [KSTypeParameters][KSTypeParameter] for use 52 | * with enclosed declarations. 53 | * 54 | * @param parent the optional parent resolver, if any. An example of this is cases where you might 55 | * create a resolver for a [KSFunction] and supply a parent resolved from the 56 | * enclosing [KSClassDeclaration]. 57 | * @param sourceTypeHint an optional hint for error messages. Unresolvable parameter IDs will 58 | * include this hint in the thrown error's message. 59 | */ 60 | public fun List.toTypeParameterResolver( 61 | parent: TypeParameterResolver? = null, 62 | sourceTypeHint: String = "", 63 | ): TypeParameterResolver { 64 | val parametersMap = LinkedHashMap() 65 | val typeParamResolver = { id: String -> 66 | parametersMap[id] 67 | ?: parent?.get(id) 68 | ?: throw IllegalStateException( 69 | "No type argument found for $id! Analyzed $sourceTypeHint with known parameters " + 70 | "${parametersMap.keys}", 71 | ) 72 | } 73 | 74 | val resolver = object : TypeParameterResolver { 75 | override val parametersMap: Map = parametersMap 76 | 77 | override operator fun get(index: String): TypeVariableName = typeParamResolver(index) 78 | } 79 | 80 | // Fill the parametersMap. Need to do sequentially and allow for referencing previously defined params 81 | for (typeVar in this) { 82 | // Put the simple typevar in first, then it can be referenced in the full toTypeVariable() 83 | // replacement later that may add bounds referencing this. 84 | val id = typeVar.name.getShortName() 85 | parametersMap[id] = TypeVariableName(id) 86 | } 87 | 88 | for (typeVar in this) { 89 | val id = typeVar.name.getShortName() 90 | // Now replace it with the full version. 91 | parametersMap[id] = typeVar.toTypeVariableName(resolver) 92 | } 93 | 94 | return resolver 95 | } 96 | -------------------------------------------------------------------------------- /interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/Visibilities.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp 17 | 18 | import com.google.devtools.ksp.symbol.Visibility 19 | import com.google.devtools.ksp.symbol.Visibility.JAVA_PACKAGE 20 | import com.google.devtools.ksp.symbol.Visibility.LOCAL 21 | import com.squareup.kotlinpoet.KModifier 22 | 23 | /** 24 | * Returns the [KModifier] representation of this visibility or null if this is [JAVA_PACKAGE] 25 | * or [LOCAL] (which do not have obvious [KModifier] alternatives). 26 | */ 27 | public fun Visibility.toKModifier(): KModifier? { 28 | return when (this) { 29 | Visibility.PUBLIC -> KModifier.PUBLIC 30 | Visibility.PRIVATE -> KModifier.PRIVATE 31 | Visibility.PROTECTED -> KModifier.PROTECTED 32 | Visibility.INTERNAL -> KModifier.INTERNAL 33 | else -> null 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/utils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp 17 | 18 | import com.google.devtools.ksp.isLocal 19 | import com.google.devtools.ksp.symbol.ClassKind 20 | import com.google.devtools.ksp.symbol.KSClassDeclaration 21 | import com.google.devtools.ksp.symbol.KSDeclaration 22 | import com.google.devtools.ksp.symbol.KSType 23 | import com.google.devtools.ksp.symbol.KSTypeAlias 24 | import com.google.devtools.ksp.symbol.KSTypeParameter 25 | import com.squareup.kotlinpoet.ClassName 26 | import com.squareup.kotlinpoet.LambdaTypeName 27 | import com.squareup.kotlinpoet.ParameterizedTypeName 28 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 29 | import com.squareup.kotlinpoet.TypeName 30 | 31 | internal fun TypeName.rawType(): ClassName { 32 | return findRawType() ?: throw IllegalArgumentException("Cannot get raw type from $this") 33 | } 34 | 35 | internal fun TypeName.findRawType(): ClassName? { 36 | return when (this) { 37 | is ClassName -> this 38 | is ParameterizedTypeName -> rawType 39 | is LambdaTypeName -> { 40 | var count = parameters.size 41 | if (receiver != null) { 42 | count++ 43 | } 44 | val functionSimpleName = if (count >= 23) { 45 | "FunctionN" 46 | } else { 47 | "Function$count" 48 | } 49 | ClassName("kotlin.jvm.functions", functionSimpleName) 50 | } 51 | else -> null 52 | } 53 | } 54 | 55 | internal fun ClassName.withTypeArguments(arguments: List): TypeName { 56 | return if (arguments.isEmpty()) { 57 | this 58 | } else { 59 | this.parameterizedBy(arguments) 60 | } 61 | } 62 | 63 | internal fun KSDeclaration.toClassNameInternal(): ClassName { 64 | require(!isLocal()) { 65 | "Local/anonymous classes are not supported!" 66 | } 67 | 68 | if (this is KSClassDeclaration && classKind == ClassKind.ENUM_ENTRY) { 69 | val simpleName = this.simpleName.asString() 70 | val parent = parentDeclaration!!.toClassNameInternal() 71 | return parent.nestedClass(simpleName) 72 | } 73 | 74 | val pkgName = packageName.asString() 75 | 76 | val typesString = checkNotNull(qualifiedName).asString().removePrefix("$pkgName.") 77 | 78 | val simpleNames = typesString 79 | .split(".") 80 | return ClassName(pkgName, simpleNames) 81 | } 82 | 83 | internal fun KSType.requireNotErrorType() { 84 | require(!isError) { 85 | "Error type '$this' is not resolvable in the current round of processing." 86 | } 87 | } 88 | 89 | /** 90 | * Resolves the [KSClassDeclaration] for this type, including following typealiases as needed. 91 | */ 92 | internal fun KSType.resolveKSClassDeclaration(): KSClassDeclaration? { 93 | requireNotErrorType() 94 | return declaration.resolveKSClassDeclaration() 95 | } 96 | 97 | /** 98 | * Resolves the [KSClassDeclaration] representation of this declaration, including following 99 | * typealiases as needed. 100 | * 101 | * [KSTypeParameter] types will return null. If you expect one here, you should check the 102 | * declaration directly. 103 | */ 104 | internal fun KSDeclaration.resolveKSClassDeclaration(): KSClassDeclaration? { 105 | return when (val declaration = unwrapTypealiases()) { 106 | is KSClassDeclaration -> declaration 107 | is KSTypeParameter -> null 108 | else -> error("Unexpected declaration type: $this") 109 | } 110 | } 111 | 112 | /** 113 | * Returns the resolved declaration following any typealiases. 114 | */ 115 | internal tailrec fun KSDeclaration.unwrapTypealiases(): KSDeclaration = when (this) { 116 | is KSTypeAlias -> type.resolve().declaration.unwrapTypealiases() 117 | else -> this 118 | } 119 | -------------------------------------------------------------------------------- /interop/ksp/test-processor/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | plugins { 17 | id("com.google.devtools.ksp") 18 | kotlin("jvm") 19 | } 20 | 21 | tasks.compileTestKotlin { 22 | compilerOptions { 23 | optIn.add("org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi") 24 | } 25 | } 26 | 27 | tasks.test { 28 | // KSP2 needs more memory to run 29 | minHeapSize = "1g" 30 | maxHeapSize = "4g" 31 | } 32 | 33 | dependencies { 34 | implementation(projects.kotlinpoet) 35 | implementation(projects.interop.ksp) 36 | implementation(libs.autoService) 37 | compileOnly(libs.ksp.api) 38 | ksp(libs.autoService.ksp) 39 | // Always force the latest version of the KSP/kotlin impl in tests to match what we're building against 40 | testImplementation(libs.ksp.api) 41 | testImplementation(libs.kotlin.compilerEmbeddable) 42 | testImplementation(libs.kotlin.annotationProcessingEmbeddable) 43 | testImplementation(libs.ksp) 44 | testImplementation(libs.kotlinCompileTesting) 45 | testImplementation(libs.kotlinCompileTesting.ksp) 46 | testImplementation(libs.ksp.aaEmbeddable) 47 | testImplementation(libs.kotlin.junit) 48 | testImplementation(libs.truth) 49 | } 50 | -------------------------------------------------------------------------------- /interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp.test.processor 17 | 18 | import com.google.auto.service.AutoService 19 | import com.google.devtools.ksp.processing.SymbolProcessor 20 | import com.google.devtools.ksp.processing.SymbolProcessorEnvironment 21 | import com.google.devtools.ksp.processing.SymbolProcessorProvider 22 | 23 | @AutoService(SymbolProcessorProvider::class) 24 | class TestProcessorProvider : SymbolProcessorProvider { 25 | override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { 26 | return TestProcessor(environment) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TypeAliasUnwrapping.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp.test.processor 17 | 18 | import com.squareup.kotlinpoet.AnnotationSpec 19 | import com.squareup.kotlinpoet.ClassName 20 | import com.squareup.kotlinpoet.LambdaTypeName 21 | import com.squareup.kotlinpoet.ParameterizedTypeName 22 | import com.squareup.kotlinpoet.TypeName 23 | import com.squareup.kotlinpoet.TypeVariableName 24 | import com.squareup.kotlinpoet.WildcardTypeName 25 | import com.squareup.kotlinpoet.tag 26 | import com.squareup.kotlinpoet.tags.TypeAliasTag 27 | import java.util.TreeSet 28 | 29 | /* 30 | * Example implementation of how to unwrap a typealias from TypeNameAliasTag 31 | */ 32 | 33 | internal fun TypeName.unwrapTypeAliasReal(): TypeName { 34 | return tag()?.abbreviatedType?.let { unwrappedType -> 35 | // If any type is nullable, then the whole thing is nullable 36 | var isAnyNullable = isNullable 37 | // Keep track of all annotations across type levels. Sort them too for consistency. 38 | val runningAnnotations = TreeSet(compareBy { it.toString() }).apply { 39 | addAll(annotations) 40 | } 41 | val nestedUnwrappedType = unwrappedType.unwrapTypeAlias() 42 | runningAnnotations.addAll(nestedUnwrappedType.annotations) 43 | isAnyNullable = isAnyNullable || nestedUnwrappedType.isNullable 44 | nestedUnwrappedType.copy(nullable = isAnyNullable, annotations = runningAnnotations.toList()) 45 | } ?: this 46 | } 47 | 48 | // TypeVariableName gets a special overload because these usually need to be kept in a type-safe 49 | // manner. 50 | internal fun TypeVariableName.unwrapTypeAlias(): TypeVariableName { 51 | return TypeVariableName( 52 | name = name, 53 | bounds = bounds.map { it.unwrapTypeAlias() }, 54 | variance = variance, 55 | ) 56 | .copy(nullable = isNullable, annotations = annotations, tags = tags) 57 | } 58 | 59 | internal fun TypeName.unwrapTypeAlias(): TypeName { 60 | return when (this) { 61 | is ClassName -> unwrapTypeAliasReal() 62 | is ParameterizedTypeName -> unwrapTypeAliasReal() 63 | is TypeVariableName -> unwrapTypeAlias() 64 | is WildcardTypeName -> unwrapTypeAliasReal() 65 | is LambdaTypeName -> unwrapTypeAliasReal() 66 | else -> throw UnsupportedOperationException("Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/exampleAnnotations.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp.test.processor 17 | 18 | import kotlin.reflect.KClass 19 | 20 | annotation class ExampleAnnotation 21 | annotation class ExampleAnnotationWithDefaults( 22 | val boolean: Boolean = true, 23 | val booleanArray: BooleanArray = [true], 24 | val byte: Byte = 1, 25 | val byteArray: ByteArray = [1], 26 | val char: Char = 'C', 27 | val charArray: CharArray = ['C'], 28 | val short: Short = 1, 29 | val shortArray: ShortArray = [1], 30 | val int: Int = 1, 31 | val intArray: IntArray = [1], 32 | val long: Long = 1, 33 | val longArray: LongArray = [1], 34 | val float: Float = 1.0f, 35 | val floatArray: FloatArray = [1.0f], 36 | val double: Double = 1.0, 37 | val doubleArray: DoubleArray = [1.0], 38 | val string: String = "", 39 | val stringArray: Array = [""], 40 | val someClass: KClass<*> = String::class, 41 | val someClasses: Array> = [String::class], 42 | val enumValue: AnnotationEnumValue = AnnotationEnumValue.ONE, 43 | val enumValueArray: Array = [AnnotationEnumValue.ONE], 44 | val anotherAnnotation: AnotherAnnotation = AnotherAnnotation(""), 45 | val anotherAnnotationArray: Array = [AnotherAnnotation("")], 46 | ) 47 | 48 | annotation class ComprehensiveAnnotation( 49 | val boolean: Boolean, 50 | val booleanArray: BooleanArray, 51 | val byte: Byte, 52 | val byteArray: ByteArray, 53 | val char: Char, 54 | val charArray: CharArray, 55 | val short: Short, 56 | val shortArray: ShortArray, 57 | val int: Int, 58 | val intArray: IntArray, 59 | val long: Long, 60 | val longArray: LongArray, 61 | val float: Float, 62 | val floatArray: FloatArray, 63 | val double: Double, 64 | val doubleArray: DoubleArray, 65 | val string: String, 66 | val stringArray: Array, 67 | val someClass: KClass<*>, 68 | val someClasses: Array>, 69 | val enumValue: AnnotationEnumValue, 70 | val enumValueArray: Array, 71 | val anotherAnnotation: AnotherAnnotation, 72 | val anotherAnnotationArray: Array, 73 | // This is still included even when the argument is omitted until https://github.com/google/ksp/issues/674 74 | val defaultingString: String = "defaultValue", 75 | ) 76 | 77 | annotation class AnotherAnnotation(val input: String) 78 | 79 | enum class AnnotationEnumValue { 80 | ONE, TWO, THREE 81 | } 82 | 83 | annotation class AnnotationWithVararg(val simpleArg: Int, vararg val args: String) 84 | annotation class AnnotationWithTypeArgs 85 | -------------------------------------------------------------------------------- /interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/KsTypesTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.ksp.test.processor 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import com.google.devtools.ksp.symbol.KSAnnotation 20 | import com.google.devtools.ksp.symbol.KSDeclaration 21 | import com.google.devtools.ksp.symbol.KSType 22 | import com.google.devtools.ksp.symbol.KSTypeArgument 23 | import com.google.devtools.ksp.symbol.Nullability 24 | import com.squareup.kotlinpoet.ksp.toClassName 25 | import com.squareup.kotlinpoet.ksp.toTypeName 26 | import kotlin.test.assertFailsWith 27 | import org.junit.Test 28 | 29 | class KsTypesTest { 30 | // Regression test for https://github.com/square/kotlinpoet/issues/1178 31 | @Test 32 | fun errorTypesShouldFail() { 33 | val type = object : KSType { 34 | override val isError: Boolean = true 35 | 36 | // Boilerplate 37 | override val annotations: Sequence 38 | get() = throw NotImplementedError() 39 | override val arguments: List 40 | get() = emptyList() 41 | override val declaration: KSDeclaration 42 | get() = throw NotImplementedError() 43 | override val isFunctionType: Boolean 44 | get() = throw NotImplementedError() 45 | override val isMarkedNullable: Boolean 46 | get() = throw NotImplementedError() 47 | override val isSuspendFunctionType: Boolean 48 | get() = throw NotImplementedError() 49 | override val nullability: Nullability 50 | get() = throw NotImplementedError() 51 | 52 | override fun isAssignableFrom(that: KSType): Boolean { 53 | throw NotImplementedError() 54 | } 55 | 56 | override fun isCovarianceFlexible(): Boolean { 57 | throw NotImplementedError() 58 | } 59 | 60 | override fun isMutabilityFlexible(): Boolean { 61 | throw NotImplementedError() 62 | } 63 | 64 | override fun makeNotNullable(): KSType { 65 | throw NotImplementedError() 66 | } 67 | 68 | override fun makeNullable(): KSType { 69 | throw NotImplementedError() 70 | } 71 | 72 | override fun replace(arguments: List): KSType { 73 | throw NotImplementedError() 74 | } 75 | 76 | override fun starProjection(): KSType { 77 | throw NotImplementedError() 78 | } 79 | } 80 | 81 | val exception1 = assertFailsWith { 82 | type.toClassName() 83 | } 84 | assertThat(exception1).hasMessageThat() 85 | .contains("is not resolvable in the current round of processing") 86 | 87 | val exception2 = assertFailsWith { 88 | type.toTypeName() 89 | } 90 | assertThat(exception2).hasMessageThat() 91 | .contains("is not resolvable in the current round of processing") 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /kotlinpoet/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi 17 | import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl 18 | 19 | plugins { 20 | kotlin("multiplatform") 21 | } 22 | 23 | spotless { 24 | kotlin { 25 | targetExclude( 26 | // Non-Square licensed files 27 | "src/*Main/kotlin/com/squareup/kotlinpoet/ClassName.kt", 28 | "src/*Main/kotlin/com/squareup/kotlinpoet/ClassName.*.kt", 29 | "src/*Test/kotlin/com/squareup/kotlinpoet/AbstractTypesTest.kt", 30 | "src/*Test/kotlin/com/squareup/kotlinpoet/ClassNameTest.kt", 31 | "src/*Test/kotlin/com/squareup/kotlinpoet/TypesEclipseTest.kt", 32 | "src/*Test/kotlin/com/squareup/kotlinpoet/TypesTest.kt", 33 | ) 34 | } 35 | } 36 | 37 | kotlin { 38 | jvm() 39 | 40 | js { 41 | nodejs { 42 | testTask { 43 | useMocha() 44 | } 45 | } 46 | binaries.library() 47 | } 48 | 49 | @OptIn(ExperimentalWasmDsl::class) 50 | wasmJs { 51 | nodejs { 52 | testTask { 53 | useMocha() 54 | } 55 | } 56 | binaries.library() 57 | } 58 | 59 | @OptIn(ExperimentalKotlinGradlePluginApi::class) 60 | compilerOptions { 61 | allWarningsAsErrors = true 62 | optIn.add("com.squareup.kotlinpoet.DelicateKotlinPoetApi") 63 | freeCompilerArgs.add("-Xexpect-actual-classes") 64 | } 65 | 66 | sourceSets { 67 | commonMain { 68 | dependencies { 69 | implementation(libs.kotlin.reflect) 70 | } 71 | } 72 | 73 | commonTest { 74 | dependencies { 75 | implementation(kotlin("test")) 76 | } 77 | } 78 | 79 | jvmTest { 80 | dependencies { 81 | implementation(libs.kotlin.junit) 82 | implementation(libs.truth) 83 | implementation(libs.compileTesting) 84 | implementation(libs.jimfs) 85 | implementation(libs.ecj) 86 | implementation(libs.kotlinCompileTesting) 87 | implementation(libs.kotlin.annotationProcessingEmbeddable) 88 | implementation(libs.kotlin.compilerEmbeddable) 89 | } 90 | } 91 | 92 | val nonJvmMain by creating { 93 | dependsOn(commonMain.get()) 94 | } 95 | 96 | jsMain { 97 | dependsOn(nonJvmMain) 98 | } 99 | wasmJsMain { 100 | dependsOn(nonJvmMain) 101 | } 102 | } 103 | } 104 | 105 | tasks.withType(org.gradle.jvm.tasks.Jar::class.java) { 106 | manifest { 107 | attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet") 108 | } 109 | } 110 | 111 | tasks.named("compileTestKotlinJvm") { 112 | compilerOptions { 113 | freeCompilerArgs.add("-opt-in=com.squareup.kotlinpoet.DelicateKotlinPoetApi") 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /kotlinpoet/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=kotlinpoet 2 | POM_NAME=KotlinPoet 3 | POM_DESCRIPTION=Use beautiful Kotlin code to generate beautiful Kotlin code. 4 | POM_PACKAGING=jar 5 | kotlin.mpp.applyDefaultHierarchyTemplate=false 6 | -------------------------------------------------------------------------------- /kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodePoint.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import kotlin.jvm.JvmInline 19 | 20 | @JvmInline 21 | internal value class CodePoint(val code: Int) 22 | 23 | internal expect fun String.codePointAt(index: Int): CodePoint 24 | 25 | internal expect fun CodePoint.isLowerCase(): Boolean 26 | internal expect fun CodePoint.isUpperCase(): Boolean 27 | 28 | internal expect fun CodePoint.isJavaIdentifierStart(): Boolean 29 | internal expect fun CodePoint.isJavaIdentifierPart(): Boolean 30 | 31 | internal expect fun CodePoint.charCount(): Int 32 | 33 | internal expect fun StringBuilder.appendCodePoint(codePoint: CodePoint): StringBuilder 34 | -------------------------------------------------------------------------------- /kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | /** 19 | * Marks declarations in the KotlinPoet API that are **delicate** — 20 | * they have limited use-case and shall be used with care in general code. 21 | * Any use of a delicate declaration has to be carefully reviewed to make sure it is 22 | * properly used and does not create problems like lossy Java -> Kotlin type parsing. 23 | * Carefully read documentation and [message] of any declaration marked as `DelicateKotlinPoetApi`. 24 | */ 25 | @MustBeDocumented 26 | @Retention(value = AnnotationRetention.BINARY) 27 | @RequiresOptIn( 28 | level = RequiresOptIn.Level.WARNING, 29 | message = "This is a delicate API and its use requires care." + 30 | " Make sure you fully read and understand documentation of the declaration that is marked as a delicate API.", 31 | ) 32 | public annotation class DelicateKotlinPoetApi(val message: String) 33 | -------------------------------------------------------------------------------- /kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import kotlin.annotation.AnnotationTarget.CLASS 19 | import kotlin.annotation.AnnotationTarget.FUNCTION 20 | import kotlin.annotation.AnnotationTarget.PROPERTY 21 | import kotlin.annotation.AnnotationTarget.TYPEALIAS 22 | 23 | /** 24 | * Indicates that a given API is experimental and subject to change. 25 | */ 26 | @RequiresOptIn 27 | @Retention(AnnotationRetention.BINARY) 28 | @Target(CLASS, FUNCTION, PROPERTY, TYPEALIAS) 29 | public annotation class ExperimentalKotlinPoetApi 30 | -------------------------------------------------------------------------------- /kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Import.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | @ExposedCopyVisibility 19 | public data class Import internal constructor( 20 | val qualifiedName: String, 21 | val alias: String? = null, 22 | ) : Comparable { 23 | 24 | private val importString = buildString { 25 | append(qualifiedName.escapeSegmentsIfNecessary()) 26 | if (alias != null) { 27 | append(" as ${alias.escapeIfNecessary()}") 28 | } 29 | } 30 | 31 | override fun toString(): String = importString 32 | 33 | override fun compareTo(other: Import): Int = importString.compareTo(other.importString) 34 | } 35 | -------------------------------------------------------------------------------- /kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KModifier.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.squareup.kotlinpoet.KModifier.INTERNAL 19 | import com.squareup.kotlinpoet.KModifier.PRIVATE 20 | import com.squareup.kotlinpoet.KModifier.PROTECTED 21 | import com.squareup.kotlinpoet.KModifier.PUBLIC 22 | 23 | public enum class KModifier( 24 | internal val keyword: String, 25 | private vararg val targets: Target, 26 | ) { 27 | // Modifier order defined here: 28 | // https://kotlinlang.org/docs/reference/coding-conventions.html#modifiers 29 | 30 | // Access. 31 | PUBLIC("public", Target.PROPERTY), 32 | PROTECTED("protected", Target.PROPERTY), 33 | PRIVATE("private", Target.PROPERTY), 34 | INTERNAL("internal", Target.PROPERTY), 35 | 36 | // Multiplatform modules. 37 | EXPECT("expect", Target.CLASS, Target.FUNCTION, Target.PROPERTY), 38 | ACTUAL("actual", Target.CLASS, Target.FUNCTION, Target.PROPERTY), 39 | 40 | FINAL("final", Target.CLASS, Target.FUNCTION, Target.PROPERTY), 41 | OPEN("open", Target.CLASS, Target.FUNCTION, Target.PROPERTY), 42 | ABSTRACT("abstract", Target.CLASS, Target.FUNCTION, Target.PROPERTY), 43 | SEALED("sealed", Target.CLASS), 44 | CONST("const", Target.PROPERTY), 45 | 46 | EXTERNAL("external", Target.CLASS, Target.FUNCTION, Target.PROPERTY), 47 | OVERRIDE("override", Target.FUNCTION, Target.PROPERTY), 48 | LATEINIT("lateinit", Target.PROPERTY), 49 | TAILREC("tailrec", Target.FUNCTION), 50 | VARARG("vararg", Target.PARAMETER), 51 | SUSPEND("suspend", Target.FUNCTION), 52 | INNER("inner", Target.CLASS), 53 | 54 | ENUM("enum", Target.CLASS), 55 | ANNOTATION("annotation", Target.CLASS), 56 | VALUE("value", Target.CLASS), 57 | FUN("fun", Target.INTERFACE), 58 | 59 | COMPANION("companion", Target.CLASS), 60 | 61 | // Call-site compiler tips. 62 | INLINE("inline", Target.FUNCTION), 63 | NOINLINE("noinline", Target.PARAMETER), 64 | CROSSINLINE("crossinline", Target.PARAMETER), 65 | REIFIED("reified", Target.TYPE_PARAMETER), 66 | 67 | INFIX("infix", Target.FUNCTION), 68 | OPERATOR("operator", Target.FUNCTION), 69 | 70 | DATA("data", Target.CLASS), 71 | 72 | IN("in", Target.VARIANCE_ANNOTATION), 73 | OUT("out", Target.VARIANCE_ANNOTATION), 74 | ; 75 | 76 | internal enum class Target { 77 | CLASS, 78 | VARIANCE_ANNOTATION, 79 | PARAMETER, 80 | TYPE_PARAMETER, 81 | FUNCTION, 82 | PROPERTY, 83 | INTERFACE, 84 | } 85 | 86 | internal fun checkTarget(target: Target) { 87 | require(target in targets) { "unexpected modifier $this for $target" } 88 | } 89 | } 90 | 91 | internal val VISIBILITY_MODIFIERS: Set = setOf(PUBLIC, INTERNAL, PROTECTED, PRIVATE) 92 | -------------------------------------------------------------------------------- /kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KOperator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | public enum class KOperator( 19 | internal val operator: String, 20 | internal val functionName: String, 21 | ) { 22 | UNARY_PLUS("+", "unaryPlus"), 23 | PLUS("+", "plus"), 24 | UNARY_MINUS("-", "unaryMinus"), 25 | MINUS("-", "minus"), 26 | TIMES("*", "times"), 27 | DIV("/", "div"), 28 | REM("%", "rem"), 29 | PLUS_ASSIGN("+=", "plusAssign"), 30 | MINUS_ASSIGN("-=", "minusAssign"), 31 | TIMES_ASSIGN("*=", "timesAssign"), 32 | DIV_ASSIGN("/=", "divAssign"), 33 | REM_ASSIGN("%=", "remAssign"), 34 | INC("++", "inc"), 35 | DEC("--", "dec"), 36 | EQUALS("==", "equals"), 37 | NOT_EQUALS("!=", "equals"), 38 | NOT("!", "not"), 39 | RANGE_TO("..", "rangeTo"), 40 | CONTAINS("in", "contains"), 41 | NOT_CONTAINS("!in", "contains"), 42 | GT(">", "compareTo"), 43 | LT("<", "compareTo"), 44 | GE(">=", "compareTo"), 45 | LE("<=", "compareTo"), 46 | ITERATOR("in", "iterator"), 47 | } 48 | -------------------------------------------------------------------------------- /kotlinpoet/src/jsMain/kotlin/com/squareup/kotlinpoet/CodePoint.js.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | internal actual fun String.codePointAt(index: Int): CodePoint { 19 | val code = jsCodePointAt(this, index) 20 | return CodePoint(code) 21 | } 22 | 23 | internal actual fun CodePoint.isLowerCase(): Boolean { 24 | // TODO Will there be a problem? Is there a better way? 25 | val str = jsFromCodePoint(this.code) 26 | 27 | if (str.length != 1) { 28 | return false 29 | } 30 | 31 | return str.first().isLowerCase() 32 | } 33 | 34 | internal actual fun CodePoint.isUpperCase(): Boolean { 35 | // TODO Will there be a problem? Is there a better way? 36 | val str = jsFromCodePoint(this.code) 37 | 38 | if (str.length != 1) { 39 | return false 40 | } 41 | 42 | return str.first().isUpperCase() 43 | } 44 | 45 | @Suppress("unused") 46 | private fun jsCodePointAt(str: String, index: Int): Int = 47 | js("str.codePointAt(index)").unsafeCast() 48 | 49 | @Suppress("unused") 50 | private fun jsFromCodePoint(code: Int): String = 51 | js("String.fromCodePoint(code)").toString() 52 | -------------------------------------------------------------------------------- /kotlinpoet/src/jsMain/kotlin/com/squareup/kotlinpoet/Util.js.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | private val IDENTIFIER_REGEX = IDENTIFIER_REGEX_VALUE.toRegex() 19 | 20 | internal actual val String.isIdentifier: Boolean get() = IDENTIFIER_REGEX.matches(this) 21 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/Annotatable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import kotlin.reflect.KClass 19 | 20 | /** A spec or type which can be annotated. */ 21 | public interface Annotatable { 22 | public val annotations: List 23 | 24 | public interface Builder> { 25 | public val annotations: MutableList 26 | 27 | @Suppress("UNCHECKED_CAST") 28 | public fun addAnnotation(annotationSpec: AnnotationSpec): T = apply { 29 | annotations += annotationSpec 30 | } as T 31 | 32 | @Suppress("UNCHECKED_CAST") 33 | public fun addAnnotations(annotationSpecs: Iterable): T = apply { 34 | for (annotationSpec in annotationSpecs) { 35 | addAnnotation(annotationSpec) 36 | } 37 | } as T 38 | 39 | public fun addAnnotation(annotation: ClassName): T = addAnnotation(AnnotationSpec.builder(annotation).build()) 40 | 41 | @DelicateKotlinPoetApi( 42 | message = "Java reflection APIs don't give complete information on Kotlin types. Consider " + 43 | "using the kotlinpoet-metadata APIs instead.", 44 | ) 45 | public fun addAnnotation(annotation: Class<*>): T = addAnnotation(annotation.asClassName()) 46 | 47 | public fun addAnnotation(annotation: KClass<*>): T = addAnnotation(annotation.asClassName()) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/CodePoint.jvm.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import kotlin.text.codePointAt as codePointAtKt 19 | 20 | internal actual fun String.codePointAt(index: Int): CodePoint = 21 | CodePoint(codePointAtKt(index)) 22 | 23 | internal actual fun CodePoint.isLowerCase(): Boolean = 24 | Character.isLowerCase(code) 25 | 26 | internal actual fun CodePoint.isUpperCase(): Boolean = 27 | Character.isUpperCase(code) 28 | 29 | internal actual fun CodePoint.isJavaIdentifierStart(): Boolean = 30 | Character.isJavaIdentifierStart(code) 31 | 32 | internal actual fun CodePoint.isJavaIdentifierPart(): Boolean = 33 | Character.isJavaIdentifierPart(code) 34 | 35 | internal actual fun CodePoint.charCount(): Int { 36 | return Character.charCount(code) 37 | } 38 | 39 | internal actual fun StringBuilder.appendCodePoint(codePoint: CodePoint): StringBuilder { 40 | return appendCodePoint(codePoint.code) 41 | } 42 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/ContextReceivable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | /** A KotlinPoet spec type that can have a context receiver. */ 19 | public interface ContextReceivable { 20 | 21 | /** The originating elements of this type. */ 22 | @ExperimentalKotlinPoetApi 23 | public val contextReceiverTypes: List 24 | 25 | /** The builder analogue to [ContextReceivable] types. */ 26 | public interface Builder> { 27 | 28 | /** Mutable map of the current originating elements this builder contains. */ 29 | @ExperimentalKotlinPoetApi 30 | public val contextReceiverTypes: MutableList 31 | 32 | /** Adds the given [receiverTypes] to this type's list of originating elements. */ 33 | @Suppress("UNCHECKED_CAST") 34 | @ExperimentalKotlinPoetApi 35 | public fun contextReceivers(receiverTypes: Iterable): T = apply { 36 | contextReceiverTypes += receiverTypes 37 | } as T 38 | 39 | /** Adds the given [receiverTypes] to this type's list of originating elements. */ 40 | @ExperimentalKotlinPoetApi 41 | public fun contextReceivers(vararg receiverTypes: TypeName): T = 42 | contextReceivers(receiverTypes.toList()) 43 | } 44 | } 45 | 46 | @ExperimentalKotlinPoetApi 47 | internal fun ContextReceivable.Builder<*>.buildContextReceivers() = 48 | ContextReceivers(contextReceiverTypes.toImmutableList()) 49 | 50 | @JvmInline 51 | @ExperimentalKotlinPoetApi 52 | internal value class ContextReceivers( 53 | override val contextReceiverTypes: List, 54 | ) : ContextReceivable 55 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/Documentable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | /** A spec which contains documentation. */ 19 | public interface Documentable { 20 | public val kdoc: CodeBlock 21 | 22 | @Suppress("UNCHECKED_CAST") 23 | public interface Builder> { 24 | public val kdoc: CodeBlock.Builder 25 | 26 | public fun addKdoc(format: String, vararg args: Any): T = apply { 27 | kdoc.add(format, *args) 28 | } as T 29 | 30 | public fun addKdoc(block: CodeBlock): T = apply { 31 | kdoc.add(block) 32 | } as T 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/Dynamic.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import kotlin.reflect.KClass 19 | 20 | public object Dynamic : TypeName(false, emptyList(), TagMap(emptyMap())) { 21 | 22 | override fun copy( 23 | nullable: Boolean, 24 | annotations: List, 25 | tags: Map, Any>, 26 | ): Nothing = throw UnsupportedOperationException("dynamic doesn't support copying") 27 | 28 | override fun emit(out: CodeWriter) = out.apply { 29 | emit("dynamic") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/MemberSpecHolder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import java.lang.reflect.Type 19 | import kotlin.reflect.KClass 20 | 21 | /** A spec which can contain [PropertySpec]s and [FunSpec]s. */ 22 | public interface MemberSpecHolder { 23 | public val propertySpecs: List 24 | public val funSpecs: List 25 | 26 | public interface Builder> { 27 | @Suppress("UNCHECKED_CAST") 28 | public fun addProperties(propertySpecs: Iterable): T = apply { 29 | propertySpecs.map(::addProperty) 30 | } as T 31 | 32 | public fun addProperty(propertySpec: PropertySpec): T 33 | 34 | public fun addProperty(name: String, type: TypeName, vararg modifiers: KModifier): T = 35 | addProperty(PropertySpec.builder(name, type, *modifiers).build()) 36 | 37 | @DelicateKotlinPoetApi( 38 | message = "Java reflection APIs don't give complete information on Kotlin types. Consider " + 39 | "using the kotlinpoet-metadata APIs instead.", 40 | ) 41 | public fun addProperty(name: String, type: Type, vararg modifiers: KModifier): T = 42 | addProperty(name, type.asTypeName(), *modifiers) 43 | 44 | public fun addProperty(name: String, type: KClass<*>, vararg modifiers: KModifier): T = 45 | addProperty(name, type.asTypeName(), *modifiers) 46 | 47 | public fun addProperty(name: String, type: TypeName, modifiers: Iterable): T = 48 | addProperty(PropertySpec.builder(name, type, modifiers).build()) 49 | 50 | @DelicateKotlinPoetApi( 51 | message = "Java reflection APIs don't give complete information on Kotlin types. Consider " + 52 | "using the kotlinpoet-metadata APIs instead.", 53 | ) 54 | public fun addProperty(name: String, type: Type, modifiers: Iterable): T = 55 | addProperty(name, type.asTypeName(), modifiers) 56 | 57 | public fun addProperty(name: String, type: KClass<*>, modifiers: Iterable): T = 58 | addProperty(name, type.asTypeName(), modifiers) 59 | 60 | @Suppress("UNCHECKED_CAST") 61 | public fun addFunctions(funSpecs: Iterable): T = apply { 62 | funSpecs.forEach(::addFunction) 63 | } as T 64 | 65 | public fun addFunction(funSpec: FunSpec): T 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/OriginatingElementsHolder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import javax.lang.model.element.Element 19 | 20 | /** A type that can have originating [elements][Element]. */ 21 | public interface OriginatingElementsHolder { 22 | 23 | /** The originating elements of this type. */ 24 | public val originatingElements: List 25 | 26 | /** The builder analogue to [OriginatingElementsHolder] types. */ 27 | @JvmDefaultWithCompatibility 28 | public interface Builder> { 29 | 30 | /** Mutable map of the current originating elements this builder contains. */ 31 | public val originatingElements: MutableList 32 | 33 | /** Adds an [originatingElement] to this type's list of originating elements. */ 34 | @Suppress("UNCHECKED_CAST") 35 | public fun addOriginatingElement(originatingElement: Element): T = apply { 36 | originatingElements += originatingElement 37 | } as T 38 | } 39 | } 40 | 41 | internal fun OriginatingElementsHolder.Builder<*>.buildOriginatingElements() = 42 | OriginatingElements(originatingElements.toImmutableList()) 43 | 44 | internal fun List.buildOriginatingElements() = 45 | OriginatingElements(toImmutableList()) 46 | 47 | @JvmInline 48 | internal value class OriginatingElements( 49 | override val originatingElements: List, 50 | ) : OriginatingElementsHolder 51 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/TypeSpecHolder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | /** A spec which can contain other [TypeSpec] */ 19 | public interface TypeSpecHolder { 20 | public val typeSpecs: List 21 | 22 | public interface Builder> { 23 | 24 | public fun addType(typeSpec: TypeSpec): T 25 | 26 | @Suppress("UNCHECKED_CAST") 27 | public fun addTypes(typeSpecs: Iterable): T = apply { 28 | for (typeSpec in typeSpecs) addType(typeSpec) 29 | } as T 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/Util.jvm.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.squareup.kotlinpoet.CodeBlock.Companion.isPlaceholder 19 | import java.util.Collections 20 | 21 | internal actual fun Map.toImmutableMap(): Map = 22 | Collections.unmodifiableMap(LinkedHashMap(this)) 23 | 24 | internal actual fun Collection.toImmutableList(): List = 25 | Collections.unmodifiableList(ArrayList(this)) 26 | 27 | internal actual fun Collection.toImmutableSet(): Set = 28 | Collections.unmodifiableSet(LinkedHashSet(this)) 29 | 30 | // TODO Waiting for `CodeBlock` migration. 31 | internal fun CodeBlock.ensureEndsWithNewLine() = trimTrailingNewLine('\n') 32 | 33 | // TODO Waiting for `CodeBlock` migration. 34 | internal fun CodeBlock.trimTrailingNewLine(replaceWith: Char? = null) = if (isEmpty()) { 35 | this 36 | } else { 37 | with(toBuilder()) { 38 | val lastFormatPart = trim().formatParts.last() 39 | if (lastFormatPart.isPlaceholder && args.isNotEmpty()) { 40 | val lastArg = args.last() 41 | if (lastArg is String) { 42 | val trimmedArg = lastArg.trimEnd('\n') 43 | args[args.size - 1] = if (replaceWith != null) { 44 | trimmedArg + replaceWith 45 | } else { 46 | trimmedArg 47 | } 48 | } 49 | } else { 50 | formatParts[formatParts.lastIndexOf(lastFormatPart)] = lastFormatPart.trimEnd('\n') 51 | if (replaceWith != null) { 52 | formatParts += "$replaceWith" 53 | } 54 | } 55 | return@with build() 56 | } 57 | } 58 | 59 | private val IDENTIFIER_REGEX = IDENTIFIER_REGEX_VALUE.toRegex() 60 | 61 | internal actual val String.isIdentifier: Boolean 62 | get() = IDENTIFIER_REGEX.matches(this) 63 | 64 | internal actual fun Char.isJavaIdentifierStart(): Boolean = 65 | Character.isJavaIdentifierStart(this) 66 | 67 | internal actual fun Char.isJavaIdentifierPart(): Boolean = 68 | Character.isJavaIdentifierPart(this) 69 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/tags/TypeAliasTag.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.tags 17 | 18 | import com.squareup.kotlinpoet.TypeName 19 | 20 | /** 21 | * This tag indicates that this [TypeName] represents a `typealias` type. 22 | * 23 | * @property [abbreviatedType] the underlying type for this alias. 24 | */ 25 | public class TypeAliasTag(public val abbreviatedType: TypeName) 26 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaAnnotationSpecTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import com.google.testing.compile.CompilationRule 20 | import kotlin.test.Test 21 | import org.junit.Rule 22 | 23 | class JavaAnnotationSpecTest { 24 | 25 | @Rule @JvmField 26 | val compilation = CompilationRule() 27 | 28 | @Test fun getOnValueArrayTypeMirrorShouldNameValueArg() { 29 | val myClazz = compilation.elements 30 | .getTypeElement(JavaClassWithArrayValueAnnotation::class.java.canonicalName) 31 | val classBuilder = TypeSpec.classBuilder("Result") 32 | 33 | myClazz.annotationMirrors.map { AnnotationSpec.get(it) } 34 | .forEach { 35 | classBuilder.addAnnotation(it) 36 | } 37 | 38 | assertThat(toString(classBuilder.build())).isEqualTo( 39 | """ 40 | |package com.squareup.tacos 41 | | 42 | |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation 43 | |import java.lang.Boolean 44 | |import java.lang.Object 45 | | 46 | |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class, Boolean::class)) 47 | |public class Result 48 | | 49 | """.trimMargin(), 50 | ) 51 | } 52 | 53 | @Test fun getOnValueArrayTypeAnnotationShouldNameValueArg() { 54 | val annotation = JavaClassWithArrayValueAnnotation::class.java.getAnnotation( 55 | JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue::class.java, 56 | ) 57 | val classBuilder = TypeSpec.classBuilder("Result") 58 | .addAnnotation(AnnotationSpec.get(annotation)) 59 | 60 | assertThat(toString(classBuilder.build()).trim()).isEqualTo( 61 | """ 62 | |package com.squareup.tacos 63 | | 64 | |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation 65 | |import java.lang.Boolean 66 | |import java.lang.Object 67 | | 68 | |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class, Boolean::class)) 69 | |public class Result 70 | """.trimMargin(), 71 | ) 72 | } 73 | 74 | private fun toString(typeSpec: TypeSpec) = 75 | FileSpec.get("com.squareup.tacos", typeSpec).toString() 76 | } 77 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.squareup.kotlinpoet; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import static com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue; 7 | 8 | @AnnotationWithArrayValue({ 9 | Object.class, Boolean.class 10 | }) 11 | public class JavaClassWithArrayValueAnnotation { 12 | 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @interface AnnotationWithArrayValue { 15 | Class[] value(); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/AssertThrows.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.ThrowableSubject 19 | import com.google.common.truth.Truth.assertThat 20 | 21 | inline fun assertThrows(block: () -> Unit): ThrowableSubject { 22 | try { 23 | block() 24 | } catch (e: Throwable) { 25 | if (e is T) { 26 | return assertThat(e) 27 | } else { 28 | throw e 29 | } 30 | } 31 | throw AssertionError("Expected ${T::class.simpleName}") 32 | } 33 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/Cased/Weird/Sup.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet.Cased.Weird // ktlint-disable package-name 17 | 18 | object Sup { 19 | object Hi 20 | } 21 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import kotlin.test.Test 20 | 21 | class ExpectDeclarationsTest { 22 | @Test fun expectFunDeclaration() { 23 | val methodSpec = FunSpec.builder("function") 24 | .addModifiers(KModifier.EXPECT) 25 | .build() 26 | 27 | assertThat(methodSpec.toString()).isEqualTo( 28 | """ 29 | |public expect fun function() 30 | | 31 | """.trimMargin(), 32 | ) 33 | } 34 | 35 | @Test fun implicitExpectFunDeclaration() { 36 | val builder = TypeSpec.classBuilder("Test") 37 | .addModifiers(KModifier.EXPECT) 38 | val methodSpec = FunSpec.builder("function") 39 | .build() 40 | builder.addFunction(methodSpec) 41 | 42 | assertThat(builder.build().toString()).isEqualTo( 43 | """ 44 | |public expect class Test { 45 | | public fun function() 46 | |} 47 | | 48 | """.trimMargin(), 49 | ) 50 | } 51 | 52 | @Test fun expectPropertyDeclaration() { 53 | val propertySpec = PropertySpec.builder("prop", String::class) 54 | .addModifiers(KModifier.EXPECT) 55 | .build() 56 | 57 | assertThat(propertySpec.toString()).isEqualTo( 58 | """ 59 | |expect val prop: kotlin.String 60 | | 61 | """.trimMargin(), 62 | ) 63 | } 64 | 65 | @Test fun implicitExpectPropertyDeclaration() { 66 | val builder = TypeSpec.classBuilder("Test") 67 | .addModifiers(KModifier.EXPECT) 68 | val propertySpec = PropertySpec.builder("prop", String::class) 69 | .build() 70 | builder.addProperty(propertySpec) 71 | 72 | assertThat(builder.build().toString()).isEqualTo( 73 | """ 74 | |public expect class Test { 75 | | public val prop: kotlin.String 76 | |} 77 | | 78 | """.trimMargin(), 79 | ) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/FileReadingTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import java.io.InputStream 20 | import java.net.URI 21 | import java.nio.charset.StandardCharsets.UTF_8 22 | import javax.tools.JavaFileObject.Kind 23 | import kotlin.test.Test 24 | 25 | class FileReadingTest { 26 | @Test fun javaFileObjectUri() { 27 | val type = TypeSpec.classBuilder("Test").build() 28 | assertThat(FileSpec.get("", type).toJavaFileObject().toUri()) 29 | .isEqualTo(URI.create("Test.kt")) 30 | assertThat(FileSpec.get("foo", type).toJavaFileObject().toUri()) 31 | .isEqualTo(URI.create("foo/Test.kt")) 32 | assertThat(FileSpec.get("com.example", type).toJavaFileObject().toUri()) 33 | .isEqualTo(URI.create("com/example/Test.kt")) 34 | } 35 | 36 | @Test fun javaFileObjectKind() { 37 | val source = FileSpec.get("", TypeSpec.classBuilder("Test").build()) 38 | assertThat(source.toJavaFileObject().kind).isEqualTo(Kind.SOURCE) 39 | } 40 | 41 | @Test fun javaFileObjectCharacterContent() { 42 | val type = TypeSpec.classBuilder("Test") 43 | .addKdoc("Pi\u00f1ata\u00a1") 44 | .addFunction(FunSpec.builder("fooBar").build()) 45 | .build() 46 | val source = FileSpec.get("foo", type) 47 | val javaFileObject = source.toJavaFileObject() 48 | 49 | // We can never have encoding issues (everything is in process) 50 | assertThat(javaFileObject.getCharContent(true)).isEqualTo(source.toString()) 51 | assertThat(javaFileObject.getCharContent(false)).isEqualTo(source.toString()) 52 | } 53 | 54 | @Test fun javaFileObjectInputStreamIsUtf8() { 55 | val source = FileSpec.builder("foo", "Test") 56 | .addType(TypeSpec.classBuilder("Test").build()) 57 | .addFileComment("Pi\u00f1ata\u00a1") 58 | .build() 59 | val bytes = source.toJavaFileObject().openInputStream().use(InputStream::readBytes) 60 | 61 | // KotlinPoet always uses UTF-8. 62 | assertThat(bytes).isEqualTo(source.toString().toByteArray(UTF_8)) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/NameAllocatorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import kotlin.test.Test 20 | 21 | class NameAllocatorTest { 22 | @Test fun usage() { 23 | val nameAllocator = NameAllocator() 24 | assertThat(nameAllocator.newName("foo", 1)).isEqualTo("foo") 25 | assertThat(nameAllocator.newName("bar", 2)).isEqualTo("bar") 26 | assertThat(nameAllocator[1]).isEqualTo("foo") 27 | assertThat(nameAllocator[2]).isEqualTo("bar") 28 | } 29 | 30 | @Test fun nameCollision() { 31 | val nameAllocator = NameAllocator() 32 | assertThat(nameAllocator.newName("foo")).isEqualTo("foo") 33 | assertThat(nameAllocator.newName("foo")).isEqualTo("foo_") 34 | assertThat(nameAllocator.newName("foo")).isEqualTo("foo__") 35 | } 36 | 37 | @Test fun nameCollisionWithTag() { 38 | val nameAllocator = NameAllocator() 39 | assertThat(nameAllocator.newName("foo", 1)).isEqualTo("foo") 40 | assertThat(nameAllocator.newName("foo", 2)).isEqualTo("foo_") 41 | assertThat(nameAllocator.newName("foo", 3)).isEqualTo("foo__") 42 | assertThat(nameAllocator[1]).isEqualTo("foo") 43 | assertThat(nameAllocator[2]).isEqualTo("foo_") 44 | assertThat(nameAllocator[3]).isEqualTo("foo__") 45 | } 46 | 47 | @Test fun characterMappingSubstitute() { 48 | val nameAllocator = NameAllocator() 49 | assertThat(nameAllocator.newName("a-b", 1)).isEqualTo("a_b") 50 | } 51 | 52 | @Test fun characterMappingSurrogate() { 53 | val nameAllocator = NameAllocator() 54 | assertThat(nameAllocator.newName("a\uD83C\uDF7Ab", 1)).isEqualTo("a_b") 55 | } 56 | 57 | @Test fun characterMappingInvalidStartButValidPart() { 58 | val nameAllocator = NameAllocator() 59 | assertThat(nameAllocator.newName("1ab", 1)).isEqualTo("_1ab") 60 | } 61 | 62 | @Test fun characterMappingInvalidStartIsInvalidPart() { 63 | val nameAllocator = NameAllocator() 64 | assertThat(nameAllocator.newName("&ab", 1)).isEqualTo("_ab") 65 | } 66 | 67 | @Test fun kotlinKeyword() { 68 | val nameAllocator = NameAllocator() 69 | assertThat(nameAllocator.newName("when", 1)).isEqualTo("when_") 70 | assertThat(nameAllocator[1]).isEqualTo("when_") 71 | } 72 | 73 | @Test fun kotlinKeywordNotPreAllocated() { 74 | val nameAllocator = NameAllocator(preallocateKeywords = false) 75 | assertThat(nameAllocator.newName("when", 1)).isEqualTo("when") 76 | assertThat(nameAllocator[1]).isEqualTo("when") 77 | } 78 | 79 | @Test fun tagReuseForbidden() { 80 | val nameAllocator = NameAllocator() 81 | nameAllocator.newName("foo", 1) 82 | assertThrows { 83 | nameAllocator.newName("bar", 1) 84 | }.hasMessageThat().isEqualTo("tag 1 cannot be used for both 'foo' and 'bar'") 85 | } 86 | 87 | @Test fun useBeforeAllocateForbidden() { 88 | val nameAllocator = NameAllocator() 89 | assertThrows { 90 | nameAllocator[1] 91 | }.hasMessageThat().isEqualTo("unknown tag: 1") 92 | } 93 | 94 | @Test fun cloneUsage() { 95 | val outerAllocator = NameAllocator() 96 | outerAllocator.newName("foo", 1) 97 | 98 | val innerAllocator1 = outerAllocator.copy() 99 | assertThat(innerAllocator1.newName("bar", 2)).isEqualTo("bar") 100 | assertThat(innerAllocator1.newName("foo", 3)).isEqualTo("foo_") 101 | 102 | val innerAllocator2 = outerAllocator.copy() 103 | assertThat(innerAllocator2.newName("foo", 2)).isEqualTo("foo_") 104 | assertThat(innerAllocator2.newName("bar", 3)).isEqualTo("bar") 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/StringsTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import org.junit.Test 20 | 21 | class StringsTest { 22 | @Test fun singleLineStringWithDollarSymbols() { 23 | val stringWithTemplate = "$" + "annoyingUser" + " is annoying." 24 | val funSpec = FunSpec.builder("getString") 25 | .returns(STRING) 26 | .addStatement("return %S", stringWithTemplate) 27 | .build() 28 | assertThat(funSpec.toString()) 29 | .isEqualTo("public fun getString(): kotlin.String = \"\${\'\$\'}annoyingUser is annoying.\"\n") 30 | } 31 | 32 | @Test fun multilineStringWithDollarSymbols() { 33 | val stringWithTemplate = "Some string\n" + "$" + "annoyingUser" + " is annoying." 34 | val funSpec = FunSpec.builder("getString") 35 | .returns(STRING) 36 | .addStatement("return %S", stringWithTemplate) 37 | .build() 38 | assertThat(funSpec.toString()).isEqualTo( 39 | "public fun getString(): kotlin.String = \"\"\"\n" + 40 | "|Some string\n" + 41 | "|\${\'\$\'}annoyingUser is annoying.\n" + 42 | "\"\"\".trimMargin()\n", 43 | ) 44 | } 45 | 46 | @Test fun singleLineStringTemplate() { 47 | val stringWithTemplate = "$" + "annoyingUser" + " is annoying." 48 | val funSpec = FunSpec.builder("getString") 49 | .returns(STRING) 50 | .addStatement("return %P", stringWithTemplate) 51 | .build() 52 | assertThat(funSpec.toString()) 53 | .isEqualTo("public fun getString(): kotlin.String = \"\"\"\$annoyingUser is annoying.\"\"\"\n") 54 | } 55 | 56 | @Test fun multilineStringTemplate() { 57 | val stringWithTemplate = "Some string\n" + "$" + "annoyingUser" + " is annoying." 58 | val funSpec = FunSpec.builder("getString") 59 | .returns(STRING) 60 | .addStatement("return %P", stringWithTemplate) 61 | .build() 62 | assertThat(funSpec.toString()).isEqualTo( 63 | "public fun getString(): kotlin.String = \"\"\"\n" + 64 | "|Some string\n" + 65 | "|\$annoyingUser is annoying.\n" + 66 | "\"\"\".trimMargin()\n", 67 | ) 68 | } 69 | 70 | // https://github.com/square/kotlinpoet/issues/572 71 | @Test fun templateStringWithStringLiteralReference() { 72 | val string = "SELECT * FROM socialFeedItem WHERE message IS NOT NULL AND userId \${ if (userId == null) \"IS\" else \"=\" } ?1 ORDER BY datetime(creation_time) DESC" 73 | val funSpec = FunSpec.builder("getString") 74 | .returns(STRING) 75 | .addStatement("return %P", string) 76 | .build() 77 | assertThat(funSpec.toString()) 78 | .isEqualTo("public fun getString(): kotlin.String = \"\"\"SELECT * FROM socialFeedItem WHERE message IS NOT NULL AND userId \${ if (userId == null) \"IS\" else \"=\" } ?1 ORDER BY datetime(creation_time) DESC\"\"\"\n") 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/TypeNameKotlinTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import org.junit.Test 20 | 21 | class TypeNameKotlinTest { 22 | 23 | @Test 24 | fun typeNameOf_simple() { 25 | val type = typeNameOf() 26 | assertThat(type.toString()).isEqualTo("com.squareup.kotlinpoet.TypeNameKotlinTest") 27 | } 28 | 29 | @Test 30 | fun typeNameOf_simple_intrinsic() { 31 | val type = typeNameOf() 32 | assertThat(type.toString()).isEqualTo("kotlin.String") 33 | } 34 | 35 | @Test 36 | fun typeNameOf_array_primitive() { 37 | val type = typeNameOf() 38 | assertThat(type.toString()).isEqualTo("kotlin.IntArray") 39 | } 40 | 41 | @Test 42 | fun typeNameOf_array_parameterized() { 43 | val type = typeNameOf>() 44 | assertThat(type.toString()).isEqualTo("kotlin.Array") 45 | } 46 | 47 | @Test 48 | fun typeNameOf_nullable() { 49 | val type = typeNameOf() 50 | assertThat(type.toString()).isEqualTo("kotlin.String?") 51 | } 52 | 53 | @Test 54 | fun typeNameOf_generic() { 55 | val type = typeNameOf>() 56 | assertThat(type.toString()).isEqualTo("kotlin.collections.List") 57 | } 58 | 59 | @Test 60 | fun typeNameOf_generic_wildcard_out() { 61 | val type = typeNameOf>() 62 | assertThat(type.toString()).isEqualTo("com.squareup.kotlinpoet.TypeNameKotlinTest.GenericType") 63 | } 64 | 65 | @Test 66 | fun typeNameOf_generic_wildcard_in() { 67 | val type = typeNameOf>() 68 | assertThat(type.toString()).isEqualTo("com.squareup.kotlinpoet.TypeNameKotlinTest.GenericType") 69 | } 70 | 71 | @Test 72 | fun typeNameOf_complex() { 73 | val type = typeNameOf?>>>>>>>() 74 | assertThat(type.toString()).isEqualTo("kotlin.collections.Map?>>>>>>") 75 | } 76 | 77 | @Suppress("unused") 78 | class GenericType 79 | 80 | @Test 81 | fun tag() { 82 | val type = typeNameOf().copy(tags = mapOf(String::class to "Test")) 83 | assertThat(type.tag()).isEqualTo("Test") 84 | } 85 | 86 | @Test 87 | fun existingTagsShouldBePreserved() { 88 | val type = typeNameOf().copy(tags = mapOf(String::class to "Test")) 89 | val copied = type.copy(nullable = true) 90 | assertThat(copied.tag()).isEqualTo("Test") 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/TypesTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Google, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.testing.compile.CompilationRule 19 | import org.junit.Rule 20 | import javax.lang.model.util.Elements 21 | import javax.lang.model.util.Types 22 | import kotlin.test.Ignore 23 | 24 | @Ignore("Not clear this test is useful to retain in the Kotlin world") 25 | class TypesTest : AbstractTypesTest() { 26 | @JvmField @Rule val compilation = CompilationRule() 27 | 28 | override val elements: Elements 29 | get() = compilation.elements 30 | 31 | override val types: Types 32 | get() = compilation.types 33 | } 34 | -------------------------------------------------------------------------------- /kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/WildcardTypeNameTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | import com.google.common.truth.Truth.assertThat 19 | import com.squareup.kotlinpoet.WildcardTypeName.Companion.consumerOf 20 | import com.squareup.kotlinpoet.WildcardTypeName.Companion.producerOf 21 | import org.junit.Test 22 | 23 | class WildcardTypeNameTest { 24 | 25 | @Test fun equalsAndHashCode() { 26 | val anyProducer1 = producerOf(Any::class) 27 | val anyProducer2 = producerOf(Any::class.asTypeName()) 28 | assertThat(anyProducer1).isEqualTo(anyProducer2) 29 | assertThat(anyProducer1.hashCode()).isEqualTo(anyProducer2.hashCode()) 30 | assertThat(anyProducer1.toString()).isEqualTo(anyProducer2.toString()) 31 | 32 | val stringConsumer1 = consumerOf(String::class) 33 | val stringConsumer2 = consumerOf(String::class.asTypeName()) 34 | assertThat(stringConsumer1).isEqualTo(stringConsumer2) 35 | assertThat(stringConsumer1.hashCode()).isEqualTo(stringConsumer2.hashCode()) 36 | assertThat(stringConsumer1.toString()).isEqualTo(stringConsumer2.toString()) 37 | } 38 | 39 | @Test fun equalsDifferentiatesNullabilityAndAnnotations() { 40 | val anyProducer = producerOf(Any::class) 41 | 42 | assertThat(anyProducer.copy(nullable = true)).isNotEqualTo(anyProducer) 43 | 44 | assertThat(anyProducer.copy(annotations = listOf(AnnotationSpec.builder(Suppress::class).build()))).isNotEqualTo(anyProducer) 45 | } 46 | 47 | @Test fun equalsAndHashCodeIgnoreTags() { 48 | val anyProducer = producerOf(Any::class) 49 | val tagged = anyProducer.copy(tags = mapOf(String::class to "test")) 50 | 51 | assertThat(anyProducer).isEqualTo(tagged) 52 | assertThat(anyProducer.hashCode()).isEqualTo(tagged.hashCode()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /kotlinpoet/src/nonJvmMain/kotlin/com/squareup/kotlinpoet/CodePoint.nonJvm.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | /* 19 | * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. 20 | * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. 21 | */ 22 | internal actual fun StringBuilder.appendCodePoint(codePoint: CodePoint): StringBuilder { 23 | // Copied from StringBuilder.kt, 24 | // TODO Is this correct? 25 | val code = codePoint.code 26 | if (code <= Char.MAX_VALUE.code) { 27 | append(code.toChar()) 28 | } else { 29 | append(Char.MIN_HIGH_SURROGATE + ((code - 0x10000) ushr 10)) 30 | append(Char.MIN_LOW_SURROGATE + (code and 0x3ff)) 31 | } 32 | return this 33 | } 34 | 35 | internal actual fun CodePoint.isJavaIdentifierStart(): Boolean { 36 | // TODO How check Java identifier start use code point? 37 | if (charCount() == 1) { 38 | return Char(code).isJavaIdentifierStart() 39 | } 40 | 41 | return true 42 | } 43 | 44 | internal actual fun CodePoint.isJavaIdentifierPart(): Boolean { 45 | // TODO How check Java identifier part use code point? 46 | if (charCount() == 1) { 47 | return Char(code).isJavaIdentifierPart() 48 | } 49 | 50 | return true 51 | } 52 | 53 | internal actual fun CodePoint.charCount(): Int { 54 | return if (code >= 0x10000) 2 else 1 55 | } 56 | -------------------------------------------------------------------------------- /kotlinpoet/src/nonJvmMain/kotlin/com/squareup/kotlinpoet/Util.nonJvm.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | internal actual fun Map.toImmutableMap(): Map = 19 | toMap() 20 | 21 | internal actual fun Collection.toImmutableList(): List = 22 | toList() 23 | 24 | internal actual fun Collection.toImmutableSet(): Set = 25 | toSet() 26 | 27 | internal actual fun Char.isJavaIdentifierStart(): Boolean { 28 | return isLetter() || 29 | this in CharCategory.LETTER_NUMBER || 30 | this == '$' || 31 | this == '_' 32 | } 33 | 34 | internal actual fun Char.isJavaIdentifierPart(): Boolean { 35 | // TODO 36 | // A character may be part of a Java identifier if any of the following conditions are true: 37 | // - it is a letter 38 | // - it is a currency symbol (such as '$') 39 | // - it is a connecting punctuation character (such as '_') 40 | // - it is a digit 41 | // - it is a numeric letter (such as a Roman numeral character) 42 | // - it is a combining mark 43 | // - it is a non-spacing mark 44 | // isIdentifierIgnorable returns true for the character. 45 | // Also missing here: 46 | // - a combining mark 47 | return isLetter() || 48 | isDigit() || 49 | this in CharCategory.LETTER_NUMBER || 50 | this in CharCategory.NON_SPACING_MARK || 51 | this == '_' || 52 | this == '$' || 53 | isIdentifierIgnorable() 54 | // 55 | } 56 | 57 | internal fun Char.isIdentifierIgnorable(): Boolean { 58 | // The following Unicode characters are ignorable in a Java identifier or a Unicode identifier: 59 | // - ISO control characters that are not whitespace 60 | // - '\u0000' through '\u0008' 61 | // - '\u000E' through '\u001B' 62 | // - '\u007F' through '\u009F' 63 | // - all characters that have the FORMAT general category value 64 | return ( 65 | isISOControl() && ( 66 | this in '\u0000'..'\u0008' || 67 | this in '\u000E'..'\u001B' || 68 | this in '\u007F'..'\u009F' 69 | ) 70 | ) || this in CharCategory.FORMAT 71 | } 72 | -------------------------------------------------------------------------------- /kotlinpoet/src/wasmJsMain/kotlin/com/squareup/kotlinpoet/CodePoint.wasmJs.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | internal actual fun String.codePointAt(index: Int): CodePoint { 19 | val str = this 20 | val code = jsCodePointAt(str, index) 21 | return CodePoint(code) 22 | } 23 | 24 | internal actual fun CodePoint.isLowerCase(): Boolean { 25 | // TODO Will there be a problem? Is there a better way? 26 | val code = this.code 27 | val str = jsFromCodePoint(code) 28 | 29 | if (str.length != 1) { 30 | return false 31 | } 32 | 33 | return str.first().isLowerCase() 34 | } 35 | 36 | internal actual fun CodePoint.isUpperCase(): Boolean { 37 | // TODO Will there be a problem? Is there a better way? 38 | val code = this.code 39 | val str = jsFromCodePoint(code) 40 | 41 | if (str.length != 1) { 42 | return false 43 | } 44 | 45 | return str.first().isUpperCase() 46 | } 47 | 48 | @Suppress("UNUSED_PARAMETER") 49 | private fun jsCodePointAt(str: String, index: Int): Int = 50 | js("str.codePointAt(index)") 51 | 52 | @Suppress("UNUSED_PARAMETER") 53 | private fun jsFromCodePoint(code: Int): String = 54 | js("String.fromCodePoint(code)") 55 | -------------------------------------------------------------------------------- /kotlinpoet/src/wasmJsMain/kotlin/com/squareup/kotlinpoet/Util.wasmJs.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.squareup.kotlinpoet 17 | 18 | internal actual val String.isIdentifier: Boolean 19 | get() { 20 | val regExp = RegExp(IDENTIFIER_REGEX_VALUE, "gu") 21 | regExp.reset() 22 | 23 | val match = regExp.exec(this) ?: return false 24 | return match.index == 0 && regExp.lastIndex == length 25 | } 26 | 27 | internal external interface RegExpMatch { 28 | val index: Int 29 | val length: Int 30 | } 31 | 32 | internal external class RegExp(pattern: String, flags: String? = definedExternally) : JsAny { 33 | fun exec(str: String): RegExpMatch? 34 | override fun toString(): String 35 | 36 | var lastIndex: Int 37 | } 38 | 39 | internal fun RegExp.reset() { 40 | lastIndex = 0 41 | } 42 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | # pip install mkdocs mkdocs-material 2 | # mkdocs serve 3 | # mkdocs gh-deploy 4 | 5 | site_name: KotlinPoet 6 | repo_name: KotlinPoet 7 | repo_url: https://github.com/square/kotlinpoet 8 | site_description: "A Kotlin API for generating .kt source files" 9 | site_author: Square, Inc. 10 | remote_branch: gh-pages 11 | 12 | copyright: 'Copyright © 2015 Square, Inc.' 13 | 14 | theme: 15 | name: 'material' 16 | logo: 'images/icon-square.png' 17 | favicon: 'images/icon-square.png' 18 | palette: 19 | - media: '(prefers-color-scheme: light)' 20 | scheme: default 21 | primary: 'cyan' 22 | accent: 'deep-purple' 23 | toggle: 24 | icon: material/weather-night 25 | name: Switch to dark mode 26 | - media: '(prefers-color-scheme: dark)' 27 | scheme: slate 28 | primary: 'black' 29 | accent: 'blue-grey' 30 | toggle: 31 | icon: material/weather-sunny 32 | name: Switch to light mode 33 | 34 | extra_css: 35 | - 'css/app.css' 36 | 37 | markdown_extensions: 38 | - smarty 39 | - codehilite: 40 | guess_lang: false 41 | - footnotes 42 | - meta 43 | - toc: 44 | permalink: true 45 | - pymdownx.betterem: 46 | smart_enable: all 47 | - pymdownx.caret 48 | - pymdownx.inlinehilite 49 | - pymdownx.magiclink 50 | - pymdownx.smartsymbols 51 | - pymdownx.superfences 52 | - pymdownx.emoji 53 | - tables 54 | - admonition 55 | 56 | nav: 57 | - 'Overview': 58 | - 'Code & Control Flow': code-control-flow.md 59 | - 'Format Specifiers': 60 | - '%S for Strings': s-for-strings.md 61 | - '%P for String Templates': p-for-string-templates.md 62 | - '%T for Types': t-for-types.md 63 | - '%M for Members': m-for-members.md 64 | - '%N for Names': n-for-names.md 65 | - '%L for Literals': l-for-literals.md 66 | - 'Code Block Format Strings': code-block-format-strings.md 67 | - 'Functions': functions.md 68 | - 'Constructors': constructors.md 69 | - 'Parameters': parameters.md 70 | - 'Context Parameters': context-parameters.md 71 | - 'Properties': properties.md 72 | - 'Interfaces': interfaces.md 73 | - 'Objects': objects.md 74 | - 'Enums': enums.md 75 | - 'Anonymous Inner Classes': anonymous-inner-classes.md 76 | - 'Annotations': annotations.md 77 | - 'Type Aliases': type-aliases.md 78 | - 'Callable References': callable-references.md 79 | - 'kotlin-reflect': kotlin-reflect.md 80 | - 'Interop - JavaPoet': interop-javapoet.md 81 | - 'Interop - kotlin-metadata': interop-kotlin-metadata.md 82 | - 'Interop - KSP': interop-ksp.md 83 | - 'API': 84 | - 'kotlinpoet': 1.x/kotlinpoet/index.html 85 | - 'interop-javapoet': 1.x/interop-javapoet/index.html 86 | - 'interop-kotlin-metadata': 1.x/interop-kotlin-metadata/index.html 87 | - 'interop-ksp': 1.x/interop-ksp/index.html 88 | - 'Stack Overflow ⏏': https://stackoverflow.com/questions/tagged/kotlinpoet?sort=active 89 | - 'Change Log': changelog.md 90 | - 'Contributing': contributing.md 91 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | pluginManagement { 17 | repositories { 18 | mavenCentral() 19 | gradlePluginPortal() 20 | } 21 | } 22 | 23 | plugins { 24 | id("org.gradle.toolchains.foojay-resolver-convention") version("1.0.0") 25 | } 26 | 27 | include( 28 | ":kotlinpoet", 29 | ":interop:javapoet", 30 | ":interop:kotlin-metadata", 31 | ":interop:ksp", 32 | ":interop:ksp:test-processor", 33 | ) 34 | 35 | rootProject.name = "kotlinpoet-root" 36 | 37 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 38 | --------------------------------------------------------------------------------