[FIR] Save info that when was used as expression

This commit is contained in:
Dmitriy Novozhilov
2021-02-04 12:49:24 +03:00
parent 490ef210ac
commit 11ab37160e
12 changed files with 81 additions and 2 deletions
@@ -499,6 +499,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
runTest("compiler/fir/analysis-tests/testData/resolve/whenInference.kt");
}
@TestMetadata("whenWithWhenAsStatement.kt")
public void testWhenWithWhenAsStatement() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/whenWithWhenAsStatement.kt");
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -0,0 +1,16 @@
FILE: whenWithWhenAsStatement.kt
public final fun test(value: R|kotlin/Int|): R|kotlin/Unit| {
when (R|<local>/value|) {
==($subj$, Int(0)) -> {
}
==($subj$, Int(1)) -> {
when (R|<local>/value|) {
==($subj$, Int(2)) -> {
Boolean(false)
}
}
}
}
}
@@ -0,0 +1,8 @@
fun test(value: Int) {
when (value) {
0 -> {}
1 -> when (value) {
2 -> false
}
}
}
@@ -590,6 +590,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
runTest("compiler/fir/analysis-tests/testData/resolve/whenInference.kt");
}
@Test
@TestMetadata("whenWithWhenAsStatement.kt")
public void testWhenWithWhenAsStatement() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/whenWithWhenAsStatement.kt");
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments")
@TestDataPath("$PROJECT_ROOT")
@@ -593,6 +593,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/whenInference.kt");
}
@Test
@TestMetadata("whenWithWhenAsStatement.kt")
public void testWhenWithWhenAsStatement() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/whenWithWhenAsStatement.kt");
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments")
@TestDataPath("$PROJECT_ROOT")
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.lightTree.converter
import com.intellij.lang.LighterASTNode
import com.intellij.psi.TokenType
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
@@ -664,6 +665,7 @@ class ExpressionsConverter(
source = whenExpression.toFirSourceElement()
this.subject = subjectExpression
this.subjectVariable = subjectVariable
usedAsExpression = whenExpression.usedAsExpression
for (entry in whenEntries) {
val branch = entry.firBlock
branches += if (!entry.isElse) {
@@ -1095,11 +1097,24 @@ class ExpressionsConverter(
condition = buildElseIfTrueCondition()
result = elseBranch
}
}
usedAsExpression = ifExpression.usedAsExpression
}
}
private val LighterASTNode.usedAsExpression: Boolean
get() {
val parent = getParent() ?: return true
val parentTokenType = parent.tokenType
if (parentTokenType == BLOCK) return false
if (parentTokenType == ELSE || parentTokenType == WHEN_ENTRY) {
return parent.getParent()?.usedAsExpression ?: true
}
if (parentTokenType != BODY) return true
val type = parent.getParent()?.tokenType ?: return true
return !(type == FOR || type == WHILE || type == DO_WHILE)
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseJump
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitBreakExpression
@@ -1546,6 +1546,7 @@ class RawFirBuilder(
result = expression.`else`.toFirBlock()
}
}
usedAsExpression = expression.usedAsExpression
}
}
@@ -1583,6 +1584,7 @@ class RawFirBuilder(
source = expression.toFirSourceElement()
this.subject = subjectExpression
this.subjectVariable = subjectVariable
usedAsExpression = expression.usedAsExpression
for (entry in expression.entries) {
val entrySource = entry.toFirSourceElement()
@@ -1621,6 +1623,20 @@ class RawFirBuilder(
}
}
private val KtExpression.usedAsExpression: Boolean
get() {
if (parent is KtBlockExpression) return false
when (parent.elementType) {
KtNodeTypes.ELSE, KtNodeTypes.WHEN_ENTRY -> {
return (parent.parent as? KtExpression)?.usedAsExpression ?: true
}
}
// Here we check that when used is a single statement of a loop
if (parent !is KtContainerNodeForControlStructureBody) return true
val type = parent.parent.elementType
return !(type == KtNodeTypes.FOR || type == KtNodeTypes.WHILE || type == KtNodeTypes.DO_WHILE)
}
override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: Unit): FirElement {
return FirDoWhileLoopBuilder().apply {
source = expression.toFirSourceElement()
@@ -137,6 +137,8 @@ fun FirWhenExpression.copy(
branches += this@copy.branches
typeRef = resultType
this.annotations += annotations
usedAsExpression = this@copy.usedAsExpression
isExhaustive = this@copy.isExhaustive
}
fun FirTryExpression.copy(
@@ -25,6 +25,7 @@ abstract class FirWhenExpression : FirExpression(), FirResolvable {
abstract val subjectVariable: FirVariable<*>?
abstract val branches: List<FirWhenBranch>
abstract val isExhaustive: Boolean
abstract val usedAsExpression: Boolean
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitWhenExpression(this, data)
@@ -37,6 +37,7 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui
var subjectVariable: FirVariable<*>? = null
val branches: MutableList<FirWhenBranch> = mutableListOf()
var isExhaustive: Boolean = false
var usedAsExpression: Boolean by kotlin.properties.Delegates.notNull<Boolean>()
override fun build(): FirWhenExpression {
return FirWhenExpressionImpl(
@@ -48,13 +49,14 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui
subjectVariable,
branches,
isExhaustive,
usedAsExpression,
)
}
}
@OptIn(ExperimentalContracts::class)
inline fun buildWhenExpression(init: FirWhenExpressionBuilder.() -> Unit = {}): FirWhenExpression {
inline fun buildWhenExpression(init: FirWhenExpressionBuilder.() -> Unit): FirWhenExpression {
contract {
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
@@ -29,6 +29,7 @@ internal class FirWhenExpressionImpl(
override var subjectVariable: FirVariable<*>?,
override val branches: MutableList<FirWhenBranch>,
override var isExhaustive: Boolean,
override val usedAsExpression: Boolean,
) : FirWhenExpression() {
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
typeRef.accept(visitor, data)
@@ -588,6 +588,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
+field("subjectVariable", variable.withArgs("F" to "*"), nullable = true)
+fieldList("branches", whenBranch).withTransform()
+booleanField("isExhaustive", withReplace = true)
+booleanField("usedAsExpression")
needTransformOtherChildren()
}