diff --git a/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index c3fa4fc5466..822a7e2182c 100644 --- a/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeProjection import org.jetbrains.kotlin.fir.types.impl.* +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -551,6 +552,48 @@ class RawFirBuilder(val session: FirSession, val stubMode: Boolean) { ) } + if (classOrObject.hasModifier(KtTokens.DATA_KEYWORD) && firPrimaryConstructor != null) { + var componentIndex = 1 + val zippedParameters = + classOrObject.primaryConstructorParameters.zip(firClass.declarations.filterIsInstance()) + for ((ktParameter, firProperty) in zippedParameters) { + if (!ktParameter.hasValOrVar()) continue + val name = Name.identifier("component$componentIndex") + componentIndex++ + val symbol = FirFunctionSymbol(callableIdForName(name)) + firClass.addDeclaration( + FirMemberFunctionImpl( + session, ktParameter, symbol, name, + Visibilities.PUBLIC, Modality.FINAL, + isExpect = false, isActual = false, + isOverride = false, isOperator = false, + isInfix = false, isInline = false, + isTailRec = false, isExternal = false, + isSuspend = false, receiverTypeRef = null, + returnTypeRef = FirImplicitTypeRefImpl(session, ktParameter) + ).apply { + val componentFunction = this + body = FirSingleExpressionBlock( + this@RawFirBuilder.session, + FirReturnExpressionImpl( + this@RawFirBuilder.session, ktParameter, + FirQualifiedAccessExpressionImpl(this@RawFirBuilder.session, ktParameter).apply { + val parameterName = ktParameter.nameAsSafeName + calleeReference = FirResolvedCallableReferenceImpl( + this@RawFirBuilder.session, ktParameter, + parameterName, firProperty.symbol + ) + } + ).apply { + target = FirFunctionTarget(null) + target.bind(componentFunction) + } + ) + } + ) + } + } + firClass } } diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/destructuring.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/destructuring.txt index 5fca598de06..7cebcfa9dc8 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/destructuring.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/destructuring.txt @@ -13,6 +13,18 @@ FILE: destructuring.kt public? final? val third: String = R|/third| public? get(): String + public final fun component1(): { + ^component1 R|/Some.first| + } + + public final fun component2(): { + ^component2 R|/Some.second| + } + + public final fun component3(): { + ^component3 R|/Some.third| + } + } public? final? fun foo(some: Some): kotlin/Unit { lval : = some# diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt index 8f38c9bc945..52e6dd89a23 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt @@ -35,6 +35,14 @@ FILE: for.kt public? final? val y: Int = R|/y| public? get(): Int + public final fun component1(): { + ^component1 R|/Some.x| + } + + public final fun component2(): { + ^component2 R|/Some.y| + } + } public? final? fun baz(set: Set): kotlin/Unit { lval : = set# diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt index f5c9fc3b97e..9817dd99d41 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt @@ -10,6 +10,14 @@ FILE: lambda.kt public? final? val y: Int = R|/y| public? get(): Int + public final fun component1(): { + ^component1 R|/Tuple.x| + } + + public final fun component2(): { + ^component2 R|/Tuple.y| + } + } public? final? inline fun use(f: ( (Tuple) -> Int )): { ^use f#(Tuple#(Int(1), Int(2))) diff --git a/compiler/fir/resolve/testData/resolve/stdlib/components.kt b/compiler/fir/resolve/testData/resolve/stdlib/components.kt new file mode 100644 index 00000000000..9437671bb30 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/components.kt @@ -0,0 +1,11 @@ +data class D(val x: Int, val y: String) + +fun foo(list: List) { + for ((x, y) in list) { + } + val (x, y) = list.first() + list.forEach { (x, y) -> + println(x) + println(y) + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/stdlib/components.txt b/compiler/fir/resolve/testData/resolve/stdlib/components.txt new file mode 100644 index 00000000000..5965126e8c2 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/components.txt @@ -0,0 +1,41 @@ +FILE: components.kt + public final data class D : R|kotlin/Any| { + public constructor(x: R|kotlin/Int|, y: R|kotlin/String|): R|D| { + super() + } + + public final val x: R|kotlin/Int| = R|/x| + public get(): R|kotlin/Int| + + public final val y: R|kotlin/String| = R|/y| + public get(): R|kotlin/String| + + public final fun component1(): R|kotlin/Int| { + ^component1 R|/D.x| + } + + public final fun component2(): R|kotlin/String| { + ^component2 R|/D.y| + } + + } + public final fun foo(list: R|kotlin/collections/List|): R|kotlin/Unit| { + lval : R|kotlin/collections/List| = R|/list| + lval : R|kotlin/collections/Iterator| = R|/|.R|FakeOverride|>|() + while(R|/|.R|FakeOverride|()) { + lval : R|D| = R|/|.R|FakeOverride|() + lval x: R|kotlin/Int| = R|/|.R|/D.component1|() + lval y: R|kotlin/String| = R|/|.R|/D.component2|() + } + + lval : = R|/list|.#() + lval x: = R|/|.component1() + lval y: = R|/|.component2() + R|/list|.R|kotlin/collections/forEach|( = forEach@fun (: R|D|): R|kotlin/Unit| { + lval x: R|kotlin/Int| = R|/|.R|/D.component1|() + lval y: R|kotlin/String| = R|/|.R|/D.component2|() + R|kotlin/io/println|(R|/x|) + R|kotlin/io/println|(R|/y|) + } + ) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java index 6ed336808f6..5f5135b9f77 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java @@ -34,6 +34,11 @@ public class FirResolveTestCaseWithStdlibGenerated extends AbstractFirResolveTes runTest("compiler/fir/resolve/testData/resolve/stdlib/arrayFirstOrNull.kt"); } + @TestMetadata("components.kt") + public void testComponents() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/stdlib/components.kt"); + } + @TestMetadata("concurrent.kt") public void testConcurrent() throws Exception { runTest("compiler/fir/resolve/testData/resolve/stdlib/concurrent.kt");