|
| 1 | +[//]: # (title: Custom compiler plugins) |
| 2 | + |
| 3 | +> The Kotlin compiler plugin API is unstable and introduces breaking changes in every release. |
| 4 | +> |
| 5 | +{style="warning"} |
| 6 | + |
| 7 | +<include from="compiler-plugins-overview.md" element-id="compiler-plugin-description"/> |
| 8 | + |
| 9 | +Before you create your own custom compiler plugin, check the [list of available compiler plugins](compiler-plugins-overview.md) to see if one is already available that suits your use case. |
| 10 | + |
| 11 | +You can also check whether you can use the [Kotlin Symbol Processing (KSP) API](https://kotlinlang.org/docs/ksp-overview.html) or an external linter such as [Android lint](https://developer.android.com/studio/write/lint) to achieve your goals. |
| 12 | + |
| 13 | +If you _still_ can't find what you need, you can create a custom compiler plugin. Be aware that the Kotlin compiler plugin |
| 14 | +API is **unstable**. You need to invest significant ongoing effort to maintain it, since each new compiler release introduces breaking changes. |
| 15 | + |
| 16 | +### The Kotlin compiler and compiler plugins |
| 17 | + |
| 18 | +<p></p> <!-- workaround for MRK057: Paragraph can only contain inline elements--> |
| 19 | +<list columns="2"> |
| 20 | + <li> |
| 21 | + <p></p> |
| 22 | + <br/> |
| 23 | + <img src="compiler-stages.svg" width="400" alt="Kotlin compiler stages"/> |
| 24 | + </li> |
| 25 | + <li> |
| 26 | + <p>The Kotlin compiler:</p> |
| 27 | + <ol> |
| 28 | + <li>Parses the source code and turns it into a structured syntax tree.</li> |
| 29 | + <li>Analyzes and resolves the code by determining what it means, resolving names, checking types, and enforcing visibility rules.</li> |
| 30 | + <li>Generates an Intermediate Representation (IR), a data structure that acts as a bridge between source code and machine code. </li> |
| 31 | + <li>Progressively lowers the IR into simpler forms.</li> |
| 32 | + <li>Translates the lowered IR into target-specific output, such as JVM bytecode, JavaScript, or native machine code.</li> |
| 33 | + </ol> |
| 34 | + </li> |
| 35 | +</list> |
| 36 | + |
| 37 | +Plugins can affect the initial compiler stages through the frontend API, changing how the compiler resolves code. |
| 38 | +For example, a plugin can add annotations or introduce new methods without bodies, or change visibility modifiers. These |
| 39 | +changes are visible in the IDE. |
| 40 | + |
| 41 | +Plugins can also affect the later stages through the backend API, modifying the behavior of declarations. These |
| 42 | +changes appear in the binaries produced after compilation completes. |
| 43 | + |
| 44 | +In practice, compiler plugins affect the stages from analysis and resolution through code generation, |
| 45 | +covering both frontend and backend. For example, the frontend part generates declarations, and the backend part adds |
| 46 | +bodies for those declarations. |
| 47 | + |
| 48 | +{width=650} |
| 49 | + |
| 50 | +The [Kotlin serialization plugin](https://github.com/Kotlin/kotlinx.serialization) is a good example. The plugin's |
| 51 | +frontend part adds a companion object and a serializer function, as well as checks to prevent name conflicts. The backend |
| 52 | +part implements the desired serialization behavior through `KSerializer` objects. |
| 53 | + |
| 54 | +### Kotlin compiler plugin template |
| 55 | + |
| 56 | +To start writing a custom compiler plugin, you can use the [Kotlin compiler plugin template](https://github.com/Kotlin/compiler-plugin-template). |
| 57 | +You then register extension points from the frontend and backend plugin APIs. |
| 58 | + |
| 59 | +> Currently, you can develop custom compiler plugins only with [Gradle](gradle.md). |
| 60 | +> |
| 61 | +{style="note"} |
| 62 | + |
| 63 | +### Frontend plugin API |
| 64 | + |
| 65 | +The frontend plugin API, also known as frontend intermediate representation (FIR), has the following specialized |
| 66 | +extension points to customize resolution: |
| 67 | + |
| 68 | +| Extension name | Description | |
| 69 | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| |
| 70 | +| [`FirAdditionalCheckersExtension`](https://github.com/JetBrains/kotlin/blob/master/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/extensions/FirAdditionalCheckersExtension.kt) | Adds custom compiler checkers. | |
| 71 | +| [`FirDeclarationGenerationExtension`](https://github.com/JetBrains/kotlin/blob/master/compiler/fir/providers/src/org/jetbrains/kotlin/fir/extensions/FirDeclarationGenerationExtension.kt) | Generates new declarations. | |
| 72 | +| [`FirExtensionSessionComponent`](https://github.com/JetBrains/kotlin/blob/master/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirExtensionSessionComponent.kt) | Registers custom components in the `FirSession` to be used by other parts of the plugin. | |
| 73 | +| [`FirFunctionTypeKindExtension`](https://github.com/JetBrains/kotlin/blob/master/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirFunctionTypeKindExtension.kt) | Defines new families of functional types. | |
| 74 | +| [`FirMetadataSerializerPlugin`](https://github.com/JetBrains/kotlin/blob/master/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirMetadataSerializerPlugin.kt) | Reads and writes information to declaration metadata. | |
| 75 | +| [`FirStatusTransformerExtension`](https://github.com/JetBrains/kotlin/blob/master/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirStatusTransformerExtension.kt) | Modifies declaration status attributes such as visibility or modality. | |
| 76 | +| [`FirSupertypeGenerationExtension`](https://github.com/JetBrains/kotlin/blob/master/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension.kt) | Adds new supertypes to an existing class. | |
| 77 | +| [`FirTypeAttributeExtension`]( https://github.com/JetBrains/kotlin/blob/master/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirTypeAttributeExtension.kt) | Adds special attributes to certain types based on their type annotations. | |
| 78 | + |
| 79 | +#### IDE integration |
| 80 | + |
| 81 | +Resolution changes affect IDE behavior such as code highlighting and suggestions, so it's important |
| 82 | +that your plugin is compatible with the IDE. Each version of Intellij IDEA and Android Studio includes a development version |
| 83 | +of the Kotlin compiler. This version is specific to the IDE and is not binary compatible with the released Kotlin compiler. |
| 84 | +As a result, when you update your IDE, you also need to update your compiler plugin to keep it working. For this reason, |
| 85 | +community plugins aren't loaded by default. |
| 86 | + |
| 87 | +To ensure that your custom compiler plugin works with different IDE versions, test it against each IDE version and fix |
| 88 | +any issues you find. |
| 89 | + |
| 90 | +Supporting multiple IDE versions could become easier if a devkit for Kotlin compiler plugins were available. If you're |
| 91 | +interested in this feature, share your feedback in our [issue tracker](https://youtrack.jetbrains.com/issue/KT-82617). |
| 92 | + |
| 93 | +### Backend plugin API |
| 94 | + |
| 95 | +> Backend plugin development is difficult to do correctly without degrading IDE or debugger performance, so be careful |
| 96 | +> and conservative with your changes. |
| 97 | +> |
| 98 | +{style="warning"} |
| 99 | + |
| 100 | +The backend plugin API, also known as IR, has a single extension point: [`IrGenerationExtension`](https://github.com/JetBrains/kotlin/blob/master/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrGenerationExtension.kt). |
| 101 | +Use this extension point and override the `generate()` function to add bodies to declarations already generated by the |
| 102 | +frontend or change existing declaration bodies. |
| 103 | + |
| 104 | +Changes made through this extension point are **not checked** by the compiler. You must ensure that your changes don't break the compiler's |
| 105 | +expectations at this stage. For example, you might accidentally introduce an invalid type, an incorrect function reference, |
| 106 | +or a reference outside the correct scope. |
| 107 | + |
| 108 | +#### Explore backend plugin code |
| 109 | + |
| 110 | +You can explore the Kotlin serialization plugin code to see what backend plugin compiler code looks like in practice. |
| 111 | +For example, [`SerializableCompanionIrGenerator.kt`](https://github.com/JetBrains/kotlin/blob/master/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt) |
| 112 | +fills in missing bodies for key serializer members. One example is the [`generateChildSerializersGetter()`](https://github.com/JetBrains/kotlin/blob/9cfa558902abc13d245c825717026af63ef82dd2/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt#L242) |
| 113 | +function, which collects a list of `KSerializer` expressions and returns them in an array. |
| 114 | + |
| 115 | +#### Check your backend plugin code for problems |
| 116 | + |
| 117 | +You can check for problems in your backend plugin code in three ways: |
| 118 | + |
| 119 | +1. **Verify the IR** |
| 120 | + |
| 121 | + Build the IR tree and enable the `Xverify-ir` compiler option. This option has a performance impact on compilation speed, so use it only during testing. |
| 122 | + |
| 123 | +2. **Dump and compare IR output** |
| 124 | + |
| 125 | + Create a dump file after the IR lowering compilation stage with the `-Xphases-to-dump-before=ExternalPackageParentPatcherLowering` compiler option. For the JVM backend, configure the dump directory with the `-Xdump-directory=<your-file-directory>` compiler option. Write the expected code manually, generate another dump file, and compare the two to see if there are differences. |
| 126 | + |
| 127 | +3. **Debug the compiler code** |
| 128 | + |
| 129 | + In the `convertToIr.kt` file, add breakpoints in the `convertToIrAndActualize()` function and run the compiler in debug mode to get more detailed information during compilation. |
| 130 | + |
| 131 | +### Test your plugin |
| 132 | + |
| 133 | +Once you implement your plugin, test it thoroughly. The [Kotlin compiler plugin template](https://github.com/Kotlin/compiler-plugin-template) |
| 134 | +is already set up to use the [Kotlin compiler test framework](https://github.com/JetBrains/kotlin/blob/master/compiler/test-infrastructure/ReadMe.md). |
| 135 | +You can add tests in the following directories: |
| 136 | + |
| 137 | +* `compiler-plugin/testData` |
| 138 | +* `compiler-plugin/testData/box` for code generation tests |
| 139 | +* `compiler-plugin/testData/diagnostics` for diagnostic tests |
| 140 | + |
| 141 | +When a test runs, the framework: |
| 142 | + |
| 143 | +1. Parses the test source file. For example, [`anotherBoxTest.kt`](https://github.com/Kotlin/compiler-plugin-template/blob/master/compiler-plugin/testData/box/anotherBoxTest.kt) |
| 144 | +2. Builds the FIR and IR for each file. |
| 145 | +3. Writes these as textual dump files. For example, [`anotherBoxTest.fir.txt`](https://github.com/Kotlin/compiler-plugin-template/blob/master/compiler-plugin/testData/box/anotherBoxTest.fir.txt) and [`anotherBoxTest.fir.ir.txt`](https://github.com/Kotlin/compiler-plugin-template/blob/master/compiler-plugin/testData/box/anotherBoxTest.fir.ir.txt). |
| 146 | +4. Compares these files with previously created files, if they exist. |
| 147 | + |
| 148 | +You can use these files to check if any changes in the generated diff weren't intended. If there are no problems, the |
| 149 | +new dump files become your latest _golden_ files: an approved and trusted source that you can compare future changes against. |
| 150 | + |
| 151 | +### Get help |
| 152 | + |
| 153 | +If you run into issues developing a custom compiler plugin, reach out in [Kotlin Slack](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up) |
| 154 | +in the [#compiler](https://slack-chats.kotlinlang.org/c/compiler) channel. We can't promise a solution but we will try |
| 155 | +to help if we can. |
0 commit comments