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");
}
@Test
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@Test
@TestMetadata("classReference.kt")
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.psi.stubs.elements.KtConstantExpressionElementType
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.util.OperatorNameConventions
@@ -1136,6 +1135,10 @@ class ExpressionsConverter(
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.toFirBlock
*/
private fun convertLoopBody(body: LighterASTNode?): FirBlock {
return convertLoopOrIfBody(body) ?: buildEmptyExpressionBlock()
}
private fun convertLoopOrIfBody(body: LighterASTNode?): FirBlock? {
var firBlock: FirBlock? = null
var firStatement: FirStatement? = null
body?.forEachChildren {
@@ -1145,11 +1148,7 @@ class ExpressionsConverter(
}
}
return when {
firStatement != null -> FirSingleExpressionBlock(firStatement!!)
firBlock == null -> buildEmptyExpressionBlock()
else -> firBlock!!
}
return firStatement?.let { FirSingleExpressionBlock(it) } ?: firBlock
}
/**
@@ -1217,6 +1216,51 @@ class ExpressionsConverter(
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitIfExpression
*/
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 thenBlock: LighterASTNode? = null
var elseBlock: LighterASTNode? = null
@@ -1227,28 +1271,7 @@ class ExpressionsConverter(
ELSE -> elseBlock = it
}
}
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
}
return IfNodeComponents(firCondition, thenBlock, elseBlock)
}
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");
}
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@TestMetadata("classReference.kt")
public void testClassReference() throws Exception {
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.psiUtil.*
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.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -1922,7 +1921,7 @@ open class RawFirBuilder(
when (val ktElse = ktLastIf.`else`) {
null -> break@whenBranches
is KtIfExpression -> ktLastIf = ktElse
else -> {
else -> {
branches += buildWhenBranch {
source = ktLastIf.elseKeyword?.toKtPsiSourceElement()
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");
}
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@TestMetadata("classReference.kt")
public void testClassReference() throws Exception {
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");
}
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@TestMetadata("classReference.kt")
public void testClassReference() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/classReference.kt");
@@ -16,9 +16,9 @@
fun case1() {
val b = true
val c = true
val a = if (b) {
val a = <!INVALID_IF_AS_EXPRESSION!>if<!> (b) {
"first true"
} else <!INVALID_IF_AS_EXPRESSION!>if<!> (c) {
} else if (c) {
"else if true"
}
}
@@ -28,5 +28,5 @@ fun case1() {
fun case2() {
var b = 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 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
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
*/
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
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
// !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION
// SKIP_TXT
@@ -331,6 +331,12 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe
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
@TestMetadata("classReference.kt")
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");
}
@Test
@TestMetadata("cascadeIf.kt")
public void testCascadeIf() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.kt");
}
@Test
@TestMetadata("classReference.kt")
public void testClassReference() throws Exception {