[FIR] Fix smartcast after elvis with escaping lambda, ^KT-44510 Fixed
This commit is contained in:
+28
-24
@@ -756,32 +756,32 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
val (node, unionNode) = graphBuilder.exitCheckNotNullCall(checkNotNullCall, callCompleted)
|
val (node, unionNode) = graphBuilder.exitCheckNotNullCall(checkNotNullCall, callCompleted)
|
||||||
node.mergeIncomingFlow()
|
node.mergeIncomingFlow()
|
||||||
|
|
||||||
fun FirExpression.propagateNotNullInfo() {
|
checkNotNullCall.argument.propagateNotNullInfo(node)
|
||||||
val symbol = this.symbol
|
|
||||||
if (symbol != null) {
|
unionNode?.let { unionFlowFromArguments(it) }
|
||||||
variableStorage.getOrCreateRealVariable(node.previousFlow, symbol, this)?.let { operandVariable ->
|
}
|
||||||
node.flow.addTypeStatement(operandVariable typeEq any)
|
|
||||||
logicSystem.approveStatementsInsideFlow(
|
fun FirExpression.propagateNotNullInfo(node: CFGNode<*>) {
|
||||||
node.flow,
|
val symbol = this.symbol
|
||||||
operandVariable notEq null,
|
if (symbol != null) {
|
||||||
shouldRemoveSynthetics = true,
|
variableStorage.getOrCreateRealVariable(node.previousFlow, symbol, this)?.let { operandVariable ->
|
||||||
shouldForkFlow = false
|
node.flow.addTypeStatement(operandVariable typeEq any)
|
||||||
)
|
logicSystem.approveStatementsInsideFlow(
|
||||||
}
|
node.flow,
|
||||||
|
operandVariable notEq null,
|
||||||
|
shouldRemoveSynthetics = true,
|
||||||
|
shouldForkFlow = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
when (this) {
|
}
|
||||||
is FirSafeCallExpression -> receiver.propagateNotNullInfo()
|
when (this) {
|
||||||
is FirTypeOperatorCall -> {
|
is FirSafeCallExpression -> receiver.propagateNotNullInfo(node)
|
||||||
if (operation == FirOperation.AS || operation == FirOperation.SAFE_AS) {
|
is FirTypeOperatorCall -> {
|
||||||
argument.propagateNotNullInfo()
|
if (operation == FirOperation.AS || operation == FirOperation.SAFE_AS) {
|
||||||
}
|
argument.propagateNotNullInfo(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNotNullCall.argument.propagateNotNullInfo()
|
|
||||||
|
|
||||||
unionNode?.let { unionFlowFromArguments(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- When -----------------------------------
|
// ----------------------------------- When -----------------------------------
|
||||||
@@ -1459,12 +1459,16 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitElvis(elvisExpression: FirElvisExpression) {
|
fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean) {
|
||||||
val node = graphBuilder.exitElvis().mergeIncomingFlow()
|
val node = graphBuilder.exitElvis().mergeIncomingFlow()
|
||||||
|
if (isLhsNotNull) {
|
||||||
|
elvisExpression.lhs.propagateNotNullInfo(node)
|
||||||
|
}
|
||||||
|
|
||||||
if (!components.session.languageVersionSettings.supportsFeature(LanguageFeature.BooleanElvisBoundSmartCasts)) return
|
if (!components.session.languageVersionSettings.supportsFeature(LanguageFeature.BooleanElvisBoundSmartCasts)) return
|
||||||
val lhs = elvisExpression.lhs
|
|
||||||
val rhs = elvisExpression.rhs
|
val rhs = elvisExpression.rhs
|
||||||
if (rhs is FirConstExpression<*> && rhs.kind == ConstantValueKind.Boolean) {
|
if (rhs is FirConstExpression<*> && rhs.kind == ConstantValueKind.Boolean) {
|
||||||
|
val lhs = elvisExpression.lhs
|
||||||
if (lhs.typeRef.coneType.classId != StandardClassIds.Boolean) return
|
if (lhs.typeRef.coneType.classId != StandardClassIds.Boolean) return
|
||||||
|
|
||||||
val flow = node.flow
|
val flow = node.flow
|
||||||
|
|||||||
+3
-1
@@ -245,11 +245,13 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isLhsNotNull = false
|
||||||
if (result.rhs.typeRef.coneTypeSafe<ConeKotlinType>()?.isNothing == true) {
|
if (result.rhs.typeRef.coneTypeSafe<ConeKotlinType>()?.isNothing == true) {
|
||||||
val lhsType = result.lhs.typeRef.coneTypeSafe<ConeKotlinType>()
|
val lhsType = result.lhs.typeRef.coneTypeSafe<ConeKotlinType>()
|
||||||
if (lhsType != null) {
|
if (lhsType != null) {
|
||||||
val newReturnType = lhsType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)
|
val newReturnType = lhsType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)
|
||||||
result.replaceTypeRef(result.typeRef.resolvedTypeFromPrototype(newReturnType))
|
result.replaceTypeRef(result.typeRef.resolvedTypeFromPrototype(newReturnType))
|
||||||
|
isLhsNotNull = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +267,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataFlowAnalyzer.exitElvis(elvisExpression)
|
dataFlowAnalyzer.exitElvis(elvisExpression, isLhsNotNull = isLhsNotNull)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ inline fun <R> callItContracted(fn: () -> R): R {
|
|||||||
|
|
||||||
fun smartIt(p1: String?, p2: String?) {
|
fun smartIt(p1: String?, p2: String?) {
|
||||||
p1 ?: callIt { return }
|
p1 ?: callIt { return }
|
||||||
p1<!UNSAFE_CALL!>.<!>length
|
p1.length
|
||||||
|
|
||||||
p2 ?: callItContracted { return }
|
p2 ?: callItContracted { return }
|
||||||
p2.length
|
p2.length
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ fun <R> callIt(fn: () -> R): R = TODO()
|
|||||||
|
|
||||||
fun smartIt(p1: String?, p2: String?) {
|
fun smartIt(p1: String?, p2: String?) {
|
||||||
p1 ?: callIt { TODO() }
|
p1 ?: callIt { TODO() }
|
||||||
p1<!UNSAFE_CALL!>.<!>length // smartcast
|
p1.length // smartcast
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user