[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"); 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") @TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @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"); 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 @Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments") @TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -593,6 +593,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/whenInference.kt"); 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 @Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments") @TestMetadata("compiler/fir/analysis-tests/testData/resolve/arguments")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.lightTree.converter
import com.intellij.lang.LighterASTNode import com.intellij.lang.LighterASTNode
import com.intellij.psi.TokenType import com.intellij.psi.TokenType
import com.intellij.util.diff.FlyweightCapableTreeStructure import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
@@ -664,6 +665,7 @@ class ExpressionsConverter(
source = whenExpression.toFirSourceElement() source = whenExpression.toFirSourceElement()
this.subject = subjectExpression this.subject = subjectExpression
this.subjectVariable = subjectVariable this.subjectVariable = subjectVariable
usedAsExpression = whenExpression.usedAsExpression
for (entry in whenEntries) { for (entry in whenEntries) {
val branch = entry.firBlock val branch = entry.firBlock
branches += if (!entry.isElse) { branches += if (!entry.isElse) {
@@ -1095,11 +1097,24 @@ class ExpressionsConverter(
condition = buildElseIfTrueCondition() condition = buildElseIfTrueCondition()
result = elseBranch 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.parsing.KotlinExpressionParsing.parseJump
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitBreakExpression * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitBreakExpression
@@ -1546,6 +1546,7 @@ class RawFirBuilder(
result = expression.`else`.toFirBlock() result = expression.`else`.toFirBlock()
} }
} }
usedAsExpression = expression.usedAsExpression
} }
} }
@@ -1583,6 +1584,7 @@ class RawFirBuilder(
source = expression.toFirSourceElement() source = expression.toFirSourceElement()
this.subject = subjectExpression this.subject = subjectExpression
this.subjectVariable = subjectVariable this.subjectVariable = subjectVariable
usedAsExpression = expression.usedAsExpression
for (entry in expression.entries) { for (entry in expression.entries) {
val entrySource = entry.toFirSourceElement() 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 { override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: Unit): FirElement {
return FirDoWhileLoopBuilder().apply { return FirDoWhileLoopBuilder().apply {
source = expression.toFirSourceElement() source = expression.toFirSourceElement()
@@ -137,6 +137,8 @@ fun FirWhenExpression.copy(
branches += this@copy.branches branches += this@copy.branches
typeRef = resultType typeRef = resultType
this.annotations += annotations this.annotations += annotations
usedAsExpression = this@copy.usedAsExpression
isExhaustive = this@copy.isExhaustive
} }
fun FirTryExpression.copy( fun FirTryExpression.copy(
@@ -25,6 +25,7 @@ abstract class FirWhenExpression : FirExpression(), FirResolvable {
abstract val subjectVariable: FirVariable<*>? abstract val subjectVariable: FirVariable<*>?
abstract val branches: List<FirWhenBranch> abstract val branches: List<FirWhenBranch>
abstract val isExhaustive: Boolean abstract val isExhaustive: Boolean
abstract val usedAsExpression: Boolean
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitWhenExpression(this, data) 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 var subjectVariable: FirVariable<*>? = null
val branches: MutableList<FirWhenBranch> = mutableListOf() val branches: MutableList<FirWhenBranch> = mutableListOf()
var isExhaustive: Boolean = false var isExhaustive: Boolean = false
var usedAsExpression: Boolean by kotlin.properties.Delegates.notNull<Boolean>()
override fun build(): FirWhenExpression { override fun build(): FirWhenExpression {
return FirWhenExpressionImpl( return FirWhenExpressionImpl(
@@ -48,13 +49,14 @@ class FirWhenExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBui
subjectVariable, subjectVariable,
branches, branches,
isExhaustive, isExhaustive,
usedAsExpression,
) )
} }
} }
@OptIn(ExperimentalContracts::class) @OptIn(ExperimentalContracts::class)
inline fun buildWhenExpression(init: FirWhenExpressionBuilder.() -> Unit = {}): FirWhenExpression { inline fun buildWhenExpression(init: FirWhenExpressionBuilder.() -> Unit): FirWhenExpression {
contract { contract {
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE) callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
} }
@@ -29,6 +29,7 @@ internal class FirWhenExpressionImpl(
override var subjectVariable: FirVariable<*>?, override var subjectVariable: FirVariable<*>?,
override val branches: MutableList<FirWhenBranch>, override val branches: MutableList<FirWhenBranch>,
override var isExhaustive: Boolean, override var isExhaustive: Boolean,
override val usedAsExpression: Boolean,
) : FirWhenExpression() { ) : FirWhenExpression() {
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) { override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
typeRef.accept(visitor, data) typeRef.accept(visitor, data)
@@ -588,6 +588,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
+field("subjectVariable", variable.withArgs("F" to "*"), nullable = true) +field("subjectVariable", variable.withArgs("F" to "*"), nullable = true)
+fieldList("branches", whenBranch).withTransform() +fieldList("branches", whenBranch).withTransform()
+booleanField("isExhaustive", withReplace = true) +booleanField("isExhaustive", withReplace = true)
+booleanField("usedAsExpression")
needTransformOtherChildren() needTransformOtherChildren()
} }