Resolution Fundamentals
This page introduces the resolution API of the Analysis API: the entry points used to ask, given a piece of Kotlin PSI, what it refers to or how it is called at that exact site. Read this before the more detailed how-to pages, as it establishes the mental model and the rules that the rest of the documentation builds on.
Two questions, two APIs
Resolution in the Analysis API answers two independent questions about a piece of Kotlin PSI.
Symbol resolution answers "what declaration does this PSI element refer to?" — the literal target of the element. For the name reference MyClass in val c = MyClass(), symbol resolution returns the class MyClass — that is what the name literally points to. Symbol resolution applies to almost every resolvable element, including those that are not calls (type references, package qualifiers, labels, supertypes, class literals, and so on).
Call resolution answers "how is this expression executed at this site — which callable, applied to which receivers, type arguments, and value arguments?". For the same MyClass() expression, call resolution returns a constructor call — because the class itself cannot be invoked (it is a type), it is the constructor that is called. Call resolution only applies to expressions and elements that actually represent an executed callable.
The two views are deliberately separate. Use the one that matches your task:
You want navigation, find-usages, or a textual rename target → symbol resolution.
You want to inspect the call site itself — selected overload, arguments, receivers, type arguments, smart-casts → call resolution.
Not every PSI element has a call. KtTypeReference, KtUserType, KtNullableType, KtLabelReferenceExpression, and similar expose only symbol resolution. Compound expressions (for-loops, delegated properties, compound assignments, compound array access) expose multiple sub-calls through the call API.
Resolvable PSI elements
Resolvable PSI elements are marked by two interfaces in org.jetbrains.kotlin.resolution:
KtResolvable marks an element that can be resolved to a symbol; KtResolvableCall marks an element that can additionally be resolved to a call. For example, KtReferenceExpression : KtResolvable, and call-bearing elements like KtCallElement implement KtResolvableCall (and therefore KtResolvable).
In practice you rarely use these names directly — you reach for the specialized form on the concrete PSI type.
How to call the resolution API
The resolution API is available inside a KaSession — that is, inside an analyze { ... } block, or inside a function with a context(_: KaSession) declaration. The resolution functions are exposed both as member-extensions on KaSession and as top-level extension functions that can be imported and used wherever a KaSession is in scope.
The canonical entry point is the specialized form on the concrete PSI type:
Each specialized method returns a precisely typed result — for instance, KtAnnotationEntry.resolveSymbol() returns KaConstructorSymbol?, and KtCallElement.resolveCall() returns KaFunctionCall<*>?. Prefer this form whenever the PSI type is known: it is shorter, type-safe, and discoverable through IDE completion on the element.
Working with a generic PsiElement
When the element type is genuinely unknown (for instance, you only have a PsiElement from a reference traversal), use a safe cast to the marker interface and then call the generic entry point. Never use an unchecked as cast.
Plain form vs try form
Every resolution flavor exists in two forms:
The plain form (
resolveSymbol,resolveSymbols,resolveCall) returns the happy-path result — a symbol or aKaSingleOrMultiCall, ornullif resolution did not succeed. Use it when you only care about a valid result and want failed/ambiguous resolutions to be silently dropped (typical for inspections that must not produce false positives).The try form (
tryResolveSymbols,tryResolveCall) returns a richer attempt — either a success with the result, or an error carrying a diagnostic and candidate symbols/calls. Use it when you want every piece of information the compiler considered, including failed attempts and partial results (typical for navigation, find-usages, and any tooling that wants to do a best-effort match on red code).
See KaSymbolResolutionAttempt and KaCallResolutionAttempt for the full attempt hierarchy.
A complete example
The following is a complete, runnable example using the resolution API on a KtSimpleNameExpression.
Subsequent pages keep snippets compact: they assume an analyze { } block is in scope and @OptIn(KaExperimentalApi::class) is applied somewhere up the call chain.
Where to go next
Resolving Symbols — symbol resolution in detail, with the table of specialized methods and the
KaSymbolResolutionAttemptflow.Resolving Calls — call resolution, the
KaSingleOrMultiCallhierarchy, and compound calls.Resolution Candidates — collecting overload candidates regardless of resolution outcome.
Migrating from the Legacy Resolution API — mapping from the old
mainReference/resolveToCall/KaCallInfoAPI to the new one.