[FIR] Remove implicit coercion of ifs and whens to Unit

The rule of thumb is the following:
If the `if` and `when` can be successfully replaced with `while`,
then it is used as a statement, otherwise, it is used as an expression.

#KT-59883
This commit is contained in:
Evgeniy.Zhelenskiy
2024-02-20 03:12:56 +01:00
committed by Space Team
parent 2e66954d01
commit 931f2eab58
16 changed files with 256 additions and 68 deletions
@@ -90,7 +90,7 @@ abstract class AbstractLightTreeRawFirBuilder(
}
}
private fun LighterASTNode.getLastChildExpression(): LighterASTNode? {
fun LighterASTNode.getLastChildExpression(): LighterASTNode? {
var result: LighterASTNode? = null
forEachChildren {
if (it.isExpression()) {
@@ -1402,13 +1402,19 @@ class LightTreeRawFirExpressionBuilder(
parent = parent.getParent() ?: return true
}
val parentTokenType = parent.tokenType
if (parentTokenType == BLOCK) return false
if (parentTokenType == THEN || parentTokenType == ELSE || parentTokenType == WHEN_ENTRY) {
return parent.getParent()?.usedAsExpression ?: true
return when (parentTokenType) {
BLOCK -> parent.getLastChildExpression() == this && parent.usedAsExpression
TRY, CATCH -> parent.usedAsExpression
THEN, ELSE, WHEN_ENTRY -> parent.getParent()?.usedAsExpression ?: true
CLASS_INITIALIZER, SCRIPT_INITIALIZER, SECONDARY_CONSTRUCTOR, FUNCTION_LITERAL, FINALLY -> false
FUN, PROPERTY_ACCESSOR -> parent.getChildrenAsArray().any { it?.tokenType == EQ }
DOT_QUALIFIED_EXPRESSION -> parent.getFirstChild() == this
BODY -> when (parent.getParent()?.tokenType) {
FOR, WHILE, DO_WHILE -> false
else -> true
}
else -> true
}
if (parentTokenType != BODY) return true
val type = parent.getParent()?.tokenType ?: return true
return !(type == FOR || type == WHILE || type == DO_WHILE)
}
/**
@@ -2690,17 +2690,33 @@ open class PsiRawFirBuilder(
) {
parent = parent.parent
}
if (parent is KtBlockExpression) return false
when (parent.elementType) {
KtNodeTypes.THEN, KtNodeTypes.ELSE, KtNodeTypes.WHEN_ENTRY -> {
return (parent.parent as? KtExpression)?.usedAsExpression != false
}
}
if (parent is KtScriptInitializer) return false
// 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)
fun PsiElement.getLastChildExpression() = children.asList().asReversed().firstIsInstanceOrNull<KtExpression>()
return when (parent) {
// todo KT-62472 Replace with the following when K2 is used in TeamCity
// is KtBlockExpression, is KtTryExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression
is KtBlockExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression
is KtTryExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression
is KtCatchClause -> (parent.parent as? KtTryExpression)?.usedAsExpression == true
is KtClassInitializer, is KtScriptInitializer, is KtSecondaryConstructor, is KtFunctionLiteral, is KtFinallySection -> false
is KtDotQualifiedExpression -> parent.firstChild == this
// todo KT-62472 Replace with the following when K2 is used in TeamCity
// is KtFunction, is KtPropertyAccessor -> parent.hasBody() && !parent.hasBlockBody()
is KtFunction -> parent.hasBody() && !parent.hasBlockBody()
is KtPropertyAccessor -> parent.hasBody() && !parent.hasBlockBody()
is KtContainerNodeForControlStructureBody -> when (parent.parent.elementType) {
KtNodeTypes.FOR, KtNodeTypes.WHILE, KtNodeTypes.DO_WHILE -> false
else -> true
}
else -> true
}
}
override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: FirElement?): FirElement {