diff --git a/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/references/Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/references/Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java index 2db6cd3a878..0543ec33422 100644 --- a/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/references/Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java +++ b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/references/Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java @@ -2023,6 +2023,12 @@ public class Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exte runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt"); } + @Test + @TestMetadata("ClassReferenceInIncorrectWhenClause.kt") + public void testClassReferenceInIncorrectWhenClause() throws Exception { + runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.kt"); + } + @Test @TestMetadata("ClassWithMultipleSuperTypeCalls.kt") public void testClassWithMultipleSuperTypeCalls() throws Exception { diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated.java index 8c310c0a70b..a153aef6ad7 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated.java @@ -2023,6 +2023,12 @@ public class FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated ex runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt"); } + @Test + @TestMetadata("ClassReferenceInIncorrectWhenClause.kt") + public void testClassReferenceInIncorrectWhenClause() throws Exception { + runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.kt"); + } + @Test @TestMetadata("ClassWithMultipleSuperTypeCalls.kt") public void testClassWithMultipleSuperTypeCalls() throws Exception { diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java index 105c9fe3dea..acfe3e4fa75 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated.java @@ -2023,6 +2023,12 @@ public class FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exten runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt"); } + @Test + @TestMetadata("ClassReferenceInIncorrectWhenClause.kt") + public void testClassReferenceInIncorrectWhenClause() throws Exception { + runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.kt"); + } + @Test @TestMetadata("ClassWithMultipleSuperTypeCalls.kt") public void testClassWithMultipleSuperTypeCalls() throws Exception { diff --git a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerated.java b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerated.java index 03d9f490470..77818fa1b0a 100644 --- a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerated.java +++ b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerated.java @@ -2023,6 +2023,12 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerate runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt"); } + @Test + @TestMetadata("ClassReferenceInIncorrectWhenClause.kt") + public void testClassReferenceInIncorrectWhenClause() throws Exception { + runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.kt"); + } + @Test @TestMetadata("ClassWithMultipleSuperTypeCalls.kt") public void testClassWithMultipleSuperTypeCalls() throws Exception { diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.kt b/analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.kt new file mode 100644 index 00000000000..4aa4e43664b --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.kt @@ -0,0 +1,7 @@ +class SomeClass() {} + +fun someFun() { + when { + is SomeClass + } +} diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.txt b/analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.txt new file mode 100644 index 00000000000..77d40f58e69 --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/ClassReferenceInIncorrectWhenClause.txt @@ -0,0 +1,2 @@ +Resolved to: +0: (in ROOT) class SomeClass diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java index dab5546a171..c7dad842be6 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java @@ -491,6 +491,12 @@ public class FirOutOfContentRootLazyBodiesCalculatorTestGenerated extends Abstra runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt"); } + @Test + @TestMetadata("invalidWhen.kt") + public void testInvalidWhen() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt"); + } + @Test @TestMetadata("labelForInfix.kt") public void testLabelForInfix() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java index a314afd814c..c8bad488433 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java @@ -491,6 +491,12 @@ public class FirSourceLazyBodiesCalculatorTestGenerated extends AbstractFirSourc runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt"); } + @Test + @TestMetadata("invalidWhen.kt") + public void testInvalidWhen() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt"); + } + @Test @TestMetadata("labelForInfix.kt") public void testLabelForInfix() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 8aba199c0d8..5bbe52a6f87 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -7312,6 +7312,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt"); } + @Test + @TestMetadata("whenWithNoSubjectAndCommas.kt") + public void testWhenWithNoSubjectAndCommas() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt"); + } + @Test @TestMetadata("whenWithNothingTypedSubject.kt") public void testWhenWithNothingTypedSubject() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 650b3c9b8f7..4ff0feb27b8 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -7312,6 +7312,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt"); } + @Test + @TestMetadata("whenWithNoSubjectAndCommas.kt") + public void testWhenWithNoSubjectAndCommas() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt"); + } + @Test @TestMetadata("whenWithNothingTypedSubject.kt") public void testWhenWithNothingTypedSubject() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 939180d5839..f3114ff941d 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -7312,6 +7312,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt"); } + @Test + @TestMetadata("whenWithNoSubjectAndCommas.kt") + public void testWhenWithNoSubjectAndCommas() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt"); + } + @Test @TestMetadata("whenWithNothingTypedSubject.kt") public void testWhenWithNothingTypedSubject() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index a02f8c3f3dd..60cbe15fdfc 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -7318,6 +7318,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt"); } + @Test + @TestMetadata("whenWithNoSubjectAndCommas.kt") + public void testWhenWithNoSubjectAndCommas() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt"); + } + @Test @TestMetadata("whenWithNothingTypedSubject.kt") public void testWhenWithNothingTypedSubject() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index d915c3389c7..3db0e6550f4 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -513,7 +513,8 @@ private fun ConeSimpleDiagnostic.getFactory(source: KtSourceElement): KtDiagnost DiagnosticKind.ExpressionExpected -> when (source.elementType) { KtNodeTypes.BINARY_EXPRESSION -> FirErrors.ASSIGNMENT_IN_EXPRESSION_CONTEXT KtNodeTypes.FUN -> FirErrors.ANONYMOUS_FUNCTION_WITH_NAME - KtNodeTypes.WHEN_CONDITION_IN_RANGE, KtNodeTypes.WHEN_CONDITION_IS_PATTERN -> FirErrors.EXPECTED_CONDITION + KtNodeTypes.WHEN_CONDITION_IN_RANGE, KtNodeTypes.WHEN_CONDITION_IS_PATTERN, KtNodeTypes.WHEN_CONDITION_EXPRESSION -> + FirErrors.EXPECTED_CONDITION else -> FirErrors.EXPRESSION_EXPECTED } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index 0c106ecad47..0f0bf6ceae4 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -738,11 +738,9 @@ class ExpressionsConverter( @OptIn(FirContractViolation::class) val subject = FirExpressionRef() + var shouldBind = hasSubject whenEntryNodes.mapTo(whenEntries) { - convertWhenEntry( - it, - subject.takeIf { hasSubject } - ) + convertWhenEntry(it, subject, hasSubject) } return buildWhenExpression { source = whenExpression.toFirSourceElement() @@ -750,6 +748,7 @@ class ExpressionsConverter( this.subjectVariable = subjectVariable usedAsExpression = whenExpression.usedAsExpression for (entry in whenEntries) { + shouldBind = shouldBind || entry.shouldBindSubject val branch = entry.firBlock val entrySource = entry.node.toFirSourceElement() branches += if (!entry.isElse) { @@ -777,7 +776,7 @@ class ExpressionsConverter( } } }.also { - if (hasSubject) { + if (shouldBind) { subject.bind(it) } } @@ -789,23 +788,33 @@ class ExpressionsConverter( */ private fun convertWhenEntry( whenEntry: LighterASTNode, - whenRefWithSubject: FirExpressionRef? + whenRefWithSubject: FirExpressionRef, + hasSubject: Boolean, ): WhenEntry { var isElse = false var firBlock: FirBlock = buildEmptyExpressionBlock() val conditions = mutableListOf() + var shouldBindSubject = false whenEntry.forEachChildren { when (it.tokenType) { - WHEN_CONDITION_EXPRESSION -> conditions += convertWhenConditionExpression(it, whenRefWithSubject) - WHEN_CONDITION_IN_RANGE -> conditions += convertWhenConditionInRange(it, whenRefWithSubject) - WHEN_CONDITION_IS_PATTERN -> conditions += convertWhenConditionIsPattern(it, whenRefWithSubject) + WHEN_CONDITION_EXPRESSION -> conditions += convertWhenConditionExpression(it, whenRefWithSubject.takeIf { hasSubject }) + WHEN_CONDITION_IN_RANGE -> { + val (condition, shouldBind) = convertWhenConditionInRange(it, whenRefWithSubject, hasSubject) + conditions += condition + shouldBindSubject = shouldBindSubject || shouldBind + } + WHEN_CONDITION_IS_PATTERN -> { + val (condition, shouldBind) = convertWhenConditionIsPattern(it, whenRefWithSubject, hasSubject) + conditions += condition + shouldBindSubject = shouldBindSubject || shouldBind + } ELSE_KEYWORD -> isElse = true BLOCK -> firBlock = declarationsConverter.convertBlock(it) else -> if (it.isExpression()) firBlock = declarationsConverter.convertBlock(it) } } - return WhenEntry(conditions, firBlock, whenEntry, isElse) + return WhenEntry(conditions, firBlock, whenEntry, isElse, shouldBindSubject) } private fun convertWhenConditionExpression( @@ -841,10 +850,13 @@ class ExpressionsConverter( } } + private data class WhenConditionConvertedResults(val expression: FirExpression, val shouldBindSubject: Boolean) + private fun convertWhenConditionInRange( whenCondition: LighterASTNode, - whenRefWithSubject: FirExpressionRef? - ): FirExpression { + whenRefWithSubject: FirExpressionRef, + hasSubject: Boolean, + ): WhenConditionConvertedResults { var isNegate = false var firExpression: FirExpression? = null var conditionSource: KtLightSourceElement? = null @@ -861,19 +873,9 @@ class ExpressionsConverter( } } - val subjectExpression = if (whenRefWithSubject != null) { - buildWhenSubjectExpression { - whenRef = whenRefWithSubject - source = whenCondition.toFirSourceElement() - } - } else { - return buildErrorExpression { - source = whenCondition.toFirSourceElement() - diagnostic = ConeSimpleDiagnostic( - "No expression in condition with expression", - DiagnosticKind.ExpressionExpected - ) - } + val subjectExpression = buildWhenSubjectExpression { + whenRef = whenRefWithSubject + source = whenCondition.toFirSourceElement() } val calculatedFirExpression = firExpression ?: buildErrorExpression( @@ -881,18 +883,34 @@ class ExpressionsConverter( ConeSyntaxDiagnostic("No range in condition with range") ) - return calculatedFirExpression.generateContainsOperation( + val result = calculatedFirExpression.generateContainsOperation( subjectExpression, inverted = isNegate, baseSource = whenCondition.toFirSourceElement(), operationReferenceSource = conditionSource ) + return if (hasSubject) { + WhenConditionConvertedResults(result, false) + } else { + WhenConditionConvertedResults( + buildErrorExpression { + source = whenCondition.toFirSourceElement() + diagnostic = ConeSimpleDiagnostic( + "No expression in condition with expression", + DiagnosticKind.ExpressionExpected + ) + nonExpressionElement = result + }, + true, + ) + } } private fun convertWhenConditionIsPattern( whenCondition: LighterASTNode, - whenRefWithSubject: FirExpressionRef? - ): FirExpression { + whenRefWithSubject: FirExpressionRef, + hasSubject: Boolean, + ): WhenConditionConvertedResults { lateinit var firOperation: FirOperation lateinit var firType: FirTypeRef whenCondition.forEachChildren { @@ -903,27 +921,33 @@ class ExpressionsConverter( } } - val subjectExpression = if (whenRefWithSubject != null) { - buildWhenSubjectExpression { - source = whenCondition.toFirSourceElement() - whenRef = whenRefWithSubject - } - } else { - return buildErrorExpression { - source = whenCondition.toFirSourceElement() - diagnostic = ConeSimpleDiagnostic( - "No expression in condition with expression", - DiagnosticKind.ExpressionExpected - ) - } + val subjectExpression = buildWhenSubjectExpression { + source = whenCondition.toFirSourceElement() + whenRef = whenRefWithSubject } - return buildTypeOperatorCall { + val result = buildTypeOperatorCall { source = whenCondition.toFirSourceElement() operation = firOperation conversionTypeRef = firType argumentList = buildUnaryArgumentList(subjectExpression) } + + return if (hasSubject) { + WhenConditionConvertedResults(result, false) + } else { + WhenConditionConvertedResults( + buildErrorExpression { + source = whenCondition.toFirSourceElement() + diagnostic = ConeSimpleDiagnostic( + "No expression in condition with expression", + DiagnosticKind.ExpressionExpected + ) + nonExpressionElement = result + }, + true, + ) + } } /** diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/WhenEntry.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/WhenEntry.kt index 3261ea92e77..7b93304ddc8 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/WhenEntry.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/WhenEntry.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.lightTree.fir import com.intellij.lang.LighterASTNode +import org.jetbrains.kotlin.KtLightSourceElement import org.jetbrains.kotlin.fir.builder.buildBalancedOrExpressionTree import org.jetbrains.kotlin.fir.diagnostics.ConeSyntaxDiagnostic import org.jetbrains.kotlin.fir.expressions.FirBlock @@ -16,7 +17,8 @@ data class WhenEntry( val conditions: List, val firBlock: FirBlock, val node: LighterASTNode, - val isElse: Boolean = false + val isElse: Boolean = false, + val shouldBindSubject: Boolean = false, ) { fun toFirWhenCondition(): FirExpression { require(conditions.isNotEmpty()) @@ -24,9 +26,9 @@ data class WhenEntry( } fun toFirWhenConditionWithoutSubject(): FirExpression { - return when (val condition = conditions.firstOrNull()) { - null -> buildErrorExpression(null, ConeSyntaxDiagnostic("No expression in condition with expression")) - else -> condition + return when (conditions.size) { + 0 -> buildErrorExpression(null, ConeSyntaxDiagnostic("No expression in condition with expression")) + else -> buildBalancedOrExpressionTree(conditions) } } } diff --git a/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java b/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java index edeea86189f..70bff2bd223 100644 --- a/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java +++ b/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java @@ -446,6 +446,11 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt"); } + @TestMetadata("invalidWhen.kt") + public void testInvalidWhen() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt"); + } + @TestMetadata("labelForInfix.kt") public void testLabelForInfix() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt"); diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index 542b020a128..d2967ffb495 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -2283,6 +2283,7 @@ open class RawFirBuilder( @OptIn(FirContractViolation::class) val ref = FirExpressionRef() + var shouldBind = hasSubject return buildWhenExpression { source = expression.toFirSourceElement() this.subject = subjectExpression @@ -2307,12 +2308,36 @@ open class RawFirBuilder( val ktCondition = entry.conditions.first() buildWhenBranch { source = entrySource - condition = ((ktCondition as? KtWhenConditionWithExpression)?.expression - ?: ktCondition) - .toFirExpression( - "No expression in condition with expression", - DiagnosticKind.ExpressionExpected - ) + condition = + if (entry.conditions.size == 1 && ktCondition is KtWhenConditionWithExpression) { + (ktCondition.expression ?: ktCondition).toFirExpression( + "No expression in condition with expression", + DiagnosticKind.ExpressionExpected, + ) + } else { + buildBalancedOrExpressionTree(entry.conditions.map { condition -> + if (condition is KtWhenConditionWithExpression) { + condition.expression.toFirExpression( + "No expression in condition with expression", + DiagnosticKind.ExpressionExpected + ) + } else { + shouldBind = true + buildErrorExpression { + source = condition.toFirSourceElement() + nonExpressionElement = condition.toFirWhenCondition( + ref, + { toFirExpression(it) }, + { toFirOrErrorType() }, + ) + diagnostic = ConeSimpleDiagnostic( + "No expression in condition with expression", + DiagnosticKind.ExpressionExpected, + ) + } + } + }) + } result = branchBody } } @@ -2325,7 +2350,7 @@ open class RawFirBuilder( } } }.also { - if (hasSubject) { + if (shouldBind) { ref.bind(it) } } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt new file mode 100644 index 00000000000..7a91039728d --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt @@ -0,0 +1,7 @@ +class SomeClass() {} + +fun someFun() { + when { + is SomeClass + } +} diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.lazyBodies.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.lazyBodies.txt new file mode 100644 index 00000000000..968bcc31435 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.lazyBodies.txt @@ -0,0 +1,8 @@ +FILE: invalidWhen.kt + public? final? class SomeClass : R|kotlin/Any| { + public? constructor(): R|SomeClass| { + LAZY_super + } + + } + public? final? fun someFun(): R|kotlin/Unit| { LAZY_BLOCK } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.txt new file mode 100644 index 00000000000..b701c3a5019 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.txt @@ -0,0 +1,14 @@ +FILE: invalidWhen.kt + public? final? class SomeClass : R|kotlin/Any| { + public? [ContainingClassKey=SomeClass] constructor(): R|SomeClass| { + super() + } + + } + public? final? fun someFun(): R|kotlin/Unit| { + when () { + ERROR_EXPR(No expression in condition with expression) -> { + } + } + + } diff --git a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java index df65cb5ff76..504a4c1a2d0 100644 --- a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java +++ b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java @@ -446,6 +446,11 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt"); } + @TestMetadata("invalidWhen.kt") + public void testInvalidWhen() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt"); + } + @TestMetadata("labelForInfix.kt") public void testLabelForInfix() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt"); diff --git a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java index a85a2a24ef3..48c5bd9f028 100644 --- a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java +++ b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java @@ -446,6 +446,11 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt"); } + @TestMetadata("invalidWhen.kt") + public void testInvalidWhen() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt"); + } + @TestMetadata("labelForInfix.kt") public void testLabelForInfix() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt"); diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenSubjectExpressionImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenSubjectExpressionImpl.kt index 1d462b76965..cd9fd55bad0 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenSubjectExpressionImpl.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirWhenSubjectExpressionImpl.kt @@ -27,7 +27,7 @@ internal class FirWhenSubjectExpressionImpl( override var annotations: MutableOrEmptyList, override val whenRef: FirExpressionRef, ) : FirWhenSubjectExpression() { - override val typeRef: FirTypeRef get() = whenRef.value.subject!!.typeRef + override val typeRef: FirTypeRef get() = whenRef.value.subject?.typeRef ?: org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef(source) override fun acceptChildren(visitor: FirVisitor, data: D) { annotations.forEach { it.accept(visitor, data) } diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt index 36861c936dd..9184ad22303 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt @@ -418,7 +418,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator() impl(whenSubjectExpression) { default("typeRef") { - value = "whenRef.value.subject!!.typeRef" + value = "whenRef.value.subject?.typeRef ?: org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef(source)" withGetter = true } useTypes(whenExpression) diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt index 504c905d714..67dbe92b0a7 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.diagnostics import com.intellij.lang.LighterASTNode import com.intellij.openapi.util.Ref import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.TokenSet import com.intellij.util.diff.FlyweightCapableTreeStructure @@ -17,6 +18,7 @@ import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET +import org.jetbrains.kotlin.psi.psiUtil.allChildren import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets @@ -1115,6 +1117,8 @@ object LightTreePositioningStrategies { } } } + + override fun isValid(node: LighterASTNode, tree: FlyweightCapableTreeStructure): Boolean = true } val NON_FINAL_MODIFIER_OR_NAME: LightTreePositioningStrategy = ModifierSetBasedLightTreePositioningStrategy( diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index ac130951134..3949ea36d53 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -992,6 +992,8 @@ object PositioningStrategies { } } } + + override fun isValid(element: PsiElement): Boolean = true } val NON_FINAL_MODIFIER_OR_NAME: PositioningStrategy = diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt657.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/kt657.fir.kt index 3633b010316..2a622d446dd 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/kt657.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/kt657.fir.kt @@ -9,7 +9,7 @@ fun foo() = cond2() -> 2 4 -> 34 Pair(1, 2) -> 3 - in 1..10 -> 34 + in 1..10 -> 34 4 -> 38 is Int -> 33 else -> 34 diff --git a/compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt b/compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt new file mode 100644 index 00000000000..73360b039a9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt @@ -0,0 +1,11 @@ +// FIR_IDENTICAL +fun someFun() { + when { + is SomeClass, is OtherClass + } + + val x = 0 + when { + x == 1, x == 2 -> {} + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 4fd64f4aba3..098d7b774d9 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -7318,6 +7318,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt"); } + @Test + @TestMetadata("whenWithNoSubjectAndCommas.kt") + public void testWhenWithNoSubjectAndCommas() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlStructures/whenWithNoSubjectAndCommas.kt"); + } + @Test @TestMetadata("whenWithNothingTypedSubject.kt") public void testWhenWithNothingTypedSubject() throws Exception { diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt index 36759aae17d..3e1a489f9ee 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt @@ -15,7 +15,7 @@ fun case_1(value_1: TypesProvider) { when { getBoolean(), value_1.getBoolean() -> return value_1.getBoolean() && getBoolean(), getLong() == 1000L -> return - Out(), getLong(), {}, Any(), throw Exception() -> return + Out(), getLong(), {}, Any(), throw Exception() -> return } return diff --git a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java index b60c710ecba..b205bd5c655 100644 --- a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java +++ b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java @@ -491,6 +491,12 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt"); } + @Test + @TestMetadata("invalidWhen.kt") + public void testInvalidWhen() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt"); + } + @Test @TestMetadata("labelForInfix.kt") public void testLabelForInfix() throws Exception { diff --git a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java index 1a0352f7ee4..5012ff1136d 100644 --- a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java +++ b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java @@ -491,6 +491,12 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt"); } + @Test + @TestMetadata("invalidWhen.kt") + public void testInvalidWhen() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/invalidWhen.kt"); + } + @Test @TestMetadata("labelForInfix.kt") public void testLabelForInfix() throws Exception {