diff --git a/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index f053e81be1e..b1d1f548320 100644 --- a/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.constants.evaluate.* import org.jetbrains.kotlin.types.expressions.OperatorConventions +import org.jetbrains.kotlin.util.OperatorNameConventions internal fun String.parseCharacter(): Char? { // Strip the quotes @@ -279,6 +280,48 @@ internal fun Array.toFirWhenCondition( return firCondition!! } +internal fun Array.toInterpolatingCall( + session: FirSession, + base: KtStringTemplateExpression, + convert: KtExpression?.(String) -> FirExpression +): FirExpression { + val sb = StringBuilder() + var hasExpressions = false + var result: FirExpression? = null + for (entry in this) { + val nextArgument = when (entry) { + is KtLiteralStringTemplateEntry -> { + sb.append(entry.text) + FirConstExpressionImpl(session, entry, IrConstKind.String, entry.text) + } + is KtEscapeStringTemplateEntry -> { + sb.append(entry.unescapedValue) + FirConstExpressionImpl(session, entry, IrConstKind.String, entry.unescapedValue) + } + is KtStringTemplateEntryWithExpression -> { + val innerExpression = entry.expression + hasExpressions = true + innerExpression.convert("Incorrect template argument") + } + else -> { + hasExpressions = true + FirErrorExpressionImpl( + session, entry, "Incorrect template entry: ${entry.text}" + ) + } + } + result = when (result) { + null -> nextArgument + else -> FirFunctionCallImpl(session, base).apply { + calleeReference = FirSimpleNamedReference(session, base, OperatorNameConventions.PLUS) + explicitReceiver = result + arguments += nextArgument + } + } + } + return if (hasExpressions) result!! else FirConstExpressionImpl(session, base, IrConstKind.String, sb.toString()) +} + internal fun generateIncrementOrDecrementBlock( session: FirSession, baseExpression: KtUnaryExpression, 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 e1244c1169d..98e2294d122 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 @@ -923,41 +923,7 @@ class RawFirBuilder(val session: FirSession, val stubMode: Boolean) { generateConstantExpressionByLiteral(session, expression) override fun visitStringTemplateExpression(expression: KtStringTemplateExpression, data: Unit): FirElement { - val sb = StringBuilder() - var hasExpressions = false - val interpolatingCall = FirFunctionCallImpl(session, expression).apply { - calleeReference = FirSimpleNamedReference(this@RawFirBuilder.session, expression, OperatorNameConventions.PLUS) - for (entry in expression.entries) { - when (entry) { - is KtLiteralStringTemplateEntry -> { - sb.append(entry.text) - arguments += FirConstExpressionImpl(this@RawFirBuilder.session, entry, IrConstKind.String, entry.text) - } - is KtEscapeStringTemplateEntry -> { - sb.append(entry.unescapedValue) - arguments += FirConstExpressionImpl(this@RawFirBuilder.session, entry, IrConstKind.String, entry.unescapedValue) - } - is KtStringTemplateEntryWithExpression -> { - val innerExpression = entry.expression - if (innerExpression != null) { - arguments += innerExpression.toFirExpression("Incorrect template argument") - hasExpressions = true - } - } - else -> { - arguments += FirErrorExpressionImpl( - this@RawFirBuilder.session, expression, "Incorrect template entry: ${entry.text}" - ) - hasExpressions = true - } - } - } - } - return if (hasExpressions) { - interpolatingCall - } else { - FirConstExpressionImpl(session, expression, IrConstKind.String, sb.toString()) - } + return expression.entries.toInterpolatingCall(session, expression) { toFirExpression(it) } } override fun visitReturnExpression(expression: KtReturnExpression, data: Unit): FirElement { @@ -1166,13 +1132,13 @@ class RawFirBuilder(val session: FirSession, val stubMode: Boolean) { val operationToken = expression.operationToken val leftArgument = expression.left.toFirExpression("No left operand") val rightArgument = expression.right.toFirExpression("No right operand") - if (operationToken == ELVIS) { - return leftArgument.generateNotNullOrOther(session, rightArgument, "elvis", expression) + when (operationToken) { + ELVIS -> + return leftArgument.generateNotNullOrOther(session, rightArgument, "elvis", expression) + ANDAND, OROR -> + return leftArgument.generateLazyLogicalOperation(session, rightArgument, operationToken == ANDAND, expression) } val conventionCallName = operationToken.toBinaryName() - if (operationToken == ANDAND || operationToken == OROR) { - return leftArgument.generateLazyLogicalOperation(session, rightArgument, operationToken == ANDAND, expression) - } return if (conventionCallName != null || operationToken == IDENTIFIER) { FirFunctionCallImpl( session, expression @@ -1181,6 +1147,8 @@ class RawFirBuilder(val session: FirSession, val stubMode: Boolean) { this@RawFirBuilder.session, expression.operationReference, conventionCallName ?: expression.operationReference.getReferencedNameAsName() ) + explicitReceiver = leftArgument + arguments += rightArgument } } else { val firOperation = operationToken.toFirOperation() @@ -1189,11 +1157,11 @@ class RawFirBuilder(val session: FirSession, val stubMode: Boolean) { toFirExpression("Incorrect expression in assignment: ${expression.text}") } } else { - FirOperatorCallImpl(session, expression, firOperation) + FirOperatorCallImpl(session, expression, firOperation).apply { + arguments += leftArgument + arguments += rightArgument + } } - }.apply { - arguments += leftArgument - arguments += rightArgument } } diff --git a/compiler/fir/psi2fir/testData/rawBuilder/declarations/enums.txt b/compiler/fir/psi2fir/testData/rawBuilder/declarations/enums.txt index 4e74cfbef4e..4fb85984ee9 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/declarations/enums.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/declarations/enums.txt @@ -54,7 +54,7 @@ FILE: enums.kt } - public? final? val g: Double = div#(times#(G#, m#), times#(r#, r#)) + public? final? val g: Double = G#.times#(m#).div#(r#.times#(r#)) public? get(): Double public? abstract fun sayHello(): kotlin/Unit diff --git a/compiler/fir/psi2fir/testData/rawBuilder/declarations/simpleClass.txt b/compiler/fir/psi2fir/testData/rawBuilder/declarations/simpleClass.txt index e20d5b000ce..4b0ee26dbcb 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/declarations/simpleClass.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/declarations/simpleClass.txt @@ -13,7 +13,7 @@ FILE: simpleClass.kt private get(): public? open? override fun foo(x: Int, y: String): String { - ^foo plus#(plus#(y#, x#), baz#) + ^foo y#.plus#(x#).plus#(baz#) } public? open? override var bar: Boolean diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/arrayAccess.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/arrayAccess.txt index 4a37e3f4553..83d13d59f67 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/arrayAccess.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/arrayAccess.txt @@ -12,5 +12,5 @@ FILE: arrayAccess.kt } public? final? fun test(a: IntArray, w: Wrapper): { - ^test plus#(plus#(plus#(a#[Int(0)], a#[p#]), a#[foo#()]), w#.v#[Int(0)]) + ^test a#[Int(0)].plus#(a#[p#]).plus#(a#[foo#()]).plus#(w#.v#[Int(0)]) } diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/branches.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/branches.txt index f9dd52933c7..921ae97dc20 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/branches.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/branches.txt @@ -57,7 +57,7 @@ FILE: branches.kt ==($subj$, Int(3)) -> { String(Mediocre) } - in($subj$, rangeTo#(Int(1), Int(2))) -> { + in($subj$, Int(1).rangeTo#(Int(2))) -> { String(Fail) } ($subj$ is Number) -> { diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/calls.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/calls.txt index 44054b56427..013e2a1cb1e 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/calls.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/calls.txt @@ -1,9 +1,9 @@ FILE: calls.kt public? final? infix fun distance(x: Int, y: Int): { - ^distance plus#(x#, y#) + ^distance x#.plus#(y#) } public? final? fun test(): Int { - ^test distance#(Int(3), Int(4)) + ^test Int(3).distance#(Int(4)) } public? final? fun testRegular(): Int { ^testRegular distance#(Int(3), Int(4)) diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt index 52c8215795b..77f560d9fe3 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/for.txt @@ -1,6 +1,6 @@ FILE: for.kt public? final? fun foo(): kotlin/Unit { - lval : = rangeTo#(Int(1), Int(10)) + lval : = Int(1).rangeTo#(Int(10)) lval : = #.iterator#() while(#.hasNext#()) { lval i: = #.next#() @@ -41,7 +41,7 @@ FILE: for.kt lval : = #.next#() lval x: = #.component1() lval y: = #.component2() - println#(plus#(String(x = ), x#, String( y = ), y#)) + println#(String(x = ).plus#(x#).plus#(String( y = )).plus#(y#)) } } diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt index b0dfbc5409a..b537c3bf82d 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/lambda.txt @@ -17,7 +17,7 @@ FILE: lambda.kt ^ { lval x: = t#.x# lval y: = t#.y# - plus#(x#, y#) + x#.plus#(y#) } } @@ -26,7 +26,7 @@ FILE: lambda.kt ^ { lval x: = #.component1() lval y: = #.component2() - plus#(x#, y#) + x#.plus#(y#) } } diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/locals.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/locals.txt index 0ab4aae69cf..ba6588260f9 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/locals.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/locals.txt @@ -7,14 +7,14 @@ FILE: locals.kt public? get(): Int public? final? fun diff(): { - ^diff minus#(pp#, p#) + ^diff pp#.minus#(p#) } } lval x: = Local#(Int(42)).diff#() local final? fun sum(y: Int, z: Int, f: ( (Int, Int) -> Int )): Int { - ^sum plus#(x#, f#(plus#(y#, z#))) + ^sum x#.plus#(f#(y#.plus#(z#))) } lval code: = object : Any { @@ -27,7 +27,7 @@ FILE: locals.kt } .foo#() ^withLocals sum#(code#, Local#(Int(1)).diff#(), fun (x: Int, y: Int): { - ^ plus#(x#, y#) + ^ x#.plus#(y#) } ) } diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/these.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/these.txt index 42f31f710e2..53f14bebb18 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/these.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/these.txt @@ -16,7 +16,7 @@ FILE: these.kt } public? final? fun String.extension(): Int { - ^extension plus#(this@Some.bar#(), this#.length#) + ^extension this@Some.bar#().plus#(this#.length#) } } @@ -26,7 +26,7 @@ FILE: these.kt public? final? fun test(some: Some): Int { ^test with#(some#, with@fun .(): { ^ { - plus#(this#.foo#(), this@with.extension#()) + this#.foo#().plus#(this@with.extension#()) } } diff --git a/compiler/fir/psi2fir/testData/rawBuilder/expressions/variables.txt b/compiler/fir/psi2fir/testData/rawBuilder/expressions/variables.txt index 28c17c4d02a..f897022abee 100644 --- a/compiler/fir/psi2fir/testData/rawBuilder/expressions/variables.txt +++ b/compiler/fir/psi2fir/testData/rawBuilder/expressions/variables.txt @@ -1,9 +1,9 @@ FILE: variables.kt public? final? fun foo(): kotlin/Unit { lval x: = Int(1) - lvar y: = plus#(x#, Int(1)) - lval z: = times#(y#, Int(2)) - y# = plus#(y#, z#) - lval w: = minus#(y#, x#) + lvar y: = x#.plus#(Int(1)) + lval z: = y#.times#(Int(2)) + y# = y#.plus#(z#) + lval w: = y#.minus#(x#) ^foo w# } diff --git a/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/RawFirBuilderTotalKotlinTestCase.kt b/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/RawFirBuilderTotalKotlinTestCase.kt index 01c40b857ff..cdfc534cc59 100644 --- a/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/RawFirBuilderTotalKotlinTestCase.kt +++ b/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/RawFirBuilderTotalKotlinTestCase.kt @@ -251,6 +251,7 @@ class RawFirBuilderTotalKotlinTestCase : AbstractRawFirBuilderTestCase() { it is KtLambdaExpression || it is KtTypeConstraintList || it is KtTypeConstraint || + it is KtStringTemplateExpression && it.entries.size <= 1 || it is KtDestructuringDeclaration && it.parent is KtParameter || it is KtArrayAccessExpression && it.parent is KtBinaryExpression || it is KtNameReferenceExpression && diff --git a/compiler/fir/resolve/testData/resolve/expresssions/access.txt b/compiler/fir/resolve/testData/resolve/expresssions/access.txt index 888ca8d9aa4..dee5a26ffb4 100644 --- a/compiler/fir/resolve/testData/resolve/expresssions/access.txt +++ b/compiler/fir/resolve/testData/resolve/expresssions/access.txt @@ -32,8 +32,8 @@ FILE: access.kt ^plus String() } - public final fun R|Foo|.check(): R|kotlin/String| { - ^check R|/Bar.plus|(R|/Foo.abc|(), R|/Bar.bar|()) + public final fun R|Foo|.check(): { + ^check R|/Foo.abc|().#(R|/Bar.bar|()) } } diff --git a/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt b/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt index 7f9b5c74fcc..f9015dceef7 100644 --- a/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt +++ b/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt @@ -54,7 +54,7 @@ FILE: enums.kt } - public final val g: R|kotlin/Double| = #(#(#, R|/Planet.m|), #(R|/Planet.r|, R|/Planet.r|)) + public final val g: R|kotlin/Double| = #.#(R|/Planet.m|).#(R|/Planet.r|.#(R|/Planet.r|)) public get(): R|kotlin/Double| public abstract fun sayHello(): R|kotlin/Unit| diff --git a/compiler/fir/resolve/testData/resolve/fromBuilder/simpleClass.txt b/compiler/fir/resolve/testData/resolve/fromBuilder/simpleClass.txt index f7ff2d92b9d..31632196f85 100644 --- a/compiler/fir/resolve/testData/resolve/fromBuilder/simpleClass.txt +++ b/compiler/fir/resolve/testData/resolve/fromBuilder/simpleClass.txt @@ -13,7 +13,7 @@ FILE: simpleClass.kt private get(): R|kotlin/Int| public final override fun foo(x: R|kotlin/Int|, y: R|kotlin/String|): R|kotlin/String| { - ^foo R|kotlin/plus|(R|kotlin/plus|(R|/y|, R|/x|), R|/SomeClass.baz|) + ^foo R|/y|.R|kotlin/String.plus|(R|/x|).R|kotlin/String.plus|(R|/SomeClass.baz|) } public final override var bar: R|kotlin/Boolean| diff --git a/compiler/fir/resolve/testData/resolve/simpleClass.txt b/compiler/fir/resolve/testData/resolve/simpleClass.txt index a361eb9627d..6c11ee9c581 100644 --- a/compiler/fir/resolve/testData/resolve/simpleClass.txt +++ b/compiler/fir/resolve/testData/resolve/simpleClass.txt @@ -13,7 +13,7 @@ FILE: simpleClass.kt private get(): R|kotlin/Int| public final override fun foo(x: R|kotlin/Int|, y: R|kotlin/String|): R|kotlin/String| { - ^foo R|kotlin/plus|(R|kotlin/plus|(R|/y|, R|/x|), R|/SomeClass.baz|) + ^foo R|/y|.R|kotlin/String.plus|(R|/x|).R|kotlin/String.plus|(R|/SomeClass.baz|) } public final override var bar: R|kotlin/Boolean|