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 27fc937b452..c600d2265db 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 @@ -208,6 +208,12 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/local.kt"); } + @Test + @TestMetadata("localDelegatedProperty.kt") + public void testLocalDelegatedProperty() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt"); + } + @Test @TestMetadata("localFunction.kt") public void testLocalFunction() throws Exception { @@ -262,6 +268,12 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt"); } + @Test + @TestMetadata("localVariable.kt") + public void testLocalVariable() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt"); + } + @Test @TestMetadata("multipleClassAndFunctionContextReceivers.kt") public void testMultipleClassAndFunctionContextReceivers() 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 cb5a00ff30b..efaca46fbc9 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 @@ -208,6 +208,12 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/local.kt"); } + @Test + @TestMetadata("localDelegatedProperty.kt") + public void testLocalDelegatedProperty() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt"); + } + @Test @TestMetadata("localFunction.kt") public void testLocalFunction() throws Exception { @@ -262,6 +268,12 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt"); } + @Test + @TestMetadata("localVariable.kt") + public void testLocalVariable() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt"); + } + @Test @TestMetadata("multipleClassAndFunctionContextReceivers.kt") public void testMultipleClassAndFunctionContextReceivers() throws Exception { diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.capturing.txt new file mode 100644 index 00000000000..97d5637b014 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.capturing.txt @@ -0,0 +1,3 @@ +LocalDelegate[name: x; isMutated: true; displayText: x$delegate] + lvar x: R|kotlin/String| + R|kotlin/String| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.fragment.kt new file mode 100644 index 00000000000..3897e79b34e --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.fragment.kt @@ -0,0 +1,2 @@ +x = "O" +x + "K" diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.ir.txt new file mode 100644 index 00000000000..a82c87ae5f5 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.ir.txt @@ -0,0 +1,16 @@ +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.String) returnType:kotlin.String + VALUE_PARAMETER SHARED_VARIABLE_IN_EVALUATOR_FRAGMENT name:p0 index:0 type:kotlin.String [assignable] + EXPRESSION_BODY + BLOCK type=kotlin.String origin=null + SET_VAR 'p0: kotlin.String [assignable] declared in .CodeFragment.run' type=kotlin.Unit origin=null + CONST String type=kotlin.String value="O" + CALL 'public final fun plus (other: kotlin.Any?): kotlin.String [operator] declared in kotlin.String' type=kotlin.String origin=PLUS + $this: GET_VAR 'p0: kotlin.String [assignable] declared in .CodeFragment.run' type=kotlin.String origin=null + other: CONST String type=kotlin.String value="K" diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt new file mode 100644 index 00000000000..8640d18270e --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt @@ -0,0 +1,16 @@ +import kotlin.reflect.KProperty + +class Delegate(private var value: String) { + operator fun getValue(thisRef: Any?, property: KProperty<*>): String { + return value + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { + this.value = value + } +} + +fun test() { + var x by Delegate("a") + x +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.txt new file mode 100644 index 00000000000..a97a3af64a0 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: java.lang.String): java.lang.String +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.capturing.txt new file mode 100644 index 00000000000..70a86aab236 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.capturing.txt @@ -0,0 +1,3 @@ +Local[name: x; isMutated: true; displayText: x] + lvar x: R|kotlin/String| + R|kotlin/String| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.fragment.kt new file mode 100644 index 00000000000..3897e79b34e --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.fragment.kt @@ -0,0 +1,2 @@ +x = "O" +x + "K" diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.ir.txt new file mode 100644 index 00000000000..a82c87ae5f5 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.ir.txt @@ -0,0 +1,16 @@ +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.String) returnType:kotlin.String + VALUE_PARAMETER SHARED_VARIABLE_IN_EVALUATOR_FRAGMENT name:p0 index:0 type:kotlin.String [assignable] + EXPRESSION_BODY + BLOCK type=kotlin.String origin=null + SET_VAR 'p0: kotlin.String [assignable] declared in .CodeFragment.run' type=kotlin.Unit origin=null + CONST String type=kotlin.String value="O" + CALL 'public final fun plus (other: kotlin.Any?): kotlin.String [operator] declared in kotlin.String' type=kotlin.String origin=PLUS + $this: GET_VAR 'p0: kotlin.String [assignable] declared in .CodeFragment.run' type=kotlin.String origin=null + other: CONST String type=kotlin.String value="K" diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt new file mode 100644 index 00000000000..1612fd40807 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt @@ -0,0 +1,4 @@ +fun test() { + var x = "a" + x +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.txt new file mode 100644 index 00000000000..a97a3af64a0 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: java.lang.String): java.lang.String +} 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 42571b63d63..59908069fc4 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 @@ -84,6 +84,12 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/local.kt"); } + @Test + @TestMetadata("localDelegatedProperty.kt") + public void testLocalDelegatedProperty() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt"); + } + @Test @TestMetadata("localFunction.kt") public void testLocalFunction() throws Exception { @@ -138,6 +144,12 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt"); } + @Test + @TestMetadata("localVariable.kt") + public void testLocalVariable() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt"); + } + @Test @TestMetadata("multipleClassAndFunctionContextReceivers.kt") public void testMultipleClassAndFunctionContextReceivers() throws Exception { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt index 23e64f09639..e8656ad5ae9 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt @@ -122,6 +122,12 @@ class Fir2IrConverter( declarationStorage.generateUnboundFakeOverrides() } + if (configuration.allowNonCachedDeclarations) { + // See the comment to fillUnboundSymbols function itself + @OptIn(LeakedDeclarationCaches::class) + declarationStorage.fillUnboundSymbols() + } + evaluateConstants(irModuleFragment, components) } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt index eb6bc2fd8c2..46dd4c58189 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt @@ -9,14 +9,12 @@ import org.jetbrains.kotlin.builtins.StandardNames.BUILT_INS_PACKAGE_FQ_NAMES import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol import org.jetbrains.kotlin.fir.backend.generators.isExternalParent import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.buildProperty import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty -import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField -import org.jetbrains.kotlin.fir.declarations.utils.isExpect -import org.jetbrains.kotlin.fir.declarations.utils.isStatic -import org.jetbrains.kotlin.fir.declarations.utils.visibility +import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.descriptors.FirBuiltInsPackageFragment import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor import org.jetbrains.kotlin.fir.java.symbols.FirJavaOverriddenSyntheticPropertySymbol @@ -1177,22 +1175,75 @@ class Fir2IrDeclarationStorage( for ((identifier, symbol) in irForFirSessionDependantDeclarationMap) { if (symbol.isBound) continue val (originalSymbol, dispatchReceiverLookupTag, _) = identifier - val irParent = findIrParent(originalSymbol.fir, dispatchReceiverLookupTag) - when (originalSymbol) { - is FirPropertySymbol -> createAndCacheIrProperty( - originalSymbol.fir, - irParent, - fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag - ) + generateDeclaration(originalSymbol, dispatchReceiverLookupTag) + } + } - is FirNamedFunctionSymbol -> createAndCacheIrFunction( - originalSymbol.fir, - irParent, - fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag - ) + /** + * This function iterates over all non f/o callable symbols created in declaration storage and binds all unbound symbols + * + * Usually all symbols are bound after fir2ir conversion is over, but it's not true for `allowNonCachedDeclarations`, when + * we convert to IR only part of sources from code fragments. + * + * ``` + * // Original code + * fun foo(x: Int) {} // (1) + * + * fun bar() { + * 1.let { // (2) + * + * } + * } + * + * // Code fragment + * foo(this@let) + * + * Here in the body of the code fragment we reference function (1) and lambda (2), which leads to creation of their symbols, + * but not to generation of their IR. And since the original code won't be processed by fir2ir, we need to manually create + * IR for all symbols from it, to avoid publication of unbound symbols after fir2ri conversion is over + * + * Note that in the code fragment we may capture even local functions and lambdas, which are stored not in global caches, + * but in `localStorage`, which is getting cleared after leaving from corresponding scope. So to generate IR for them we need + * to call this function not only after fir2ir conversion, but also after leaving each local scope (see `leaveScope` function) + */ + @LeakedDeclarationCaches + internal fun fillUnboundSymbols() { + fillUnboundSymbols(functionCache) + fillUnboundSymbols(propertyCache) + } - else -> error("Unexpected declaration: $originalSymbol") - } + @LeakedDeclarationCaches + private fun fillUnboundSymbols(cache: Map) { + for ((firDeclaration, irSymbol) in cache) { + if (irSymbol.isBound) continue + generateDeclaration(firDeclaration.symbol, dispatchReceiverLookupTag = null) + } + } + + private fun generateDeclaration( + originalSymbol: FirBasedSymbol<*>, + dispatchReceiverLookupTag: ConeClassLikeLookupTag?, + ) { + val irParent = findIrParent( + originalSymbol.packageFqName(), + dispatchReceiverLookupTag ?: originalSymbol.getContainingClassSymbol(session)?.toLookupTag(), + originalSymbol, + originalSymbol.origin + ) + when (originalSymbol) { + is FirPropertySymbol -> createAndCacheIrProperty( + originalSymbol.fir, + irParent, + fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag + ) + + is FirNamedFunctionSymbol -> createAndCacheIrFunction( + originalSymbol.fir, + irParent, + fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag + ) + + else -> error("Unexpected declaration: $originalSymbol") } } @@ -1240,6 +1291,11 @@ class Fir2IrDeclarationStorage( symbol is IrEnumEntrySymbol || symbol is IrScriptSymbol ) { + if (configuration.allowNonCachedDeclarations) { + // See KDoc to `fillUnboundSymbols` function + @OptIn(LeakedDeclarationCaches::class) + fillUnboundSymbols(localStorage.lastCache.localFunctions) + } localStorage.leaveCallable() } symbolTable.leaveScope(symbol) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrLocalCallableStorage.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrLocalCallableStorage.kt index 54dc5ba17aa..bccecbe27ce 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrLocalCallableStorage.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrLocalCallableStorage.kt @@ -19,6 +19,10 @@ class Fir2IrLocalCallableStorage { cacheStack += Fir2IrScopeCache() } + @LeakedDeclarationCaches + val lastCache: Fir2IrScopeCache + get() = cacheStack.last() + fun leaveCallable() { cacheStack.last().clear() cacheStack.removeAt(cacheStack.size - 1) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrScopeCache.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrScopeCache.kt index e9c1523bcd5..b9c064fec6a 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrScopeCache.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrScopeCache.kt @@ -17,6 +17,9 @@ class Fir2IrScopeCache { private val localFunctionCache = mutableMapOf() + val localFunctions: Map + get() = localFunctionCache + private val delegatedPropertyCache = mutableMapOf() fun getParameter(parameter: FirValueParameter): IrValueParameterSymbol? { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt index 7be73961585..70dd10069e8 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt @@ -464,15 +464,6 @@ abstract class FirVisibilityChecker : FirSessionComponent { return false } - - protected fun FirBasedSymbol<*>.packageFqName(): FqName { - return when (this) { - is FirClassLikeSymbol<*> -> classId.packageFqName - is FirPropertyAccessorSymbol -> propertySymbol.packageFqName() - is FirCallableSymbol<*> -> callableId.packageName - else -> error("No package fq name for $this") - } - } } val FirSession.moduleVisibilityChecker: FirModuleVisibilityChecker? by FirSession.nullableSessionComponentAccessor() diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt index 5cd2bbea8f2..73bb6f653a0 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt @@ -20,6 +20,10 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin import org.jetbrains.kotlin.fir.renderer.FirRenderer +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.* import org.jetbrains.kotlin.fir.types.impl.* @@ -273,3 +277,12 @@ val FirExpression.isStatementLikeExpression: Boolean private val FirExpression.isIndexedAssignment: Boolean get() = this is FirBlock && statements.lastOrNull()?.source?.kind == KtFakeSourceElementKind.ImplicitUnit.IndexedAssignmentCoercion + +fun FirBasedSymbol<*>.packageFqName(): FqName { + return when (this) { + is FirClassLikeSymbol<*> -> classId.packageFqName + is FirPropertyAccessorSymbol -> propertySymbol.packageFqName() + is FirCallableSymbol<*> -> callableId.packageName + else -> error("No package fq name for $this") + } +}