From 00bc04b3dff027448ee35298efd2843a86471a3d Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Wed, 14 Apr 2021 22:56:25 +0300 Subject: [PATCH] [FIR] Implement ELSE_MISPLACED_IN_WHEN diagnostics, fix tests --- .../generator/diagnostics/DiagnosticData.kt | 1 + .../generator/diagnostics/FirDiagnosticsList.kt | 1 + .../kotlin/fir/analysis/diagnostics/FirErrors.kt | 2 ++ .../expression/FirExhaustiveWhenChecker.kt | 9 +++++++++ .../diagnostics/LightTreePositioningStrategies.kt | 14 ++++++++++++++ .../SourceElementPositioningStrategies.kt | 5 +++++ .../tests/controlStructures/kt786.fir.kt | 4 ++-- .../controlStructures/when.kt234.kt973.fir.kt | 2 +- .../diagnostics/tests/when/DuplicatedLabels.fir.kt | 2 +- .../testData/diagnostics/tests/when/When.fir.kt | 2 +- .../expressions/when-expression/p-3/neg/2.1.fir.kt | 6 +++--- .../expressions/when-expression/p-6/neg/7.1.fir.kt | 6 +++--- .../expressions/when-expression/p-6/neg/7.2.fir.kt | 6 +++--- .../fir/diagnostics/KtFirDataClassConverters.kt | 7 +++++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 5 +++++ .../api/fir/diagnostics/KtFirDiagnosticsImpl.kt | 8 ++++++++ idea/testData/checker/When.fir.kt | 2 +- 17 files changed, 67 insertions(+), 15 deletions(-) diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt index 700154504fe..f3a12b92a79 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt @@ -39,6 +39,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { OPEN_MODIFIER, WHEN_EXPRESSION, IF_EXPRESSION, + ELSE_ENTRY, VARIANCE_MODIFIER, LATEINIT_MODIFIER, INLINE_OR_VALUE_MODIFIER, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index c6cb03ef6c3..325cd045bcc 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -642,6 +642,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { parameter>("missingWhenCases") } val INVALID_IF_AS_EXPRESSION by error(PositioningStrategy.IF_EXPRESSION) + val ELSE_MISPLACED_IN_WHEN by error(PositioningStrategy.ELSE_ENTRY) } val CONTEXT_TRACKING by object : DiagnosticGroup("Context tracking") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 6992844e0a8..7d4edacdf37 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -57,6 +57,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget @@ -377,6 +378,7 @@ object FirErrors { // When expressions val NO_ELSE_IN_WHEN by error1>(SourceElementPositioningStrategies.WHEN_EXPRESSION) val INVALID_IF_AS_EXPRESSION by error0(SourceElementPositioningStrategies.IF_EXPRESSION) + val ELSE_MISPLACED_IN_WHEN by error0(SourceElementPositioningStrategies.ELSE_ENTRY) // Context tracking val TYPE_PARAMETER_IS_NOT_AN_EXPRESSION by error1() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt index 913081955df..2c91827197c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExhaustiveWhenChecker.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.expressions.ExhaustivenessStatus import org.jetbrains.kotlin.fir.expressions.FirWhenExpression +import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition import org.jetbrains.kotlin.fir.expressions.isExhaustive object FirExhaustiveWhenChecker : FirWhenExpressionChecker() { @@ -27,6 +28,14 @@ object FirExhaustiveWhenChecker : FirWhenExpressionChecker() { reporter.reportOn(source, FirErrors.NO_ELSE_IN_WHEN, missingCases, context) } } + + val branchesCount = expression.branches.size + for (indexedValue in expression.branches.withIndex()) { + val branch = indexedValue.value + if (branch.condition is FirElseIfTrueCondition && indexedValue.index < branchesCount - 1) { + reporter.reportOn(branch.source, FirErrors.ELSE_MISPLACED_IN_WHEN, context) + } + } } private val FirSourceElement.isIfExpression: Boolean diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt index 7102e86bb72..6294d997a6c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt @@ -501,6 +501,17 @@ object LightTreePositioningStrategies { } } + val ELSE_ENTRY = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + return markElement(tree.elseKeyword(node) ?: node, startOffset, endOffset, tree, node) + } + } + val ARRAY_ACCESS = object : LightTreePositioningStrategy() { override fun mark( node: LighterASTNode, @@ -577,6 +588,9 @@ private fun FlyweightCapableTreeStructure.whenKeyword(node: Ligh private fun FlyweightCapableTreeStructure.ifKeyword(node: LighterASTNode): LighterASTNode? = findChildByType(node, KtTokens.IF_KEYWORD) +private fun FlyweightCapableTreeStructure.elseKeyword(node: LighterASTNode): LighterASTNode? = + findChildByType(node, KtTokens.ELSE_KEYWORD) + private fun FlyweightCapableTreeStructure.returnKeyword(node: LighterASTNode): LighterASTNode? = findChildByType(node, KtTokens.RETURN_KEYWORD) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt index 9ae921f3584..59adff1b4fd 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt @@ -163,6 +163,11 @@ object SourceElementPositioningStrategies { PositioningStrategies.IF_EXPRESSION ) + val ELSE_ENTRY = SourceElementPositioningStrategy( + LightTreePositioningStrategies.ELSE_ENTRY, + PositioningStrategies.ELSE_ENTRY + ) + val ARRAY_ACCESS = SourceElementPositioningStrategy( LightTreePositioningStrategies.ARRAY_ACCESS, PositioningStrategies.ARRAY_ACCESS diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt786.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/kt786.fir.kt index 26d6aeb6e5b..f8401ff5abc 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/kt786.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/kt786.fir.kt @@ -6,7 +6,7 @@ fun foo() : Int { var z = 0 when(d) { 5, 3 -> z++ - else -> { z = -1000 } + else -> { z = -1000 } return z -> 34 } } @@ -23,4 +23,4 @@ fun fff(): Int { return 34 } -fun bar(): Int = 8 \ No newline at end of file +fun bar(): Int = 8 diff --git a/compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.fir.kt index baa658b8210..33c1afbc435 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.fir.kt @@ -31,7 +31,7 @@ fun t5(x: Int) = when (x) { } fun foo3(x: Int) = when(x) { - else -> 1 + else -> 1 2 -> 2 } diff --git a/compiler/testData/diagnostics/tests/when/DuplicatedLabels.fir.kt b/compiler/testData/diagnostics/tests/when/DuplicatedLabels.fir.kt index 1d9dc3ced95..4b05362114d 100644 --- a/compiler/testData/diagnostics/tests/when/DuplicatedLabels.fir.kt +++ b/compiler/testData/diagnostics/tests/when/DuplicatedLabels.fir.kt @@ -56,6 +56,6 @@ fun fourth(arg: Color) = when (arg) { fun fifth(arg: Any?) = when (arg) { is Any -> "Any" - else -> "" + else -> "" else -> null } diff --git a/compiler/testData/diagnostics/tests/when/When.fir.kt b/compiler/testData/diagnostics/tests/when/When.fir.kt index bd7b7e2cff1..ff304ab963e 100644 --- a/compiler/testData/diagnostics/tests/when/When.fir.kt +++ b/compiler/testData/diagnostics/tests/when/When.fir.kt @@ -50,7 +50,7 @@ fun test() { val z = 1 when (z) { - else -> 1 + else -> 1 1 -> 2 } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-3/neg/2.1.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-3/neg/2.1.fir.kt index 8a557eccdd8..f3f46bbe0d0 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-3/neg/2.1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-3/neg/2.1.fir.kt @@ -18,7 +18,7 @@ fun case1() { val z = JavaEnum.Val_3 val when1 = when (z) { JavaEnum.Val_1 -> { false } - else -> {true} + else -> {true} JavaEnum.Val_2 -> { false } } } @@ -28,7 +28,7 @@ fun case1() { fun case2() { val z = JavaEnum.Val_3 val when1 = when (z) { - else -> {true} + else -> {true} JavaEnum.Val_1 -> { false } JavaEnum.Val_2 -> { false } } @@ -37,7 +37,7 @@ fun case2() { fun case3() { val z = JavaEnum.Val_3 val when1 = when (z) { - else -> {true} + else -> {true} JavaEnum.Val_1 -> { false } JavaEnum.Val_2 -> { false } else -> { true } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.1.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.1.fir.kt index 00f4e2d2cf9..6294d3fd8a4 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.1.fir.kt @@ -3,21 +3,21 @@ // TESTCASE NUMBER: 1 fun case_1(value_1: Int): String = when (value_1) { - else -> "" + else -> "" 1 -> "" } // TESTCASE NUMBER: 2 fun case_2(value_1: Int): String = when (value_1) { 1 -> "" - else -> "" + else -> "" 2 -> "" } // TESTCASE NUMBER: 3 fun case_3(value_1: Int): String { when (value_1) { - else -> return "" + else -> return "" else -> return "" } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.2.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.2.fir.kt index 32bdd2e5cdf..542f8be4df0 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/neg/7.2.fir.kt @@ -3,21 +3,21 @@ // TESTCASE NUMBER: 1 fun case_1(value_1: Int): String = when { - else -> "" + else -> "" value_1 == 1 -> "" } // TESTCASE NUMBER: 2 fun case_2(value_1: Int): String = when { value_1 == 1 -> "" - else -> "" + else -> "" value_1 == 2 -> "" } // TESTCASE NUMBER: 3 fun case_3(): String { when { - else -> return "" + else -> return "" else -> return "" } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 5494b60fda3..8a4a6d52f7f 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression /* @@ -1793,6 +1794,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ELSE_MISPLACED_IN_WHEN) { firDiagnostic -> + ElseMisplacedInWhenImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.TYPE_PARAMETER_IS_NOT_AN_EXPRESSION) { firDiagnostic -> TypeParameterIsNotAnExpressionImpl( firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol(firDiagnostic.a.fir), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 32fab085c1d..745ac1a0942 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -50,6 +50,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget @@ -1260,6 +1261,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = InvalidIfAsExpression::class } + abstract class ElseMisplacedInWhen : KtFirDiagnostic() { + override val diagnosticClass get() = ElseMisplacedInWhen::class + } + abstract class TypeParameterIsNotAnExpression : KtFirDiagnostic() { override val diagnosticClass get() = TypeParameterIsNotAnExpression::class abstract val typeParameter: KtTypeParameterSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 0336d673118..e1b39e34084 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -52,6 +52,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget @@ -2041,6 +2042,13 @@ internal class InvalidIfAsExpressionImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class ElseMisplacedInWhenImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.ElseMisplacedInWhen(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class TypeParameterIsNotAnExpressionImpl( override val typeParameter: KtTypeParameterSymbol, firDiagnostic: FirPsiDiagnostic<*>, diff --git a/idea/testData/checker/When.fir.kt b/idea/testData/checker/When.fir.kt index 8ffc17839e1..fcc1075621c 100644 --- a/idea/testData/checker/When.fir.kt +++ b/idea/testData/checker/When.fir.kt @@ -35,7 +35,7 @@ fun test() { val z = 1 when (z) { - else -> 1 + else -> 1 1 -> 2 }