diff --git a/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java b/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java index f5d0976fc8b..5bc66151416 100644 --- a/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java +++ b/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java @@ -353,6 +353,11 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/lambdaAndAnonymousFunction.kt"); } + @TestMetadata("localDeclarationWithExpression.kt") + public void testLocalDeclarationWithExpression() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt"); + } + @TestMetadata("locals.kt") public void testLocals() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/locals.kt"); diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index d7415df6607..ab6c4737ac3 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -210,7 +210,7 @@ open class RawFirBuilder( private fun KtElement?.toFirExpression( errorReason: String, - kind: DiagnosticKind = DiagnosticKind.ExpressionExpected, + kind: DiagnosticKind = DiagnosticKind.ExpressionExpected ): FirExpression { if (stubMode) { return buildExpressionStub() @@ -1401,7 +1401,7 @@ open class RawFirBuilder( else -> initializer.toFirExpression("Should have initializer") } - val delegateExpression by lazy { delegate?.expression } + val propertySource = toFirSourceElement() return buildProperty { @@ -1417,27 +1417,31 @@ open class RawFirBuilder( if (this@toFirProperty.isLocal) { isLocal = true symbol = FirPropertySymbol(propertyName) - val delegateBuilder = delegateExpression?.let { - FirWrappedDelegateExpressionBuilder().apply { - source = it.toFirSourceElement(FirFakeSourceElementKind.WrappedDelegate) - expression = it.toFirExpression("Incorrect delegate expression") - } - } - status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL).apply { isLateInit = hasModifier(LATEINIT_KEYWORD) } - val receiver = delegateExpression?.toFirExpression("Incorrect delegate expression") - generateAccessorsByDelegate( - delegateBuilder, - baseModuleData, - ownerRegularOrAnonymousObjectSymbol = null, - ownerRegularClassTypeParametersCount = null, - isExtension = false, - stubMode = stubMode, - receiver = receiver - ) + if (hasDelegate()) { + fun extractDelegateExpression() = + this@toFirProperty.delegate?.expression.toFirExpression("Incorrect delegate expression") + + val delegateBuilder = FirWrappedDelegateExpressionBuilder().apply { + val delegateFirExpression = extractDelegateExpression() + source = delegateFirExpression.source?.fakeElement(FirFakeSourceElementKind.WrappedDelegate) + expression = delegateFirExpression + } + + generateAccessorsByDelegate( + delegateBuilder, + baseModuleData, + ownerRegularOrAnonymousObjectSymbol = null, + ownerRegularClassTypeParametersCount = null, + isExtension = false, + stubMode = stubMode, + //TODO This expression should be the same for wrapper and receiver + receiver = extractDelegateExpression() + ) + } } else { isLocal = false receiverTypeRef = receiverTypeReference.convertSafe() @@ -1445,21 +1449,6 @@ open class RawFirBuilder( dispatchReceiverType = currentDispatchReceiverType() extractTypeParametersTo(this, symbol) withCapturedTypeParameters(true, this.typeParameters) { - val delegateBuilder = if (hasDelegate()) { - FirWrappedDelegateExpressionBuilder().apply { - source = - if (stubMode) null else delegateExpression?.toFirSourceElement(FirFakeSourceElementKind.WrappedDelegate) - expression = when (mode) { - BodyBuildingMode.NORMAL -> delegateExpression.toFirExpression("Should have delegate") - BodyBuildingMode.STUBS -> buildExpressionStub() - BodyBuildingMode.LAZY_BODIES -> buildLazyExpression { - source = delegateExpression!!.toFirSourceElement() - } - } - - } - } else null - getter = this@toFirProperty.getter.toFirPropertyAccessor(this@toFirProperty, propertyType, isGetter = true) setter = this@toFirProperty.setter.toFirPropertyAccessor(this@toFirProperty, propertyType, isGetter = false) @@ -1472,16 +1461,34 @@ open class RawFirBuilder( isExternal = hasModifier(EXTERNAL_KEYWORD) } - val receiver = delegateExpression?.toFirExpression("Should have delegate") - generateAccessorsByDelegate( - delegateBuilder, - baseModuleData, - ownerRegularOrAnonymousObjectSymbol, - ownerRegularClassTypeParametersCount, - isExtension = receiverTypeReference != null, - stubMode = stubMode, - receiver = receiver - ) + if (hasDelegate()) { + fun extractDelegateExpression() = when (mode) { + BodyBuildingMode.NORMAL -> this@toFirProperty.delegate?.expression.toFirExpression("Should have delegate") + BodyBuildingMode.STUBS -> buildExpressionStub() + BodyBuildingMode.LAZY_BODIES -> this@toFirProperty.delegate?.expression?.let { + buildLazyExpression { source = it.toFirSourceElement() } + } ?: buildErrorExpression { + ConeSimpleDiagnostic("Should have delegate", DiagnosticKind.ExpressionExpected) + } + } + + val delegateBuilder = FirWrappedDelegateExpressionBuilder().apply { + val delegateExpression = extractDelegateExpression() + source = delegateExpression.source?.fakeElement(FirFakeSourceElementKind.WrappedDelegate) + expression = extractDelegateExpression() + } + + generateAccessorsByDelegate( + delegateBuilder, + baseModuleData, + ownerRegularOrAnonymousObjectSymbol, + ownerRegularClassTypeParametersCount, + isExtension = receiverTypeReference != null, + stubMode = stubMode, + //TODO This expression should be the same for wrapper and receiver + receiver = extractDelegateExpression() + ) + } } } extractAnnotationsTo(this) diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt new file mode 100644 index 00000000000..85c3940f2c0 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt @@ -0,0 +1,4 @@ +private val nonLocalProperty: List by lazy { + val localProperty = mutableListOf() + localProperty +} \ No newline at end of file diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.lazyBodies.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.lazyBodies.txt new file mode 100644 index 00000000000..17cb4b80584 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.lazyBodies.txt @@ -0,0 +1,5 @@ +FILE: localDeclarationWithExpression.kt + private final? val nonLocalProperty: Listby LAZY_EXPRESSION + public? get(): { + ^ D|/nonLocalProperty|.getValue#(Null(null), ::R|/nonLocalProperty|) + } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.txt new file mode 100644 index 00000000000..cfbce47feae --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.txt @@ -0,0 +1,9 @@ +FILE: localDeclarationWithExpression.kt + private final? val nonLocalProperty: Listby lazy#( = lazy@fun .(): { + lval localProperty: = mutableListOf#() + localProperty# + } + ) + public? get(): { + ^ D|/nonLocalProperty|.getValue#(Null(null), ::R|/nonLocalProperty|) + } diff --git a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java index d306443634e..106490e03ce 100644 --- a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java +++ b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java @@ -353,6 +353,11 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/lambdaAndAnonymousFunction.kt"); } + @TestMetadata("localDeclarationWithExpression.kt") + public void testLocalDeclarationWithExpression() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt"); + } + @TestMetadata("locals.kt") public void testLocals() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/locals.kt"); diff --git a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java index 5be06767410..2c769a086d4 100644 --- a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java +++ b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java @@ -353,6 +353,11 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/lambdaAndAnonymousFunction.kt"); } + @TestMetadata("localDeclarationWithExpression.kt") + public void testLocalDeclarationWithExpression() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt"); + } + @TestMetadata("locals.kt") public void testLocals() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/locals.kt"); diff --git a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java index 393d3427993..9cff0390e8a 100644 --- a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java +++ b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java @@ -385,6 +385,12 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/lambdaAndAnonymousFunction.kt"); } + @Test + @TestMetadata("localDeclarationWithExpression.kt") + public void testLocalDeclarationWithExpression() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt"); + } + @Test @TestMetadata("locals.kt") public void testLocals() throws Exception { diff --git a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java index 1105bb82d2d..22d1d85bc88 100644 --- a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java +++ b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java @@ -385,6 +385,12 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/lambdaAndAnonymousFunction.kt"); } + @Test + @TestMetadata("localDeclarationWithExpression.kt") + public void testLocalDeclarationWithExpression() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt"); + } + @Test @TestMetadata("locals.kt") public void testLocals() throws Exception { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyBodiesCalculator.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyBodiesCalculator.kt index 0894166bb36..32873905a80 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyBodiesCalculator.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyBodiesCalculator.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirWrappedDelegateExpression import org.jetbrains.kotlin.fir.expressions.impl.FirLazyBlock import org.jetbrains.kotlin.fir.expressions.impl.FirLazyExpression @@ -90,10 +92,20 @@ internal object FirLazyBodiesCalculator { firProperty.replaceInitializer(newProperty.initializer) } - val delegate = firProperty.delegate - if (delegate is FirWrappedDelegateExpression && delegate.expression is FirLazyExpression) { - val newDelegate = newProperty.delegate as FirWrappedDelegateExpression + val delegate = firProperty.delegate as? FirWrappedDelegateExpression + val delegateExpression = delegate?.expression + if (delegateExpression is FirLazyExpression) { + val newDelegate = newProperty.delegate as? FirWrappedDelegateExpression + check(newDelegate != null) { "Invalid replacement delegate" } delegate.replaceExpression(newDelegate.expression) + + val delegateProviderCall = delegate.delegateProvider as? FirFunctionCall + val delegateProviderExplicitReceiver = delegateProviderCall?.explicitReceiver + if (delegateProviderExplicitReceiver is FirLazyExpression) { + val newDelegateProviderExplicitReceiver = (newDelegate.delegateProvider as? FirFunctionCall)?.explicitReceiver + check(newDelegateProviderExplicitReceiver != null) { "Invalid replacement expression" } + delegateProviderCall.replaceExplicitReceiver(newDelegateProviderExplicitReceiver) + } } } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyBodiesCalculatorTest.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyBodiesCalculatorTest.kt index ea2faa78e35..68dee8de39e 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyBodiesCalculatorTest.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyBodiesCalculatorTest.kt @@ -6,10 +6,14 @@ package org.jetbrains.kotlin.idea.fir.low.level.api import junit.framework.TestCase +import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirRenderer import org.jetbrains.kotlin.fir.builder.RawFirBuilder import org.jetbrains.kotlin.fir.builder.BodyBuildingMode import org.jetbrains.kotlin.fir.builder.PsiHandlingMode +import org.jetbrains.kotlin.fir.expressions.impl.FirLazyBlock +import org.jetbrains.kotlin.fir.expressions.impl.FirLazyExpression +import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyBodiesCalculator import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider import org.jetbrains.kotlin.idea.fir.low.level.api.test.base.AbstractLowLevelApiSingleFileTest @@ -18,6 +22,15 @@ import org.jetbrains.kotlin.test.services.TestModuleStructure import org.jetbrains.kotlin.test.services.TestServices abstract class AbstractFirLazyBodiesCalculatorTest : AbstractLowLevelApiSingleFileTest() { + + private val lazyChecker = object : FirVisitorVoid() { + override fun visitElement(element: FirElement) { + TestCase.assertFalse("${FirLazyBlock::class.qualifiedName} should not present in the tree", element is FirLazyBlock) + TestCase.assertFalse("${FirLazyExpression::class.qualifiedName} should not present in the tree", element is FirLazyExpression) + element.acceptChildren(this) + } + } + override fun doTestByFileStructure(ktFile: KtFile, moduleStructure: TestModuleStructure, testServices: TestServices) { resolveWithClearCaches(ktFile) { resolveState -> val session = resolveState.rootModuleSession @@ -31,6 +44,7 @@ abstract class AbstractFirLazyBodiesCalculatorTest : AbstractLowLevelApiSingleFi ).buildFirFile(ktFile) FirLazyBodiesCalculator.calculateLazyBodies(laziedFirFile) + laziedFirFile.accept(lazyChecker) val fullFirFile = RawFirBuilder( session, diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/FirLazyBodiesCalculatorTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/FirLazyBodiesCalculatorTestGenerated.java index 0caba9f2239..3d4837394c2 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/FirLazyBodiesCalculatorTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/FirLazyBodiesCalculatorTestGenerated.java @@ -385,6 +385,12 @@ public class FirLazyBodiesCalculatorTestGenerated extends AbstractFirLazyBodiesC runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/lambdaAndAnonymousFunction.kt"); } + @Test + @TestMetadata("localDeclarationWithExpression.kt") + public void testLocalDeclarationWithExpression() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/localDeclarationWithExpression.kt"); + } + @Test @TestMetadata("locals.kt") public void testLocals() throws Exception {