FIR: Change semantics for combination of safe calls and operators

^KT-41034 Fixed
This commit is contained in:
Denis.Zharkov
2022-01-26 18:19:17 +03:00
parent 0dd5042e62
commit 772579143b
34 changed files with 811 additions and 68 deletions
@@ -433,6 +433,24 @@ public class FirLazyBodiesCalculatorTestGenerated extends AbstractFirLazyBodiesC
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
}
@Test
@TestMetadata("safeCallsWithAssignment.kt")
public void testSafeCallsWithAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
}
@Test
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
public void testSafeCallsWithAugmentedAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
}
@Test
@TestMetadata("safeCallsWithUnaryOperators.kt")
public void testSafeCallsWithUnaryOperators() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
}
@Test
@TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception {
@@ -391,7 +391,9 @@ class Fir2IrVisitor(
)
return conversionScope.withSafeCallSubject(receiverVariable) {
val afterNotNullCheck = safeCallExpression.selector.accept(this, data) as IrExpression
val afterNotNullCheck =
(safeCallExpression.selector as? FirExpression)?.let(::convertToIrExpression)
?: safeCallExpression.selector.accept(this, data) as IrExpression
components.createSafeCallConstruction(receiverVariable, variableSymbol, afterNotNullCheck)
}
}
@@ -41267,12 +41267,36 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/safeCall"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("augmentedAssigmentPlus.kt")
public void testAugmentedAssigmentPlus() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlus.kt");
}
@Test
@TestMetadata("augmentedAssigmentPlusAssign.kt")
public void testAugmentedAssigmentPlusAssign() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlusAssign.kt");
}
@Test
@TestMetadata("genericNull.kt")
public void testGenericNull() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/genericNull.kt");
}
@Test
@TestMetadata("incrementPostfix.kt")
public void testIncrementPostfix() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/incrementPostfix.kt");
}
@Test
@TestMetadata("incrementPrefix.kt")
public void testIncrementPrefix() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/incrementPrefix.kt");
}
@Test
@TestMetadata("kt1572.kt")
public void testKt1572() throws Exception {
@@ -41368,6 +41392,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
public void testSafeCallWithElvisFolding() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/safeCallWithElvisFolding.kt");
}
@Test
@TestMetadata("withAssignment.kt")
public void testWithAssignment() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/withAssignment.kt");
}
}
@Nested
@@ -12,8 +12,8 @@ import org.jetbrains.kotlin.ElementTypeUtils.getOperationSymbol
import org.jetbrains.kotlin.ElementTypeUtils.isExpression
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtLightSourceElement
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fakeElement
@@ -556,7 +556,7 @@ class ExpressionsConverter(
if (isSafe) {
@OptIn(FirImplementationDetail::class)
it.replaceSource(dotQualifiedExpression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression))
return it.wrapWithSafeCall(
return it.createSafeCall(
firReceiver!!,
dotQualifiedExpression.toFirSourceElement()
)
@@ -918,7 +918,7 @@ class ExpressionsConverter(
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseArrayAccess
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitArrayAccessExpression
*/
private fun convertArrayAccessExpression(arrayAccess: LighterASTNode): FirFunctionCall {
private fun convertArrayAccessExpression(arrayAccess: LighterASTNode): FirExpression {
var firExpression: FirExpression? = null
val indices: MutableList<FirExpression> = mutableListOf()
arrayAccess.forEachChildren {
@@ -942,7 +942,7 @@ class ExpressionsConverter(
getArgument?.let { arguments += it }
}
origin = FirFunctionCallOrigin.Operator
}
}.pullUpSafeCallIfNecessary()
}
/**
@@ -393,6 +393,21 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
}
@TestMetadata("safeCallsWithAssignment.kt")
public void testSafeCallsWithAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
}
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
public void testSafeCallsWithAugmentedAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
}
@TestMetadata("safeCallsWithUnaryOperators.kt")
public void testSafeCallsWithUnaryOperators() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
}
@TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
@@ -10,7 +10,10 @@ import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD
import org.jetbrains.kotlin.builtins.StandardNames.DEFAULT_VALUE_PARAMETER
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
@@ -2354,7 +2357,7 @@ open class RawFirBuilder(
}
}
origin = FirFunctionCallOrigin.Operator
}
}.pullUpSafeCallIfNecessary()
}
override fun visitQualifiedExpression(expression: KtQualifiedExpression, data: Unit): FirElement {
@@ -2369,7 +2372,7 @@ open class RawFirBuilder(
if (expression is KtSafeQualifiedExpression) {
@OptIn(FirImplementationDetail::class)
firSelector.replaceSource(expression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression))
return firSelector.wrapWithSafeCall(
return firSelector.createSafeCall(
receiver,
expression.toFirSourceElement()
)
@@ -0,0 +1,15 @@
fun foo() {
a?.b = 1
a?.b?.c = 1
a?.b.c = 1
a?.b[0] = 1
a?.b?.c[0] = 1
a?.b.c[0] = 1
a?.b[0][0] = 1
a?.b?.c[0][0] = 1
a?.b.c[0][0] = 1
a?.b.d() = 1
}
@@ -0,0 +1,2 @@
FILE: safeCallsWithAssignment.kt
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
@@ -0,0 +1,13 @@
FILE: safeCallsWithAssignment.kt
public? final? fun foo(): R|kotlin/Unit| {
a#?.{ $subj$.b# = IntegerLiteral(1) }
a#?.{ $subj$.b# }?.{ $subj$.c# = IntegerLiteral(1) }
a#?.{ $subj$.b# }.c# = IntegerLiteral(1)
a#?.{ $subj$.b#.set#(IntegerLiteral(0), IntegerLiteral(1)) }
a#?.{ $subj$.b# }?.{ $subj$.c#.set#(IntegerLiteral(0), IntegerLiteral(1)) }
a#?.{ $subj$.b# }.c#.set#(IntegerLiteral(0), IntegerLiteral(1))
a#?.{ $subj$.b#.get#(IntegerLiteral(0)).set#(IntegerLiteral(0), IntegerLiteral(1)) }
a#?.{ $subj$.b# }?.{ $subj$.c#.get#(IntegerLiteral(0)).set#(IntegerLiteral(0), IntegerLiteral(1)) }
a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)).set#(IntegerLiteral(0), IntegerLiteral(1))
a#?.{ $subj$.b# }.d# = IntegerLiteral(1)
}
@@ -0,0 +1,15 @@
fun foo() {
a?.b += 1
a?.b?.c += 1
a?.b.c += 1
a?.b[0] += 1
a?.b?.c[0] += 1
a?.b.c[0] += 1
a?.b[0][0] += 1
a?.b?.c[0][0] += 1
a?.b.c[0][0] += 1
a?.b.d() += 1
}
@@ -0,0 +1,2 @@
FILE: safeCallsWithAugmentedAssignment.kt
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
@@ -0,0 +1,13 @@
FILE: safeCallsWithAugmentedAssignment.kt
public? final? fun foo(): R|kotlin/Unit| {
a#?.{ +=($subj$.b#, IntegerLiteral(1)) }
a#?.{ $subj$.b# }?.{ +=($subj$.c#, IntegerLiteral(1)) }
+=(a#?.{ $subj$.b# }.c#, IntegerLiteral(1))
a#?.{ ArraySet:[$subj$.b#.get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
a#?.{ $subj$.b# }?.{ ArraySet:[$subj$.c#.get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
ArraySet:[a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))]
a#?.{ ArraySet:[$subj$.b#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
a#?.{ $subj$.b# }?.{ ArraySet:[$subj$.c#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
ArraySet:[a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))]
+=(a#?.{ $subj$.b# }.d#(), IntegerLiteral(1))
}
@@ -0,0 +1,31 @@
fun foo() {
a?.b++
a?.b?.c++
a?.b.c++
a?.b[0]++
a?.b?.c[0]++
a?.b.c[0]++
a?.b[0][0]++
a?.b?.c[0][0]++
a?.b.c[0][0]++
a?.b.d()++
}
fun foo2() {
++a?.b
++a?.b?.c
++a?.b.c
++a?.b[0]
++a?.b?.c[0]
++a?.b.c[0]
++a?.b[0][0]
++a?.b?.c[0][0]
++a?.b.c[0][0]
++a?.b.d()
}
@@ -0,0 +1,3 @@
FILE: safeCallsWithUnaryOperators.kt
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
public? final? fun foo2(): R|kotlin/Unit| { LAZY_BLOCK }
@@ -0,0 +1,133 @@
FILE: safeCallsWithUnaryOperators.kt
public? final? fun foo(): R|kotlin/Unit| {
a#?.{ {
lval <receiver>: <implicit> = $subj$
lval <unary>: <implicit> = R|<local>/<receiver>|.b#
R|<local>/<receiver>|.b# = R|<local>/<unary>|.inc#()
R|<local>/<unary>|
}
}
a#?.{ $subj$.b# }?.{ {
lval <receiver>: <implicit> = $subj$
lval <unary>: <implicit> = R|<local>/<receiver>|.c#
R|<local>/<receiver>|.c# = R|<local>/<unary>|.inc#()
R|<local>/<unary>|
}
}
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
lval <unary>: <implicit> = R|<local>/<receiver>|.c#
R|<local>/<receiver>|.c# = R|<local>/<unary>|.inc#()
R|<local>/<unary>|
a#?.{ {
lval <array>: <implicit> = $subj$.b#
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
R|<local>/<unary>|
}
}
a#?.{ $subj$.b# }?.{ {
lval <array>: <implicit> = $subj$.c#
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
R|<local>/<unary>|
}
}
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
R|<local>/<unary>|
a#?.{ {
lval <array>: <implicit> = $subj$.b#.get#(IntegerLiteral(0))
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
R|<local>/<unary>|
}
}
a#?.{ $subj$.b# }?.{ {
lval <array>: <implicit> = $subj$.c#.get#(IntegerLiteral(0))
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
R|<local>/<unary>|
}
}
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0))
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
R|<local>/<unary>|
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
lval <unary>: <implicit> = R|<local>/<receiver>|.d#()
R|<local>/<receiver>|.d# = R|<local>/<unary>|.inc#()
R|<local>/<unary>|
}
public? final? fun foo2(): R|kotlin/Unit| {
a#?.{ {
lval <receiver>: <implicit> = $subj$
lval <unary-result>: <implicit> = R|<local>/<receiver>|.b#.inc#()
R|<local>/<receiver>|.b# = R|<local>/<unary-result>|
R|<local>/<unary-result>|
}
}
a#?.{ $subj$.b# }?.{ {
lval <receiver>: <implicit> = $subj$
lval <unary-result>: <implicit> = R|<local>/<receiver>|.c#.inc#()
R|<local>/<receiver>|.c# = R|<local>/<unary-result>|
R|<local>/<unary-result>|
}
}
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
lval <unary-result>: <implicit> = R|<local>/<receiver>|.c#.inc#()
R|<local>/<receiver>|.c# = R|<local>/<unary-result>|
R|<local>/<unary-result>|
a#?.{ {
lval <array>: <implicit> = $subj$.b#
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
}
}
a#?.{ $subj$.b# }?.{ {
lval <array>: <implicit> = $subj$.c#
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
}
}
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
a#?.{ {
lval <array>: <implicit> = $subj$.b#.get#(IntegerLiteral(0))
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
}
}
a#?.{ $subj$.b# }?.{ {
lval <array>: <implicit> = $subj$.c#.get#(IntegerLiteral(0))
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
}
}
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0))
lval <index0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
lval <unary-result>: <implicit> = R|<local>/<receiver>|.d#().inc#()
R|<local>/<receiver>|.d# = R|<local>/<unary-result>|
R|<local>/<unary-result>|
}
@@ -393,6 +393,21 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
}
@TestMetadata("safeCallsWithAssignment.kt")
public void testSafeCallsWithAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
}
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
public void testSafeCallsWithAugmentedAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
}
@TestMetadata("safeCallsWithUnaryOperators.kt")
public void testSafeCallsWithUnaryOperators() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
}
@TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
@@ -393,6 +393,21 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
}
@TestMetadata("safeCallsWithAssignment.kt")
public void testSafeCallsWithAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
}
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
public void testSafeCallsWithAugmentedAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
}
@TestMetadata("safeCallsWithUnaryOperators.kt")
public void testSafeCallsWithUnaryOperators() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
}
@TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
@@ -489,7 +489,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
diagnostic = ConeSimpleDiagnostic("Inc/dec without operand", DiagnosticKind.Syntax)
}
if (unwrappedReceiver.elementType == DOT_QUALIFIED_EXPRESSION) {
if (unwrappedReceiver.elementType == DOT_QUALIFIED_EXPRESSION || unwrappedReceiver.elementType == SAFE_ACCESS_EXPRESSION) {
return generateIncrementOrDecrementBlockForQualifiedAccess(
wholeExpression,
operationReference,
@@ -670,35 +670,35 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private fun generateIncrementOrDecrementBlockForQualifiedAccess(
wholeExpression: T,
operationReference: T?,
receiverForOperation: T,
receiverForOperation: T, // a.b
callName: Name,
prefix: Boolean,
convert: T.() -> FirExpression
): FirExpression {
return buildBlock {
val argumentReceiver = receiverForOperation.receiverExpression // a
return buildBlockProbablyUnderSafeCall(
receiverForOperation,
convert,
receiverForOperation.toFirSourceElement(),
) { qualifiedFir ->
val receiverFir = (qualifiedFir as? FirQualifiedAccess)?.explicitReceiver ?: buildErrorExpression {
source = receiverForOperation.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax)
}
val baseSource = wholeExpression?.toFirSourceElement()
val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement)
source = desugaredSource
val argumentReceiver = receiverForOperation.receiverExpression
val argumentSelector = receiverForOperation.selectorExpression
val argumentReceiverVariable = generateTemporaryVariable(
baseModuleData,
argumentReceiver?.toFirSourceElement(),
Name.special("<receiver>"),
argumentReceiver?.convert() ?: buildErrorExpression {
source = receiverForOperation.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic("Qualified expression without receiver", DiagnosticKind.Syntax)
}
initializer = receiverFir,
).also { statements += it }
val firArgument = generateResolvedAccessExpression(argumentReceiverVariable.source, argumentReceiverVariable).let { receiver ->
val firArgumentSelector = argumentSelector?.convert() ?: buildErrorExpression {
source = receiverForOperation.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax)
}
firArgumentSelector.also { if (it is FirQualifiedAccessExpression) it.replaceExplicitReceiver(receiver) }
qualifiedFir.also { if (it is FirQualifiedAccessExpression) it.replaceExplicitReceiver(receiver) }
}
putIncrementOrDecrementStatements(
@@ -759,12 +759,14 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
prefix: Boolean,
convert: T.() -> FirExpression
): FirExpression {
return buildBlock {
val array = receiver.arrayExpression
return buildBlockProbablyUnderSafeCall(
array, convert, receiver.toFirSourceElement(),
) { arrayReceiver ->
val baseSource = wholeExpression?.toFirSourceElement()
val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement)
source = desugaredSource
val array = receiver.arrayExpression
val indices = receiver.indexExpressions
requireNotNull(indices) { "No indices in ${wholeExpression.asText}" }
@@ -772,10 +774,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
baseModuleData,
array?.toFirSourceElement(),
Name.special("<array>"),
array?.convert() ?: buildErrorExpression {
source = receiver.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic("No array expression", DiagnosticKind.Syntax)
}
initializer = arrayReceiver,
).also { statements += it }
val indexVariables = indices.mapIndexed { i, index ->
@@ -829,6 +828,42 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
}
}
// if `receiver` is a safe call a?.f(...), insert a block under safe call
// a?.{ val receiver = $subj$.f() ... } where `...` is generated by `init(FIR<$subj$.f()>)`
//
// Otherwise just returns buildBlock { init(FIR<receiver>)) }
private fun buildBlockProbablyUnderSafeCall(
receiver: T?,
convert: T.() -> FirExpression,
sourceElementForError: KtSourceElement?,
init: FirBlockBuilder.(receiver: FirExpression) -> Unit = {}
): FirExpression {
val receiverFir = receiver?.convert() ?: buildErrorExpression {
source = sourceElementForError
diagnostic = ConeSimpleDiagnostic("No receiver expression", DiagnosticKind.Syntax)
}
if (receiverFir is FirSafeCallExpression) {
receiverFir.replaceSelector(
buildBlock {
init(
receiverFir.selector as? FirExpression ?: buildErrorExpression {
source = sourceElementForError
diagnostic = ConeSimpleDiagnostic("Safe call selector expected to be an expression here", DiagnosticKind.Syntax)
}
)
}
)
return receiverFir
}
return buildBlock {
init(receiverFir)
}
}
private fun FirQualifiedAccessBuilder.initializeLValue(
left: T?,
convertQualified: T.() -> FirQualifiedAccess?
@@ -893,26 +928,51 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
return if (operation == FirOperation.ASSIGN) {
val result = unwrappedLhs.convert()
(result.annotations as MutableList<FirAnnotation>) += annotations
result
result.pullUpSafeCallIfNecessary()
} else {
generateAugmentedArraySetCall(unwrappedLhs, baseSource, arrayAccessSource, operation, annotations, rhsAST, convert)
val receiver = unwrappedLhs.convert()
if (receiver is FirSafeCallExpression) {
receiver.replaceSelector(
generateAugmentedArraySetCall(
receiver.selector as FirExpression, baseSource, arrayAccessSource, operation, annotations, rhsAST, convert
)
)
receiver
} else {
generateAugmentedArraySetCall(receiver, baseSource, arrayAccessSource, operation, annotations, rhsAST, convert)
}
}
}
if (operation in FirOperation.ASSIGNMENTS && operation != FirOperation.ASSIGN) {
return buildAssignmentOperatorStatement {
val lhsReceiver = this@generateAssignment?.convert()
val receiverToUse =
if (lhsReceiver is FirSafeCallExpression)
lhsReceiver.selector as? FirExpression
else
lhsReceiver
val result = buildAssignmentOperatorStatement {
source = baseSource
this.operation = operation
leftArgument = this@generateAssignment?.convert()
?: buildErrorExpression {
source = null
diagnostic = ConeSimpleDiagnostic(
"Unsupported left value of assignment: ${baseSource?.psi?.text}", DiagnosticKind.ExpressionExpected
)
}
leftArgument = receiverToUse ?: buildErrorExpression {
source = null
diagnostic = ConeSimpleDiagnostic(
"Unsupported left value of assignment: ${baseSource?.psi?.text}", DiagnosticKind.ExpressionExpected
)
}
rightArgument = rhsExpression
this.annotations += annotations
}
return if (lhsReceiver is FirSafeCallExpression) {
lhsReceiver.replaceSelector(result)
lhsReceiver
} else {
result
}
}
require(operation == FirOperation.ASSIGN)
@@ -956,7 +1016,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
}
private fun generateAugmentedArraySetCall(
unwrappedReceiver: T,
receiver: FirExpression, // a.get(x,y)
baseSource: KtSourceElement?,
arrayAccessSource: KtSourceElement?,
operation: FirOperation,
@@ -967,15 +1027,15 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
return buildAugmentedArraySetCall {
source = baseSource
this.operation = operation
assignCall = generateAugmentedCallForAugmentedArraySetCall(unwrappedReceiver, baseSource, operation, rhs, convert)
assignCall = generateAugmentedCallForAugmentedArraySetCall(receiver, baseSource, operation, rhs, convert)
setGetBlock =
generateSetGetBlockForAugmentedArraySetCall(unwrappedReceiver, baseSource, arrayAccessSource, operation, rhs, convert)
generateSetGetBlockForAugmentedArraySetCall(receiver, baseSource, arrayAccessSource, operation, rhs, convert)
this.annotations += annotations
}
}
private fun generateAugmentedCallForAugmentedArraySetCall(
unwrappedReceiver: T,
receiver: FirExpression, // a.get(x,y)
baseSource: KtSourceElement?,
operation: FirOperation,
rhs: T?,
@@ -990,7 +1050,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
calleeReference = buildSimpleNamedReference {
name = FirOperationNameConventions.ASSIGNMENTS.getValue(operation)
}
explicitReceiver = unwrappedReceiver.convert()
explicitReceiver = receiver
argumentList = buildArgumentList {
arguments += rhs?.convert() ?: buildErrorExpression(
null,
@@ -1003,7 +1063,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private fun generateSetGetBlockForAugmentedArraySetCall(
unwrappedReceiver: T,
receiver: FirExpression,
baseSource: KtSourceElement?,
arrayAccessSource: KtSourceElement?,
operation: FirOperation,
@@ -1020,7 +1080,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
* }
*/
return buildBlock {
val baseCall = unwrappedReceiver.convert() as FirFunctionCall
val baseCall = receiver as FirFunctionCall
val arrayVariable = generateTemporaryVariable(
baseModuleData,
@@ -1105,7 +1165,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
classTypeRefWithCorrectSourceKind: FirTypeRef,
firPropertyReturnTypeRefWithCorrectSourceKind: FirTypeRef
) =
buildPropertyAccessExpression {
buildPropertyAccessExpression {
source = parameterSource
typeRef = firPropertyReturnTypeRefWithCorrectSourceKind
dispatchReceiver = buildThisReceiverExpression {
@@ -566,7 +566,10 @@ private fun FirExpression.checkReceiver(name: String?): Boolean {
return receiverName == name
}
fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression, source: KtSourceElement): FirSafeCallExpression {
// this = .f(...)
// receiver = <expr>
// Returns safe call <expr>?.{ f(...) }
fun FirQualifiedAccess.createSafeCall(receiver: FirExpression, source: KtSourceElement): FirSafeCallExpression {
val checkedSafeCallSubject = buildCheckedSafeCallSubject {
@OptIn(FirContractViolation::class)
this.originalReceiverRef = FirExpressionRef<FirExpression>().apply {
@@ -582,11 +585,24 @@ fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression, source: KtSourc
this.checkedSubjectRef = FirExpressionRef<FirCheckedSafeCallSubject>().apply {
bind(checkedSafeCallSubject)
}
this.selector = this@wrapWithSafeCall
this.selector = this@createSafeCall
this.source = source
}
}
// Turns (a?.b).f(...) to a?.{ b.f(...) ) -- for any qualified access `.f(...)`
// Other patterns remain unchanged
fun FirExpression.pullUpSafeCallIfNecessary(): FirExpression {
if (this !is FirQualifiedAccess) return this
val safeCall = explicitReceiver as? FirSafeCallExpression ?: return this
val safeCallSelector = safeCall.selector as? FirExpression ?: return this
replaceExplicitReceiver(safeCallSelector)
safeCall.replaceSelector(this)
return safeCall
}
fun List<FirAnnotationCall>.filterUseSiteTarget(target: AnnotationUseSiteTarget): List<FirAnnotationCall> =
mapNotNull {
if (it.useSiteTarget != target) null
@@ -392,7 +392,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
session: FirSession,
) {
val receiverType = nullableReceiverExpression.typeRef.coneTypeSafe<ConeKotlinType>()
val typeAfterNullCheck = (selector as? FirQualifiedAccess)?.expressionTypeOrUnitForAssignment() ?: return
val typeAfterNullCheck = selector.expressionTypeOrUnitForAssignment() ?: return
val isReceiverActuallyNullable = if (session.languageVersionSettings.supportsFeature(LanguageFeature.SafeCallsAreAlwaysNullable)) {
true
} else {
@@ -409,7 +409,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, null)
}
private fun FirQualifiedAccess.expressionTypeOrUnitForAssignment(): ConeKotlinType? {
private fun FirStatement.expressionTypeOrUnitForAssignment(): ConeKotlinType? {
if (this is FirExpression) return typeRef.coneTypeSafe()
require(this is FirVariableAssignment) {
-2
View File
@@ -1,8 +1,6 @@
// WITH_STDLIB
// WITH_COROUTINES
// IGNORE_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: VARIABLE_EXPECTED at b?.a += ... Not sure FIR supports this (contact Dmitry Novozhilov)
import kotlin.coroutines.*
import helpers.*
@@ -0,0 +1,56 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND: JVM_IR
var cnt = 0
class A
var A?.b: A?
get() {
return this
}
set(v) {
cnt++
}
var A?.c: A?
get() {
return this
}
set(v) {
cnt++
}
operator fun A?.get(i: Int): A? = this
operator fun A?.set(i: Int, v: A?): A? {
cnt++
return this
}
operator fun A?.plus(a: A?) = this
fun test(a: A?) {
a?.b += null
a?.b?.c += null
a?.b.c += null // ".c" will be called anyway
a?.b[0] += null
a?.b?.c[0] += null
a?.b.c[0] += null // ".c" will be called anyway
a?.b[0][0] += null
a?.b?.c[0][0] += null
a?.b.c[0][0] += null // ".c" will be called anyway
}
fun box(): String {
test(null)
if (cnt != 3) return "fail 1: $cnt"
cnt = 0
test(A())
if (cnt != 9) return "fail 2: $cnt"
return "OK"
}
@@ -0,0 +1,53 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND: JVM_IR
var cnt = 0
class A
var A?.b: A?
get() {
return this
}
set(v) {
cnt++
}
var A?.c: A?
get() {
return this
}
set(v) {
cnt++
}
operator fun A?.get(i: Int): A? = this
operator fun A?.plusAssign(a: A?) {
cnt++
}
fun test(a: A?) {
a?.b += null
a?.b?.c += null
a?.b.c += null // ".c" will be called anyway
a?.b[0] += null
a?.b?.c[0] += null
a?.b.c[0] += null // ".c" will be called anyway
a?.b[0][0] += null
a?.b?.c[0][0] += null
a?.b.c[0][0] += null // ".c" will be called anyway
}
fun box(): String {
test(null)
if (cnt != 3) return "fail 1: $cnt"
cnt = 0
test(A())
if (cnt != 9) return "fail 2: $cnt"
return "OK"
}
@@ -0,0 +1,58 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND: JVM_IR
var cnt = 0
class A
var A?.b: A?
get() {
return this
}
set(v) {
cnt++
}
var A?.c: A?
get() {
return this
}
set(v) {
cnt++
}
operator fun A?.get(i: Int): A? = this
operator fun A?.set(i: Int, v: A?): A? {
cnt++
return this
}
operator fun A?.inc(): A? {
return this
}
fun test(a: A?) {
a?.b++
a?.b?.c++
a?.b.c++ // ".c" will be called anyway
a?.b[0]++
a?.b?.c[0]++
a?.b.c[0]++ // ".c" will be called anyway
a?.b[0][0]++
a?.b?.c[0][0]++
a?.b.c[0][0]++ // ".c" will be called anyway
}
fun box(): String {
test(null)
if (cnt != 3) return "fail 1: $cnt"
cnt = 0
test(A())
if (cnt != 9) return "fail 2: $cnt"
return "OK"
}
@@ -0,0 +1,57 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND: JVM_IR
var cnt = 0
class A
var A?.b: A?
get() {
return this
}
set(v) {
cnt++
}
var A?.c: A?
get() {
return this
}
set(v) {
cnt++
}
operator fun A?.get(i: Int): A? = this
operator fun A?.set(i: Int, v: A?): A? {
cnt++
return this
}
operator fun A?.inc(): A? {
return this
}
fun test(a: A?) {
++a?.b
++a?.b?.c
++a?.b.c // ".c" will be called anyway
++a?.b[0]
++a?.b?.c[0]
++a?.b.c[0] // ".c" will be called anyway
++a?.b[0][0]
++a?.b?.c[0][0]
++a?.b.c[0][0] // ".c" will be called anyway
}
fun box(): String {
test(null)
if (cnt != 3) return "fail 1: $cnt"
cnt = 0
test(A())
if (cnt != 9) return "fail 2: $cnt"
return "OK"
}
@@ -0,0 +1,54 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND: JVM_IR
var cnt = 0
class A
var A?.b: A?
get() {
return this
}
set(v) {
cnt++
}
var A?.c: A?
get() {
return this
}
set(v) {
cnt++
}
operator fun A?.get(i: Int): A? = this
operator fun A?.set(i: Int, v: A?): A? {
cnt++
return this
}
fun test(a: A?) {
a?.b = null
a?.b?.c = null
a?.b.c = null // ".c" will be called anyway
a?.b[0] = null
a?.b?.c[0] = null
a?.b.c[0] = null // ".c" will be called anyway
a?.b[0][0] = null
a?.b?.c[0][0] = null
a?.b.c[0][0] = null // ".c" will be called anyway
}
fun box(): String {
test(null)
if (cnt != 3) return "fail 1: $cnt"
cnt = 0
test(A())
if (cnt != 9) return "fail 2: $cnt"
return "OK"
}
@@ -41267,12 +41267,36 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/safeCall"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("augmentedAssigmentPlus.kt")
public void testAugmentedAssigmentPlus() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlus.kt");
}
@Test
@TestMetadata("augmentedAssigmentPlusAssign.kt")
public void testAugmentedAssigmentPlusAssign() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlusAssign.kt");
}
@Test
@TestMetadata("genericNull.kt")
public void testGenericNull() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/genericNull.kt");
}
@Test
@TestMetadata("incrementPostfix.kt")
public void testIncrementPostfix() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/incrementPostfix.kt");
}
@Test
@TestMetadata("incrementPrefix.kt")
public void testIncrementPrefix() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/incrementPrefix.kt");
}
@Test
@TestMetadata("kt1572.kt")
public void testKt1572() throws Exception {
@@ -41368,6 +41392,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
public void testSafeCallWithElvisFolding() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/safeCallWithElvisFolding.kt");
}
@Test
@TestMetadata("withAssignment.kt")
public void testWithAssignment() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/withAssignment.kt");
}
}
@Nested
@@ -15,7 +15,7 @@ class A() {
fun case1() {
var b: Case1? = Case1()
<!UNSAFE_CALL!>--<!>b?.a
--b?.a
}
@@ -15,7 +15,7 @@ class A() {
fun case1() {
var b: Case1? = Case1()
<!UNSAFE_CALL!>++<!>b?.a
++b?.a
}
@@ -21,15 +21,15 @@ package testPackCase2
fun case2(a: A?, c: C) {
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = {
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
}()
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
@@ -60,15 +60,15 @@ package testPackCase3
fun case3(a: A?, c: C) {
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = {
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
}()
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
@@ -43,15 +43,15 @@ package testPackCase2
fun case2(a: A?, c: C) {
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = {
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
}()
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
@@ -81,15 +81,15 @@ package testPackCase3
fun case3(a: A?, c: C) {
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = {
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
}()
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
@@ -5,7 +5,7 @@
// TESTCASE NUMBER: 1
fun case_1(x: Class?) {
x!!
<!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[if (true) {<!VAL_REASSIGNMENT!>x<!>=null;0} else 0] += <!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0]
<!DEBUG_INFO_EXPRESSION_TYPE("Class & Class"), DEBUG_INFO_EXPRESSION_TYPE("Class?")!>x<!>[if (true) {<!VAL_REASSIGNMENT!>x<!>=null;0} else 0] += <!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0]
<!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0].inv()
}
@@ -433,6 +433,24 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
}
@Test
@TestMetadata("safeCallsWithAssignment.kt")
public void testSafeCallsWithAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
}
@Test
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
public void testSafeCallsWithAugmentedAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
}
@Test
@TestMetadata("safeCallsWithUnaryOperators.kt")
public void testSafeCallsWithUnaryOperators() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
}
@Test
@TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception {
@@ -433,6 +433,24 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
}
@Test
@TestMetadata("safeCallsWithAssignment.kt")
public void testSafeCallsWithAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
}
@Test
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
public void testSafeCallsWithAugmentedAssignment() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
}
@Test
@TestMetadata("safeCallsWithUnaryOperators.kt")
public void testSafeCallsWithUnaryOperators() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
}
@Test
@TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception {