Flatten if chains also in LT FIR

This commit is contained in:
Mikhail Glukhikh
2022-02-14 09:56:27 +03:00
committed by teamcity
parent bb766a5235
commit 2ac2fa5353
16 changed files with 272 additions and 69 deletions
@@ -331,6 +331,12 @@ public class FirLazyBodiesCalculatorTestGenerated extends AbstractFirLazyBodiesC
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt");
} }
@Test
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@Test @Test
@TestMetadata("classReference.kt") @TestMetadata("classReference.kt")
public void testClassReference() throws Exception { public void testClassReference() throws Exception {
@@ -47,7 +47,6 @@ import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
import org.jetbrains.kotlin.psi.stubs.elements.KtNameReferenceExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtNameReferenceExpressionElementType
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -1136,6 +1135,10 @@ class ExpressionsConverter(
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.toFirBlock * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.toFirBlock
*/ */
private fun convertLoopBody(body: LighterASTNode?): FirBlock { private fun convertLoopBody(body: LighterASTNode?): FirBlock {
return convertLoopOrIfBody(body) ?: buildEmptyExpressionBlock()
}
private fun convertLoopOrIfBody(body: LighterASTNode?): FirBlock? {
var firBlock: FirBlock? = null var firBlock: FirBlock? = null
var firStatement: FirStatement? = null var firStatement: FirStatement? = null
body?.forEachChildren { body?.forEachChildren {
@@ -1145,11 +1148,7 @@ class ExpressionsConverter(
} }
} }
return when { return firStatement?.let { FirSingleExpressionBlock(it) } ?: firBlock
firStatement != null -> FirSingleExpressionBlock(firStatement!!)
firBlock == null -> buildEmptyExpressionBlock()
else -> firBlock!!
}
} }
/** /**
@@ -1217,6 +1216,51 @@ class ExpressionsConverter(
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitIfExpression * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitIfExpression
*/ */
private fun convertIfExpression(ifExpression: LighterASTNode): FirExpression { private fun convertIfExpression(ifExpression: LighterASTNode): FirExpression {
var components = parseIfExpression(ifExpression)
return buildWhenExpression {
source = ifExpression.toFirSourceElement()
whenBranches@ while (true) {
with(components) {
val trueBranch = convertLoopBody(thenBlock)
branches += buildWhenBranch {
source = thenBlock?.toFirSourceElement()
condition = firCondition ?: buildErrorExpression(
null,
ConeSimpleDiagnostic("If statement should have condition", DiagnosticKind.Syntax)
)
result = trueBranch
}
}
if (components.elseBlock == null) break@whenBranches
var cascadeIf = false
components.elseBlock?.forEachChildren {
if (it.tokenType == IF) {
cascadeIf = true
components = parseIfExpression(it)
}
}
if (!cascadeIf) {
with(components) {
val elseBranch = convertLoopOrIfBody(elseBlock)
if (elseBranch != null) {
branches += buildWhenBranch {
source = elseBlock?.toFirSourceElement()
condition = buildElseIfTrueCondition()
result = elseBranch
}
}
}
break@whenBranches
}
}
usedAsExpression = ifExpression.usedAsExpression
}
}
private class IfNodeComponents(val firCondition: FirExpression?, val thenBlock: LighterASTNode?, val elseBlock: LighterASTNode?)
private fun parseIfExpression(ifExpression: LighterASTNode): IfNodeComponents {
var firCondition: FirExpression? = null var firCondition: FirExpression? = null
var thenBlock: LighterASTNode? = null var thenBlock: LighterASTNode? = null
var elseBlock: LighterASTNode? = null var elseBlock: LighterASTNode? = null
@@ -1227,28 +1271,7 @@ class ExpressionsConverter(
ELSE -> elseBlock = it ELSE -> elseBlock = it
} }
} }
return IfNodeComponents(firCondition, thenBlock, elseBlock)
return buildWhenExpression {
source = ifExpression.toFirSourceElement()
val trueBranch = convertLoopBody(thenBlock)
branches += buildWhenBranch {
source = thenBlock?.toFirSourceElement()
condition = firCondition ?: buildErrorExpression(
null,
ConeSimpleDiagnostic("If statement should have condition", DiagnosticKind.Syntax)
)
result = trueBranch
}
if (elseBlock != null) {
val elseBranch = convertLoopBody(elseBlock)
branches += buildWhenBranch {
source = elseBlock?.toFirSourceElement()
condition = buildElseIfTrueCondition()
result = elseBranch
}
}
usedAsExpression = ifExpression.usedAsExpression
}
} }
private val LighterASTNode.usedAsExpression: Boolean private val LighterASTNode.usedAsExpression: Boolean
@@ -308,6 +308,11 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt");
} }
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@TestMetadata("classReference.kt") @TestMetadata("classReference.kt")
public void testClassReference() throws Exception { public void testClassReference() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/classReference.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/classReference.kt");
@@ -43,7 +43,6 @@ import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -1922,7 +1921,7 @@ open class RawFirBuilder(
when (val ktElse = ktLastIf.`else`) { when (val ktElse = ktLastIf.`else`) {
null -> break@whenBranches null -> break@whenBranches
is KtIfExpression -> ktLastIf = ktElse is KtIfExpression -> ktLastIf = ktElse
else -> { else -> {
branches += buildWhenBranch { branches += buildWhenBranch {
source = ktLastIf.elseKeyword?.toKtPsiSourceElement() source = ktLastIf.elseKeyword?.toKtPsiSourceElement()
condition = buildElseIfTrueCondition() condition = buildElseIfTrueCondition()
@@ -0,0 +1,45 @@
fun foo(first: Boolean, second: Boolean, third: Boolean): Int {
if (first) {
return 4
} else if (second) {
val x = 3
return x + 2
} else {
// Yet we don't flatten this 'if', it matches the current PSI2IR behavior
if (third) {
return 0
} else return -1
}
}
fun example() {
val a = if (true) true else false
val b = if (true) else false
val c = if (true) true
val d = if (true) true else;
val e = if (true) {} else false
val f = if (true) true else {}
{
if (true) true
}();
{
if (true) true else false
}();
{
if (true) {} else false
}();
{
if (true) true else {}
}()
fun t(): Boolean {
return if (true) true
}
return if (true) true else {}
}
@@ -0,0 +1,3 @@
FILE: cascadeIf.kt
public? final? fun foo(first: Boolean, second: Boolean, third: Boolean): Int { LAZY_BLOCK }
public? final? fun example(): R|kotlin/Unit| { LAZY_BLOCK }
@@ -0,0 +1,131 @@
FILE: cascadeIf.kt
public? final? fun foo(first: Boolean, second: Boolean, third: Boolean): Int {
when () {
first# -> {
^foo IntegerLiteral(4)
}
second# -> {
lval x: <implicit> = IntegerLiteral(3)
^foo x#.plus#(IntegerLiteral(2))
}
else -> {
when () {
third# -> {
^foo IntegerLiteral(0)
}
else -> {
^foo IntegerLiteral(-1)
}
}
}
}
}
public? final? fun example(): R|kotlin/Unit| {
lval a: <implicit> = when () {
Boolean(true) -> {
Boolean(true)
}
else -> {
Boolean(false)
}
}
lval b: <implicit> = when () {
Boolean(true) -> {
}
else -> {
Boolean(false)
}
}
lval c: <implicit> = when () {
Boolean(true) -> {
Boolean(true)
}
}
lval d: <implicit> = when () {
Boolean(true) -> {
Boolean(true)
}
}
lval e: <implicit> = when () {
Boolean(true) -> {
}
else -> {
Boolean(false)
}
}
lval f: <implicit> = when () {
Boolean(true) -> {
Boolean(true)
}
else -> {
}
}
fun <implicit>.<anonymous>(): <implicit> <inline=Unknown> {
when () {
Boolean(true) -> {
Boolean(true)
}
}
}
.invoke#()
fun <implicit>.<anonymous>(): <implicit> <inline=Unknown> {
when () {
Boolean(true) -> {
Boolean(true)
}
else -> {
Boolean(false)
}
}
}
.invoke#()
fun <implicit>.<anonymous>(): <implicit> <inline=Unknown> {
when () {
Boolean(true) -> {
}
else -> {
Boolean(false)
}
}
}
.invoke#()
fun <implicit>.<anonymous>(): <implicit> <inline=Unknown> {
when () {
Boolean(true) -> {
Boolean(true)
}
else -> {
}
}
}
.invoke#()
local final? fun t(): Boolean {
^t when () {
Boolean(true) -> {
Boolean(true)
}
}
}
^example when () {
Boolean(true) -> {
Boolean(true)
}
else -> {
}
}
}
@@ -308,6 +308,11 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt");
} }
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@TestMetadata("classReference.kt") @TestMetadata("classReference.kt")
public void testClassReference() throws Exception { public void testClassReference() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/classReference.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/classReference.kt");
@@ -308,6 +308,11 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt");
} }
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@TestMetadata("classReference.kt") @TestMetadata("classReference.kt")
public void testClassReference() throws Exception { public void testClassReference() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/classReference.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/classReference.kt");
@@ -16,9 +16,9 @@
fun case1() { fun case1() {
val b = true val b = true
val c = true val c = true
val a = if (b) { val a = <!INVALID_IF_AS_EXPRESSION!>if<!> (b) {
"first true" "first true"
} else <!INVALID_IF_AS_EXPRESSION!>if<!> (c) { } else if (c) {
"else if true" "else if true"
} }
} }
@@ -28,5 +28,5 @@ fun case1() {
fun case2() { fun case2() {
var b = true var b = true
val c = true val c = true
val a = if (b) 1 else if (c) 2 else ; val a = <!INVALID_IF_AS_EXPRESSION!>if<!> (b) 1 else if (c) 2 else ;
} }
@@ -37,7 +37,7 @@ fun case4() {
val x2 = <!INVALID_IF_AS_EXPRESSION!>if<!> (TODO()) true val x2 = <!INVALID_IF_AS_EXPRESSION!>if<!> (TODO()) true
val x0 = if (false) true else <!INVALID_IF_AS_EXPRESSION!>if<!> (throw Exception()) ; val x0 = <!INVALID_IF_AS_EXPRESSION!>if<!> (false) true else if (throw Exception()) ;
} }
@@ -5,7 +5,7 @@
// TESTCASE NUMBER: 1 // TESTCASE NUMBER: 1
fun case1() { fun case1() {
val y0else = if (false) true else ; val y0else = <!INVALID_IF_AS_EXPRESSION!>if<!> (false) true else ;
} }
/* /*
@@ -14,7 +14,7 @@ fun case1() {
* ISSUES: KT-35510 * ISSUES: KT-35510
*/ */
fun case2(nothing: Nothing) { fun case2(nothing: Nothing) {
val n1else = if (nothing) true else; val n1else = <!INVALID_IF_AS_EXPRESSION!>if<!> (nothing) true else;
} }
/* /*
@@ -35,5 +35,5 @@ fun case4(nothing: Nothing) {
// TESTCASE NUMBER: 5 // TESTCASE NUMBER: 5
fun case5(nothing: Nothing) { fun case5(nothing: Nothing) {
val x = if (false) else if (nothing) { "foo"} else ; val x = <!INVALID_IF_AS_EXPRESSION!>if<!> (false) else if (nothing) { "foo"} else ;
} }
@@ -1,32 +0,0 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION
// SKIP_TXT
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-296
* MAIN LINK: control--and-data-flow-analysis, control-flow-graph, expressions-1, conditional-expressions -> paragraph 1 -> sentence 2
* NUMBER: 1
* DESCRIPTION: check any if-statement in kotlin may be trivially turned into such an expression by replacing the missing branch with a kotlin.Unit object expression.
* HELPERS: checkType
*/
// TESTCASE NUMBER: 1
fun case1() {
val b = true
if (!b) {
println("this is statement")
}
val statement = <!INVALID_IF_AS_EXPRESSION!>if<!> (!b) { println("statement could not be assigned") }
}
// TESTCASE NUMBER: 2
fun case2() {
val a = 1
val b = 2
if (a > b) a else ; //statement
val expression: Any = if (a > b) a else ;
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +NewInference // !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION // !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION
// SKIP_TXT // SKIP_TXT
@@ -331,6 +331,12 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt");
} }
@Test
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@Test @Test
@TestMetadata("classReference.kt") @TestMetadata("classReference.kt")
public void testClassReference() throws Exception { public void testClassReference() throws Exception {
@@ -331,6 +331,12 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/calls.kt");
} }
@Test
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@Test @Test
@TestMetadata("classReference.kt") @TestMetadata("classReference.kt")
public void testClassReference() throws Exception { public void testClassReference() throws Exception {