Compiler Plugins

WARNING: The Compiler Plugin API is very unstable and low-level.
Please consider other options whenever possible. Many use cases for compiler plugins can be addressed by tools like KSP or external linters.

The compiler provides several extension points that can modify its behavior.

Currently, no stable API for compiler plugins is available. This means there is no guarantee that a compiler plugin built against one version of the compiler will work with another version.
You need to either build the compiler plugin within your project or publish versions built specifically for all the compiler versions you need as dependencies.

Types of Extension Points

There are two types of extension points:

  1. Frontend extension points – Required for working with declarations that need to be visible during resolution.
  2. Backend extension points – Required for working with declaration bodies and private declarations.

Frontend Part

This part is specific to the version of the frontend being used.
If you need to work with language versions below 1.9, you must use the K1 Compiler Plugin API.

The frontend part of Compiler Plugin API allows you to:

  • Declare new publicly visible declarations.
  • Modify some properties of existing declarations.
  • Provide additional compiler warnings and errors relevant to your plugin.

For new plugins, you should work with the K2 Compiler Plugin API.

Backend Part

The backend part enables you to generate bodies of declarations created in frontend part and modify bodies of preexisting declarations.
This is achieved by changing the compiler's intermediate representation (IR), which is generated by compiler.

The primary entry point for this is the org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension::generate method.

Unfortunately, there is no comprehensive documentation on what specific IR represents or how it corresponds to Kotlin code.
The best way to understand which IR needs to be generated for your use case is as follows:

  1. Write the code that you want to generate manually.
  2. Observe the IR generated by the compiler for that code.
  3. Mimic the generated IR as closely as possible.

Here are some useful utilities for working with IR:

  • The IrElement.dump() extension function allows you to inspect IR by logging it or checking it in a debugger within your generate method.

  • You can dump IR to text files by passing the following options to the compiler:

    • -Xphases-to-dump-before=<phase-name>
    • -Xdump-directory=<path>

    You can check the name of the first backend compiler phase to dump IR for the JVM backend here, and in similar places for other backends. Currently, for the JVM backend, the name of the first phase is ExternalPackageParentPatcherLowering.

Examples

There are several plugins in the Kotlin repository, located in the plugins directory.
Also, there is a toy sandbox plugin which tests all existing extension points, and can be used as example for simple cases.