[FIR] Support boolean elvis bound smartcasts
#KT-44511 Fixed
This commit is contained in:
committed by
TeamCityServer
parent
2b088f1147
commit
9724d81a49
+5
@@ -2818,6 +2818,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("booleanElvisBoundSmartcast.kt")
|
||||||
|
public void testBooleanElvisBoundSmartcast() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/booleanElvisBoundSmartcast.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("booleanOperators.kt")
|
@TestMetadata("booleanOperators.kt")
|
||||||
public void testBooleanOperators() throws Exception {
|
public void testBooleanOperators() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/booleanOperators.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/booleanOperators.kt");
|
||||||
|
|||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
FILE: booleanElvisBoundSmartcast.kt
|
||||||
|
public final class A : R|kotlin/Any| {
|
||||||
|
public constructor(b: R|kotlin/Boolean|): R|A| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val b: R|kotlin/Boolean| = R|<local>/b|
|
||||||
|
public get(): R|kotlin/Boolean|
|
||||||
|
|
||||||
|
public final fun foo(): R|kotlin/Unit| {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final fun test_1(a: R|A?|): R|kotlin/Unit| {
|
||||||
|
when () {
|
||||||
|
R|<local>/a|?.{ $subj$.R|/A.b| } ?: Boolean(false) -> {
|
||||||
|
R|<local>/a|.R|/A.foo|()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
R|<local>/a|.<Inapplicable(INAPPLICABLE_WRONG_RECEIVER): /A.foo>#()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final fun test_2(a: R|A?|): R|kotlin/Unit| {
|
||||||
|
when () {
|
||||||
|
R|<local>/a|?.{ $subj$.R|/A.b| } ?: Boolean(true) -> {
|
||||||
|
R|<local>/a|.<Inapplicable(INAPPLICABLE_WRONG_RECEIVER): /A.foo>#()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
R|<local>/a|.R|/A.foo|()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// !LANGUAGE: +BooleanElvisBoundSmartCasts
|
||||||
|
// ISSUE: KT-44511, also relates to KT-8492 and KT-26357
|
||||||
|
|
||||||
|
class A(val b: Boolean) {
|
||||||
|
fun foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_1(a: A?) {
|
||||||
|
if (a?.b ?: false) {
|
||||||
|
a.foo() // OK
|
||||||
|
} else {
|
||||||
|
a<!UNSAFE_CALL!>.<!>foo() // Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_2(a: A?) {
|
||||||
|
if (a?.b ?: true) {
|
||||||
|
a<!UNSAFE_CALL!>.<!>foo() // Error
|
||||||
|
} else {
|
||||||
|
a.foo() // OK
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -3194,6 +3194,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("booleanElvisBoundSmartcast.kt")
|
||||||
|
public void testBooleanElvisBoundSmartcast() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/booleanElvisBoundSmartcast.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("booleanOperators.kt")
|
@TestMetadata("booleanOperators.kt")
|
||||||
public void testBooleanOperators() throws Exception {
|
public void testBooleanOperators() throws Exception {
|
||||||
|
|||||||
+6
@@ -3229,6 +3229,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("booleanElvisBoundSmartcast.kt")
|
||||||
|
public void testBooleanElvisBoundSmartcast() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/booleanElvisBoundSmartcast.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("booleanOperators.kt")
|
@TestMetadata("booleanOperators.kt")
|
||||||
public void testBooleanOperators() throws Exception {
|
public void testBooleanOperators() throws Exception {
|
||||||
|
|||||||
+20
-2
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.resolve.dfa
|
package org.jetbrains.kotlin.fir.resolve.dfa
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.PrivateForInline
|
import org.jetbrains.kotlin.fir.PrivateForInline
|
||||||
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
|
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
|
||||||
@@ -14,6 +15,7 @@ import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
|
|||||||
import org.jetbrains.kotlin.fir.contracts.description.ConeReturnsEffectDeclaration
|
import org.jetbrains.kotlin.fir.contracts.description.ConeReturnsEffectDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack
|
import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack
|
||||||
@@ -26,6 +28,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBod
|
|||||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -1123,8 +1126,23 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitElvis() {
|
fun exitElvis(elvisExpression: FirElvisExpression) {
|
||||||
graphBuilder.exitElvis().mergeIncomingFlow()
|
val node = graphBuilder.exitElvis().mergeIncomingFlow()
|
||||||
|
if (!components.session.languageVersionSettings.supportsFeature(LanguageFeature.BooleanElvisBoundSmartCasts)) return
|
||||||
|
val lhs = elvisExpression.lhs
|
||||||
|
val rhs = elvisExpression.rhs
|
||||||
|
if (rhs is FirConstExpression<*> && rhs.kind == ConstantValueKind.Boolean) {
|
||||||
|
if (lhs.typeRef.coneType.classId != StandardClassIds.Boolean) return
|
||||||
|
|
||||||
|
val flow = node.flow
|
||||||
|
// a ?: false == true -> a != null
|
||||||
|
// a ?: true == false -> a != null
|
||||||
|
val elvisVariable = variableStorage.getOrCreateVariable(flow, elvisExpression)
|
||||||
|
val lhsVariable = variableStorage.getOrCreateVariable(flow, lhs)
|
||||||
|
|
||||||
|
val value = rhs.value as Boolean
|
||||||
|
flow.addImplication(elvisVariable.eq(!value) implies (lhsVariable.notEq(null)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callable reference
|
// Callable reference
|
||||||
|
|||||||
+1
-1
@@ -244,7 +244,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataFlowAnalyzer.exitElvis()
|
dataFlowAnalyzer.exitElvis(elvisExpression)
|
||||||
return result.compose()
|
return result.compose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ interface Order {
|
|||||||
fun foo(o: Any) {
|
fun foo(o: Any) {
|
||||||
val order = o as? Order
|
val order = o as? Order
|
||||||
if (order?.expired ?: false) {
|
if (order?.expired ?: false) {
|
||||||
order<!UNSAFE_CALL!>.<!>doSomething()
|
order.doSomething()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
}
|
}
|
||||||
if (order?.notExpired() ?: false) {
|
if (order?.notExpired() ?: false) {
|
||||||
order<!UNSAFE_CALL!>.<!>doSomething()
|
order.doSomething()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,4 +47,4 @@ fun baz(o: Boolean?) {
|
|||||||
else {
|
else {
|
||||||
o<!UNSAFE_CALL!>.<!>hashCode()
|
o<!UNSAFE_CALL!>.<!>hashCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user