diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java index d2df7310b22..8f93f278bf4 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java @@ -262,6 +262,18 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/privateMemberCall.kt"); } + @Test + @TestMetadata("secondClassContextReceiver.kt") + public void testSecondClassContextReceiver() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.kt"); + } + + @Test + @TestMetadata("singleClassContextReceiver.kt") + public void testSingleClassContextReceiver() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt"); + } + @Test @TestMetadata("valueParameter.kt") public void testValueParameter() throws Exception { diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.capturing.txt new file mode 100644 index 00000000000..510c7cc73ad --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.capturing.txt @@ -0,0 +1,4 @@ +ContainingClass[name: ; isMutated: false; displayText: this@Test] + context(R|Ctx1|, R|Ctx2|) + class Test : R|kotlin/Any| + R|Test| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.fragment.kt new file mode 100644 index 00000000000..7a4277b7c32 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.fragment.kt @@ -0,0 +1 @@ +useWithCtx2() \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.ir.txt new file mode 100644 index 00000000000..67c634f8154 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.ir.txt @@ -0,0 +1,14 @@ +MODULE_FRAGMENT name: + FILE fqName: fileName:/fragment.kt + CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.CodeFragment + CONSTRUCTOR visibility:public <> () returnType:.CodeFragment [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + FUN name:run visibility:public modality:FINAL <> (p0:.Test) returnType:kotlin.Int + VALUE_PARAMETER name:p0 index:0 type:.Test + EXPRESSION_BODY + BLOCK type=kotlin.Int origin=null + CALL 'public final fun useWithCtx2 ($context_receiver_0: .Ctx2): kotlin.Int declared in .SecondClassContextReceiverKt' type=kotlin.Int origin=null + $context_receiver_0: GET_FIELD 'FIELD FIELD_FOR_CLASS_CONTEXT_RECEIVER name:contextReceiverField1 type:.Ctx2 visibility:private [final]' type=.Ctx2 origin=null + receiver: GET_VAR 'p0: .Test declared in .CodeFragment.run' type=.Test origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.kt new file mode 100644 index 00000000000..c7a859dcb1c --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.kt @@ -0,0 +1,13 @@ +// !LANGUAGE: +ContextReceivers +class Ctx1 +class Ctx2 + +context(Ctx2) +fun useWithCtx2() = 3 + +context(Ctx1, Ctx2) +class Test { + fun foo() { + val x = 1 + } +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.txt new file mode 100644 index 00000000000..7e6d7645658 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: Test): int +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.capturing.txt new file mode 100644 index 00000000000..a0205d6b6b9 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.capturing.txt @@ -0,0 +1,4 @@ +ContainingClass[name: ; isMutated: false; displayText: this@Test] + context(R|Ctx1|) + class Test : R|kotlin/Any| + R|Test| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.fragment.kt new file mode 100644 index 00000000000..2625eaf32b0 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.fragment.kt @@ -0,0 +1 @@ +useWithCtx1() \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.ir.txt new file mode 100644 index 00000000000..dfaf6f1c4d6 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.ir.txt @@ -0,0 +1,14 @@ +MODULE_FRAGMENT name: + FILE fqName: fileName:/fragment.kt + CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.CodeFragment + CONSTRUCTOR visibility:public <> () returnType:.CodeFragment [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + FUN name:run visibility:public modality:FINAL <> (p0:.Test) returnType:kotlin.Int + VALUE_PARAMETER name:p0 index:0 type:.Test + EXPRESSION_BODY + BLOCK type=kotlin.Int origin=null + CALL 'public final fun useWithCtx1 ($context_receiver_0: .Ctx1): kotlin.Int declared in .SingleClassContextReceiverKt' type=kotlin.Int origin=null + $context_receiver_0: GET_FIELD 'FIELD FIELD_FOR_CLASS_CONTEXT_RECEIVER name:contextReceiverField0 type:.Ctx1 visibility:private [final]' type=.Ctx1 origin=null + receiver: GET_VAR 'p0: .Test declared in .CodeFragment.run' type=.Test origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt new file mode 100644 index 00000000000..42729f16b7c --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt @@ -0,0 +1,12 @@ +// !LANGUAGE: +ContextReceivers +class Ctx1 + +context(Ctx1) +fun useWithCtx1() = 3 + +context(Ctx1) +class Test { + fun foo() { + val x = 1 + } +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.txt new file mode 100644 index 00000000000..7e6d7645658 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: Test): int +} diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java index 7120e147ae5..a5736a2559d 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java @@ -162,6 +162,18 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/privateMemberCall.kt"); } + @Test + @TestMetadata("secondClassContextReceiver.kt") + public void testSecondClassContextReceiver() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/secondClassContextReceiver.kt"); + } + + @Test + @TestMetadata("singleClassContextReceiver.kt") + public void testSingleClassContextReceiver() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt"); + } + @Test @TestMetadata("valueParameter.kt") public void testValueParameter() throws Exception { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index 31ae7a462d2..51aba36cf7e 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -655,9 +655,12 @@ class Fir2IrVisitor( ): IrElement = whileAnalysing(session, thisReceiverExpression) { val calleeReference = thisReceiverExpression.calleeReference - callGenerator.injectGetValueCall(thisReceiverExpression, calleeReference)?.let { return it } - val boundSymbol = calleeReference.boundSymbol + + if (boundSymbol !is FirClassSymbol || calleeReference.contextReceiverNumber == -1) { + callGenerator.injectGetValueCall(thisReceiverExpression, calleeReference)?.let { return it } + } + when (boundSymbol) { is FirClassSymbol -> { // Object case @@ -680,7 +683,9 @@ class Fir2IrVisitor( val dispatchReceiver = conversionScope.dispatchReceiverParameter(irClass) if (dispatchReceiver != null) { return thisReceiverExpression.convertWithOffsets { startOffset, endOffset -> - val thisRef = IrGetValueImpl(startOffset, endOffset, dispatchReceiver.type, dispatchReceiver.symbol) + val thisRef = callGenerator.findInjectedValue(calleeReference)?.let { + callGenerator.useInjectedValue(it, calleeReference, startOffset, endOffset) + } ?: IrGetValueImpl(startOffset, endOffset, dispatchReceiver.type, dispatchReceiver.symbol) if (calleeReference.contextReceiverNumber != -1) { val constructorForCurrentlyGeneratedDelegatedConstructor = conversionScope.getConstructorForCurrentlyGeneratedDelegatedConstructor(irClass) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index ef44ad74aa7..319aa615bac 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -343,18 +343,27 @@ class CallAndReferenceGenerator( } internal fun injectGetValueCall(element: FirElement, calleeReference: FirReference): IrExpression? { - val injectedValue = extensions.findInjectedValue(calleeReference, conversionScope) + val injectedValue = findInjectedValue(calleeReference) if (injectedValue != null) { return element.convertWithOffsets { startOffset, endOffset -> - val type = injectedValue.typeRef.toIrType() - val origin = calleeReference.statementOrigin() - IrGetValueImpl(startOffset, endOffset, type, injectedValue.irParameterSymbol, origin) + useInjectedValue(injectedValue, calleeReference, startOffset, endOffset) } } return null } + internal fun useInjectedValue( + injectedValue: InjectedValue, + calleeReference: FirReference, + startOffset: Int, + endOffset: Int, + ): IrGetValueImpl { + val type = injectedValue.typeRef.toIrType() + val origin = calleeReference.statementOrigin() + return IrGetValueImpl(startOffset, endOffset, type, injectedValue.irParameterSymbol, origin) + } + fun convertToIrCall( qualifiedAccess: FirQualifiedAccessExpression, typeRef: FirTypeRef, @@ -552,7 +561,7 @@ class CallAndReferenceGenerator( } private fun injectSetValueCall(element: FirElement, calleeReference: FirReference, assignedValue: IrExpression): IrExpression? { - val injectedValue = extensions.findInjectedValue(calleeReference, conversionScope) + val injectedValue = findInjectedValue(calleeReference) if (injectedValue != null) { return element.convertWithOffsets { startOffset, endOffset -> val type = irBuiltIns.unitType @@ -564,6 +573,8 @@ class CallAndReferenceGenerator( return null } + internal fun findInjectedValue(calleeReference: FirReference) = extensions.findInjectedValue(calleeReference, conversionScope) + fun convertToIrSetCall(variableAssignment: FirVariableAssignment, explicitReceiverExpression: IrExpression?): IrExpression { try { val type = irBuiltIns.unitType