Resolving Calls
Call resolution answers "how is this expression executed at this site — which callable, applied to which receivers, type arguments, and value arguments?". Read Resolution Fundamentals first if you have not.
Entry points
For a given resolvable PSI element, prefer the specialized form — an extension on the concrete PSI type with a precisely typed return value:
When the PSI type is unknown, fall back to the generic form on KtResolvableCall via a safe cast:
For the rich form, use tryResolveCall() and the KaCallResolutionAttempt hierarchy.
Specialized methods
PSI element | Returns |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
What resolveCall() returns
The result is a KaSingleOrMultiCall? — sealed into two branches:
KaSingleCall describes a single resolved callable applied at this site. This is what you get for ordinary function calls, property accesses, callable references, annotation entries, supertype calls, and so on.
KaMultiCall describes a compound or desugared call that involves several sub-calls. This is what you get for
forloops, delegated properties, compound assignments (+=,++,--), and compound array access (a[i] += v).
The type system tells you which branch you are on. Many specializations narrow the return further — for instance KtForExpression.resolveCall(): KaForLoopCall? already commits to the multi-call branch.
Working with a KaSingleCall
Every KaSingleCall<S, C> exposes the call-site context inline — no partiallyAppliedSymbol wrapper to drill through:
Concrete subtypes add their own fields. KaFunctionCall<S> adds the argument mappings:
KaVariableAccessCall adds the access kind: call.kind is a KaVariableAccessCall.Kind.Read or KaVariableAccessCall.Kind.Write — the latter exposes value: KtExpression?, the right-hand side of the assignment. The boolean call.isContextSensitive indicates whether context-sensitive resolution was used.
KaCallableReferenceCall is the cleanest example of the new shape: it extends KaSingleCall<S, C> only — no legacy base. A callable reference does not invoke the callable, so there is no valueArgumentMapping and no read/write kind — only the signature, bound receivers, and type arguments.
Use call is KaImplicitInvokeCall to detect implicit f("...") invocations on values with a functional type. The legacy KaSimpleFunctionCall.isImplicitInvoke boolean is deprecated.
KtNameReferenceExpression on a constructor reference
The call counterpart of a name reference can return a different symbol from the symbol counterpart. For MyClass() the name MyClass literally points to the class (resolveSymbol() returns the KaClassLikeSymbol), but the call wraps the constructor that is actually invoked:
Pick the one that matches your question: who is invoked here → resolveCall(); what does the name literally mean → resolveSymbol().
Compound and desugared calls
Some Kotlin constructs desugar into several operator calls. The Analysis API exposes the assembled multi-call and the individual sub-calls through dedicated KaMultiCall subtypes. The names of the sub-calls match what the language spec calls them.
for loops
A for loop desugars into iterator(), hasNext(), and next(). The call is a KaForLoopCall:
The richer form is KtForExpression.tryResolveCall(): KaForLoopCallResolutionAttempt?, which exposes each sub-call attempt separately — if one of iterator()/hasNext()/next() fails to resolve, the others remain available.
Delegated properties
A delegated property desugars into getValue(), optionally setValue() (for var), and optionally provideDelegate(). The call is a KaDelegatedPropertyCall:
The matching attempt type is KaDelegatedPropertyCallResolutionAttempt.
Compound variable access (+=, ++, --)
A compound assignment or unary increment on a variable resolves to a KaCompoundVariableAccessCall:
Compound array access (a[i] += v)
The most elaborate compound is the array form, represented by KaCompoundArrayAccessCall. It involves both get and set, plus the operator function:
The matching attempt is KaCompoundArrayAccessCallResolutionAttempt with getterCallAttempt, operationCallAttempt, and setterCallAttempt.
A flat view of the sub-calls
Every KaMultiCall also exposes calls: List<KaSingleCall<*, *>>. If you do not care which sub-call is which, this is the simplest accessor:
Plain form vs try form
Use resolveCall() when you only need a valid result; resolution failure becomes null. Use tryResolveCall() when you want the diagnostic and the candidate calls the compiler considered. The richer form returns a KaCallResolutionAttempt — see KaCallResolutionAttempt for the full hierarchy and helpers.
For compound expressions, the multi-attempt types (KaForLoopCallResolutionAttempt, KaDelegatedPropertyCallResolutionAttempt, KaCompoundVariableAccessCallResolutionAttempt, KaCompoundArrayAccessCallResolutionAttempt) keep each sub-call attempt addressable, so you can still inspect the sub-calls that did resolve when another sub-call failed.