FIR: Support safe-calls new format in FIR builders

^KT-38444 In Progress
This commit is contained in:
Denis Zharkov
2020-06-01 15:08:24 +03:00
parent cb1c3f87f0
commit 9f793f1486
5 changed files with 75 additions and 8 deletions
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.builder.* import org.jetbrains.kotlin.fir.references.builder.*
import org.jetbrains.kotlin.fir.symbols.CallableId import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
@@ -536,6 +536,14 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
} }
} }
require(operation == FirOperation.ASSIGN) require(operation == FirOperation.ASSIGN)
if (this?.elementType == SAFE_ACCESS_EXPRESSION && this != null) {
val safeCallNonAssignment = convert() as? FirSafeCallExpression
if (safeCallNonAssignment != null) {
return putAssignmentToSafeCall(safeCallNonAssignment, baseSource, value)
}
}
return buildVariableAssignment { return buildVariableAssignment {
source = baseSource source = baseSource
safe = false safe = false
@@ -544,6 +552,29 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
} }
} }
// gets a?.{ $subj.x } and turns it to a?.{ $subj.x = v }
private fun putAssignmentToSafeCall(
safeCallNonAssignment: FirSafeCallExpression,
baseSource: FirSourceElement?,
value: FirExpression
): FirSafeCallExpression {
val nestedAccess = safeCallNonAssignment.regularQualifiedAccess
val assignment = buildVariableAssignment {
source = baseSource
safe = false
rValue = value
calleeReference = nestedAccess.calleeReference
explicitReceiver = safeCallNonAssignment.checkedSubject.value
}
safeCallNonAssignment.replaceRegularQualifiedAccess(
assignment
)
return safeCallNonAssignment
}
private fun T.generateAugmentedArraySetCall( private fun T.generateAugmentedArraySetCall(
baseSource: FirSourceElement?, baseSource: FirSourceElement?,
operation: FirOperation, operation: FirOperation,
@@ -19,13 +19,20 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.expressions.impl.FirModifiableQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.expressions.impl.FirStubStatement import org.jetbrains.kotlin.fir.expressions.impl.FirStubStatement
import org.jetbrains.kotlin.fir.expressions.impl.buildSingleExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.buildSingleExpressionBlock
import org.jetbrains.kotlin.fir.references.FirNamedReference import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.builder.* import org.jetbrains.kotlin.fir.references.builder.buildDelegateFieldReference
import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
import org.jetbrains.kotlin.fir.symbols.constructStarProjectedType import org.jetbrains.kotlin.fir.symbols.constructStarProjectedType
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.FirDelegateFieldSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeStarProjection import org.jetbrains.kotlin.fir.types.ConeStarProjection
import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef
@@ -479,3 +486,22 @@ private fun FirExpression.checkReceiver(name: String?): Boolean {
val receiverName = (receiver.calleeReference as? FirNamedReference)?.name?.asString() ?: return false val receiverName = (receiver.calleeReference as? FirNamedReference)?.name?.asString() ?: return false
return receiverName == name return receiverName == name
} }
fun FirModifiableQualifiedAccess.wrapWithSafeCall(receiver: FirExpression): FirSafeCallExpression {
// TODO: Refactor tree to make FirModifiableQualifiedAccess inherit FirQualifiedAccess
require(this is FirQualifiedAccess) {
"Safe-call instances are expected to be FirQualifiedAccess, but ${this::class} was found"
}
val checkedSafeCallSubject = buildCheckedSafeCallSubject {
this.originalReceiverReference = FirSafeCallOriginalReceiverReference(receiver)
}
explicitReceiver = checkedSafeCallSubject
return buildSafeCallExpression {
this.receiver = receiver
this.checkedSubject = FirSafeCallCheckedSubjectReference(checkedSafeCallSubject)
this.regularQualifiedAccess = this@wrapWithSafeCall
this.source = this@wrapWithSafeCall.source
}
}
@@ -483,7 +483,11 @@ class ExpressionsConverter(
} }
(firSelector as? FirModifiableQualifiedAccess)?.let { (firSelector as? FirModifiableQualifiedAccess)?.let {
it.safe = isSafe if (isSafe) {
return it.wrapWithSafeCall(firReceiver!!)
}
it.safe = false
it.explicitReceiver = firReceiver it.explicitReceiver = firReceiver
} }
return firSelector return firSelector
@@ -1699,8 +1699,14 @@ class RawFirBuilder(
) )
val firSelector = selector.toFirExpression("Incorrect selector expression") val firSelector = selector.toFirExpression("Incorrect selector expression")
if (firSelector is FirModifiableQualifiedAccess) { if (firSelector is FirModifiableQualifiedAccess) {
firSelector.safe = expression is KtSafeQualifiedExpression val receiver = expression.receiverExpression.toFirExpression("Incorrect receiver expression")
firSelector.explicitReceiver = expression.receiverExpression.toFirExpression("Incorrect receiver expression")
if (expression is KtSafeQualifiedExpression) {
return firSelector.wrapWithSafeCall(receiver)
}
firSelector.safe = false
firSelector.explicitReceiver = receiver
} }
return firSelector return firSelector
} }
@@ -34,9 +34,9 @@ FILE: calls.kt
} }
public? final? fun testQualified(first: My, second: My?): R|kotlin/Unit| { public? final? fun testQualified(first: My, second: My?): R|kotlin/Unit| {
println#(first#.x#) println#(first#.x#)
println#(second#?.x#) println#(second#?.{ $subj$.x# })
first#.foo#() first#.foo#()
second#?.foo#() second#?.{ $subj$.foo#() }
first#.copy#().foo#() first#.copy#().foo#()
first#.x# = IntegerLiteral(42) first#.x# = IntegerLiteral(42)
} }