[FIR] Declarations in conditionals, prototype
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt
index 5b16d6d..90ab745 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt
@@ -19,6 +19,7 @@
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirAnonymousFunctionExpression
import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression
+import org.jetbrains.kotlin.fir.expressions.FirVariableInConditionalExpression
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -116,6 +117,7 @@
private fun findDeclarationInSourceViaResolve(ktDeclaration: KtExpression): FirBasedSymbol<*> {
val firDeclaration = when (val fir = getOrBuildFirFor(ktDeclaration)) {
is FirDeclaration -> fir
+ is FirVariableInConditionalExpression -> fir.declaration
is FirAnonymousFunctionExpression -> fir.anonymousFunction
is FirAnonymousObjectExpression -> fir.anonymousObject
else -> errorWithFirSpecificEntries(
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java
index 910b253..b713e39 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java
@@ -159,6 +159,12 @@
}
@Test
+ @TestMetadata("DeclarationsInConditionals.kt")
+ public void testDeclarationsInConditionals() {
+ runTest("compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.kt");
+ }
+
+ @Test
@TestMetadata("defaultJavaImportHiding.kt")
public void testDefaultJavaImportHiding() {
runTest("compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt");
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java
index b18966c..a75d677 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java
@@ -159,6 +159,12 @@
}
@Test
+ @TestMetadata("DeclarationsInConditionals.kt")
+ public void testDeclarationsInConditionals() {
+ runTest("compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.kt");
+ }
+
+ @Test
@TestMetadata("defaultJavaImportHiding.kt")
public void testDefaultJavaImportHiding() {
runTest("compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt");
diff --git a/compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.fir.txt b/compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.fir.txt
new file mode 100644
index 0000000..389d2d8
--- /dev/null
+++ b/compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.fir.txt
@@ -0,0 +1,92 @@
+FILE: DeclarationsInConditionals.kt
+ public open class Developer : R|kotlin/Any| {
+ public constructor(): R|Developer| {
+ super<R|kotlin/Any|>()
+ }
+
+ }
+ public final class Person : R|Developer| {
+ public constructor(name: R|kotlin/String|): R|Person| {
+ super<R|Developer|>()
+ }
+
+ public final val name: R|kotlin/String| = R|<local>/name|
+ public get(): R|kotlin/String|
+
+ }
+ public final class Company : R|Developer| {
+ public constructor(legalName: R|kotlin/String|, cto: R|Person|): R|Company| {
+ super<R|Developer|>()
+ }
+
+ public final val legalName: R|kotlin/String| = R|<local>/legalName|
+ public get(): R|kotlin/String|
+
+ public final val cto: R|Person| = R|<local>/cto|
+ public get(): R|Person|
+
+ }
+ public sealed class Download : R|kotlin/Any| {
+ protected constructor(): R|Download| {
+ super<R|kotlin/Any|>()
+ }
+
+ }
+ public final data class App : R|Download| {
+ public constructor(title: R|kotlin/String|, developer: R|Developer|): R|App| {
+ super<R|Download|>()
+ }
+
+ public final val title: R|kotlin/String| = R|<local>/title|
+ public get(): R|kotlin/String|
+
+ public final val developer: R|Developer| = R|<local>/developer|
+ public get(): R|Developer|
+
+ public final operator fun component1(): R|kotlin/String|
+
+ public final operator fun component2(): R|Developer|
+
+ public final fun copy(title: R|kotlin/String| = this@R|/App|.R|/App.title|, developer: R|Developer| = this@R|/App|.R|/App.developer|): R|App|
+
+ }
+ public final data class Movie : R|Download| {
+ public constructor(title: R|kotlin/String|, director: R|Person|): R|Movie| {
+ super<R|Download|>()
+ }
+
+ public final val title: R|kotlin/String| = R|<local>/title|
+ public get(): R|kotlin/String|
+
+ public final val director: R|Person| = R|<local>/director|
+ public get(): R|Person|
+
+ public final operator fun component1(): R|kotlin/String|
+
+ public final operator fun component2(): R|Person|
+
+ public final fun copy(title: R|kotlin/String| = this@R|/Movie|.R|/Movie.title|, director: R|Person| = this@R|/Movie|.R|/Movie.director|): R|Movie|
+
+ }
+ public final fun f(download: R|Download|): R|kotlin/String| {
+ ^f when () {
+ (R|<local>/download| is R|App|) && lval <destruct>: R|Download| = R|<local>/download| && lval title: R|kotlin/String| = R|<local>/<destruct>|.R|/App.component1|() && lval developer: R|Developer| = R|<local>/<destruct>|.R|/App.component2|() && (R|<local>/developer| is R|Person|) && ==(R|<local>/developer|.R|/Person.name|, String(Alice)) -> {
+ <strcat>(R|<local>/title|, String( in Wonderland))
+ }
+ else -> {
+ String(Boo)
+ }
+ }
+
+ }
+ public final fun g(download: R|Download|): R|kotlin/String| {
+ ^g when () {
+ lval <isDestructor>: R|Download| = R|<local>/download| && (R|<local>/<isDestructor>| is R|App|) && lval title: R|kotlin/String| = R|<local>/<isDestructor>|.R|/App.component1|() && lval developer: R|Developer| = R|<local>/<isDestructor>|.R|/App.component2|() && (R|<local>/developer| is R|Person|) && ==(R|<local>/developer|.R|/Person.name|, String(Alice)) -> {
+ <strcat>(R|<local>/title|, String( in Wonderland))
+ }
+ else -> {
+ String(Boo)
+ }
+ }
+
+ }
diff --git a/compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.kt b/compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.kt
new file mode 100644
index 0000000..8625b17
--- /dev/null
+++ b/compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.kt
@@ -0,0 +1,22 @@
+open class Developer
+class Person(val name: String) : Developer()
+class Company(val legalName: String, val cto: Person) : Developer()
+
+sealed class Download
+data class App(val title: String, val developer: Developer) : Download()
+data class Movie(val title: String, val director: Person) : Download()
+
+fun f(download: Download) = when {
+ download is App
+ && val (title, developer) = download
+ && developer is Person
+ && developer.name == "Alice" -> "$title in Wonderland"
+ else -> "Boo"
+}
+
+fun g(download: Download) = when {
+ download is App (title, developer)
+ && developer is Person
+ && developer.name == "Alice" -> "$title in Wonderland"
+ else -> "Boo"
+}
diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java
index af16b67..f7626e6 100644
--- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java
+++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java
@@ -159,6 +159,12 @@
}
@Test
+ @TestMetadata("DeclarationsInConditionals.kt")
+ public void testDeclarationsInConditionals() {
+ runTest("compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.kt");
+ }
+
+ @Test
@TestMetadata("defaultJavaImportHiding.kt")
public void testDefaultJavaImportHiding() {
runTest("compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt");
diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java
index 4e1d46c..ef70e04 100644
--- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java
+++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java
@@ -159,6 +159,12 @@
}
@Test
+ @TestMetadata("DeclarationsInConditionals.kt")
+ public void testDeclarationsInConditionals() {
+ runTest("compiler/fir/analysis-tests/testData/resolve/DeclarationsInConditionals.kt");
+ }
+
+ @Test
@TestMetadata("defaultJavaImportHiding.kt")
public void testDefaultJavaImportHiding() {
runTest("compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt");
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt
index ca272b7..09589ab 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt
@@ -113,6 +113,13 @@
checkers.allWhenExpressionCheckers.check(whenExpression, data)
}
+ override fun visitVariableInConditionalExpression(
+ variableInConditionalExpression: FirVariableInConditionalExpression,
+ data: CheckerContext
+ ) {
+ checkers.allBasicExpressionCheckers.check(variableInConditionalExpression, data)
+ }
+
override fun visitWhileLoop(whileLoop: FirWhileLoop, data: CheckerContext) {
checkers.allWhileLoopCheckers.check(whileLoop, data)
}
diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt
index df78952..224ac56 100644
--- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt
+++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt
@@ -1270,7 +1270,7 @@
/**
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseProperty
*/
- fun convertPropertyDeclaration(property: LighterASTNode, classWrapper: ClassWrapper? = null): FirDeclaration {
+ fun convertPropertyDeclaration(property: LighterASTNode, classWrapper: ClassWrapper? = null): FirVariable {
var modifiers: Modifier? = null
val propertyAnnotations = mutableListOf<FirAnnotationCall>()
var identifier: String? = null
diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt
index 66b7d7e..2b6dc57 100644
--- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt
+++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt
@@ -30,6 +30,7 @@
import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.expressions.impl.buildSingleExpressionBlock
+import org.jetbrains.kotlin.fir.lightTree.fir.DestructuringEntry
import org.jetbrains.kotlin.fir.lightTree.fir.ValueParameter
import org.jetbrains.kotlin.fir.lightTree.fir.WhenEntry
import org.jetbrains.kotlin.fir.lightTree.fir.addDestructuringStatements
@@ -106,8 +107,42 @@
BINARY_WITH_TYPE -> convertBinaryWithTypeRHSExpression(expression) {
this.getOperationSymbol().toFirOperation()
}
- IS_EXPRESSION -> convertBinaryWithTypeRHSExpression(expression) {
- if (this == "is") FirOperation.IS else FirOperation.NOT_IS
+ IS_EXPRESSION -> {
+ val destructuringEntries = expression.getChildNodesByType(KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY)
+ when {
+ destructuringEntries.isNullOrEmpty() -> convertBinaryWithTypeRHSExpression(expression) {
+ if (this == "is") FirOperation.IS else FirOperation.NOT_IS
+ }
+ else -> {
+ lateinit var operationTokenName: String
+ var leftArgAsFir: FirExpression? = null
+ lateinit var firType: FirTypeRef
+ expression.forEachChildren {
+ when (it.tokenType) {
+ OPERATION_REFERENCE -> operationTokenName = it.asText
+ TYPE_REFERENCE -> firType = declarationBuilder.convertType(it)
+ else ->
+ if (it.isExpression() && it.elementType != KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY)
+ leftArgAsFir = getAsFirExpression(it, "No left operand")
+ }
+ }
+ val entries = declarationBuilder.convertDestructingDeclaration(expression)
+ buildIsDestructuring(
+ baseModuleData,
+ expression.toFirSourceElement(),
+ DestructuringEntry,
+ leftArgAsFir!!,
+ entries.entries,
+ ) {
+ buildTypeOperatorCall {
+ source = expression.toFirSourceElement()
+ operation = if (operationTokenName == "is") FirOperation.IS else FirOperation.NOT_IS
+ conversionTypeRef = firType
+ argumentList = buildUnaryArgumentList(it)
+ }
+ }
+ }
+ }
}
LABELED_EXPRESSION -> convertLabeledExpression(expression)
PREFIX_EXPRESSION, POSTFIX_EXPRESSION -> convertUnaryExpression(expression)
@@ -142,8 +177,24 @@
OBJECT_LITERAL -> declarationBuilder.convertObjectLiteral(expression)
FUN -> declarationBuilder.convertFunctionDeclaration(expression)
- DESTRUCTURING_DECLARATION -> declarationBuilder.convertDestructingDeclaration(expression)
- .toFirDestructingDeclaration(this, baseModuleData)
+ DESTRUCTURING_DECLARATION -> {
+ val block = declarationBuilder.convertDestructingDeclaration(expression)
+ .toFirDestructingDeclaration(this, baseModuleData)
+ if (block is FirBlock && block.statements.all { it is FirVariable }) {
+ block.statements.filterIsInstance<FirVariable>().map<_, FirExpression> {
+ buildVariableInConditionalExpression {
+ declaration = it
+ source = it.source?.realElement()
+ }
+ }.reduce { acc, next -> acc.generateLazyLogicalOperation(next, isAnd = true, null) }
+ } else block
+ }
+ PROPERTY -> declarationBuilder.convertPropertyDeclaration(expression).let { property ->
+ buildVariableInConditionalExpression {
+ declaration = property
+ source = property.source?.realElement()
+ }
+ }
else -> buildErrorExpression(
expression.toFirSourceElement(KtFakeSourceElementKind.ErrorTypeRef),
ConeSimpleDiagnostic(errorReason, DiagnosticKind.ExpressionExpected)
diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiConversionUtils.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiConversionUtils.kt
index 1f02378..2b788da 100644
--- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiConversionUtils.kt
+++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiConversionUtils.kt
@@ -165,3 +165,27 @@
configure
)
}
+
+internal fun AbstractRawFirBuilder<*>.addDestructuringVariables(
+ destination: MutableList<in FirVariable>,
+ c: DestructuringContext<KtDestructuringDeclarationEntry>,
+ moduleData: FirModuleData,
+ entries: List<KtDestructuringDeclarationEntry>,
+ isVar: Boolean,
+ container: FirVariable,
+ tmpVariable: Boolean,
+ forceLocal: Boolean,
+ configure: (FirVariable) -> Unit = {}
+) {
+ addDestructuringVariables(
+ destination,
+ c,
+ moduleData,
+ container,
+ entries,
+ isVar,
+ tmpVariable,
+ forceLocal,
+ configure
+ )
+}
diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt
index dd77f18..fa99ca9 100644
--- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt
+++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt
@@ -312,6 +312,20 @@
return when (val fir = convertElement(this, null)) {
is FirExpression -> when {
+ fir is FirBlock && fir.statements.isNotEmpty() && fir.statements.all { it is FirVariable } -> {
+ fir.statements.filterIsInstance<FirVariable>().map<_, FirExpression> {
+ buildVariableInConditionalExpression {
+ declaration = it
+ source = it.source
+ }
+ }.reduce { acc, next ->
+ acc.generateLazyLogicalOperation(
+ next,
+ isAnd = true,
+ toFirSourceElement(KtFakeSourceElementKind.WhenCondition)
+ )
+ }
+ }
isValidExpression(fir) -> checkSelectorInvariant(fir)
else -> buildErrorExpression {
nonExpressionElement = fir
@@ -319,10 +333,16 @@
source = sourceWhenInvalidExpression?.toFirSourceElement()
}
}
- else -> buildErrorExpression {
- nonExpressionElement = fir
- diagnostic = diagnosticFn()
- source = fir?.source?.realElement() ?: toFirSourceElement()
+ else -> when {
+ fir is FirVariable -> buildVariableInConditionalExpression {
+ declaration = fir
+ source = fir.source
+ }
+ else -> buildErrorExpression {
+ nonExpressionElement = fir
+ diagnostic = diagnosticFn()
+ source = fir?.source?.realElement() ?: toFirSourceElement()
+ }
}
}
}
@@ -2934,14 +2954,30 @@
}
}
- override fun visitIsExpression(expression: KtIsExpression, data: FirElement?): FirElement {
- return buildTypeOperatorCall {
- source = expression.toFirSourceElement()
- operation = if (expression.isNegated) FirOperation.NOT_IS else FirOperation.IS
- conversionTypeRef = expression.typeReference.toFirOrErrorType()
- argumentList = buildUnaryArgumentList(expression.leftHandSide.toFirExpression("No left operand"))
+ override fun visitIsExpression(expression: KtIsExpression, data: FirElement?): FirElement =
+ if (expression.destructuringEntries.isNullOrEmpty()) {
+ buildTypeOperatorCall {
+ source = expression.toFirSourceElement()
+ operation = if (expression.isNegated) FirOperation.NOT_IS else FirOperation.IS
+ conversionTypeRef = expression.typeReference.toFirOrErrorType()
+ argumentList = buildUnaryArgumentList(expression.leftHandSide.toFirExpression("No left operand"))
+ }
+ } else {
+ buildIsDestructuring(
+ baseModuleData,
+ expression.toFirSourceElement(),
+ this@Visitor,
+ expression.leftHandSide.toFirExpression { ConeSyntaxDiagnostic("Initializer required for destructuring declaration") },
+ expression.destructuringEntries!!
+ ) {
+ buildTypeOperatorCall {
+ source = expression.toFirSourceElement()
+ operation = if (expression.isNegated) FirOperation.NOT_IS else FirOperation.IS
+ conversionTypeRef = expression.typeReference.toFirOrErrorType()
+ argumentList = buildUnaryArgumentList(it)
+ }
+ }
}
- }
override fun visitUnaryExpression(expression: KtUnaryExpression, data: FirElement?): FirElement {
val operationToken = expression.operationToken
diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Destructuring.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Destructuring.kt
index da3a7f2..4ec8590 100644
--- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Destructuring.kt
+++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Destructuring.kt
@@ -18,6 +18,12 @@
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter
import org.jetbrains.kotlin.fir.expressions.FirExpression
+import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall
+import org.jetbrains.kotlin.fir.expressions.builder.buildBlock
+import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression
+import org.jetbrains.kotlin.fir.expressions.builder.buildVariableInConditionalExpression
+import org.jetbrains.kotlin.fir.generateTemporaryVariable
+import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.FirTypeRef
@@ -100,3 +106,58 @@
}
}.also(configure)
}
+
+fun <T> AbstractRawFirBuilder<*>.buildIsDestructuring(
+ moduleData: FirModuleData,
+ originalSource: KtSourceElement?,
+ c: DestructuringContext<T>,
+ leftHandSide: FirExpression,
+ destructuringEntries: List<T>,
+ buildTypeOperator: (FirExpression) -> FirTypeOperatorCall
+): FirExpression {
+ val baseVariable = generateTemporaryVariable(
+ moduleData,
+ originalSource,
+ Name.special("<isDestructor>"),
+ leftHandSide
+ )
+ val wrappedBaseVariable =
+ buildVariableInConditionalExpression {
+ declaration = baseVariable
+ source = baseVariable.source
+ }
+ val variableAccess = buildPropertyAccessExpression {
+ calleeReference = buildResolvedNamedReference {
+ name = baseVariable.name
+ resolvedSymbol = baseVariable.symbol
+ }
+ source = originalSource
+ }
+ val typeOperator = buildTypeOperator(variableAccess)
+ val block = buildBlock {
+ addDestructuringVariables(
+ statements,
+ c,
+ baseModuleData,
+ baseVariable,
+ destructuringEntries,
+ false,
+ false,
+ false
+ )
+ }
+ val code: List<FirExpression> =
+ listOf(wrappedBaseVariable, typeOperator) + block.statements.filterIsInstance<FirVariable>().map<_, FirExpression> {
+ buildVariableInConditionalExpression {
+ declaration = it
+ source = it.source
+ }
+ }
+ return code.reduce { acc, next ->
+ acc.generateLazyLogicalOperation(
+ next,
+ isAnd = true,
+ originalSource
+ )
+ }
+}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt
index 2f44828..1d1ecb1 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt
@@ -57,6 +57,8 @@
}
private fun getSubjectType(session: FirSession, whenExpression: FirWhenExpression): ConeKotlinType? {
+ if (whenExpression.subject is FirVariableInConditionalExpression) return null
+
val subjectType = whenExpression.subjectVariable?.returnTypeRef?.coneType
?: whenExpression.subject?.resolvedType
?: return null
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformerDispatcher.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformerDispatcher.kt
index ca4dd8f..a05b2cb 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformerDispatcher.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformerDispatcher.kt
@@ -639,6 +639,15 @@
FirControlFlowStatementsResolveTransformer::transformWhenExpression,
)
+ override fun transformVariableInConditionalExpression(
+ variableInConditionalExpression: FirVariableInConditionalExpression,
+ data: ResolutionMode
+ ): FirStatement = controlFlowStatementsTransformation(
+ variableInConditionalExpression,
+ data,
+ FirControlFlowStatementsResolveTransformer::transformVariableInConditionalExpression,
+ )
+
override fun transformWhenBranch(
whenBranch: FirWhenBranch,
data: ResolutionMode,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
index dbb12b4..9fb6c14 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
@@ -127,14 +127,24 @@
return lastBranch.source != null && lastBranch.condition is FirElseIfTrueCondition && lastBranch.result is FirEmptyExpressionBlock
}
+ override fun transformVariableInConditionalExpression(
+ variableInConditionalExpression: FirVariableInConditionalExpression,
+ data: ResolutionMode
+ ): FirStatement {
+ variableInConditionalExpression.replaceConeTypeOrNull(session.builtinTypes.booleanType.coneTypeUnsafe())
+ variableInConditionalExpression.transformDeclaration(transformer, ResolutionMode.ContextIndependent)
+ return variableInConditionalExpression
+ }
+
override fun transformWhenBranch(whenBranch: FirWhenBranch, data: ResolutionMode): FirWhenBranch {
dataFlowAnalyzer.enterWhenBranchCondition(whenBranch)
- return context.withWhenSubjectImportingScope {
- whenBranch.transformCondition(transformer, withExpectedType(session.builtinTypes.booleanType))
- }.also { dataFlowAnalyzer.exitWhenBranchCondition(it) }
- .transformResult(transformer, data)
- .also { dataFlowAnalyzer.exitWhenBranchResult(it) }
-
+ return context.forBlock(session) {
+ context.withWhenSubjectImportingScope {
+ whenBranch.transformCondition(transformer, withExpectedType(session.builtinTypes.booleanType))
+ }.also { dataFlowAnalyzer.exitWhenBranchCondition(it) }
+ .transformResult(transformer, data)
+ .also { dataFlowAnalyzer.exitWhenBranchResult(it) }
+ }
}
override fun transformWhenSubjectExpression(
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirVariableInConditionalExpression.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirVariableInConditionalExpression.kt
new file mode 100644
index 0000000..aa7746a
--- /dev/null
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirVariableInConditionalExpression.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+// This file was generated automatically. See compiler/fir/tree/tree-generator/Readme.md.
+// DO NOT MODIFY IT MANUALLY.
+
+package org.jetbrains.kotlin.fir.expressions
+
+import org.jetbrains.kotlin.KtSourceElement
+import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.declarations.FirVariable
+import org.jetbrains.kotlin.fir.types.ConeKotlinType
+import org.jetbrains.kotlin.fir.visitors.FirTransformer
+import org.jetbrains.kotlin.fir.visitors.FirVisitor
+
+/**
+ * Generated from: [org.jetbrains.kotlin.fir.tree.generator.FirTree.variableInConditionalExpression]
+ */
+abstract class FirVariableInConditionalExpression : FirExpression() {
+ abstract override val source: KtSourceElement?
+ @UnresolvedExpressionTypeAccess
+ abstract override val coneTypeOrNull: ConeKotlinType?
+ abstract override val annotations: List<FirAnnotation>
+ abstract val declaration: FirVariable
+
+ override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R =
+ visitor.visitVariableInConditionalExpression(this, data)
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <E : FirElement, D> transform(transformer: FirTransformer<D>, data: D): E =
+ transformer.transformVariableInConditionalExpression(this, data) as E
+
+ abstract override fun replaceConeTypeOrNull(newConeTypeOrNull: ConeKotlinType?)
+
+ abstract override fun replaceAnnotations(newAnnotations: List<FirAnnotation>)
+
+ abstract override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirVariableInConditionalExpression
+
+ abstract fun <D> transformDeclaration(transformer: FirTransformer<D>, data: D): FirVariableInConditionalExpression
+}
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirVariableInConditionalExpressionBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirVariableInConditionalExpressionBuilder.kt
new file mode 100644
index 0000000..5212df0
--- /dev/null
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirVariableInConditionalExpressionBuilder.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+// This file was generated automatically. See compiler/fir/tree/tree-generator/Readme.md.
+// DO NOT MODIFY IT MANUALLY.
+
+@file:Suppress("DuplicatedCode", "unused")
+
+package org.jetbrains.kotlin.fir.expressions.builder
+
+import kotlin.contracts.*
+import org.jetbrains.kotlin.KtSourceElement
+import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder
+import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
+import org.jetbrains.kotlin.fir.builder.toMutableOrEmpty
+import org.jetbrains.kotlin.fir.declarations.FirVariable
+import org.jetbrains.kotlin.fir.expressions.FirAnnotation
+import org.jetbrains.kotlin.fir.expressions.FirVariableInConditionalExpression
+import org.jetbrains.kotlin.fir.expressions.impl.FirVariableInConditionalExpressionImpl
+import org.jetbrains.kotlin.fir.types.ConeKotlinType
+
+@FirBuilderDsl
+class FirVariableInConditionalExpressionBuilder : FirAnnotationContainerBuilder, FirExpressionBuilder {
+ override var source: KtSourceElement? = null
+ override var coneTypeOrNull: ConeKotlinType? = null
+ override val annotations: MutableList<FirAnnotation> = mutableListOf()
+ lateinit var declaration: FirVariable
+
+ override fun build(): FirVariableInConditionalExpression {
+ return FirVariableInConditionalExpressionImpl(
+ source,
+ coneTypeOrNull,
+ annotations.toMutableOrEmpty(),
+ declaration,
+ )
+ }
+
+}
+
+@OptIn(ExperimentalContracts::class)
+inline fun buildVariableInConditionalExpression(init: FirVariableInConditionalExpressionBuilder.() -> Unit): FirVariableInConditionalExpression {
+ contract {
+ callsInPlace(init, InvocationKind.EXACTLY_ONCE)
+ }
+ return FirVariableInConditionalExpressionBuilder().apply(init).build()
+}
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirVariableInConditionalExpressionImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirVariableInConditionalExpressionImpl.kt
new file mode 100644
index 0000000..61f2cfd
--- /dev/null
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirVariableInConditionalExpressionImpl.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+// This file was generated automatically. See compiler/fir/tree/tree-generator/Readme.md.
+// DO NOT MODIFY IT MANUALLY.
+
+@file:Suppress("DuplicatedCode")
+
+package org.jetbrains.kotlin.fir.expressions.impl
+
+import org.jetbrains.kotlin.KtSourceElement
+import org.jetbrains.kotlin.fir.MutableOrEmptyList
+import org.jetbrains.kotlin.fir.builder.toMutableOrEmpty
+import org.jetbrains.kotlin.fir.declarations.FirVariable
+import org.jetbrains.kotlin.fir.expressions.FirAnnotation
+import org.jetbrains.kotlin.fir.expressions.FirVariableInConditionalExpression
+import org.jetbrains.kotlin.fir.expressions.UnresolvedExpressionTypeAccess
+import org.jetbrains.kotlin.fir.types.ConeKotlinType
+import org.jetbrains.kotlin.fir.visitors.FirTransformer
+import org.jetbrains.kotlin.fir.visitors.FirVisitor
+import org.jetbrains.kotlin.fir.visitors.transformInplace
+
+@OptIn(UnresolvedExpressionTypeAccess::class)
+internal class FirVariableInConditionalExpressionImpl(
+ override val source: KtSourceElement?,
+ @property:UnresolvedExpressionTypeAccess
+ override var coneTypeOrNull: ConeKotlinType?,
+ override var annotations: MutableOrEmptyList<FirAnnotation>,
+ override var declaration: FirVariable,
+) : FirVariableInConditionalExpression() {
+
+ override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
+ annotations.forEach { it.accept(visitor, data) }
+ declaration.accept(visitor, data)
+ }
+
+ override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirVariableInConditionalExpressionImpl {
+ transformAnnotations(transformer, data)
+ transformDeclaration(transformer, data)
+ return this
+ }
+
+ override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirVariableInConditionalExpressionImpl {
+ annotations.transformInplace(transformer, data)
+ return this
+ }
+
+ override fun <D> transformDeclaration(transformer: FirTransformer<D>, data: D): FirVariableInConditionalExpressionImpl {
+ declaration = declaration.transform(transformer, data)
+ return this
+ }
+
+ override fun replaceConeTypeOrNull(newConeTypeOrNull: ConeKotlinType?) {
+ coneTypeOrNull = newConeTypeOrNull
+ }
+
+ override fun replaceAnnotations(newAnnotations: List<FirAnnotation>) {
+ annotations = newAnnotations.toMutableOrEmpty()
+ }
+}
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitor.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitor.kt
index a50b656..55e1f3c 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitor.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitor.kt
@@ -254,6 +254,9 @@
override fun visitThisReceiverExpression(thisReceiverExpression: FirThisReceiverExpression, data: D): R =
visitQualifiedAccessExpression(thisReceiverExpression, data)
+ override fun visitVariableInConditionalExpression(variableInConditionalExpression: FirVariableInConditionalExpression, data: D): R =
+ visitExpression(variableInConditionalExpression, data)
+
override fun visitTypeProjectionWithVariance(typeProjectionWithVariance: FirTypeProjectionWithVariance, data: D): R =
visitTypeProjection(typeProjectionWithVariance, data)
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitorVoid.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitorVoid.kt
index b2a96b8..6b1eeb6 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitorVoid.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirDefaultVisitorVoid.kt
@@ -330,6 +330,10 @@
visitQualifiedAccessExpression(thisReceiverExpression)
}
+ override fun visitVariableInConditionalExpression(variableInConditionalExpression: FirVariableInConditionalExpression) {
+ visitExpression(variableInConditionalExpression)
+ }
+
override fun visitTypeProjectionWithVariance(typeProjectionWithVariance: FirTypeProjectionWithVariance) {
visitTypeProjection(typeProjectionWithVariance)
}
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirTransformer.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirTransformer.kt
index 9d2334c..6a7d8de 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirTransformer.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirTransformer.kt
@@ -1171,6 +1171,14 @@
return transformWhenExpression(whenExpression, data)
}
+ open fun transformVariableInConditionalExpression(variableInConditionalExpression: FirVariableInConditionalExpression, data: D): FirStatement {
+ return transformElement(variableInConditionalExpression, data)
+ }
+
+ final override fun visitVariableInConditionalExpression(variableInConditionalExpression: FirVariableInConditionalExpression, data: D): FirStatement {
+ return transformVariableInConditionalExpression(variableInConditionalExpression, data)
+ }
+
open fun transformTypeProjection(typeProjection: FirTypeProjection, data: D): FirTypeProjection {
return transformElement(typeProjection, data)
}
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitor.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitor.kt
index c383b4a..7a9b096 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitor.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitor.kt
@@ -452,6 +452,9 @@
open fun visitWhenExpression(whenExpression: FirWhenExpression, data: D): R =
visitElement(whenExpression, data)
+ open fun visitVariableInConditionalExpression(variableInConditionalExpression: FirVariableInConditionalExpression, data: D): R =
+ visitElement(variableInConditionalExpression, data)
+
open fun visitTypeProjection(typeProjection: FirTypeProjection, data: D): R =
visitElement(typeProjection, data)
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitorVoid.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitorVoid.kt
index 48defbc..db51044 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitorVoid.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitorVoid.kt
@@ -1171,6 +1171,14 @@
visitElement(whenExpression)
}
+ final override fun visitVariableInConditionalExpression(variableInConditionalExpression: FirVariableInConditionalExpression, data: Nothing?) {
+ visitVariableInConditionalExpression(variableInConditionalExpression)
+ }
+
+ open fun visitVariableInConditionalExpression(variableInConditionalExpression: FirVariableInConditionalExpression) {
+ visitElement(variableInConditionalExpression)
+ }
+
final override fun visitTypeProjection(typeProjection: FirTypeProjection, data: Nothing?) {
visitTypeProjection(typeProjection)
}
diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt
index 859fa9e..1d6395d 100644
--- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt
+++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt
@@ -308,6 +308,9 @@
additionalImports(stubReferenceType)
}
+ builder(variableInConditionalExpression) {
+ }
+
builder(resolvedTypeRef) {
defaultNull("delegatedTypeRef")
withCopy()
diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt
index 996441a..6de1ec2 100644
--- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt
+++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt
@@ -1216,6 +1216,12 @@
+field("usedAsExpression", boolean)
}
+ val variableInConditionalExpression: Element by element(Expression) {
+ parent(expression)
+
+ +field("declaration", variable, withTransform = true)
+ }
+
val typeProjection: Element by element(TypeRefElement)
val typeProjectionWithVariance: Element by element(TypeRefElement) {
diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java
index 011253c..36c9efc 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java
@@ -59,6 +59,8 @@
private static final TokenSet L_PAR_L_BRACE_R_PAR_SET = TokenSet.create(LPAR, LBRACE, RPAR);
private static final TokenSet IN_KEYWORD_SET = TokenSet.create(IN_KEYWORD);
private static final TokenSet TRY_CATCH_RECOVERY_TOKEN_SET = TokenSet.create(LBRACE, RBRACE, FINALLY_KEYWORD, CATCH_KEYWORD);
+ private static final TokenSet IS_DESTRUCTURING_SET = TokenSet.create(ANDAND, ARROW);
+ private static final TokenSet IS_DESTRUCTURING_SET_RECOVERY = TokenSet.orSet(IS_DESTRUCTURING_SET, PARAMETER_NAME_RECOVERY_SET);
private static ImmutableMap<String, KtToken> tokenSetToMap(TokenSet tokens) {
ImmutableMap.Builder<String, KtToken> builder = ImmutableMap.builder();
@@ -160,21 +162,21 @@
PREFIX(MINUS, PLUS, MINUSMINUS, PLUSPLUS, EXCL) { // annotations
@Override
- public void parseHigherPrecedence(KotlinExpressionParsing parser) {
+ public void parseHigherPrecedence(KotlinExpressionParsing parser, boolean conditionalContext) {
throw new IllegalStateException("Don't call this method");
}
},
AS(AS_KEYWORD, AS_SAFE) {
@Override
- public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
+ public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser, boolean conditionalContext) {
parser.myKotlinParsing.parseTypeRefWithoutIntersections();
return BINARY_WITH_TYPE;
}
@Override
- public void parseHigherPrecedence(KotlinExpressionParsing parser) {
- parser.parsePrefixExpression();
+ public void parseHigherPrecedence(KotlinExpressionParsing parser, boolean conditionalContext) {
+ parser.parsePrefixExpression(conditionalContext);
}
},
@@ -185,18 +187,30 @@
ELVIS(KtTokens.ELVIS),
IN_OR_IS(IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS) {
@Override
- public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
- if (operation == IS_KEYWORD || operation == NOT_IS) {
+ public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser, boolean conditionalContext) {
+ if (operation == IS_KEYWORD && conditionalContext) {
+ parser.myKotlinParsing.parseTypeRefWithoutIntersections();
+ if (parser.myKotlinParsing.at(LPAR)) {
+ parser.myKotlinParsing.parseMultiDeclarationName(IS_DESTRUCTURING_SET, IS_DESTRUCTURING_SET_RECOVERY);
+ }
+ return IS_EXPRESSION;
+ } else if (operation == IS_KEYWORD || operation == NOT_IS) {
parser.myKotlinParsing.parseTypeRefWithoutIntersections();
return IS_EXPRESSION;
}
- return super.parseRightHandSide(operation, parser);
+ return super.parseRightHandSide(operation, parser, conditionalContext);
}
},
COMPARISON(LT, GT, LTEQ, GTEQ),
EQUALITY(EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ),
- CONJUNCTION(ANDAND),
+ CONJUNCTION(ANDAND) {
+ @Override
+ public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser, boolean conditionalContext) {
+ parseHigherPrecedence(parser, conditionalContext); // propagate conditionalContext correctly
+ return BINARY_EXPRESSION;
+ }
+ },
DISJUNCTION(OROR),
// ARROW(KtTokens.ARROW),
ASSIGNMENT(EQ, PLUSEQ, MINUSEQ, MULTEQ, DIVEQ, PERCEQ),
@@ -210,16 +224,16 @@
}
}
- private Precedence higher;
+ protected Precedence higher;
private final TokenSet operations;
Precedence(IElementType... operations) {
this.operations = TokenSet.create(operations);
}
- public void parseHigherPrecedence(KotlinExpressionParsing parser) {
+ public void parseHigherPrecedence(KotlinExpressionParsing parser, boolean conditionalContext) {
assert higher != null;
- parser.parseBinaryExpression(higher);
+ parser.parseBinaryExpression(higher, conditionalContext);
}
/**
@@ -228,8 +242,8 @@
* @param parser the parser object
* @return node type of the result
*/
- public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
- parseHigherPrecedence(parser);
+ public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser, boolean conditionalContext) {
+ parseHigherPrecedence(parser, false);
return BINARY_EXPRESSION;
}
@@ -309,12 +323,20 @@
* // block is syntactically equivalent to a functionLiteral with no parameters
* ;
*/
- public void parseExpression() {
+ private void parseExpression(boolean conditionalContext) {
if (!atSet(EXPRESSION_FIRST)) {
error("Expecting an expression");
return;
}
- parseBinaryExpression(Precedence.ASSIGNMENT);
+ parseBinaryExpression(Precedence.ASSIGNMENT, conditionalContext);
+ }
+
+ public void parseExpression() {
+ parseExpression(false);
+ }
+
+ public void parseConditionalExpression() {
+ parseExpression(true);
}
/*
@@ -322,17 +344,17 @@
*
* see the precedence table
*/
- private void parseBinaryExpression(Precedence precedence) {
+ private void parseBinaryExpression(Precedence precedence, boolean conditionalContext) {
PsiBuilder.Marker expression = mark();
- precedence.parseHigherPrecedence(this);
+ precedence.parseHigherPrecedence(this, conditionalContext);
while (!interruptedWithNewLine() && atSet(precedence.getOperations())) {
IElementType operation = tt();
parseOperationReference();
- IElementType resultType = precedence.parseRightHandSide(operation, this);
+ IElementType resultType = precedence.parseRightHandSide(operation, this, conditionalContext);
expression.done(resultType);
expression = expression.precede();
}
@@ -346,19 +368,19 @@
private void parseLabeledExpression() {
PsiBuilder.Marker expression = mark();
parseLabelDefinition();
- parsePrefixExpression();
+ parsePrefixExpression(false);
expression.done(LABELED_EXPRESSION);
}
/*
* operation? prefixExpression
*/
- private void parsePrefixExpression() {
+ public void parsePrefixExpression(boolean conditionalContext) {
if (at(AT)) {
if (!parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */ false, false)) {
PsiBuilder.Marker expression = mark();
myKotlinParsing.parseAnnotations(DEFAULT);
- parsePrefixExpression();
+ parsePrefixExpression(false);
expression.done(ANNOTATED_EXPRESSION);
}
}
@@ -375,12 +397,12 @@
myBuilder.restoreJoiningComplexTokensState();
- parsePrefixExpression();
+ parsePrefixExpression(false);
expression.done(PREFIX_EXPRESSION);
}
else {
myBuilder.restoreJoiningComplexTokensState();
- parsePostfixExpression();
+ parsePostfixExpression(conditionalContext);
}
}
}
@@ -450,10 +472,10 @@
* : memberAccessOperation postfixUnaryExpression // TODO: Review
* ;
*/
- private void parsePostfixExpression() {
+ private void parsePostfixExpression(boolean conditionalContext) {
PsiBuilder.Marker expression = mark();
- boolean firstExpressionParsed = at(COLONCOLON) ? parseDoubleColonSuffix(mark()) : parseAtomicExpression();
+ boolean firstExpressionParsed = at(COLONCOLON) ? parseDoubleColonSuffix(mark()) : parseAtomicExpression(conditionalContext);
while (true) {
if (interruptedWithNewLine()) {
@@ -473,7 +495,7 @@
if (!firstExpressionParsed) {
expression.drop();
expression = mark();
- firstExpressionParsed = parseAtomicExpression();
+ firstExpressionParsed = parseAtomicExpression(false);
continue;
}
@@ -534,7 +556,7 @@
*/
private void parseSelectorCallExpression() {
PsiBuilder.Marker mark = mark();
- parseAtomicExpression();
+ parseAtomicExpression(false);
if (!myBuilder.newlineBeforeCurrentToken() && parseCallSuffix()) {
mark.done(CALL_EXPRESSION);
}
@@ -632,7 +654,7 @@
* : collectionLiteral
* ;
*/
- private boolean parseAtomicExpression() {
+ private boolean parseAtomicExpression(boolean conditionalContext) {
boolean ok = true;
switch (getTokenId()) {
@@ -718,10 +740,17 @@
case NULL_KEYWORD_Id:
parseOneTokenExpression(NULL);
break;
+ case VAL_KEYWORD_Id:
+ if (conditionalContext) {
+ PsiBuilder.Marker start = mark();
+ IElementType result = myKotlinParsing.parseProperty(KotlinParsing.DeclarationParsingMode.CONDITIONAL);
+ start.done(result);
+ break;
+ }
+ // else, cascade
case CLASS_KEYWORD_Id:
case INTERFACE_KEYWORD_Id:
case FUN_KEYWORD_Id:
- case VAL_KEYWORD_Id:
case VAR_KEYWORD_Id:
case TYPE_ALIAS_KEYWORD_Id:
if (!parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */ myBuilder.newlineBeforeCurrentToken(), false)) {
@@ -1023,7 +1052,7 @@
condition.done(WHEN_CONDITION_EXPRESSION);
break;
default:
- parseExpression();
+ parseConditionalExpression();
condition.done(WHEN_CONDITION_EXPRESSION);
break;
}
diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java
index b1e896a..d32b2c9 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java
@@ -1437,18 +1437,21 @@
}
enum DeclarationParsingMode {
- MEMBER_OR_TOPLEVEL(false, true, true),
- LOCAL(true, false, false),
- SCRIPT_TOPLEVEL(true, true, false);
+ MEMBER_OR_TOPLEVEL(false, true, true, false),
+ LOCAL(true, false, false, false),
+ SCRIPT_TOPLEVEL(true, true, false, false),
+ CONDITIONAL(true, false, false, true);
public final boolean destructuringAllowed;
public final boolean accessorsAllowed;
public final boolean canBeEnumUsedAsSoftKeyword;
+ public final boolean onlyAtomicExpressions;
- DeclarationParsingMode(boolean destructuringAllowed, boolean accessorsAllowed, boolean canBeEnumUsedAsSoftKeyword) {
+ DeclarationParsingMode(boolean destructuringAllowed, boolean accessorsAllowed, boolean canBeEnumUsedAsSoftKeyword, boolean onlyAtomicExpressions) {
this.destructuringAllowed = destructuringAllowed;
this.accessorsAllowed = accessorsAllowed;
this.canBeEnumUsedAsSoftKeyword = canBeEnumUsedAsSoftKeyword;
+ this.onlyAtomicExpressions = onlyAtomicExpressions;
}
}
@@ -1507,7 +1510,7 @@
parseTypeConstraintsGuarded(typeParametersDeclared);
- if (!parsePropertyDelegateOrAssignment() && isNameOnTheNextLine && noTypeReference && !receiverTypeDeclared) {
+ if (!parsePropertyDelegateOrAssignment(mode) && isNameOnTheNextLine && noTypeReference && !receiverTypeDeclared) {
// Do not parse property identifier on the next line if declaration is invalid
// In most cases this identifier relates to next statement/declaration
beforeName.rollbackTo();
@@ -1550,14 +1553,18 @@
return multiDeclaration ? DESTRUCTURING_DECLARATION : PROPERTY;
}
- private boolean parsePropertyDelegateOrAssignment() {
- if (at(BY_KEYWORD)) {
+ private boolean parsePropertyDelegateOrAssignment(DeclarationParsingMode mode) {
+ if (at(BY_KEYWORD) && !mode.onlyAtomicExpressions) {
parsePropertyDelegate();
return true;
}
else if (at(EQ)) {
advance(); // EQ
- myExpressionParsing.parseExpression();
+ if (mode.onlyAtomicExpressions) {
+ myExpressionParsing.parsePrefixExpression(false);
+ } else {
+ myExpressionParsing.parseExpression();
+ }
return true;
}
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtDestructuringDeclarationEntry.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtDestructuringDeclarationEntry.java
index 20a7783..7fb4050 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtDestructuringDeclarationEntry.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtDestructuringDeclarationEntry.java
@@ -20,6 +20,7 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
+import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,8 +32,7 @@
import java.util.Collections;
import java.util.List;
-import static org.jetbrains.kotlin.lexer.KtTokens.VAL_KEYWORD;
-import static org.jetbrains.kotlin.lexer.KtTokens.VAR_KEYWORD;
+import static org.jetbrains.kotlin.lexer.KtTokens.*;
@SuppressWarnings("deprecation")
public class KtDestructuringDeclarationEntry extends KtNamedDeclarationNotStubbed implements KtVariableDeclaration {
@@ -132,7 +132,8 @@
@NotNull
private ASTNode getParentNode() {
ASTNode parent = getNode().getTreeParent();
- assert parent.getElementType() == KtNodeTypes.DESTRUCTURING_DECLARATION :
+ IElementType parentType = parent.getElementType();
+ assert (parentType == KtNodeTypes.DESTRUCTURING_DECLARATION || parentType == KtNodeTypes.IS_EXPRESSION) :
"parent is " + parent.getElementType();
return parent;
}
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtIsExpression.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtIsExpression.java
index 56b5a50..b3d7ab1 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtIsExpression.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtIsExpression.java
@@ -22,6 +22,8 @@
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.lexer.KtTokens;
+import java.util.List;
+
public class KtIsExpression extends KtExpressionImpl implements KtOperationExpression {
public KtIsExpression(@NotNull ASTNode node) {
super(node);
@@ -52,4 +54,9 @@
return getOperationReference().getReferencedNameElementType() == KtTokens.NOT_IS;
}
+ @Nullable @IfNotParsed
+ public List<KtDestructuringDeclarationEntry> getDestructuringEntries() {
+ return findChildrenByType(KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY);
+ }
+
}
diff --git a/compiler/testData/psi/DeclarationsInConditionals.kt b/compiler/testData/psi/DeclarationsInConditionals.kt
new file mode 100644
index 0000000..208d881
--- /dev/null
+++ b/compiler/testData/psi/DeclarationsInConditionals.kt
@@ -0,0 +1,22 @@
+open class Developer
+class Person(val name: String) : Developer()
+class Company(val name: String, val cto: Person) : Developer()
+
+sealed class Download
+data class App(val title: String, val developer: Developer) : Download()
+data class Movie(val title: String, val director: Person) : Download()
+
+fun f(download: Download) = when {
+ download is App
+ && val (title, developer) = download
+ && developer is Person
+ && developer.name == "Alice" -> "$title in Wonderland"
+ else -> "Boo"
+}
+
+fun g(download: Download) = when {
+ download is App (title, developer)
+ && developer is Person
+ && developer.name == "Alice" -> "$title in Wonderland"
+ else -> "Boo"
+}
diff --git a/compiler/testData/psi/DeclarationsInConditionals.txt b/compiler/testData/psi/DeclarationsInConditionals.txt
new file mode 100644
index 0000000..ef0967e
--- /dev/null
+++ b/compiler/testData/psi/DeclarationsInConditionals.txt
@@ -0,0 +1,418 @@
+KtFile: DeclarationsInConditionals.kt
+ PACKAGE_DIRECTIVE
+ <empty list>
+ IMPORT_LIST
+ <empty list>
+ CLASS
+ MODIFIER_LIST
+ PsiElement(open)('open')
+ PsiWhiteSpace(' ')
+ PsiElement(class)('class')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('Developer')
+ PsiWhiteSpace('\n')
+ CLASS
+ PsiElement(class)('class')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('Person')
+ PRIMARY_CONSTRUCTOR
+ VALUE_PARAMETER_LIST
+ PsiElement(LPAR)('(')
+ VALUE_PARAMETER
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('name')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('String')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ SUPER_TYPE_LIST
+ SUPER_TYPE_CALL_ENTRY
+ CONSTRUCTOR_CALLEE
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Developer')
+ VALUE_ARGUMENT_LIST
+ PsiElement(LPAR)('(')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace('\n')
+ CLASS
+ PsiElement(class)('class')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('Company')
+ PRIMARY_CONSTRUCTOR
+ VALUE_PARAMETER_LIST
+ PsiElement(LPAR)('(')
+ VALUE_PARAMETER
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('name')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('String')
+ PsiElement(COMMA)(',')
+ PsiWhiteSpace(' ')
+ VALUE_PARAMETER
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('cto')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Person')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ SUPER_TYPE_LIST
+ SUPER_TYPE_CALL_ENTRY
+ CONSTRUCTOR_CALLEE
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Developer')
+ VALUE_ARGUMENT_LIST
+ PsiElement(LPAR)('(')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace('\n\n')
+ CLASS
+ MODIFIER_LIST
+ PsiElement(sealed)('sealed')
+ PsiWhiteSpace(' ')
+ PsiElement(class)('class')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('Download')
+ PsiWhiteSpace('\n')
+ CLASS
+ MODIFIER_LIST
+ PsiElement(data)('data')
+ PsiWhiteSpace(' ')
+ PsiElement(class)('class')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('App')
+ PRIMARY_CONSTRUCTOR
+ VALUE_PARAMETER_LIST
+ PsiElement(LPAR)('(')
+ VALUE_PARAMETER
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('title')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('String')
+ PsiElement(COMMA)(',')
+ PsiWhiteSpace(' ')
+ VALUE_PARAMETER
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('developer')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Developer')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ SUPER_TYPE_LIST
+ SUPER_TYPE_CALL_ENTRY
+ CONSTRUCTOR_CALLEE
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Download')
+ VALUE_ARGUMENT_LIST
+ PsiElement(LPAR)('(')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace('\n')
+ CLASS
+ MODIFIER_LIST
+ PsiElement(data)('data')
+ PsiWhiteSpace(' ')
+ PsiElement(class)('class')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('Movie')
+ PRIMARY_CONSTRUCTOR
+ VALUE_PARAMETER_LIST
+ PsiElement(LPAR)('(')
+ VALUE_PARAMETER
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('title')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('String')
+ PsiElement(COMMA)(',')
+ PsiWhiteSpace(' ')
+ VALUE_PARAMETER
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('director')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Person')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ SUPER_TYPE_LIST
+ SUPER_TYPE_CALL_ENTRY
+ CONSTRUCTOR_CALLEE
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Download')
+ VALUE_ARGUMENT_LIST
+ PsiElement(LPAR)('(')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace('\n\n')
+ FUN
+ PsiElement(fun)('fun')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('f')
+ VALUE_PARAMETER_LIST
+ PsiElement(LPAR)('(')
+ VALUE_PARAMETER
+ PsiElement(IDENTIFIER)('download')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Download')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(EQ)('=')
+ PsiWhiteSpace(' ')
+ WHEN
+ PsiElement(when)('when')
+ PsiWhiteSpace(' ')
+ PsiElement(LBRACE)('{')
+ PsiWhiteSpace('\n ')
+ WHEN_ENTRY
+ WHEN_CONDITION_WITH_EXPRESSION
+ BINARY_EXPRESSION
+ BINARY_EXPRESSION
+ BINARY_EXPRESSION
+ IS_EXPRESSION
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('download')
+ PsiWhiteSpace(' ')
+ OPERATION_REFERENCE
+ PsiElement(is)('is')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('App')
+ PsiWhiteSpace('\n ')
+ OPERATION_REFERENCE
+ PsiElement(ANDAND)('&&')
+ PsiWhiteSpace(' ')
+ DESTRUCTURING_DECLARATION
+ PsiElement(val)('val')
+ PsiWhiteSpace(' ')
+ PsiElement(LPAR)('(')
+ DESTRUCTURING_DECLARATION_ENTRY
+ PsiElement(IDENTIFIER)('title')
+ PsiElement(COMMA)(',')
+ PsiWhiteSpace(' ')
+ DESTRUCTURING_DECLARATION_ENTRY
+ PsiElement(IDENTIFIER)('developer')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(EQ)('=')
+ PsiWhiteSpace(' ')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('download')
+ PsiWhiteSpace('\n ')
+ OPERATION_REFERENCE
+ PsiElement(ANDAND)('&&')
+ PsiWhiteSpace(' ')
+ IS_EXPRESSION
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('developer')
+ PsiWhiteSpace(' ')
+ OPERATION_REFERENCE
+ PsiElement(is)('is')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Person')
+ PsiWhiteSpace('\n ')
+ OPERATION_REFERENCE
+ PsiElement(ANDAND)('&&')
+ PsiWhiteSpace(' ')
+ BINARY_EXPRESSION
+ DOT_QUALIFIED_EXPRESSION
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('developer')
+ PsiElement(DOT)('.')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('name')
+ PsiWhiteSpace(' ')
+ OPERATION_REFERENCE
+ PsiElement(EQEQ)('==')
+ PsiWhiteSpace(' ')
+ STRING_TEMPLATE
+ PsiElement(OPEN_QUOTE)('"')
+ LITERAL_STRING_TEMPLATE_ENTRY
+ PsiElement(REGULAR_STRING_PART)('Alice')
+ PsiElement(CLOSING_QUOTE)('"')
+ PsiWhiteSpace(' ')
+ PsiElement(ARROW)('->')
+ PsiWhiteSpace(' ')
+ STRING_TEMPLATE
+ PsiElement(OPEN_QUOTE)('"')
+ SHORT_STRING_TEMPLATE_ENTRY
+ PsiElement(SHORT_TEMPLATE_ENTRY_START)('$')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('title')
+ LITERAL_STRING_TEMPLATE_ENTRY
+ PsiElement(REGULAR_STRING_PART)(' in Wonderland')
+ PsiElement(CLOSING_QUOTE)('"')
+ PsiWhiteSpace('\n ')
+ WHEN_ENTRY
+ PsiElement(else)('else')
+ PsiWhiteSpace(' ')
+ PsiElement(ARROW)('->')
+ PsiWhiteSpace(' ')
+ STRING_TEMPLATE
+ PsiElement(OPEN_QUOTE)('"')
+ LITERAL_STRING_TEMPLATE_ENTRY
+ PsiElement(REGULAR_STRING_PART)('Boo')
+ PsiElement(CLOSING_QUOTE)('"')
+ PsiWhiteSpace('\n')
+ PsiElement(RBRACE)('}')
+ PsiWhiteSpace('\n\n')
+ FUN
+ PsiElement(fun)('fun')
+ PsiWhiteSpace(' ')
+ PsiElement(IDENTIFIER)('g')
+ VALUE_PARAMETER_LIST
+ PsiElement(LPAR)('(')
+ VALUE_PARAMETER
+ PsiElement(IDENTIFIER)('download')
+ PsiElement(COLON)(':')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Download')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(EQ)('=')
+ PsiWhiteSpace(' ')
+ WHEN
+ PsiElement(when)('when')
+ PsiWhiteSpace(' ')
+ PsiElement(LBRACE)('{')
+ PsiWhiteSpace('\n ')
+ WHEN_ENTRY
+ WHEN_CONDITION_WITH_EXPRESSION
+ BINARY_EXPRESSION
+ BINARY_EXPRESSION
+ IS_EXPRESSION
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('download')
+ PsiWhiteSpace(' ')
+ OPERATION_REFERENCE
+ PsiElement(is)('is')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('App')
+ PsiWhiteSpace(' ')
+ PsiElement(LPAR)('(')
+ DESTRUCTURING_DECLARATION_ENTRY
+ PsiElement(IDENTIFIER)('title')
+ PsiElement(COMMA)(',')
+ PsiWhiteSpace(' ')
+ DESTRUCTURING_DECLARATION_ENTRY
+ PsiElement(IDENTIFIER)('developer')
+ PsiElement(RPAR)(')')
+ PsiWhiteSpace('\n ')
+ OPERATION_REFERENCE
+ PsiElement(ANDAND)('&&')
+ PsiWhiteSpace(' ')
+ IS_EXPRESSION
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('developer')
+ PsiWhiteSpace(' ')
+ OPERATION_REFERENCE
+ PsiElement(is)('is')
+ PsiWhiteSpace(' ')
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('Person')
+ PsiWhiteSpace('\n ')
+ OPERATION_REFERENCE
+ PsiElement(ANDAND)('&&')
+ PsiWhiteSpace(' ')
+ BINARY_EXPRESSION
+ DOT_QUALIFIED_EXPRESSION
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('developer')
+ PsiElement(DOT)('.')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('name')
+ PsiWhiteSpace(' ')
+ OPERATION_REFERENCE
+ PsiElement(EQEQ)('==')
+ PsiWhiteSpace(' ')
+ STRING_TEMPLATE
+ PsiElement(OPEN_QUOTE)('"')
+ LITERAL_STRING_TEMPLATE_ENTRY
+ PsiElement(REGULAR_STRING_PART)('Alice')
+ PsiElement(CLOSING_QUOTE)('"')
+ PsiWhiteSpace(' ')
+ PsiElement(ARROW)('->')
+ PsiWhiteSpace(' ')
+ STRING_TEMPLATE
+ PsiElement(OPEN_QUOTE)('"')
+ SHORT_STRING_TEMPLATE_ENTRY
+ PsiElement(SHORT_TEMPLATE_ENTRY_START)('$')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('title')
+ LITERAL_STRING_TEMPLATE_ENTRY
+ PsiElement(REGULAR_STRING_PART)(' in Wonderland')
+ PsiElement(CLOSING_QUOTE)('"')
+ PsiWhiteSpace('\n ')
+ WHEN_ENTRY
+ PsiElement(else)('else')
+ PsiWhiteSpace(' ')
+ PsiElement(ARROW)('->')
+ PsiWhiteSpace(' ')
+ STRING_TEMPLATE
+ PsiElement(OPEN_QUOTE)('"')
+ LITERAL_STRING_TEMPLATE_ENTRY
+ PsiElement(REGULAR_STRING_PART)('Boo')
+ PsiElement(CLOSING_QUOTE)('"')
+ PsiWhiteSpace('\n')
+ PsiElement(RBRACE)('}')
\ No newline at end of file
diff --git a/compiler/tests-gen/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java
index 7e6e2ed..75165b1 100644
--- a/compiler/tests-gen/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java
+++ b/compiler/tests-gen/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java
@@ -151,6 +151,11 @@
runTest("compiler/testData/psi/ControlStructures.kt");
}
+ @TestMetadata("DeclarationsInConditionals.kt")
+ public void testDeclarationsInConditionals() {
+ runTest("compiler/testData/psi/DeclarationsInConditionals.kt");
+ }
+
@TestMetadata("DefaultKeyword.kt")
public void testDefaultKeyword() {
runTest("compiler/testData/psi/DefaultKeyword.kt");