diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java index a882be13d45..1abf272f959 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java @@ -298,6 +298,18 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt"); } + @Test + @TestMetadata("smartCastExtensionReceiver.kt") + public void testSmartCastExtensionReceiver() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.kt"); + } + + @Test + @TestMetadata("smartCastValueParameter.kt") + public void testSmartCastValueParameter() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.kt"); + } + @Test @TestMetadata("valueParameter.kt") public void testValueParameter() throws Exception { diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java index 0c1e2b542fd..ccc8eda15df 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java @@ -298,6 +298,18 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt"); } + @Test + @TestMetadata("smartCastExtensionReceiver.kt") + public void testSmartCastExtensionReceiver() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.kt"); + } + + @Test + @TestMetadata("smartCastValueParameter.kt") + public void testSmartCastValueParameter() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.kt"); + } + @Test @TestMetadata("valueParameter.kt") public void testValueParameter() throws Exception { diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.capturing.txt new file mode 100644 index 00000000000..1b185e30eb7 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.capturing.txt @@ -0,0 +1,6 @@ +ExtensionReceiver[name: dumbWith; isMutated: false; displayText: this@dumbWith] + dumbWith@fun R|kotlin/Any?|.(): R|kotlin/Unit| + R|kotlin/Any?| +ExtensionReceiver[name: dumbWith; isMutated: false; displayText: this@dumbWith] + dumbWith@fun R|kotlin/Any?|.(): R|kotlin/Unit| + R|kotlin/Any?| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.fragment.kt new file mode 100644 index 00000000000..01f711e33c5 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.fragment.kt @@ -0,0 +1 @@ +length + foo.length \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.ir.txt new file mode 100644 index 00000000000..de016b80c45 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.ir.txt @@ -0,0 +1,20 @@ +MODULE_FRAGMENT + FILE fqName: fileName:fragment.kt + CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.CodeFragment + CONSTRUCTOR visibility:public <> () returnType:.CodeFragment [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + FUN name:run visibility:public modality:FINAL <> (p0:kotlin.Any?, p1:kotlin.Any?) returnType:kotlin.Int + VALUE_PARAMETER name:p0 index:0 type:kotlin.Any? + VALUE_PARAMETER name:p1 index:1 type:kotlin.Any? + EXPRESSION_BODY + BLOCK type=kotlin.Int origin=null + CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS + $this: CALL 'public open fun (): kotlin.Int declared in kotlin.String' type=kotlin.Int origin=GET_PROPERTY + $this: TYPE_OP type=kotlin.String origin=IMPLICIT_CAST typeOperand=kotlin.String + GET_VAR 'p0: kotlin.Any? declared in .CodeFragment.run' type=kotlin.Any? origin=null + other: CALL 'public open fun (): kotlin.Int declared in kotlin.String' type=kotlin.Int origin=GET_PROPERTY + $this: CALL 'public final fun (): kotlin.String declared in .Foo' type=kotlin.String origin=GET_PROPERTY + $this: TYPE_OP type=.Foo origin=IMPLICIT_CAST typeOperand=.Foo + GET_VAR 'p1: kotlin.Any? declared in .CodeFragment.run' type=kotlin.Any? origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.kt new file mode 100644 index 00000000000..8e989a68929 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.kt @@ -0,0 +1,19 @@ +fun test() { + dumbWith("Hello, world!") { + if (this is String) { + dumbWith(Foo()) { + if (this is Foo) { + val x = 0 + } + } + } + } +} + +inline fun dumbWith(obj: Any?, block: Any?.() -> Unit) { + obj.block() +} + +class Foo { + val foo: String = "foo" +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.txt new file mode 100644 index 00000000000..f181a982f91 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: java.lang.Object, p1: java.lang.Object): int +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.capturing.txt new file mode 100644 index 00000000000..d37bf562d7f --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.capturing.txt @@ -0,0 +1,3 @@ +Local[name: foo; isMutated: false; displayText: foo] + foo: R|kotlin/Any?| + R|kotlin/Any?| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.fragment.kt new file mode 100644 index 00000000000..a5f6d5dcbab --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.fragment.kt @@ -0,0 +1 @@ +foo.call() \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.ir.txt new file mode 100644 index 00000000000..b79cab9b49b --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.ir.txt @@ -0,0 +1,14 @@ +MODULE_FRAGMENT + FILE fqName: fileName:fragment.kt + CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.CodeFragment + CONSTRUCTOR visibility:public <> () returnType:.CodeFragment [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + FUN name:run visibility:public modality:FINAL <> (p0:kotlin.Any?) returnType:kotlin.Unit + VALUE_PARAMETER name:p0 index:0 type:kotlin.Any? + EXPRESSION_BODY + BLOCK type=kotlin.Unit origin=null + CALL 'public final fun call (): kotlin.Unit declared in .Foo' type=kotlin.Unit origin=null + $this: TYPE_OP type=.Foo origin=IMPLICIT_CAST typeOperand=.Foo + GET_VAR 'p0: kotlin.Any? declared in .CodeFragment.run' type=kotlin.Any? origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.kt new file mode 100644 index 00000000000..e78bba2024a --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.kt @@ -0,0 +1,9 @@ +fun test(foo: Any?) { + if (foo is Foo) { + val x = 0 + } +} + +class Foo { + fun call() {} +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.txt new file mode 100644 index 00000000000..23e7a12805d --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: java.lang.Object): void +} \ No newline at end of file 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 bf169fe636b..64339f4fd22 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 @@ -39,13 +39,13 @@ import org.jetbrains.kotlin.fir.resolve.FirCodeFragmentContext import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl import org.jetbrains.kotlin.fir.resolve.codeFragmentContext +import org.jetbrains.kotlin.fir.resolve.dfa.RealVariable import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector import org.jetbrains.kotlin.fir.resolve.transformers.contracts.FirContractsDslNames import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.resolve.dfa.cfg.isUsedInControlFlowGraphBuilderForClass import org.jetbrains.kotlin.fir.resolve.dfa.cfg.isUsedInControlFlowGraphBuilderForFile -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtFile @@ -225,8 +225,7 @@ private class LLFirBodyTargetResolver( withPsiEntry("contextPsiElement", contextPsiElement) } - val variables = elementContext.smartCasts.mapKeys { it.key.identifier.symbol } - LLFirCodeFragmentContext(elementContext.towerDataContext.withExtraScopes(), variables) + LLFirCodeFragmentContext(elementContext.towerDataContext.withExtraScopes(), elementContext.smartCasts) } else { val towerDataContext = FirTowerDataContext().withExtraScopes() LLFirCodeFragmentContext(towerDataContext, emptyMap()) @@ -513,5 +512,5 @@ private fun requireSameSize(old: List, new: List) { private class LLFirCodeFragmentContext( override val towerDataContext: FirTowerDataContext, - override val variables: Map, Set> + override val smartCasts: Map> ) : FirCodeFragmentContext diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java index a781207247a..2e811bb291b 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java @@ -192,6 +192,18 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/singleClassContextReceiver.kt"); } + @Test + @TestMetadata("smartCastExtensionReceiver.kt") + public void testSmartCastExtensionReceiver() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastExtensionReceiver.kt"); + } + + @Test + @TestMetadata("smartCastValueParameter.kt") + public void testSmartCastValueParameter() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/smartCastValueParameter.kt"); + } + @Test @TestMetadata("valueParameter.kt") public void testValueParameter() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index e4b83afd32c..7eb3a810872 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -255,9 +255,9 @@ abstract class FirDataFlowAnalyzer( fun enterCodeFragment(codeFragment: FirCodeFragment) { graphBuilder.enterCodeFragment(codeFragment).mergeIncomingFlow { _, flow -> - val realVariablesFromContext = codeFragment.codeFragmentContext?.variables.orEmpty() - for ((symbol, exactTypes) in realVariablesFromContext) { - val realVariable = variableStorage.getOrCreateIfReal(flow, symbol.fir) as? RealVariable ?: continue + val smartCasts = codeFragment.codeFragmentContext?.smartCasts.orEmpty() + for ((originalRealVariable, exactTypes) in smartCasts) { + val realVariable = variableStorage.getOrPut(originalRealVariable.identifier) { originalRealVariable } val typeStatement = PersistentTypeStatement(realVariable, exactTypes.toPersistentSet()) flow.addTypeStatement(typeStatement) } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt index 15ffeeba01a..f845bd762d9 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt @@ -9,8 +9,8 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.containingClassForLocalAttr import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isLocal +import org.jetbrains.kotlin.fir.resolve.dfa.RealVariable import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol @@ -98,7 +98,7 @@ val FirConstructorSymbol.typeAliasForConstructor: FirTypeAliasSymbol? interface FirCodeFragmentContext { val towerDataContext: FirTowerDataContext - val variables: Map, Set> + val smartCasts: Map> } private object CodeFragmentContextDataKey : FirDeclarationDataKey() diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorageImpl.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorageImpl.kt index 268d37d168d..73964e2ad6a 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorageImpl.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorageImpl.kt @@ -144,7 +144,7 @@ class VariableStorageImpl(private val session: FirSession) : VariableStorage() { extensionReceiver = if (extensionReceiver == from) to else extensionReceiver, ) } - return _realVariables.getOrPut(newIdentifier) { + return getOrPut(newIdentifier) { with(variable) { RealVariable( newIdentifier, isThisReference, if (explicitReceiverVariable == from) to else explicitReceiverVariable, @@ -154,6 +154,10 @@ class VariableStorageImpl(private val session: FirSession) : VariableStorage() { } } + fun getOrPut(identifier: Identifier, factory: () -> RealVariable): RealVariable { + return _realVariables.getOrPut(identifier, factory) + } + private fun FirBasedSymbol<*>.getStability(originalFir: FirElement): PropertyStability? { if (originalFir is FirThisReceiverExpression) return PropertyStability.STABLE_VALUE when (this) {