[FIR IDE] Use correct extension receiver FIR expression in completion

`getOrBuildFir` does not record `FirCheckedSafeCallSubject`
expression, and it would be impossible to get them anyway
since there are no PSI corresponding to "not null receiver"
in expressions like `foo?.bar` - there is only `foo`,
which is considered nullable by FIR

`FirCheckedSafeCallSubject` wrapper has non-null type during
resolve, and it becomes very important in
`KtFirCompletionCandidateChecker`. If we loose the wrapper,
then it would be impossible to call extensions on nullable
types using `?.` syntax

^KTIJ-21021 Fixed
This commit is contained in:
Roman Golyshev
2022-02-07 17:57:12 +03:00
committed by Space
parent f32c355190
commit 42010282d5
@@ -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<FirExpression>(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<FirExpression>(firResolveState)
val firSafeCall = psiWholeCall.getOrBuildFirOfType<FirSafeCallExpression>(firResolveState)
return firSafeCall.checkedSubjectRef.value
}
}