[FIR IDE] Fix invalid lazy expressions for property initializers

This commit is contained in:
Igor Yakovlev
2021-08-24 21:21:55 +02:00
committed by TeamCityServer
parent 19e5e7d511
commit e802ef27f1
12 changed files with 131 additions and 47 deletions
@@ -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");
@@ -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)
@@ -0,0 +1,4 @@
private val nonLocalProperty: List<XXX> by lazy {
val localProperty = mutableListOf<KtLightField>()
localProperty
}
@@ -0,0 +1,5 @@
FILE: localDeclarationWithExpression.kt
private final? val nonLocalProperty: List<XXX>by LAZY_EXPRESSION
public? get(): <implicit> {
^ D|/nonLocalProperty|.getValue#(Null(null), ::R|/nonLocalProperty|)
}
@@ -0,0 +1,9 @@
FILE: localDeclarationWithExpression.kt
private final? val nonLocalProperty: List<XXX>by lazy#(<L> = lazy@fun <implicit>.<anonymous>(): <implicit> <inline=Unknown> {
lval localProperty: <implicit> = mutableListOf#<KtLightField>()
localProperty#
}
)
public? get(): <implicit> {
^ D|/nonLocalProperty|.getValue#(Null(null), ::R|/nonLocalProperty|)
}
@@ -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");
@@ -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");
@@ -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 {
@@ -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 {
@@ -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)
}
}
}
@@ -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,
@@ -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 {