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
@@ -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");