JetBrains/kotlin
Patterns and conventions
This page documents recurring patterns in the codebase: naming prefixes, generated-vs-handwritten boundaries, error handling, and the conventions that span multiple subsystems. New contributors save time by recognizing these patterns before diving into a specific area.
Naming prefixes
The repository uses several short prefix conventions that act as a navigation aid. Knowing the prefix tells you what subsystem a type belongs to:
| Prefix | Meaning | Example | Lives in |
|---|---|---|---|
Kt |
PSI element (Kotlin AST node) | KtFile, KtClass, KtNamedFunction |
compiler/psi/ |
Fir |
K2 / FIR IR node or service | FirRegularClass, FirResolvePhase, FirSymbol |
compiler/fir/ |
Ka |
Analysis API public type | KaSession, KaSymbol, KaCallableSymbol |
analysis/analysis-api*/ |
Ir |
Backend IR node | IrClass, IrFunction, IrCall |
compiler/ir/ir.tree/ |
LL |
Low-level FIR API (IDE-facing, on-demand) | LLFirSession, LLFirResolveSession |
analysis/low-level-api-fir/ |
Cone |
K2 type system primitive | ConeKotlinType, ConeClassLikeType |
compiler/fir/cones/ |
Js |
JS backend / AST | JsExpression, JsStatement |
js/js.ast/ |
Wasm |
Wasm backend / IR | WasmModule, WasmFunction |
wasm/wasm.ir/ |
When you see KaFirX, that's an Analysis API type (Ka) with a FIR-backed implementation (Fir).
K1 / K2 dual-implementation pattern
Many language features must be implemented twice — once for K1 and once for K2 — until K1 is fully retired:
- K1 diagnostic checkers in
compiler/frontend/.../checkers - K2 diagnostic checkers in
compiler/fir/checkers/ - K1 → IR via
compiler/ir/ir.psi2ir/(PSI + descriptors → IR) - K2 → IR via
compiler/fir/fir2ir/(FIR → IR) - Analysis API K1 backend in
analysis/analysis-api-fe10/ - Analysis API K2 backend in
analysis/analysis-api-fir/
When adding a new language feature, expect to do work on both sides for the deprecation window, with K2 as the primary target.
Generated code
A non-trivial fraction of the source tree is generated. The convention is to keep generated outputs in a gen/ directory next to handwritten src/:
compiler/fir/checkers/
├── src/ # handwritten
└── gen/ # generated by checkers-component-generatorGenerators themselves live near their outputs (e.g., compiler/fir/checkers/checkers-component-generator/) or in the top-level generators/ directory for cross-cutting generation. The repo also has standalone generators for FIR tree definitions, IR tree definitions, compiler arguments, and Analysis API diagnostics.
The rule: edit the generator and the input definition, not the generated output. Generated files start with a header comment indicating their generator; some have Generated.kt/Generated.java suffixes.
Phase invariants in FIR
K2's correctness depends on a small but strict invariant in compiler/fir/resolve:
Within phase B that follows phase A, every FIR element visible to B has been resolved through phase A.
(See compiler/AGENTS.md for the canonical statement.) This is checked in tests; violating it is one of the most common causes of K2 crashes. When introducing a new phase or moving work between phases, audit every FirSymbol/FirElement access for what phase must have completed.
Error handling
The compiler distinguishes:
- Diagnostics — user-visible compile errors and warnings. Reported via
DiagnosticReporter(K2) orDiagnosticSink(K1). Always have a stable error code (KaFirDiagnostic,FirError*) and a localized message in*ErrorsDefaultMessages.kt. - Internal errors —
IllegalStateException,IllegalArgumentException,errorWithAttachment(...)(incompiler/utils/compiler/util). These crash the compiler and indicate a bug, not user error. Wrap user-facing diagnostics around plausible-but-recoverable conditions; reserve internal exceptions for "this should not happen".
errorWithAttachment includes structured attachment information (FIR/IR dumps of the offending element, file paths, etc.) in crash reports — it's the preferred way to crash, because the report is rich enough to debug from.
Symbol resolution
The K2 resolve pipeline is a series of incremental refinements over FirSymbol. The rule is: never call code that needs phase N+1 information from a checker running in phase N. The Analysis API's low-level FIR (analysis/low-level-api-fir/) sits on top of the same machinery and exposes on-demand resolve via LLFirResolveSession.
Coding style
The Kotlin team follows the Kotlin coding conventions (https://kotlinlang.org/docs/coding-conventions.html), enforced via the IntelliJ formatter and the .editorconfig at the repo root. Highlights:
- 4-space indentation in Kotlin and Java sources.
- 120-column soft limit (some files hold to 100, especially older code).
- One top-level declaration per file is encouraged but not enforced.
- Internal types use
internalrather than package-private when they need to be hidden from external module consumers.
The Native subtree (kotlin-native/) follows its own clang-format style for C/C++ (kotlin-native/.clang-format).
Multiplatform code organization
Standard library and any Kotlin Multiplatform module follow a standard source-set layout:
src/
├── commonMain/ # shared expect declarations + common implementations
├── jvmMain/ # JVM actuals
├── jsMain/ # JS actuals
├── nativeMain/ # Native actuals
├── wasmMain/ # Wasm actuals
└── ...Test/ # corresponding test source setsThe convention used inside libraries/stdlib/ is slightly different (top-level common/, jvm/, js/, native-wasm/, wasm/ directories, each with their own source roots) due to the bootstrapping needs of the stdlib build.
Public API surface management
Modules with a public API (KGP, KGP-API, BTAPI, etc.) commit a .api file capturing every exported symbol. Changing public API → updating the .api file in the same commit. CI fails if they drift.
Files to know:
libraries/tools/kotlin-gradle-plugin/api/all/kotlin-gradle-plugin.apicompiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api- klib ABI dumps under
libraries/tools/abi-validation/
Internal vs experimental
The codebase distinguishes:
@Experimental/@RequiresOptInannotated APIs — usable but require opt-in.@InternalKotlinGradlePluginApi,@InternalKotlinNativeApi, etc. — usable across modules in this repo, not stable for external use.internalmodifier — limited to the module.
Public APIs without an opt-in are stable. Don't change them without a deprecation cycle.
Cross-references
- Testing — test infrastructure and runners
- Debugging — diagnostics, dumps, tracing
- Tooling — build, lint, generators
- Compiler architecture — frontends, IR, backends
- Analysis API — IDE-facing API
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.