diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt index e8504dfb4ff..13d40a01fb7 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt @@ -24,13 +24,16 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirVariable import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.receiverType import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver internal class KtFirCompletionCandidateChecker( analysisSession: KtFirAnalysisSession, @@ -58,7 +61,7 @@ internal class KtFirCompletionCandidateChecker( possibleExplicitReceiver: KtExpression?, ): KtExtensionApplicabilityResult { val file = originalFile.getOrBuildFirFile(firResolveState) - val explicitReceiverExpression = possibleExplicitReceiver?.getOrBuildFirOfType(firResolveState) + val explicitReceiverExpression = possibleExplicitReceiver?.getMatchingFirExpressionForCallReceiver() val resolver = SingleCandidateResolver(firResolveState.rootModuleSession, file) val implicitReceivers = getImplicitReceivers(originalFile, nameExpression) for (implicitReceiverValue in implicitReceivers) { @@ -96,4 +99,20 @@ internal class KtFirCompletionCandidateChecker( yieldAll(towerDataContext.implicitReceiverStack) } } + + /** + * It is not enough to just call the `getOrBuildFirOfType` on [this] receiver expression, because for calls + * like `foo?.bar()` the receiver is additionally wrapped into `FirCheckedSafeCallSubject`, which is important + * for type-checks during resolve. + * + * @receiver PSI receiver expression in some qualified expression (e.g. `foo` in `foo?.bar()`, `a` in `a.b`) + * @return A FIR expression which most precisely represents the receiver for the corresponding FIR call. + */ + private fun KtExpression.getMatchingFirExpressionForCallReceiver(): FirExpression { + val psiWholeCall = this.getQualifiedExpressionForReceiver() + if (psiWholeCall !is KtSafeQualifiedExpression) return this.getOrBuildFirOfType(firResolveState) + + val firSafeCall = psiWholeCall.getOrBuildFirOfType(firResolveState) + return firSafeCall.checkedSubjectRef.value + } }