Diagnostics
Diagnostics are the errors, warnings, and informational messages that compiler checkers report on Kotlin code. The Analysis API exposes them through a single entry point, diagnostics(), which returns a KaDiagnostics — a query over the diagnostics of a KtElement.
The entry point
diagnostics() is a top-level extension function on KtElement declared in the org.jetbrains.kotlin.analysis.api.diagnostics package with a KaSession context parameter. Import it and use it wherever a session is available:
The query covers the element together with its subtree, so its scope is chosen by the element it is asked about:
Defaults
By default, element.diagnostics() yields exactly the diagnostics the compiler reports for the element and its subtree. Each aspect of the query has a modifier which overrides that default:
Aspect | Default | Modifier |
|---|---|---|
Checker kinds | Only common compiler checkers | |
Suppressed diagnostics | Not yielded | |
Scope | The element together with its subtree |
Modifiers are chained before the iteration begins:
The query is immutable: each modifier returns a new KaDiagnostics instead of mutating the receiver, so a query can be kept around and adjusted for several use sites. Modifiers do not accumulate — applying the same modifier twice keeps the last value, and withCheckers() replaces the requested kinds rather than adding to them.
See KaDiagnostics for the exact semantics of every modifier.
Laziness
diagnostics() and its modifiers only build a description of the query. The analysis runs during the iteration, and only as far as the iteration goes, so the cost of a query is proportional to how much of it is consumed.
A query which stops early analyzes only what it had to. The following code stops at the first error, however many diagnostics the element has:
toList(), on the other hand, always analyzes the whole scope. The same holds for anything that has to see every diagnostic, such as count() or groupBy {}.
Iterating a query does not consume it: the same instance may be iterated again, and adjusted with modifiers in between.
Handling a diagnostic
The query yields KaDiagnosticWithPsi instances, which expose the reported message, its severity, and the PSI it is attached to:
Selecting diagnostics by kind
The generated KaFirDiagnostic hierarchy has an interface per diagnostic kind, which exposes the parameters of that diagnostic as typed properties:
This is usually the form to prefer, but it comes with no compatibility guarantees: KaFirDiagnostic and every diagnostic in it are annotated @KaUnstableDiagnosticApi. Diagnostics mirror the ones reported by the compiler, and the Analysis API does not control that set — a diagnostic may be renamed, split, merged, gain or lose parameters, or be removed, without a deprecation cycle.
Where the code has to keep working across Analysis API versions, compare the factoryName instead:
The two differ in how they fail. A renamed or removed diagnostic breaks the typed form at compile time, which is loud but has to be migrated; the factoryName comparison keeps compiling and silently stops matching anything.
Suppressed diagnostics
A diagnostic which is suppressed at its use site, for example by a @Suppress annotation, is not reported by the compiler. Such diagnostics are filtered out by default, and ignoreSuppressed(false) yields them as well, each marked with isSuppressed:
Suppressed diagnostics should not be presented to the user as if the compiler reported them. They are meant for tooling which analyzes the suppressions themselves — an inspection reporting a redundant @Suppress annotation, for instance. Collecting them requires no additional analysis.
Performance
Every requested checker kind runs its checkers, so requesting more kinds means more work. Prefer listing the kinds you need over
KaDiagnosticCheckerKind.ALL.Ask about the narrowest element that covers your interest. A subtree query only computes the file structure needed for that subtree, so it is cheaper than collecting the diagnostics of the whole file and filtering them by PSI afterwards.
Prefer short-circuiting sequence operations over materializing the query with
toList().
Where to go next
KaDiagnostics — the query interface and the precise semantics of its modifiers.
KaDiagnosticCheckerKind — the available checker kinds and what each of them reports.
KaDiagnostic and KaDiagnosticWithPsi — the diagnostic itself.
Legacy Diagnostics API — mapping from the former
collectDiagnostics/KaDiagnosticCheckerFilterAPI to the query.