JetBrains/kotlin
PSI and parser
Active contributors: Dmitriy Novozhilov, Kirill Rakhman
compiler/psi/ holds the lexer, parser, and PSI (Program Structure Interface) types for Kotlin. Every Kotlin source file that enters the compiler is first turned into a KtFile here, and both K1 and K2 build their representations on top of that.
Purpose
Parse Kotlin source text into a structured, IDE-friendly tree of PSI nodes. The PSI tree is intentionally close to the source: comments, whitespace, and trivia are preserved so that the IDE plugin can use the same trees for editing and refactoring.
Directory layout
compiler/psi/
├── psi/ Core PSI types (Kt*)
├── psi-api/ Public-facing PSI surface
├── parser/ Kotlin grammar / parser implementation
├── lexer/ Kotlin lexer
├── stub/ Indexing / stubbed forms used by IDE indexes
└── light-tree/ Lightweight tree representation for fast pathsKey abstractions
| Type | Where | Role |
|---|---|---|
KtFile |
compiler/psi/.../KtFile.kt |
Root node for a Kotlin source file. |
KtClass, KtNamedFunction, KtProperty, KtCallExpression, ... |
compiler/psi/.../psi/ |
Declaration and expression nodes. |
KotlinLexer |
compiler/psi/.../lexer/KotlinLexer.kt |
Token-producing lexer. |
KotlinParser |
compiler/psi/.../parser/KotlinParser.kt |
Top-level parser entry point. |
KtElement |
compiler/psi/.../KtElement.kt |
Common supertype of all Kotlin PSI elements. |
PSI is built on top of IntelliJ's psi infrastructure (com.intellij.psi), which is pulled in via intellij-core. As a result the PSI types feel very IDE-flavored.
Two tree shapes
There are actually two PSI-like representations:
- Full PSI (
KtFileand its descendants) — heavyweight, IDE-friendly, retains all whitespace and trivia. Used by the IDE plugin and by any path that needs to read or modify source. - Light tree (
compiler/psi/light-tree/) — a much cheaper tree representation used by parts of the compiler that don't need full PSI. K2 has a "light tree" path that builds raw FIR directly from this representation, skipping a heavy PSI allocation.
Both share the same lexer and parser front-end; they differ in what tree structure they materialize.
Stubs
compiler/psi/stub/ defines the stub form of declarations — a compact, indexable summary used by IntelliJ's indexes. Stub-aware PSI lets the IDE answer "show me all classes named Foo" without parsing every file.
How K1 and K2 use PSI
Both frontends start from PSI:
- K1 treats PSI as the source of truth for resolution.
BindingContextkeys are PSI elements (KtCallExpression,KtReferenceExpression, ...). - K2 builds raw FIR from PSI in
compiler/fir/raw-fir/and then walks FIR alone. The PSI is retained as a back-reference (fir.source) so diagnostics can still be reported on a PSI element.
Light classes
Java-aware tooling (the IDEA Java plugin, Lombok, etc.) often wants a Java-style view of a Kotlin declaration. PSI grew "light classes" — synthesized PsiClass views over Kotlin elements:
compiler/light-classes/— the K1 implementation.analysis/symbol-light-classes/— the K2 / Analysis API implementation.
Both produce the same Java PSI shapes from different inputs.
Stability
The PSI surface is very stable; external code (notably the IntelliJ Kotlin plugin) reads it heavily. Adding a new PSI element requires propagating through both K1 and K2 raw-FIR builders, and through the light-class generators. Read compiler/psi/AGENTS.md before changing the API.
Integration points
- Produced by the lexer/parser from Kotlin source.
- Consumed by K1 (
compiler/frontend/), the K2 raw-FIR builder (compiler/fir/raw-fir/), light classes (compiler/light-classes/,analysis/symbol-light-classes/), and IDE tooling. - Indexed in stub form by IntelliJ.
See frontend-fir.md, frontend-k1.md, and the analysis API for the consumers.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.