[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:
Egor Kulikov
2023-06-20 16:11:31 +00:00
committed by Space Team
parent 37876313c9
commit b147b7e929
32 changed files with 258 additions and 58 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -0,0 +1,7 @@
class SomeClass() {}
fun someFun() {
when {
is <caret>SomeClass
}
}
@@ -0,0 +1,2 @@
Resolved to:
0: (in ROOT) class SomeClass
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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
} }
@@ -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,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)
} }
} }
} }
@@ -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
}
}
@@ -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) -> {
}
}
}
@@ -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");
@@ -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");
@@ -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) }
@@ -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)
@@ -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(
@@ -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
@@ -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 -> {}
}
}
@@ -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 {
@@ -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
@@ -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 {
@@ -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 {