[FIR] Store fir for invalid when branches
^KTIJ-25646 fixed Merge-request: KT-MR-10646 Merged-by: Egor Kulikov <Egor.Kulikov@jetbrains.com>
This commit is contained in:
+6
@@ -2023,6 +2023,12 @@ public class Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exte
|
|||||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt");
|
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
|
@Test
|
||||||
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
||||||
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
||||||
|
|||||||
+6
@@ -2023,6 +2023,12 @@ public class FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated ex
|
|||||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt");
|
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
|
@Test
|
||||||
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
||||||
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
||||||
|
|||||||
+6
@@ -2023,6 +2023,12 @@ public class FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exten
|
|||||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt");
|
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
|
@Test
|
||||||
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
||||||
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
||||||
|
|||||||
+6
@@ -2023,6 +2023,12 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerate
|
|||||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/ClassNameBeforeDot.kt");
|
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
|
@Test
|
||||||
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
@TestMetadata("ClassWithMultipleSuperTypeCalls.kt")
|
||||||
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
public void testClassWithMultipleSuperTypeCalls() throws Exception {
|
||||||
|
|||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class SomeClass() {}
|
||||||
|
|
||||||
|
fun someFun() {
|
||||||
|
when {
|
||||||
|
is <caret>SomeClass
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
0: (in ROOT) class SomeClass
|
||||||
+6
@@ -491,6 +491,12 @@ public class FirOutOfContentRootLazyBodiesCalculatorTestGenerated extends Abstra
|
|||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt");
|
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
|
@Test
|
||||||
@TestMetadata("labelForInfix.kt")
|
@TestMetadata("labelForInfix.kt")
|
||||||
public void testLabelForInfix() throws Exception {
|
public void testLabelForInfix() throws Exception {
|
||||||
|
|||||||
+6
@@ -491,6 +491,12 @@ public class FirSourceLazyBodiesCalculatorTestGenerated extends AbstractFirSourc
|
|||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt");
|
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
|
@Test
|
||||||
@TestMetadata("labelForInfix.kt")
|
@TestMetadata("labelForInfix.kt")
|
||||||
public void testLabelForInfix() throws Exception {
|
public void testLabelForInfix() throws Exception {
|
||||||
|
|||||||
+6
@@ -7312,6 +7312,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt");
|
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
|
@Test
|
||||||
@TestMetadata("whenWithNothingTypedSubject.kt")
|
@TestMetadata("whenWithNothingTypedSubject.kt")
|
||||||
public void testWhenWithNothingTypedSubject() throws Exception {
|
public void testWhenWithNothingTypedSubject() throws Exception {
|
||||||
|
|||||||
+6
@@ -7312,6 +7312,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt");
|
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
|
@Test
|
||||||
@TestMetadata("whenWithNothingTypedSubject.kt")
|
@TestMetadata("whenWithNothingTypedSubject.kt")
|
||||||
public void testWhenWithNothingTypedSubject() throws Exception {
|
public void testWhenWithNothingTypedSubject() throws Exception {
|
||||||
|
|||||||
+6
@@ -7312,6 +7312,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt");
|
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
|
@Test
|
||||||
@TestMetadata("whenWithNothingTypedSubject.kt")
|
@TestMetadata("whenWithNothingTypedSubject.kt")
|
||||||
public void testWhenWithNothingTypedSubject() throws Exception {
|
public void testWhenWithNothingTypedSubject() throws Exception {
|
||||||
|
|||||||
+6
@@ -7318,6 +7318,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt");
|
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
|
@Test
|
||||||
@TestMetadata("whenWithNothingTypedSubject.kt")
|
@TestMetadata("whenWithNothingTypedSubject.kt")
|
||||||
public void testWhenWithNothingTypedSubject() throws Exception {
|
public void testWhenWithNothingTypedSubject() throws Exception {
|
||||||
|
|||||||
+2
-1
@@ -513,7 +513,8 @@ private fun ConeSimpleDiagnostic.getFactory(source: KtSourceElement): KtDiagnost
|
|||||||
DiagnosticKind.ExpressionExpected -> when (source.elementType) {
|
DiagnosticKind.ExpressionExpected -> when (source.elementType) {
|
||||||
KtNodeTypes.BINARY_EXPRESSION -> FirErrors.ASSIGNMENT_IN_EXPRESSION_CONTEXT
|
KtNodeTypes.BINARY_EXPRESSION -> FirErrors.ASSIGNMENT_IN_EXPRESSION_CONTEXT
|
||||||
KtNodeTypes.FUN -> FirErrors.ANONYMOUS_FUNCTION_WITH_NAME
|
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
|
else -> FirErrors.EXPRESSION_EXPECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+66
-42
@@ -738,11 +738,9 @@ class ExpressionsConverter(
|
|||||||
|
|
||||||
@OptIn(FirContractViolation::class)
|
@OptIn(FirContractViolation::class)
|
||||||
val subject = FirExpressionRef<FirWhenExpression>()
|
val subject = FirExpressionRef<FirWhenExpression>()
|
||||||
|
var shouldBind = hasSubject
|
||||||
whenEntryNodes.mapTo(whenEntries) {
|
whenEntryNodes.mapTo(whenEntries) {
|
||||||
convertWhenEntry(
|
convertWhenEntry(it, subject, hasSubject)
|
||||||
it,
|
|
||||||
subject.takeIf { hasSubject }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return buildWhenExpression {
|
return buildWhenExpression {
|
||||||
source = whenExpression.toFirSourceElement()
|
source = whenExpression.toFirSourceElement()
|
||||||
@@ -750,6 +748,7 @@ class ExpressionsConverter(
|
|||||||
this.subjectVariable = subjectVariable
|
this.subjectVariable = subjectVariable
|
||||||
usedAsExpression = whenExpression.usedAsExpression
|
usedAsExpression = whenExpression.usedAsExpression
|
||||||
for (entry in whenEntries) {
|
for (entry in whenEntries) {
|
||||||
|
shouldBind = shouldBind || entry.shouldBindSubject
|
||||||
val branch = entry.firBlock
|
val branch = entry.firBlock
|
||||||
val entrySource = entry.node.toFirSourceElement()
|
val entrySource = entry.node.toFirSourceElement()
|
||||||
branches += if (!entry.isElse) {
|
branches += if (!entry.isElse) {
|
||||||
@@ -777,7 +776,7 @@ class ExpressionsConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also {
|
}.also {
|
||||||
if (hasSubject) {
|
if (shouldBind) {
|
||||||
subject.bind(it)
|
subject.bind(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -789,23 +788,33 @@ class ExpressionsConverter(
|
|||||||
*/
|
*/
|
||||||
private fun convertWhenEntry(
|
private fun convertWhenEntry(
|
||||||
whenEntry: LighterASTNode,
|
whenEntry: LighterASTNode,
|
||||||
whenRefWithSubject: FirExpressionRef<FirWhenExpression>?
|
whenRefWithSubject: FirExpressionRef<FirWhenExpression>,
|
||||||
|
hasSubject: Boolean,
|
||||||
): WhenEntry {
|
): WhenEntry {
|
||||||
var isElse = false
|
var isElse = false
|
||||||
var firBlock: FirBlock = buildEmptyExpressionBlock()
|
var firBlock: FirBlock = buildEmptyExpressionBlock()
|
||||||
val conditions = mutableListOf<FirExpression>()
|
val conditions = mutableListOf<FirExpression>()
|
||||||
|
var shouldBindSubject = false
|
||||||
whenEntry.forEachChildren {
|
whenEntry.forEachChildren {
|
||||||
when (it.tokenType) {
|
when (it.tokenType) {
|
||||||
WHEN_CONDITION_EXPRESSION -> conditions += convertWhenConditionExpression(it, whenRefWithSubject)
|
WHEN_CONDITION_EXPRESSION -> conditions += convertWhenConditionExpression(it, whenRefWithSubject.takeIf { hasSubject })
|
||||||
WHEN_CONDITION_IN_RANGE -> conditions += convertWhenConditionInRange(it, whenRefWithSubject)
|
WHEN_CONDITION_IN_RANGE -> {
|
||||||
WHEN_CONDITION_IS_PATTERN -> conditions += convertWhenConditionIsPattern(it, whenRefWithSubject)
|
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
|
ELSE_KEYWORD -> isElse = true
|
||||||
BLOCK -> firBlock = declarationsConverter.convertBlock(it)
|
BLOCK -> firBlock = declarationsConverter.convertBlock(it)
|
||||||
else -> if (it.isExpression()) 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(
|
private fun convertWhenConditionExpression(
|
||||||
@@ -841,10 +850,13 @@ class ExpressionsConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class WhenConditionConvertedResults(val expression: FirExpression, val shouldBindSubject: Boolean)
|
||||||
|
|
||||||
private fun convertWhenConditionInRange(
|
private fun convertWhenConditionInRange(
|
||||||
whenCondition: LighterASTNode,
|
whenCondition: LighterASTNode,
|
||||||
whenRefWithSubject: FirExpressionRef<FirWhenExpression>?
|
whenRefWithSubject: FirExpressionRef<FirWhenExpression>,
|
||||||
): FirExpression {
|
hasSubject: Boolean,
|
||||||
|
): WhenConditionConvertedResults {
|
||||||
var isNegate = false
|
var isNegate = false
|
||||||
var firExpression: FirExpression? = null
|
var firExpression: FirExpression? = null
|
||||||
var conditionSource: KtLightSourceElement? = null
|
var conditionSource: KtLightSourceElement? = null
|
||||||
@@ -861,19 +873,9 @@ class ExpressionsConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val subjectExpression = if (whenRefWithSubject != null) {
|
val subjectExpression = buildWhenSubjectExpression {
|
||||||
buildWhenSubjectExpression {
|
whenRef = whenRefWithSubject
|
||||||
whenRef = whenRefWithSubject
|
source = whenCondition.toFirSourceElement()
|
||||||
source = whenCondition.toFirSourceElement()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return buildErrorExpression {
|
|
||||||
source = whenCondition.toFirSourceElement()
|
|
||||||
diagnostic = ConeSimpleDiagnostic(
|
|
||||||
"No expression in condition with expression",
|
|
||||||
DiagnosticKind.ExpressionExpected
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val calculatedFirExpression = firExpression ?: buildErrorExpression(
|
val calculatedFirExpression = firExpression ?: buildErrorExpression(
|
||||||
@@ -881,18 +883,34 @@ class ExpressionsConverter(
|
|||||||
ConeSyntaxDiagnostic("No range in condition with range")
|
ConeSyntaxDiagnostic("No range in condition with range")
|
||||||
)
|
)
|
||||||
|
|
||||||
return calculatedFirExpression.generateContainsOperation(
|
val result = calculatedFirExpression.generateContainsOperation(
|
||||||
subjectExpression,
|
subjectExpression,
|
||||||
inverted = isNegate,
|
inverted = isNegate,
|
||||||
baseSource = whenCondition.toFirSourceElement(),
|
baseSource = whenCondition.toFirSourceElement(),
|
||||||
operationReferenceSource = conditionSource
|
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(
|
private fun convertWhenConditionIsPattern(
|
||||||
whenCondition: LighterASTNode,
|
whenCondition: LighterASTNode,
|
||||||
whenRefWithSubject: FirExpressionRef<FirWhenExpression>?
|
whenRefWithSubject: FirExpressionRef<FirWhenExpression>,
|
||||||
): FirExpression {
|
hasSubject: Boolean,
|
||||||
|
): WhenConditionConvertedResults {
|
||||||
lateinit var firOperation: FirOperation
|
lateinit var firOperation: FirOperation
|
||||||
lateinit var firType: FirTypeRef
|
lateinit var firType: FirTypeRef
|
||||||
whenCondition.forEachChildren {
|
whenCondition.forEachChildren {
|
||||||
@@ -903,27 +921,33 @@ class ExpressionsConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val subjectExpression = if (whenRefWithSubject != null) {
|
val subjectExpression = buildWhenSubjectExpression {
|
||||||
buildWhenSubjectExpression {
|
source = whenCondition.toFirSourceElement()
|
||||||
source = whenCondition.toFirSourceElement()
|
whenRef = whenRefWithSubject
|
||||||
whenRef = whenRefWithSubject
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return buildErrorExpression {
|
|
||||||
source = whenCondition.toFirSourceElement()
|
|
||||||
diagnostic = ConeSimpleDiagnostic(
|
|
||||||
"No expression in condition with expression",
|
|
||||||
DiagnosticKind.ExpressionExpected
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildTypeOperatorCall {
|
val result = buildTypeOperatorCall {
|
||||||
source = whenCondition.toFirSourceElement()
|
source = whenCondition.toFirSourceElement()
|
||||||
operation = firOperation
|
operation = firOperation
|
||||||
conversionTypeRef = firType
|
conversionTypeRef = firType
|
||||||
argumentList = buildUnaryArgumentList(subjectExpression)
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
-4
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.fir.lightTree.fir
|
package org.jetbrains.kotlin.fir.lightTree.fir
|
||||||
|
|
||||||
import com.intellij.lang.LighterASTNode
|
import com.intellij.lang.LighterASTNode
|
||||||
|
import org.jetbrains.kotlin.KtLightSourceElement
|
||||||
import org.jetbrains.kotlin.fir.builder.buildBalancedOrExpressionTree
|
import org.jetbrains.kotlin.fir.builder.buildBalancedOrExpressionTree
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSyntaxDiagnostic
|
import org.jetbrains.kotlin.fir.diagnostics.ConeSyntaxDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirBlock
|
import org.jetbrains.kotlin.fir.expressions.FirBlock
|
||||||
@@ -16,7 +17,8 @@ data class WhenEntry(
|
|||||||
val conditions: List<FirExpression>,
|
val conditions: List<FirExpression>,
|
||||||
val firBlock: FirBlock,
|
val firBlock: FirBlock,
|
||||||
val node: LighterASTNode,
|
val node: LighterASTNode,
|
||||||
val isElse: Boolean = false
|
val isElse: Boolean = false,
|
||||||
|
val shouldBindSubject: Boolean = false,
|
||||||
) {
|
) {
|
||||||
fun toFirWhenCondition(): FirExpression {
|
fun toFirWhenCondition(): FirExpression {
|
||||||
require(conditions.isNotEmpty())
|
require(conditions.isNotEmpty())
|
||||||
@@ -24,9 +26,9 @@ data class WhenEntry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun toFirWhenConditionWithoutSubject(): FirExpression {
|
fun toFirWhenConditionWithoutSubject(): FirExpression {
|
||||||
return when (val condition = conditions.firstOrNull()) {
|
return when (conditions.size) {
|
||||||
null -> buildErrorExpression(null, ConeSyntaxDiagnostic("No expression in condition with expression"))
|
0 -> buildErrorExpression(null, ConeSyntaxDiagnostic("No expression in condition with expression"))
|
||||||
else -> condition
|
else -> buildBalancedOrExpressionTree(conditions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -446,6 +446,11 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F
|
|||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt");
|
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")
|
@TestMetadata("labelForInfix.kt")
|
||||||
public void testLabelForInfix() throws Exception {
|
public void testLabelForInfix() throws Exception {
|
||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt");
|
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt");
|
||||||
|
|||||||
@@ -2283,6 +2283,7 @@ open class RawFirBuilder(
|
|||||||
|
|
||||||
@OptIn(FirContractViolation::class)
|
@OptIn(FirContractViolation::class)
|
||||||
val ref = FirExpressionRef<FirWhenExpression>()
|
val ref = FirExpressionRef<FirWhenExpression>()
|
||||||
|
var shouldBind = hasSubject
|
||||||
return buildWhenExpression {
|
return buildWhenExpression {
|
||||||
source = expression.toFirSourceElement()
|
source = expression.toFirSourceElement()
|
||||||
this.subject = subjectExpression
|
this.subject = subjectExpression
|
||||||
@@ -2307,12 +2308,36 @@ open class RawFirBuilder(
|
|||||||
val ktCondition = entry.conditions.first()
|
val ktCondition = entry.conditions.first()
|
||||||
buildWhenBranch {
|
buildWhenBranch {
|
||||||
source = entrySource
|
source = entrySource
|
||||||
condition = ((ktCondition as? KtWhenConditionWithExpression)?.expression
|
condition =
|
||||||
?: ktCondition)
|
if (entry.conditions.size == 1 && ktCondition is KtWhenConditionWithExpression) {
|
||||||
.toFirExpression(
|
(ktCondition.expression ?: ktCondition).toFirExpression(
|
||||||
"No expression in condition with expression",
|
"No expression in condition with expression",
|
||||||
DiagnosticKind.ExpressionExpected
|
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
|
result = branchBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2325,7 +2350,7 @@ open class RawFirBuilder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also {
|
}.also {
|
||||||
if (hasSubject) {
|
if (shouldBind) {
|
||||||
ref.bind(it)
|
ref.bind(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class SomeClass() {}
|
||||||
|
|
||||||
|
fun someFun() {
|
||||||
|
when {
|
||||||
|
is SomeClass
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
FILE: invalidWhen.kt
|
||||||
|
public? final? class SomeClass : R|kotlin/Any| {
|
||||||
|
public? constructor(): R|SomeClass| {
|
||||||
|
LAZY_super<R|kotlin/Any|>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public? final? fun someFun(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
FILE: invalidWhen.kt
|
||||||
|
public? final? class SomeClass : R|kotlin/Any| {
|
||||||
|
public? [ContainingClassKey=SomeClass] constructor(): R|SomeClass| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public? final? fun someFun(): R|kotlin/Unit| {
|
||||||
|
when () {
|
||||||
|
ERROR_EXPR(No expression in condition with expression) -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+5
@@ -446,6 +446,11 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil
|
|||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt");
|
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")
|
@TestMetadata("labelForInfix.kt")
|
||||||
public void testLabelForInfix() throws Exception {
|
public void testLabelForInfix() throws Exception {
|
||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt");
|
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt");
|
||||||
|
|||||||
+5
@@ -446,6 +446,11 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas
|
|||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt");
|
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")
|
@TestMetadata("labelForInfix.kt")
|
||||||
public void testLabelForInfix() throws Exception {
|
public void testLabelForInfix() throws Exception {
|
||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt");
|
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/labelForInfix.kt");
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ internal class FirWhenSubjectExpressionImpl(
|
|||||||
override var annotations: MutableOrEmptyList<FirAnnotation>,
|
override var annotations: MutableOrEmptyList<FirAnnotation>,
|
||||||
override val whenRef: FirExpressionRef<FirWhenExpression>,
|
override val whenRef: FirExpressionRef<FirWhenExpression>,
|
||||||
) : FirWhenSubjectExpression() {
|
) : 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 <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
|
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
|
||||||
annotations.forEach { it.accept(visitor, data) }
|
annotations.forEach { it.accept(visitor, data) }
|
||||||
|
|||||||
+1
-1
@@ -418,7 +418,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
|
|||||||
|
|
||||||
impl(whenSubjectExpression) {
|
impl(whenSubjectExpression) {
|
||||||
default("typeRef") {
|
default("typeRef") {
|
||||||
value = "whenRef.value.subject!!.typeRef"
|
value = "whenRef.value.subject?.typeRef ?: org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef(source)"
|
||||||
withGetter = true
|
withGetter = true
|
||||||
}
|
}
|
||||||
useTypes(whenExpression)
|
useTypes(whenExpression)
|
||||||
|
|||||||
+4
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.diagnostics
|
|||||||
import com.intellij.lang.LighterASTNode
|
import com.intellij.lang.LighterASTNode
|
||||||
import com.intellij.openapi.util.Ref
|
import com.intellij.openapi.util.Ref
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.tree.IElementType
|
import com.intellij.psi.tree.IElementType
|
||||||
import com.intellij.psi.tree.TokenSet
|
import com.intellij.psi.tree.TokenSet
|
||||||
import com.intellij.util.diff.FlyweightCapableTreeStructure
|
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.lexer.KtTokens.*
|
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||||
import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET
|
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.KtConstantExpressionElementType
|
||||||
import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType
|
import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType
|
||||||
import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets
|
import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets
|
||||||
@@ -1115,6 +1117,8 @@ object LightTreePositioningStrategies {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isValid(node: LighterASTNode, tree: FlyweightCapableTreeStructure<LighterASTNode>): Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val NON_FINAL_MODIFIER_OR_NAME: LightTreePositioningStrategy = ModifierSetBasedLightTreePositioningStrategy(
|
val NON_FINAL_MODIFIER_OR_NAME: LightTreePositioningStrategy = ModifierSetBasedLightTreePositioningStrategy(
|
||||||
|
|||||||
+2
@@ -992,6 +992,8 @@ object PositioningStrategies {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isValid(element: PsiElement): Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val NON_FINAL_MODIFIER_OR_NAME: PositioningStrategy<KtModifierListOwner> =
|
val NON_FINAL_MODIFIER_OR_NAME: PositioningStrategy<KtModifierListOwner> =
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fun foo() =
|
|||||||
cond2() -> 2
|
cond2() -> 2
|
||||||
<!CONDITION_TYPE_MISMATCH!>4<!> -> 34
|
<!CONDITION_TYPE_MISMATCH!>4<!> -> 34
|
||||||
<!CONDITION_TYPE_MISMATCH, TYPE_MISMATCH!>Pair(1, 2)<!> -> 3
|
<!CONDITION_TYPE_MISMATCH, TYPE_MISMATCH!>Pair(1, 2)<!> -> 3
|
||||||
<!EXPECTED_CONDITION!>in 1..10<!> -> 34
|
<!ARGUMENT_TYPE_MISMATCH, EXPECTED_CONDITION!>in 1..10<!> -> 34
|
||||||
<!CONDITION_TYPE_MISMATCH!>4<!> -> 38
|
<!CONDITION_TYPE_MISMATCH!>4<!> -> 38
|
||||||
<!EXPECTED_CONDITION!>is Int<!> -> 33
|
<!EXPECTED_CONDITION!>is Int<!> -> 33
|
||||||
else -> 34
|
else -> 34
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
fun someFun() {
|
||||||
|
when {
|
||||||
|
<!EXPECTED_CONDITION!>is <!UNRESOLVED_REFERENCE!>SomeClass<!><!><!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> <!EXPECTED_CONDITION!>is <!UNRESOLVED_REFERENCE!>OtherClass<!><!><!SYNTAX!><!>
|
||||||
|
}
|
||||||
|
|
||||||
|
val x = 0
|
||||||
|
when {
|
||||||
|
x == 1<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> x == 2 -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+6
@@ -7318,6 +7318,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt");
|
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
|
@Test
|
||||||
@TestMetadata("whenWithNothingTypedSubject.kt")
|
@TestMetadata("whenWithNothingTypedSubject.kt")
|
||||||
public void testWhenWithNothingTypedSubject() throws Exception {
|
public void testWhenWithNothingTypedSubject() throws Exception {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -15,7 +15,7 @@ fun case_1(value_1: TypesProvider) {
|
|||||||
when {
|
when {
|
||||||
getBoolean()<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> value_1.getBoolean() -> return
|
getBoolean()<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> value_1.getBoolean() -> return
|
||||||
value_1.getBoolean() && getBoolean()<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> getLong() == 1000L -> return
|
value_1.getBoolean() && getBoolean()<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> getLong() == 1000L -> return
|
||||||
<!CONDITION_TYPE_MISMATCH, TYPE_MISMATCH!>Out<Int>()<!><!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> getLong()<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> {}<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> Any()<!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> throw Exception() -> return
|
<!CONDITION_TYPE_MISMATCH, TYPE_MISMATCH!>Out<Int>()<!><!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> <!CONDITION_TYPE_MISMATCH, TYPE_MISMATCH!>getLong()<!><!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> <!CONDITION_TYPE_MISMATCH!>{}<!><!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> <!CONDITION_TYPE_MISMATCH, TYPE_MISMATCH!>Any()<!><!COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT!>,<!> throw Exception() -> return
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
+6
@@ -491,6 +491,12 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe
|
|||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt");
|
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
|
@Test
|
||||||
@TestMetadata("labelForInfix.kt")
|
@TestMetadata("labelForInfix.kt")
|
||||||
public void testLabelForInfix() throws Exception {
|
public void testLabelForInfix() throws Exception {
|
||||||
|
|||||||
+6
@@ -491,6 +491,12 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe
|
|||||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/init.kt");
|
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
|
@Test
|
||||||
@TestMetadata("labelForInfix.kt")
|
@TestMetadata("labelForInfix.kt")
|
||||||
public void testLabelForInfix() throws Exception {
|
public void testLabelForInfix() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user