[FIR] Fix NO_RECEIVER_ALLOWED

If we do create an implicit invoke call, and
then put the receiver into the argument list,
we should mark it.
This commit is contained in:
Nikolay Lunyak
2024-01-22 12:34:39 +02:00
committed by Space Team
parent eb11901d43
commit 9b786d35f8
12 changed files with 38 additions and 9 deletions
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.getChild import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
@@ -301,6 +300,8 @@ private fun mapInapplicableCandidateError(
is MultipleContextReceiversApplicableForExtensionReceivers -> is MultipleContextReceiversApplicableForExtensionReceivers ->
FirErrors.AMBIGUOUS_CALL_WITH_IMPLICIT_CONTEXT_RECEIVER.createOn(qualifiedAccessSource ?: source) FirErrors.AMBIGUOUS_CALL_WITH_IMPLICIT_CONTEXT_RECEIVER.createOn(qualifiedAccessSource ?: source)
is NoReceiverAllowed -> FirErrors.NO_RECEIVER_ALLOWED.createOn(qualifiedAccessSource ?: source)
is NoApplicableValueForContextReceiver -> is NoApplicableValueForContextReceiver ->
FirErrors.NO_CONTEXT_RECEIVER.createOn( FirErrors.NO_CONTEXT_RECEIVER.createOn(
qualifiedAccessSource ?: source, qualifiedAccessSource ?: source,
@@ -1074,6 +1074,7 @@ abstract class AbstractRawFirBuilder<T>(val baseSession: FirSession, val context
arguments.add(receiver) arguments.add(receiver)
arguments.addAll(firSelector.arguments) arguments.addAll(firSelector.arguments)
} }
isCallWithExplicitReceiver = true
calleeReference = firSelector.calleeReference calleeReference = firSelector.calleeReference
} }
} else { } else {
@@ -76,6 +76,18 @@ internal object CheckExplicitReceiverConsistency : ResolutionStage() {
object CheckExtensionReceiver : ResolutionStage() { object CheckExtensionReceiver : ResolutionStage() {
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) { override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
val callSite = callInfo.callSite
if (callSite is FirImplicitInvokeCall) {
val isInvokeFromExtensionFunctionType = candidate.isInvokeFromExtensionFunctionType
val isImplicitInvokeCallWithExplicitReceiver = callSite.isCallWithExplicitReceiver
// We do allow automatic conversion in the other direction, though
if (!isInvokeFromExtensionFunctionType && isImplicitInvokeCallWithExplicitReceiver) {
sink.reportDiagnostic(NoReceiverAllowed)
}
}
val expectedReceiverType = candidate.getExpectedReceiverType() ?: return val expectedReceiverType = candidate.getExpectedReceiverType() ?: return
val expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverType.type) val expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverType.type)
@@ -506,10 +518,7 @@ internal object CheckArguments : CheckerStage() {
candidate.symbol.lazyResolveToPhase(FirResolvePhase.STATUS) candidate.symbol.lazyResolveToPhase(FirResolvePhase.STATUS)
val argumentMapping = val argumentMapping =
candidate.argumentMapping ?: error("Argument should be already mapped while checking arguments!") candidate.argumentMapping ?: error("Argument should be already mapped while checking arguments!")
val isInvokeFromExtensionFunctionType = candidate.isInvokeFromExtensionFunctionType
val isInvokeFromExtensionFunctionType = candidate.explicitReceiverKind == DISPATCH_RECEIVER
&& candidate.dispatchReceiver?.resolvedType?.isExtensionFunctionType == true
&& (candidate.symbol as? FirNamedFunctionSymbol)?.name == OperatorNameConventions.INVOKE
for ((index, argument) in callInfo.arguments.withIndex()) { for ((index, argument) in callInfo.arguments.withIndex()) {
candidate.resolveArgument( candidate.resolveArgument(
@@ -543,6 +552,11 @@ internal object CheckArguments : CheckerStage() {
} }
} }
private val Candidate.isInvokeFromExtensionFunctionType: Boolean
get() = explicitReceiverKind == DISPATCH_RECEIVER
&& dispatchReceiver?.resolvedType?.isExtensionFunctionType == true
&& (symbol as? FirNamedFunctionSymbol)?.name == OperatorNameConventions.INVOKE
internal fun Candidate.shouldHaveLowPriorityDueToSAM(bodyResolveComponents: BodyResolveComponents): Boolean { internal fun Candidate.shouldHaveLowPriorityDueToSAM(bodyResolveComponents: BodyResolveComponents): Boolean {
if (!usesSAM || isJavaApplicableCandidate()) return false if (!usesSAM || isJavaApplicableCandidate()) return false
return argumentMapping!!.values.any { return argumentMapping!!.values.any {
@@ -142,6 +142,8 @@ class DslScopeViolation(val calleeSymbol: FirBasedSymbol<*>) : ResolutionDiagnos
class MultipleContextReceiversApplicableForExtensionReceivers : ResolutionDiagnostic(INAPPLICABLE) class MultipleContextReceiversApplicableForExtensionReceivers : ResolutionDiagnostic(INAPPLICABLE)
object NoReceiverAllowed : ResolutionDiagnostic(INAPPLICABLE)
class NoApplicableValueForContextReceiver( class NoApplicableValueForContextReceiver(
val expectedContextReceiverType: ConeKotlinType val expectedContextReceiverType: ConeKotlinType
) : ResolutionDiagnostic(INAPPLICABLE) ) : ResolutionDiagnostic(INAPPLICABLE)
@@ -36,6 +36,7 @@ abstract class FirImplicitInvokeCall : FirFunctionCall() {
abstract override val argumentList: FirArgumentList abstract override val argumentList: FirArgumentList
abstract override val calleeReference: FirNamedReference abstract override val calleeReference: FirNamedReference
abstract override val origin: FirFunctionCallOrigin abstract override val origin: FirFunctionCallOrigin
abstract val isCallWithExplicitReceiver: Boolean
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R =
visitor.visitImplicitInvokeCall(this, data) visitor.visitImplicitInvokeCall(this, data)
@@ -35,6 +35,7 @@ open class FirImplicitInvokeCallBuilder : FirAbstractFunctionCallBuilder, FirAnn
override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf() override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf()
override var argumentList: FirArgumentList = FirEmptyArgumentList override var argumentList: FirArgumentList = FirEmptyArgumentList
override lateinit var calleeReference: FirNamedReference override lateinit var calleeReference: FirNamedReference
open var isCallWithExplicitReceiver: Boolean = false
override fun build(): FirImplicitInvokeCall { override fun build(): FirImplicitInvokeCall {
return FirImplicitInvokeCallImpl( return FirImplicitInvokeCallImpl(
@@ -49,6 +50,7 @@ open class FirImplicitInvokeCallBuilder : FirAbstractFunctionCallBuilder, FirAnn
nonFatalDiagnostics.toMutableOrEmpty(), nonFatalDiagnostics.toMutableOrEmpty(),
argumentList, argumentList,
calleeReference, calleeReference,
isCallWithExplicitReceiver,
) )
} }
@@ -38,6 +38,7 @@ internal class FirImplicitInvokeCallImpl(
override var nonFatalDiagnostics: MutableOrEmptyList<ConeDiagnostic>, override var nonFatalDiagnostics: MutableOrEmptyList<ConeDiagnostic>,
override var argumentList: FirArgumentList, override var argumentList: FirArgumentList,
override var calleeReference: FirNamedReference, override var calleeReference: FirNamedReference,
override val isCallWithExplicitReceiver: Boolean,
) : FirImplicitInvokeCall() { ) : FirImplicitInvokeCall() {
override val origin: FirFunctionCallOrigin = FirFunctionCallOrigin.Operator override val origin: FirFunctionCallOrigin = FirFunctionCallOrigin.Operator
@@ -227,7 +227,10 @@ object BuilderConfigurator : AbstractFirBuilderConfigurator<FirTreeBuilder>(FirT
} }
} }
builder(integerLiteralOperatorCall, config = configurationForFunctionCallBuilder) builder(integerLiteralOperatorCall, config = configurationForFunctionCallBuilder)
builder(implicitInvokeCall, config = configurationForFunctionCallBuilder) builder(implicitInvokeCall) {
configurationForFunctionCallBuilder()
defaultFalse("isCallWithExplicitReceiver")
}
builder(getClassCall) { builder(getClassCall) {
parents += callBuilder parents += callBuilder
@@ -408,6 +408,10 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
shouldBeAnInterface() shouldBeAnInterface()
} }
implicitInvokeCall.configure {
+booleanField("isCallWithExplicitReceiver")
}
constructor.configure { constructor.configure {
+annotations +annotations
+symbol("FirConstructorSymbol") +symbol("FirConstructorSymbol")
@@ -30,5 +30,5 @@ fun rain() {
5.(<!UNRESOLVED_REFERENCE!>::<!UNRESOLVED_REFERENCE!>x<!><!>)().inv() 5.(<!UNRESOLVED_REFERENCE!>::<!UNRESOLVED_REFERENCE!>x<!><!>)().inv()
5.(Int::x)().inv() <!NO_RECEIVER_ALLOWED!>5.(Int::x)()<!>.inv()
} }
@@ -1,5 +1,5 @@
// ISSUE: KT-64891 // ISSUE: KT-64891
fun test(f: (Int) -> Int) { fun test(f: (Int) -> Int) {
2.(f)() <!NO_RECEIVER_ALLOWED!>2.(f)()<!>
} }
@@ -7,5 +7,5 @@ fun test1(f: String.() -> Unit) {
fun test2(f: (Int) -> Int) { fun test2(f: (Int) -> Int) {
1.<!UNRESOLVED_REFERENCE!>f<!>(2) 1.<!UNRESOLVED_REFERENCE!>f<!>(2)
2.(f)(<!TOO_MANY_ARGUMENTS!>2<!>) <!NO_RECEIVER_ALLOWED!>2.(f)(<!TOO_MANY_ARGUMENTS!>2<!>)<!>
} }