提交 9724d81a 编写于 作者: D Dmitriy Novozhilov 提交者: TeamCityServer

[FIR] Support boolean elvis bound smartcasts

#KT-44511 Fixed
上级 2b088f11
...@@ -2818,6 +2818,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract ...@@ -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");
......
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|()
}
}
}
// !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
}
}
...@@ -3194,6 +3194,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { ...@@ -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 {
......
...@@ -3229,6 +3229,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos ...@@ -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 {
......
...@@ -5,6 +5,7 @@ ...@@ -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 ...@@ -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 ...@@ -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>( ...@@ -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
......
...@@ -244,7 +244,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTran ...@@ -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 { ...@@ -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?) { ...@@ -47,4 +47,4 @@ fun baz(o: Boolean?) {
else { else {
o<!UNSAFE_CALL!>.<!>hashCode() o<!UNSAFE_CALL!>.<!>hashCode()
} }
} }
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册