From 0fbf648971c98cfb50ba29b2be805dac0347b1ad Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Wed, 30 Aug 2023 22:49:10 +0900 Subject: [PATCH] [LL API] Pull smart casts from the CFG in 'ContextCollector' ^KT-61599 Fixed --- .../fir/transformers/LLFirBodyLazyResolver.kt | 3 +- .../level/api/fir/util/ContextCollector.kt | 103 ++++++++++++----- .../testdata/contextCollector/localClass.kt | 9 ++ .../testdata/contextCollector/localClass.txt | 45 ++++++++ .../contextCollector/smartCasts/afterIf.kt | 10 ++ .../contextCollector/smartCasts/afterIf.txt | 36 ++++++ .../contextCollector/smartCasts/afterLoop.kt | 17 +++ .../contextCollector/smartCasts/afterLoop.txt | 48 ++++++++ .../contextCollector/smartCasts/andRight.kt | 11 ++ .../contextCollector/smartCasts/andRight.txt | 43 +++++++ .../argument.kt} | 4 +- .../argument.txt} | 4 +- .../argumentAsReceiver.kt} | 2 +- .../argumentAsReceiver.txt} | 2 +- .../contextCollector/smartCasts/beforeIf.kt | 10 ++ .../contextCollector/smartCasts/beforeIf.txt | 36 ++++++ .../contextCollector/smartCasts/beforeLoop.kt | 14 +++ .../smartCasts/beforeLoop.txt | 38 +++++++ .../smartCasts/dispatchReceiver.kt | 11 ++ .../smartCasts/dispatchReceiver.txt | 55 +++++++++ .../smartCasts/extensionReceiver.kt | 9 ++ .../smartCasts/extensionReceiver.txt | 44 ++++++++ .../contextCollector/smartCasts/insideLoop.kt | 14 +++ .../smartCasts/insideLoop.txt | 46 ++++++++ .../contextCollector/smartCasts/orRight.kt | 13 +++ .../contextCollector/smartCasts/orRight.txt | 41 +++++++ .../contextCollector/smartCasts/plainCheck.kt | 9 ++ .../smartCasts/plainCheck.txt | 43 +++++++ .../contextCollector/smartCasts/require.kt | 10 ++ .../contextCollector/smartCasts/require.txt | 36 ++++++ .../contextCollector/smartCasts/when.kt | 14 +++ .../contextCollector/smartCasts/when.txt | 49 ++++++++ .../api/fir/AbstractContextCollectorTest.kt | 8 +- .../fir/ContextCollectorTestGenerated.java | 106 ++++++++++++++++-- .../PersistentImplicitReceiverStack.kt | 4 + 35 files changed, 903 insertions(+), 44 deletions(-) create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/localClass.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/localClass.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.txt rename analysis/low-level-api-fir/testdata/contextCollector/{smartCastArgument.kt => smartCasts/argument.kt} (54%) rename analysis/low-level-api-fir/testdata/contextCollector/{smartCastArgument.txt => smartCasts/argument.txt} (91%) rename analysis/low-level-api-fir/testdata/contextCollector/{smartCastReceiverArgument.kt => smartCasts/argumentAsReceiver.kt} (78%) rename analysis/low-level-api-fir/testdata/contextCollector/{smartCastReceiverArgument.txt => smartCasts/argumentAsReceiver.txt} (96%) create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.txt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.kt create mode 100644 analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.txt diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt index 861cd784bee..bf169fe636b 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt @@ -225,7 +225,8 @@ private class LLFirBodyTargetResolver( withPsiEntry("contextPsiElement", contextPsiElement) } - LLFirCodeFragmentContext(elementContext.towerDataContext.withExtraScopes(), elementContext.smartCasts) + val variables = elementContext.smartCasts.mapKeys { it.key.identifier.symbol } + LLFirCodeFragmentContext(elementContext.towerDataContext.withExtraScopes(), variables) } else { val towerDataContext = FirTowerDataContext().withExtraScopes() LLFirCodeFragmentContext(towerDataContext, emptyMap()) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt index c3d0a490697..4c87b67094a 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt @@ -7,8 +7,6 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.util import com.intellij.openapi.progress.ProgressManager import com.intellij.psi.PsiElement -import kotlinx.collections.immutable.PersistentMap -import kotlinx.collections.immutable.persistentMapOf import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignation import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.ContextKind @@ -23,11 +21,17 @@ import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowAnalyzerContext +import org.jetbrains.kotlin.fir.resolve.dfa.PropertyStability +import org.jetbrains.kotlin.fir.resolve.dfa.RealVariable +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ClassExitNode +import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph +import org.jetbrains.kotlin.fir.resolve.dfa.smartCastedType import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFullBodyResolve import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.typeContext import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf @@ -43,7 +47,7 @@ internal object ContextCollector { class Context( val towerDataContext: FirTowerDataContext, - val smartCasts: Map, Set>, + val smartCasts: Map>, ) enum class FilterResponse { @@ -171,8 +175,6 @@ private class ContextCollectorVisitor( dataFlowAnalyzerContext = DataFlowAnalyzerContext(session) ) - private var smartCasts: PersistentMap, Set> = persistentMapOf() - private val result = HashMap() override fun visitElement(element: FirElement) { @@ -199,7 +201,7 @@ private class ContextCollectorVisitor( val response = filter(psi) if (response != FilterResponse.SKIP) { - result[key] = Context(context.towerDataContext, smartCasts) + result[key] = computeContext(fir) } if (response == FilterResponse.STOP) { @@ -207,6 +209,75 @@ private class ContextCollectorVisitor( } } + private fun computeContext(fir: FirElement): Context { + val implicitReceiverStack = context.towerDataContext.implicitReceiverStack + + val smartCasts = mutableMapOf>() + + // Receiver types cannot be updated in an immutable snapshot. + // So here we modify the types inside the 'context', then make a snapshot, and restore the types back. + val oldReceiverTypes = mutableListOf>() + + val cfgNode = getControlFlowNode(fir) + + if (cfgNode != null) { + val flow = cfgNode.flow + + for (realVariable in flow.knownVariables) { + val typeStatement = flow.getTypeStatement(realVariable) ?: continue + if (realVariable.stability != PropertyStability.STABLE_VALUE && realVariable.stability != PropertyStability.LOCAL_VAR) { + continue + } + + smartCasts[typeStatement.variable] = typeStatement.exactType + + // The compiler pushes smart-cast types for implicit receivers to ease later lookups. + // Here we emulate such behavior. Unlike the compiler, though, modified types are only reflected in the created snapshot. + // See other usages of 'replaceReceiverType()' for more information. + if (realVariable.isThisReference) { + val identifier = typeStatement.variable.identifier + val receiverIndex = implicitReceiverStack.getReceiverIndex(identifier.symbol) + if (receiverIndex != null) { + oldReceiverTypes.add(receiverIndex to implicitReceiverStack.getType(receiverIndex)) + + val originalType = implicitReceiverStack.getOriginalType(receiverIndex) + val smartCastedType = typeStatement.smartCastedType(session.typeContext, originalType) + implicitReceiverStack.replaceReceiverType(receiverIndex, smartCastedType) + } + } + } + } + + val towerDataContextSnapshot = context.towerDataContext.createSnapshot() + + for ((index, oldType) in oldReceiverTypes) { + implicitReceiverStack.replaceReceiverType(index, oldType) + } + + return Context(towerDataContextSnapshot, smartCasts) + } + + private fun getControlFlowNode(fir: FirElement): CFGNode<*>? { + for (container in context.containers.asReversed()) { + val cfgOwner = container as? FirControlFlowGraphOwner ?: continue + val cfgReference = cfgOwner.controlFlowGraphReference ?: continue + val cfg = cfgReference.controlFlowGraph ?: continue + + val node = cfg.nodes.lastOrNull { isAcceptedControlFlowNode(it) && it.fir === fir } + if (node != null) { + return node + } else if (!cfg.isSubGraph) { + return null + } + } + + return null + } + + private fun isAcceptedControlFlowNode(node: CFGNode<*>): Boolean { + return node !is ClassExitNode + } + override fun visitFile(file: FirFile) { context.withFile(file, holder) { withInterceptor { @@ -477,24 +548,6 @@ private class ContextCollectorVisitor( } } - override fun visitSmartCastExpression(smartCastExpression: FirSmartCastExpression) { - if (smartCastExpression.isStable) { - val symbol = smartCastExpression.originalExpression.toResolvedCallableSymbol() - if (symbol != null) { - val previousSmartCasts = smartCasts - try { - smartCasts = smartCasts.put(symbol, smartCastExpression.typesFromSmartCast.toSet()) - super.visitSmartCastExpression(smartCastExpression) - return - } finally { - smartCasts = previousSmartCasts - } - } - } - - super.visitSmartCastExpression(smartCastExpression) - } - @ContextCollectorDsl private fun Processor.processSignatureAnnotations(declaration: FirDeclaration) { for (annotation in declaration.annotations) { diff --git a/analysis/low-level-api-fir/testdata/contextCollector/localClass.kt b/analysis/low-level-api-fir/testdata/contextCollector/localClass.kt new file mode 100644 index 00000000000..0bd03324758 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/localClass.kt @@ -0,0 +1,9 @@ +fun test() { + class Local { + fun foo(): String { + return "foo" + } + } + + Local().foo() +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/localClass.txt b/analysis/low-level-api-fir/testdata/contextCollector/localClass.txt new file mode 100644 index 00000000000..97e01e8efc8 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/localClass.txt @@ -0,0 +1,45 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Element 8 + Scope: FirLocalScope + Classifiers: + FirRegularClassSymbol local final class Local : R|kotlin/Any| + Element 9 + Implicit receiver: + FirRegularClassSymbol local final class Local : R|kotlin/Any| + Type: /Local + Element 10 + Scope: FirLocalScope + Element 11 + Scope: FirLocalScope + +FILE: [ResolvedTo(IMPORTS)] localClass.kt + public final [ResolvedTo(BODY_RESOLVE)] fun test(): R|kotlin/Unit| { + local final [ResolvedTo(BODY_RESOLVE)] class Local : R|kotlin/Any| { + public [ResolvedTo(BODY_RESOLVE)] constructor(): R|/Local| { + super() + } + + public final [ResolvedTo(BODY_RESOLVE)] fun foo(): R|kotlin/String| { + ^foo String(foo) + } + + } + + R|/Local.Local|().R|/foo|() + } diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.kt new file mode 100644 index 00000000000..da8de51259c --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.kt @@ -0,0 +1,10 @@ +interface Foo {} + +fun test(obj: Any) { + if (obj is Foo) { + consume(obj) + } + obj.hashCode() +} + +fun consume(obj: Foo) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.txt new file mode 100644 index 00000000000..53efbdef5db --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.txt @@ -0,0 +1,36 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol obj: R|kotlin/Any| + Element 8 + Scope: FirLocalScope + +FILE: [ResolvedTo(IMPORTS)] afterIf.kt + public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|): R|kotlin/Unit| { + when () { + (R|/obj| is R|Foo|) -> { + R|/consume|(R|/obj|) + } + } + + R|/obj|.R|kotlin/Any.hashCode|() + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Foo|): R|kotlin/Unit| { + } diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.kt new file mode 100644 index 00000000000..b9f91d55e94 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.kt @@ -0,0 +1,17 @@ +interface Node { + val shouldProcess: Boolean + val parent: Node? +} + +fun test(initial: Node?) { + var current = initial + + while (current!!.shouldProcess) { + consume(current) + current = current.parent + } + + consume(current) +} + +fun consume(node: Node) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.txt new file mode 100644 index 00000000000..ba3821f67d9 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.txt @@ -0,0 +1,48 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol initial: R|Node?| + Element 8 + Scope: FirLocalScope + Properties: + FirPropertySymbol lvar current: R|Node?| +Smart Casts: + FirPropertySymbol lvar current: R|Node?| + Types: + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] afterLoop.kt + public abstract [ResolvedTo(STATUS)] interface Node : R|kotlin/Any| { + public abstract [ResolvedTo(CONTRACTS)] val shouldProcess: R|kotlin/Boolean| + public [ResolvedTo(CONTRACTS)] get(): R|kotlin/Boolean| + + public abstract [ResolvedTo(CONTRACTS)] val parent: R|Node?| + public [ResolvedTo(CONTRACTS)] get(): R|Node?| + + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] initial: R|Node?|): R|kotlin/Unit| { + [ResolvedTo(BODY_RESOLVE)] lvar current: R|Node?| = R|/initial| + while(R|/current|!!.R|/Node.shouldProcess|) { + R|/consume|(R|/current|) + R|/current| = R|/current|.R|/Node.parent| + } + + R|/consume|(R|/current|) + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] node: R|Node|): R|kotlin/Unit| { + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.kt new file mode 100644 index 00000000000..65d32ebd1dc --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.kt @@ -0,0 +1,11 @@ +interface Foo { + val isValid: Boolean +} + +fun test(obj: Any) { + if (obj is Foo && obj.isValid) { + consume(obj) + } +} + +fun consume(obj: Foo) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.txt new file mode 100644 index 00000000000..9cb913b60bd --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.txt @@ -0,0 +1,43 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol obj: R|kotlin/Any| + Element 8 + Scope: FirLocalScope +Smart Casts: + FirValueParameterSymbol obj: R|kotlin/Any| + Types: + Foo + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] andRight.kt + public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { + public abstract [ResolvedTo(CONTRACTS)] val isValid: R|kotlin/Boolean| + public [ResolvedTo(CONTRACTS)] get(): R|kotlin/Boolean| + + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|): R|kotlin/Unit| { + when () { + (R|/obj| is R|Foo|) && R|/obj|.R|/Foo.isValid| -> { + R|/consume|(R|/obj|) + } + } + + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Foo|): R|kotlin/Unit| { + } diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argument.kt similarity index 54% rename from analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.kt rename to analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argument.kt index ddb770203ca..e42d6d8477f 100644 --- a/analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.kt +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argument.kt @@ -2,8 +2,8 @@ interface Foo {} fun test(obj: Any) { if (obj is Foo) { - consume(obj) + consume(obj) } } -fun consume(obj: Any) {} \ No newline at end of file +fun consume(obj: Foo) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argument.txt similarity index 91% rename from analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.txt rename to analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argument.txt index 274b72a276a..0f664ad7cef 100644 --- a/analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.txt +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argument.txt @@ -27,7 +27,7 @@ Smart Casts: Foo kotlin/Any -FILE: [ResolvedTo(IMPORTS)] smartCastArgument.kt +FILE: [ResolvedTo(IMPORTS)] argument.kt public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { } public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|): R|kotlin/Unit| { @@ -38,5 +38,5 @@ FILE: [ResolvedTo(IMPORTS)] smartCastArgument.kt } } - public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|kotlin/Any|): R|kotlin/Unit| { + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Foo|): R|kotlin/Unit| { } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argumentAsReceiver.kt similarity index 78% rename from analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.kt rename to analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argumentAsReceiver.kt index da03294957b..d7027670bd6 100644 --- a/analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.kt +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argumentAsReceiver.kt @@ -4,7 +4,7 @@ interface Bar {} fun test(obj: Any) { if (obj is Foo) { if (obj is Bar) { - obj.consume(obj) + obj.consume(obj) } } } diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argumentAsReceiver.txt similarity index 96% rename from analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.txt rename to analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argumentAsReceiver.txt index 363a8d6c671..972e672d7bd 100644 --- a/analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.txt +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argumentAsReceiver.txt @@ -30,7 +30,7 @@ Smart Casts: kotlin/Any Bar -FILE: [ResolvedTo(IMPORTS)] smartCastReceiverArgument.kt +FILE: [ResolvedTo(IMPORTS)] argumentAsReceiver.kt public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { } public abstract [ResolvedTo(STATUS)] interface Bar : R|kotlin/Any| { diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.kt new file mode 100644 index 00000000000..5f0f64056aa --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.kt @@ -0,0 +1,10 @@ +interface Foo {} + +fun test(obj: Any) { + obj.hashCode() + if (obj is Foo) { + consume(obj) + } +} + +fun consume(obj: Foo) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.txt new file mode 100644 index 00000000000..1d4a61329b2 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.txt @@ -0,0 +1,36 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol obj: R|kotlin/Any| + Element 8 + Scope: FirLocalScope + +FILE: [ResolvedTo(IMPORTS)] beforeIf.kt + public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|): R|kotlin/Unit| { + R|/obj|.R|kotlin/Any.hashCode|() + when () { + (R|/obj| is R|Foo|) -> { + R|/consume|(R|/obj|) + } + } + + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Foo|): R|kotlin/Unit| { + } diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.kt new file mode 100644 index 00000000000..3312b22eddd --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.kt @@ -0,0 +1,14 @@ +interface Node { + val parent: Node? +} + +fun test(initial: Node) { + var current = initial + + while (current != null) { + consume(current) + current = current.parent + } +} + +fun consume(node: Node) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.txt new file mode 100644 index 00000000000..baef0cc2b7e --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.txt @@ -0,0 +1,38 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol initial: R|Node| + Element 8 + Scope: FirLocalScope + +FILE: [ResolvedTo(IMPORTS)] beforeLoop.kt + public abstract [ResolvedTo(STATUS)] interface Node : R|kotlin/Any| { + public abstract [ResolvedTo(CONTRACTS)] val parent: R|Node?| + public [ResolvedTo(CONTRACTS)] get(): R|Node?| + + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] initial: R|Node|): R|kotlin/Unit| { + [ResolvedTo(BODY_RESOLVE)] lvar current: R|Node| = R|/initial| + while(!=(R|/current|, Null(null))) { + R|/consume|(R|/current|) + R|/current| = R|/current|.R|/Node.parent| + } + + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] node: R|Node|): R|kotlin/Unit| { + } diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.kt new file mode 100644 index 00000000000..d8596ef723f --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.kt @@ -0,0 +1,11 @@ +open class Foo { + fun foo() { + if (this is Bar) { + consume(this) + } + } +} + +class Bar : Foo() + +fun consume(obj: Bar) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.txt new file mode 100644 index 00000000000..d4e6676234a --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.txt @@ -0,0 +1,55 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Implicit receiver: + FirRegularClassSymbol public open class Foo : R|kotlin/Any| + Type: Bar + Element 8 + Scope: FirLocalScope + Element 9 + Scope: FirLocalScope + Element 10 + Scope: FirLocalScope +Smart Casts: + FirRegularClassSymbol public open class Foo : R|kotlin/Any| + Types: + Bar + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] dispatchReceiver.kt + public open [ResolvedTo(STATUS)] class Foo : R|kotlin/Any| { + public [ResolvedTo(STATUS)] constructor(): R|Foo| { + LAZY_super + } + + public final [ResolvedTo(BODY_RESOLVE)] fun foo(): R|kotlin/Unit| { + when () { + (this@R|/Foo| is R|Bar|) -> { + R|/consume|(this@R|/Foo|) + } + } + + } + + } + public final [ResolvedTo(STATUS)] class Bar : R|Foo| { + public [ResolvedTo(STATUS)] constructor(): R|Bar| { + LAZY_super + } + + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Bar|): R|kotlin/Unit| { + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.kt new file mode 100644 index 00000000000..47048385058 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.kt @@ -0,0 +1,9 @@ +interface Foo {} + +fun Any.test() { + if (this is Foo) { + consume(this) + } +} + +fun consume(obj: Foo) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.txt new file mode 100644 index 00000000000..2a4aef4995b --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.txt @@ -0,0 +1,44 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Element 8 + Implicit receiver: + FirNamedFunctionSymbol public final fun R|kotlin/Any|.test(): R|kotlin/Unit| + Type: Foo + Element 9 + Scope: FirLocalScope + Element 10 + Scope: FirLocalScope +Smart Casts: + FirNamedFunctionSymbol public final fun R|kotlin/Any|.test(): R|kotlin/Unit| + Types: + Foo + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] extensionReceiver.kt + public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { + } + public final [ResolvedTo(BODY_RESOLVE)] fun R|kotlin/Any|.test(): R|kotlin/Unit| { + when () { + (this@R|/test| is R|Foo|) -> { + R|/consume|(this@R|/test|) + } + } + + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Foo|): R|kotlin/Unit| { + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.kt new file mode 100644 index 00000000000..ad9d680cedb --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.kt @@ -0,0 +1,14 @@ +interface Node { + val parent: Node? +} + +fun test(initial: Node) { + var current = initial + + while (current != null) { + consume(current) + current = current.parent + } +} + +fun consume(node: Node) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.txt new file mode 100644 index 00000000000..c6e32237ccb --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.txt @@ -0,0 +1,46 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol initial: R|Node| + Element 8 + Scope: FirLocalScope + Properties: + FirPropertySymbol lvar current: R|Node| + Element 9 + Scope: FirLocalScope +Smart Casts: + FirPropertySymbol lvar current: R|Node| + Types: + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] insideLoop.kt + public abstract [ResolvedTo(STATUS)] interface Node : R|kotlin/Any| { + public abstract [ResolvedTo(CONTRACTS)] val parent: R|Node?| + public [ResolvedTo(CONTRACTS)] get(): R|Node?| + + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] initial: R|Node|): R|kotlin/Unit| { + [ResolvedTo(BODY_RESOLVE)] lvar current: R|Node| = R|/initial| + while(!=(R|/current|, Null(null))) { + R|/consume|(R|/current|) + R|/current| = R|/current|.R|/Node.parent| + } + + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] node: R|Node|): R|kotlin/Unit| { + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.kt new file mode 100644 index 00000000000..ec05871d972 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.kt @@ -0,0 +1,13 @@ +interface Foo { + val isValid: Boolean +} + +fun test(obj: Any) { + if (obj is Foo || isValid(obj)) { + consume(obj) + } +} + +fun isValid(obj: Any): Boolean = true + +fun consume(obj: Foo) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.txt new file mode 100644 index 00000000000..661a391a0c7 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.txt @@ -0,0 +1,41 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol obj: R|kotlin/Any| + Element 8 + Scope: FirLocalScope + +FILE: [ResolvedTo(IMPORTS)] orRight.kt + public? final? [ResolvedTo(RAW_FIR)] interface Foo : R|kotlin/Any| { + public? final? [ResolvedTo(RAW_FIR)] val isValid: Boolean + public? [ResolvedTo(RAW_FIR)] get(): Boolean + + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|): R|kotlin/Unit| { + when () { + (R|/obj| is R|Foo|) || R|/isValid|(R|/obj|) -> { + R|/consume#|(R|/obj|) + } + } + + } + public final [ResolvedTo(CONTRACTS)] fun isValid([ResolvedTo(CONTRACTS)] obj: R|kotlin/Any|): R|kotlin/Boolean| { + ^isValid Boolean(true) + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Foo|): R|kotlin/Unit| { + } diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.kt new file mode 100644 index 00000000000..0641829e306 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.kt @@ -0,0 +1,9 @@ +interface Foo {} + +fun test(obj: Any, another: Foo) { + if (obj is Foo) { + consume(another) + } +} + +fun consume(obj: Foo) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.txt new file mode 100644 index 00000000000..199c5fa323b --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.txt @@ -0,0 +1,43 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol another: R|Foo| + FirValueParameterSymbol obj: R|kotlin/Any| + Element 8 + Scope: FirLocalScope + Element 9 + Scope: FirLocalScope +Smart Casts: + FirValueParameterSymbol obj: R|kotlin/Any| + Types: + Foo + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] plainCheck.kt + public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|, [ResolvedTo(BODY_RESOLVE)] another: R|Foo|): R|kotlin/Unit| { + when () { + (R|/obj| is R|Foo|) -> { + R|/consume|(R|/another|) + } + } + + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|Foo|): R|kotlin/Unit| { + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.kt new file mode 100644 index 00000000000..fb87423100d --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.kt @@ -0,0 +1,10 @@ +// WITH_STDLIB + +interface Foo + +fun test(obj: Any) { + require(obj is Foo) + consume(obj) +} + +fun consume(obj: Any) {} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.txt new file mode 100644 index 00000000000..8bb345fb721 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.txt @@ -0,0 +1,36 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol obj: R|kotlin/Any| + Element 8 + Scope: FirLocalScope +Smart Casts: + FirValueParameterSymbol obj: R|kotlin/Any| + Types: + Foo + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] require.kt + public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|): R|kotlin/Unit| { + R|kotlin/require|((R|/obj| is R|Foo|)) + R|/consume|(R|/obj|) + } + public final [ResolvedTo(CONTRACTS)] fun consume([ResolvedTo(CONTRACTS)] obj: R|kotlin/Any|): R|kotlin/Unit| { + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.kt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.kt new file mode 100644 index 00000000000..68f88691552 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.kt @@ -0,0 +1,14 @@ +interface Foo { + fun foo() +} + +interface Bar { + fun bar() +} + +fun test(obj: Any) { + when (obj) { + is Foo -> obj.foo() + is Bar -> obj.bar() + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.txt b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.txt new file mode 100644 index 00000000000..65418150cc5 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.txt @@ -0,0 +1,49 @@ +Tower Data Context: + Element 0 + Scope: FirDefaultStarImportingScope + Element 1 + Scope: FirDefaultSimpleImportingScope + Element 2 + Scope: FirExplicitStarImportingScope + Element 3 + Scope: FirDefaultSimpleImportingScope + Element 4 + Scope: FirDefaultSimpleImportingScope + Element 5 + Scope: FirPackageMemberScope + Element 6 + Scope: FirExplicitSimpleImportingScope + Element 7 + Scope: FirLocalScope + Properties: + FirValueParameterSymbol obj: R|kotlin/Any| + Element 8 + Scope: FirLocalScope + Element 9 + Scope: FirLocalScope +Smart Casts: + FirValueParameterSymbol obj: R|kotlin/Any| + Types: + Bar + kotlin/Any + +FILE: [ResolvedTo(IMPORTS)] when.kt + public abstract [ResolvedTo(STATUS)] interface Foo : R|kotlin/Any| { + public abstract [ResolvedTo(CONTRACTS)] fun foo(): R|kotlin/Unit| + + } + public abstract [ResolvedTo(STATUS)] interface Bar : R|kotlin/Any| { + public abstract [ResolvedTo(CONTRACTS)] fun bar(): R|kotlin/Unit| + + } + public final [ResolvedTo(BODY_RESOLVE)] fun test([ResolvedTo(BODY_RESOLVE)] obj: R|kotlin/Any|): R|kotlin/Unit| { + when (R|/obj|) { + ($subj$ is R|Foo|) -> { + R|/obj|.R|/Foo.foo|() + } + ($subj$ is R|Bar|) -> { + R|/obj|.R|/Bar.bar|() + } + } + + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractContextCollectorTest.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractContextCollectorTest.kt index 0f97e9cfb59..dba7102d1b2 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractContextCollectorTest.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractContextCollectorTest.kt @@ -16,6 +16,8 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvedImport import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext import org.jetbrains.kotlin.fir.renderer.FirRenderer import org.jetbrains.kotlin.fir.resolve.SessionHolderImpl +import org.jetbrains.kotlin.fir.resolve.dfa.Identifier +import org.jetbrains.kotlin.fir.resolve.dfa.RealVariable import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.* import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol @@ -180,14 +182,14 @@ internal object ElementContextRenderer { } } - private fun StringBuilder.renderSmartCasts(smartCasts: Map, Set>) { + private fun StringBuilder.renderSmartCasts(smartCasts: Map>) { if (smartCasts.isEmpty()) { return } appendBlock("Smart Casts:") { - for ((symbol, types) in smartCasts) { - appendSymbol(symbol).appendLine() + for ((realVariable, types) in smartCasts) { + appendSymbol(realVariable.identifier.symbol).appendLine() appendBlock("Types:") { for (type in types) { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/ContextCollectorTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/ContextCollectorTestGenerated.java index 2d1e8ba3397..da946b413d7 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/ContextCollectorTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/ContextCollectorTestGenerated.java @@ -72,6 +72,12 @@ public class ContextCollectorTestGenerated extends AbstractContextCollectorTest runTest("analysis/low-level-api-fir/testdata/contextCollector/lambdaArguments.kt"); } + @Test + @TestMetadata("localClass.kt") + public void testLocalClass() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/localClass.kt"); + } + @Test @TestMetadata("nestedClasses.kt") public void testNestedClasses() throws Exception { @@ -90,15 +96,97 @@ public class ContextCollectorTestGenerated extends AbstractContextCollectorTest runTest("analysis/low-level-api-fir/testdata/contextCollector/simple.kt"); } - @Test - @TestMetadata("smartCastArgument.kt") - public void testSmartCastArgument() throws Exception { - runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.kt"); - } + @Nested + @TestMetadata("analysis/low-level-api-fir/testdata/contextCollector/smartCasts") + @TestDataPath("$PROJECT_ROOT") + public class SmartCasts { + @Test + @TestMetadata("afterIf.kt") + public void testAfterIf() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterIf.kt"); + } - @Test - @TestMetadata("smartCastReceiverArgument.kt") - public void testSmartCastReceiverArgument() throws Exception { - runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.kt"); + @Test + @TestMetadata("afterLoop.kt") + public void testAfterLoop() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/afterLoop.kt"); + } + + @Test + public void testAllFilesPresentInSmartCasts() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector/smartCasts"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("andRight.kt") + public void testAndRight() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/andRight.kt"); + } + + @Test + @TestMetadata("argument.kt") + public void testArgument() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argument.kt"); + } + + @Test + @TestMetadata("argumentAsReceiver.kt") + public void testArgumentAsReceiver() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/argumentAsReceiver.kt"); + } + + @Test + @TestMetadata("beforeIf.kt") + public void testBeforeIf() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeIf.kt"); + } + + @Test + @TestMetadata("beforeLoop.kt") + public void testBeforeLoop() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/beforeLoop.kt"); + } + + @Test + @TestMetadata("dispatchReceiver.kt") + public void testDispatchReceiver() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/dispatchReceiver.kt"); + } + + @Test + @TestMetadata("extensionReceiver.kt") + public void testExtensionReceiver() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/extensionReceiver.kt"); + } + + @Test + @TestMetadata("insideLoop.kt") + public void testInsideLoop() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/insideLoop.kt"); + } + + @Test + @TestMetadata("orRight.kt") + public void testOrRight() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/orRight.kt"); + } + + @Test + @TestMetadata("plainCheck.kt") + public void testPlainCheck() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/plainCheck.kt"); + } + + @Test + @TestMetadata("require.kt") + public void testRequire() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/require.kt"); + } + + @Test + @TestMetadata("when.kt") + public void testWhen() throws Exception { + runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCasts/when.kt"); + } } } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt index 90eda405bd1..bbf16d0deea 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt @@ -98,6 +98,10 @@ class PersistentImplicitReceiverStack private constructor( return originalTypes[index] } + fun getType(index: Int): ConeKotlinType { + return stack[index].type + } + // This method is only used from DFA and it's in some sense breaks persistence contracts of the data structure // But it's ok since DFA handles everything properly yet, but still may be it should be rewritten somehow @OptIn(ImplicitReceiverValue.ImplicitReceiverInternals::class)