[LL][KMP] Don't create JVM container source in non-JVM symbol providers

Multiple .knm parts of the same KMP library were considered part of the
same JVM file facade. Because of that, an incorrect overload could
be selected, depending on the order of dependencies.

KT-69395

(cherry picked from commit 70b108608599450739867174e7e2a3dc29607275)
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/DeserializedContainerSourceProviders.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/DeserializedContainerSourceProviders.kt
new file mode 100644
index 0000000..780fb45
--- /dev/null
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/DeserializedContainerSourceProviders.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization
+
+import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.stubs.impl.KotlinStubOrigin
+import org.jetbrains.kotlin.resolve.jvm.JvmClassName
+import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
+
+internal interface DeserializedContainerSourceProvider {
+    fun getFacadeContainerSource(
+        file: KtFile,
+        stubOrigin: KotlinStubOrigin?,
+        declarationOrigin: FirDeclarationOrigin,
+    ): DeserializedContainerSource?
+
+    fun getClassContainerSource(classId: ClassId): DeserializedContainerSource?
+}
+
+// Currently, `null` is returned for KLIBs to avoid incorrect application of JVM file facade logic and overload filtering.
+// We might want to provide non-`null` container source for all types of binaries in the future.
+internal object NullDeserializedContainerSourceProvider : DeserializedContainerSourceProvider {
+    override fun getFacadeContainerSource(
+        file: KtFile,
+        stubOrigin: KotlinStubOrigin?,
+        declarationOrigin: FirDeclarationOrigin,
+    ): DeserializedContainerSource? = null
+
+    override fun getClassContainerSource(classId: ClassId): DeserializedContainerSource? = null
+}
+
+internal object JvmDeserializedContainerSourceProvider : DeserializedContainerSourceProvider {
+    override fun getFacadeContainerSource(
+        file: KtFile,
+        stubOrigin: KotlinStubOrigin?,
+        declarationOrigin: FirDeclarationOrigin,
+    ): DeserializedContainerSource {
+        return when (stubOrigin) {
+            is KotlinStubOrigin.Facade -> {
+                val className = JvmClassName.byInternalName(stubOrigin.className)
+                JvmStubDeserializedFacadeContainerSource(className, facadeClassName = null)
+            }
+            is KotlinStubOrigin.MultiFileFacade -> {
+                val className = JvmClassName.byInternalName(stubOrigin.className)
+                val facadeClassName = JvmClassName.byInternalName(stubOrigin.facadeClassName)
+                JvmStubDeserializedFacadeContainerSource(className, facadeClassName)
+            }
+            else -> {
+                val virtualFile = file.virtualFile
+                val classId = ClassId(file.packageFqName, Name.identifier(virtualFile.nameWithoutExtension))
+                val className = JvmClassName.byClassId(classId)
+                JvmStubDeserializedFacadeContainerSource(className, facadeClassName = null)
+            }
+        }
+    }
+
+    override fun getClassContainerSource(classId: ClassId): DeserializedContainerSource? {
+        return JvmStubDeserializedContainerSource(classId)
+    }
+}
+
+internal object BuiltinsDeserializedContainerSourceProvider : DeserializedContainerSourceProvider {
+    override fun getFacadeContainerSource(
+        file: KtFile,
+        stubOrigin: KotlinStubOrigin?,
+        declarationOrigin: FirDeclarationOrigin,
+    ): DeserializedContainerSource {
+        require(stubOrigin is KotlinStubOrigin.Facade) {
+            "Expected builtins file to have Facade origin, got origin=$stubOrigin instead"
+        }
+
+        return JvmStubDeserializedBuiltInsContainerSource(
+            facadeClassName = JvmClassName.byInternalName(stubOrigin.className)
+        )
+    }
+
+    override fun getClassContainerSource(classId: ClassId): DeserializedContainerSource? {
+        return JvmStubDeserializedContainerSource(classId)
+    }
+}
+
+internal object JvmAndBuiltinsDeserializedContainerSourceProvider : DeserializedContainerSourceProvider {
+    override fun getFacadeContainerSource(
+        file: KtFile,
+        stubOrigin: KotlinStubOrigin?,
+        declarationOrigin: FirDeclarationOrigin
+    ): DeserializedContainerSource? {
+        if (declarationOrigin is FirDeclarationOrigin.BuiltIns) {
+            return BuiltinsDeserializedContainerSourceProvider.getFacadeContainerSource(file, stubOrigin, declarationOrigin)
+        }
+
+        return JvmDeserializedContainerSourceProvider.getFacadeContainerSource(file, stubOrigin, declarationOrigin)
+    }
+
+    override fun getClassContainerSource(classId: ClassId): DeserializedContainerSource? {
+        return JvmStubDeserializedContainerSource(classId)
+    }
+}
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/LLStubBasedLibrarySymbolProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/LLStubBasedLibrarySymbolProviderFactory.kt
index 65b7006..725bca3 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/LLStubBasedLibrarySymbolProviderFactory.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/LLStubBasedLibrarySymbolProviderFactory.kt
@@ -142,6 +142,7 @@
     session,
     SingleModuleDataProvider(moduleData),
     kotlinScopeProvider,
+    BuiltinsDeserializedContainerSourceProvider,
     project,
     BuiltinsVirtualFileProvider.getInstance().createBuiltinsScope(project),
     isFallbackDependenciesProvider = false,
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt
index d4d3795..6720798 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt
@@ -30,9 +30,7 @@
 import org.jetbrains.kotlin.psi.*
 import org.jetbrains.kotlin.psi.psiUtil.getTopmostParentOfType
 import org.jetbrains.kotlin.psi.stubs.impl.*
-import org.jetbrains.kotlin.resolve.jvm.JvmClassName
 import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
-import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
 import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
 
 typealias DeserializedTypeAliasPostProcessor = (FirTypeAliasSymbol) -> Unit
@@ -53,6 +51,7 @@
     session: FirSession,
     moduleDataProvider: SingleModuleDataProvider,
     private val kotlinScopeProvider: FirKotlinScopeProvider,
+    private val deserializedContainerSourceProvider: DeserializedContainerSourceProvider,
     project: Project,
     scope: GlobalSearchScope,
 
@@ -153,7 +152,7 @@
                 StubBasedAnnotationDeserializer(session),
                 kotlinScopeProvider,
                 parentContext = parentContext,
-                containerSource = JvmStubDeserializedContainerSource(classId),
+                containerSource = deserializedContainerSourceProvider.getClassContainerSource(classId),
                 deserializeNestedClass = this::getClass,
                 initialOrigin = parentContext?.initialOrigin ?: getDeclarationOriginFor(classLikeDeclaration.containingKtFile)
             )
@@ -173,8 +172,9 @@
             for (function in topLevelFunctions) {
                 val functionStub = function.stub as? KotlinFunctionStubImpl ?: loadStubByElement(function)
                 val functionFile = function.containingKtFile
-                val containerSource = getContainerSource(functionFile, functionStub?.origin)
                 val functionOrigin = getDeclarationOriginFor(functionFile)
+                val containerSource =
+                    deserializedContainerSourceProvider.getFacadeContainerSource(functionFile, functionStub?.origin, functionOrigin)
 
                 if (functionOrigin != FirDeclarationOrigin.BuiltIns &&
                     containerSource is FacadeClassSource &&
@@ -199,8 +199,12 @@
             for (property in topLevelProperties) {
                 val propertyStub = property.stub as? KotlinPropertyStubImpl ?: loadStubByElement(property)
                 val propertyFile = property.containingKtFile
-                val containerSource = getContainerSource(propertyFile, propertyStub?.origin)
                 val propertyOrigin = getDeclarationOriginFor(propertyFile)
+                val containerSource = deserializedContainerSourceProvider.getFacadeContainerSource(
+                    propertyFile,
+                    propertyStub?.origin,
+                    propertyOrigin,
+                )
 
                 val symbol = FirPropertySymbol(callableId)
                 val rootContext = StubBasedFirDeserializationContext
@@ -211,36 +215,6 @@
         }
     }
 
-    private fun getContainerSource(file: KtFile, origin: KotlinStubOrigin?): DeserializedContainerSource {
-        if (getDeclarationOriginFor(file) == FirDeclarationOrigin.BuiltIns) {
-            require(origin is KotlinStubOrigin.Facade) {
-                "Expected builtins file to have Facade origin, got origin=$origin instead"
-            }
-
-            return JvmStubDeserializedBuiltInsContainerSource(
-                facadeClassName = JvmClassName.byInternalName(origin.className)
-            )
-        }
-
-        return when (origin) {
-            is KotlinStubOrigin.Facade -> {
-                val className = JvmClassName.byInternalName(origin.className)
-                JvmStubDeserializedFacadeContainerSource(className, facadeClassName = null)
-            }
-            is KotlinStubOrigin.MultiFileFacade -> {
-                val className = JvmClassName.byInternalName(origin.className)
-                val facadeClassName = JvmClassName.byInternalName(origin.facadeClassName)
-                JvmStubDeserializedFacadeContainerSource(className, facadeClassName)
-            }
-            else -> {
-                val virtualFile = file.virtualFile
-                val classId = ClassId(file.packageFqName, Name.identifier(virtualFile.nameWithoutExtension))
-                val className = JvmClassName.byClassId(classId)
-                JvmStubDeserializedFacadeContainerSource(className, facadeClassName = null)
-            }
-        }
-    }
-
     private fun getClass(classId: ClassId, parentContext: StubBasedFirDeserializationContext? = null): FirRegularClassSymbol? =
         if (parentContext?.classLikeDeclaration != null) {
             classCache.getNotNullValueForNotNullContext(classId, parentContext)
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt
index 18b3c41..744138b 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt
@@ -165,7 +165,7 @@
             parameterListOwner: KtTypeParameterListOwner,
             symbol: FirBasedSymbol<*>,
             initialOrigin: FirDeclarationOrigin,
-            containerSource: DeserializedContainerSource
+            containerSource: DeserializedContainerSource?,
         ): StubBasedFirDeserializationContext {
             return createRootContext(
                 moduleData,
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirSymbolProviderFactories.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirSymbolProviderFactories.kt
index 1039594..0d554c7 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirSymbolProviderFactories.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirSymbolProviderFactories.kt
@@ -31,6 +31,7 @@
     session,
     moduleDataProvider,
     kotlinScopeProvider,
+    JvmAndBuiltinsDeserializedContainerSourceProvider,
     isFallbackDependenciesProvider,
     fileFilter = { file ->
         val extension = file.extension
@@ -51,6 +52,7 @@
     session,
     moduleDataProvider,
     kotlinScopeProvider,
+    NullDeserializedContainerSourceProvider,
     isFallbackDependenciesProvider,
     fileFilter = { file ->
         val extension = file.extension
@@ -74,6 +76,7 @@
     session,
     moduleDataProvider,
     kotlinScopeProvider,
+    NullDeserializedContainerSourceProvider,
     isFallbackDependenciesProvider,
     fileFilter = { file -> file.extension == KLIB_METADATA_FILE_EXTENSION },
 )
@@ -84,6 +87,7 @@
     session: FirSession,
     moduleDataProvider: SingleModuleDataProvider,
     kotlinScopeProvider: FirKotlinScopeProvider,
+    deserializedContainerSourceProvider: DeserializedContainerSourceProvider,
     isFallbackDependenciesProvider: Boolean,
     fileFilter: (VirtualFile) -> Boolean,
 ): StubBasedFirDeserializedSymbolProvider {
@@ -94,6 +98,7 @@
                 session,
                 moduleDataProvider,
                 kotlinScopeProvider,
+                deserializedContainerSourceProvider,
                 project,
                 reducedScope,
                 isFallbackDependenciesProvider,
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberFunction.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberFunction.kt
new file mode 100644
index 0000000..3153390
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberFunction.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
+
+package test
+
+class MyClass {
+    fun fn() = Unit
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberFunction.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberFunction.txt
new file mode 100644
index 0000000..d2856ea
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberFunction.txt
@@ -0,0 +1,10 @@
+KT element: KtNamedFunction
+KT element text:
+public final fun fn(): kotlin.Unit { /* compiled code */ }
+FIR element: FirSimpleFunctionImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberProperty.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberProperty.kt
new file mode 100644
index 0000000..1708c30
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberProperty.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
+
+package test
+
+class MyClass {
+    val prop: String = ""
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberProperty.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberProperty.txt
new file mode 100644
index 0000000..c5c7318
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberProperty.txt
@@ -0,0 +1,11 @@
+KT element: KtProperty
+KT element text:
+public final val prop: kotlin.String /* compiled code */
+FIR element: FirPropertyImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] val prop: R|kotlin/String|
+    public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/primaryConstructor.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/primaryConstructor.kt
new file mode 100644
index 0000000..ffacda8
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/primaryConstructor.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtConstructor
+
+package test
+
+class MyClass()
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/primaryConstructor.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/primaryConstructor.txt
new file mode 100644
index 0000000..ac0c648
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/primaryConstructor.txt
@@ -0,0 +1,10 @@
+KT element: KtPrimaryConstructor
+KT element text:
+public constructor()
+FIR element: FirPrimaryConstructor
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=MyClass] constructor(): R|test/MyClass|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/secondaryConstructor.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/secondaryConstructor.kt
new file mode 100644
index 0000000..76e68b7
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/secondaryConstructor.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtConstructor
+
+package test
+
+class MyClass {
+    constructor(a: Int)
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/secondaryConstructor.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/secondaryConstructor.txt
new file mode 100644
index 0000000..54ef104
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/secondaryConstructor.txt
@@ -0,0 +1,10 @@
+KT element: KtSecondaryConstructor
+KT element text:
+public constructor(a: kotlin.Int) { /* compiled code */ }
+FIR element: FirConstructorImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=MyClass] constructor([ResolvedTo(BODY_RESOLVE)] a: R|kotlin/Int|): R|test/MyClass|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelFunction.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelFunction.kt
new file mode 100644
index 0000000..c257c7c
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelFunction.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
+
+package test
+
+fun fn() = Unit
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelFunction.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelFunction.txt
new file mode 100644
index 0000000..3c08ec6
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelFunction.txt
@@ -0,0 +1,10 @@
+KT element: KtNamedFunction
+KT element text:
+public fun fn(): kotlin.Unit { /* compiled code */ }
+FIR element: FirSimpleFunctionImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelProperty.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelProperty.kt
new file mode 100644
index 0000000..9fc2afd
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelProperty.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
+
+package test
+
+val prop: Int = 37
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelProperty.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelProperty.txt
new file mode 100644
index 0000000..f1b049d
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelProperty.txt
@@ -0,0 +1,11 @@
+KT element: KtProperty
+KT element text:
+public val prop: kotlin.Int /* compiled code */
+FIR element: FirPropertyImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] val prop: R|kotlin/Int|
+    public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberFunction.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberFunction.kt
new file mode 100644
index 0000000..3153390
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberFunction.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
+
+package test
+
+class MyClass {
+    fun fn() = Unit
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberFunction.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberFunction.txt
new file mode 100644
index 0000000..1dece5a
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberFunction.txt
@@ -0,0 +1,10 @@
+KT element: KtNamedFunction
+KT element text:
+public final fun fn(): kotlin.Unit { /* compiled code */ }
+FIR element: FirSimpleFunctionImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedContainerSource test/MyClass
+File name: MyClass.class
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberProperty.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberProperty.kt
new file mode 100644
index 0000000..1708c30
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberProperty.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
+
+package test
+
+class MyClass {
+    val prop: String = ""
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberProperty.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberProperty.txt
new file mode 100644
index 0000000..50c6fb5
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberProperty.txt
@@ -0,0 +1,11 @@
+KT element: KtProperty
+KT element text:
+public final val prop: kotlin.String /* compiled code */
+FIR element: FirPropertyImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedContainerSource test/MyClass
+File name: MyClass.class
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] val prop: R|kotlin/String|
+    public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/primaryConstructor.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/primaryConstructor.kt
new file mode 100644
index 0000000..ffacda8
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/primaryConstructor.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtConstructor
+
+package test
+
+class MyClass()
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/primaryConstructor.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/primaryConstructor.txt
new file mode 100644
index 0000000..acc85f6
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/primaryConstructor.txt
@@ -0,0 +1,10 @@
+KT element: KtPrimaryConstructor
+KT element text:
+public constructor()
+FIR element: FirPrimaryConstructor
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedContainerSource test/MyClass
+File name: MyClass.class
+
+FIR element rendered:
+public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=MyClass] constructor(): R|test/MyClass|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/secondaryConstructor.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/secondaryConstructor.kt
new file mode 100644
index 0000000..76e68b7
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/secondaryConstructor.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtConstructor
+
+package test
+
+class MyClass {
+    constructor(a: Int)
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/secondaryConstructor.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/secondaryConstructor.txt
new file mode 100644
index 0000000..b9f9838
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/secondaryConstructor.txt
@@ -0,0 +1,10 @@
+KT element: KtSecondaryConstructor
+KT element text:
+public constructor(a: kotlin.Int) { /* compiled code */ }
+FIR element: FirConstructorImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedContainerSource test/MyClass
+File name: MyClass.class
+
+FIR element rendered:
+public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=MyClass] constructor([ResolvedTo(BODY_RESOLVE)] a: R|kotlin/Int|): R|test/MyClass|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunction.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunction.kt
new file mode 100644
index 0000000..c257c7c
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunction.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
+
+package test
+
+fun fn() = Unit
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunction.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunction.txt
new file mode 100644
index 0000000..c7fb6e1
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunction.txt
@@ -0,0 +1,10 @@
+KT element: KtNamedFunction
+KT element text:
+public fun fn(): kotlin.Unit { /* compiled code */ }
+FIR element: FirSimpleFunctionImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedFacadeContainerSource test/TopLevelFunctionKt
+File name: TopLevelFunctionKt.class
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunctionWithJvmName.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunctionWithJvmName.kt
new file mode 100644
index 0000000..5467990
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunctionWithJvmName.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
+
+@file:JvmName("ChangedFileName")
+
+package test
+
+fun fn() = Unit
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunctionWithJvmName.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunctionWithJvmName.txt
new file mode 100644
index 0000000..68b7b7d
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunctionWithJvmName.txt
@@ -0,0 +1,10 @@
+KT element: KtNamedFunction
+KT element text:
+public fun fn(): kotlin.Unit { /* compiled code */ }
+FIR element: FirSimpleFunctionImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedFacadeContainerSource test/ChangedFileName
+File name: ChangedFileName.class
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelProperty.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelProperty.kt
new file mode 100644
index 0000000..9fc2afd
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelProperty.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
+
+package test
+
+val prop: Int = 37
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelProperty.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelProperty.txt
new file mode 100644
index 0000000..a325c60
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelProperty.txt
@@ -0,0 +1,11 @@
+KT element: KtProperty
+KT element text:
+public val prop: kotlin.Int /* compiled code */
+FIR element: FirPropertyImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedFacadeContainerSource test/TopLevelPropertyKt
+File name: TopLevelPropertyKt.class
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] val prop: R|kotlin/Int|
+    public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelPropertyWithJvmName.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelPropertyWithJvmName.kt
new file mode 100644
index 0000000..0a0e6d1
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelPropertyWithJvmName.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
+
+@file:JvmName("ChangedFileName")
+
+package test
+
+val prop: Int = 37
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelPropertyWithJvmName.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelPropertyWithJvmName.txt
new file mode 100644
index 0000000..c0ba8ab
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelPropertyWithJvmName.txt
@@ -0,0 +1,11 @@
+KT element: KtProperty
+KT element text:
+public val prop: kotlin.Int /* compiled code */
+FIR element: FirPropertyImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: JvmStubDeserializedFacadeContainerSource test/ChangedFileName
+File name: ChangedFileName.class
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] val prop: R|kotlin/Int|
+    public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberFunction.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberFunction.kt
new file mode 100644
index 0000000..3153390
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberFunction.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
+
+package test
+
+class MyClass {
+    fun fn() = Unit
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberFunction.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberFunction.txt
new file mode 100644
index 0000000..d2856ea
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberFunction.txt
@@ -0,0 +1,10 @@
+KT element: KtNamedFunction
+KT element text:
+public final fun fn(): kotlin.Unit { /* compiled code */ }
+FIR element: FirSimpleFunctionImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberProperty.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberProperty.kt
new file mode 100644
index 0000000..1708c30
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberProperty.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
+
+package test
+
+class MyClass {
+    val prop: String = ""
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberProperty.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberProperty.txt
new file mode 100644
index 0000000..c5c7318
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberProperty.txt
@@ -0,0 +1,11 @@
+KT element: KtProperty
+KT element text:
+public final val prop: kotlin.String /* compiled code */
+FIR element: FirPropertyImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] val prop: R|kotlin/String|
+    public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/primaryConstructor.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/primaryConstructor.kt
new file mode 100644
index 0000000..ffacda8
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/primaryConstructor.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtConstructor
+
+package test
+
+class MyClass()
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/primaryConstructor.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/primaryConstructor.txt
new file mode 100644
index 0000000..ac0c648
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/primaryConstructor.txt
@@ -0,0 +1,10 @@
+KT element: KtPrimaryConstructor
+KT element text:
+public constructor()
+FIR element: FirPrimaryConstructor
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=MyClass] constructor(): R|test/MyClass|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/secondaryConstructor.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/secondaryConstructor.kt
new file mode 100644
index 0000000..76e68b7
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/secondaryConstructor.kt
@@ -0,0 +1,7 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtConstructor
+
+package test
+
+class MyClass {
+    constructor(a: Int)
+}
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/secondaryConstructor.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/secondaryConstructor.txt
new file mode 100644
index 0000000..54ef104
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/secondaryConstructor.txt
@@ -0,0 +1,10 @@
+KT element: KtSecondaryConstructor
+KT element text:
+public constructor(a: kotlin.Int) { /* compiled code */ }
+FIR element: FirConstructorImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=MyClass] constructor([ResolvedTo(BODY_RESOLVE)] a: R|kotlin/Int|): R|test/MyClass|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelFunction.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelFunction.kt
new file mode 100644
index 0000000..c257c7c
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelFunction.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
+
+package test
+
+fun fn() = Unit
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelFunction.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelFunction.txt
new file mode 100644
index 0000000..3c08ec6
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelFunction.txt
@@ -0,0 +1,10 @@
+KT element: KtNamedFunction
+KT element text:
+public fun fn(): kotlin.Unit { /* compiled code */ }
+FIR element: FirSimpleFunctionImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelProperty.kt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelProperty.kt
new file mode 100644
index 0000000..9fc2afd
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelProperty.kt
@@ -0,0 +1,5 @@
+// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
+
+package test
+
+val prop: Int = 37
diff --git a/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelProperty.txt b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelProperty.txt
new file mode 100644
index 0000000..f1b049d
--- /dev/null
+++ b/analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelProperty.txt
@@ -0,0 +1,11 @@
+KT element: KtProperty
+KT element text:
+public val prop: kotlin.Int /* compiled code */
+FIR element: FirPropertyImpl
+FIR source kind: KtRealSourceElementKind
+FIR container source: null
+File name: 0_test.knm
+
+FIR element rendered:
+public final [ResolvedTo(BODY_RESOLVE)] val prop: R|kotlin/Int|
+    public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractGetOrBuildFirTest.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractGetOrBuildFirTest.kt
index 9456f3f..8f9fbd7 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractGetOrBuildFirTest.kt
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractGetOrBuildFirTest.kt
@@ -7,6 +7,8 @@
 
 import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
 import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
+import org.jetbrains.kotlin.analysis.low.level.api.fir.services.FirRenderingOptions
+import org.jetbrains.kotlin.analysis.low.level.api.fir.services.firRenderingOptions
 import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirOutOfContentRootTestConfigurator
 import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirScriptTestConfigurator
 import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirSourceTestConfigurator
@@ -14,11 +16,13 @@
 import org.jetbrains.kotlin.analysis.test.framework.projectStructure.KtTestModule
 import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
 import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
 import org.jetbrains.kotlin.fir.declarations.FirFile
 import org.jetbrains.kotlin.fir.declarations.FirImport
 import org.jetbrains.kotlin.fir.renderer.*
 import org.jetbrains.kotlin.psi.KtElement
 import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
 import org.jetbrains.kotlin.test.services.TestServices
 import org.jetbrains.kotlin.test.services.assertions
 
@@ -30,6 +34,7 @@
             renderActualFir(
                 fir = selectedElement.getOrBuildFir(session),
                 ktElement = selectedElement,
+                renderingOptions = testServices.firRenderingOptions,
                 firFile = mainFile.getOrBuildFirFile(session),
             )
         }
@@ -38,18 +43,30 @@
     }
 }
 
-fun renderActualFir(
+internal fun renderActualFir(
     fir: FirElement?,
     ktElement: KtElement,
-    renderKtText: Boolean = false,
+    renderingOptions: FirRenderingOptions,
     firFile: FirFile? = null,
-): String = """
-       |KT element: ${ktElement::class.simpleName}${if (renderKtText) "\nKT element text:\n" + ktElement.text else ""}
-       |FIR element: ${fir?.let { it::class.simpleName }}
-       |FIR source kind: ${fir?.source?.kind?.let { it::class.simpleName }}
-       |
-       |FIR element rendered:
-       |${render(fir).trimEnd()}${if (firFile != null) "\n\nFIR FILE:\n${render(firFile).trimEnd()}" else ""}""".trimMargin()
+): String = buildString {
+    appendLine("KT element: ${ktElement::class.simpleName}")
+    if (renderingOptions.renderKtText) {
+        appendLine("KT element text:")
+        appendLine(ktElement.text)
+    }
+    appendLine("FIR element: ${fir?.let { it::class.simpleName }}")
+    appendLine("FIR source kind: ${fir?.source?.kind?.let { it::class.simpleName }}")
+    if (renderingOptions.renderContainerSource)
+        appendLine("FIR container source: ${fir.renderContainerSource()}")
+    if (renderingOptions.renderKtFileName)
+        appendLine("File name: ${ktElement.containingKtFile.name}")
+    appendLine("\nFIR element rendered:")
+    appendLine(render(fir).trimEnd())
+    if (firFile != null) {
+        appendLine("\nFIR FILE:")
+        append(render(firFile).trimEnd())
+    }
+}
 
 private fun render(firElement: FirElement?): String = when (firElement) {
     null -> "null"
@@ -61,6 +78,9 @@
     ).renderElementAsString(firElement)
 }
 
+private fun FirElement?.renderContainerSource(): String =
+    (this as? FirCallableDeclaration)?.containerSource?.let { "${it::class.simpleName} ${it.presentableString}" } ?: "null"
+
 abstract class AbstractSourceGetOrBuildFirTest : AbstractGetOrBuildFirTest() {
     override val configurator = AnalysisApiFirSourceTestConfigurator(analyseInDependentSession = false)
 }
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractLibraryGetOrBuildFirTest.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractLibraryGetOrBuildFirTest.kt
index a833403..cde95f0 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractLibraryGetOrBuildFirTest.kt
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractLibraryGetOrBuildFirTest.kt
@@ -6,13 +6,19 @@
 package org.jetbrains.kotlin.analysis.low.level.api.fir
 
 import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.analysis.low.level.api.fir.services.FirRenderingOptions
+import org.jetbrains.kotlin.analysis.low.level.api.fir.services.firRenderingOptions
 import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirLibraryBinaryDecompiledTestConfigurator
 import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirDeclarationForCompiledElementSearcher
 import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
 import org.jetbrains.kotlin.analysis.test.framework.projectStructure.KtTestModule
 import org.jetbrains.kotlin.analysis.test.framework.services.libraries.CompiledLibraryProvider
+import org.jetbrains.kotlin.analysis.test.framework.services.libraries.TestModuleDecompiler
+import org.jetbrains.kotlin.analysis.test.framework.services.libraries.TestModuleDecompilerDirectory
 import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
+import org.jetbrains.kotlin.platform.CommonPlatforms
 import org.jetbrains.kotlin.platform.js.JsPlatforms
+import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
 import org.jetbrains.kotlin.psi.*
 import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
 import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
@@ -26,13 +32,29 @@
     override val configurator get() = AnalysisApiFirLibraryBinaryDecompiledTestConfigurator
 
     override fun configureTest(builder: TestConfigurationBuilder) {
-        builder.forTestsMatching("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/*") {
+        val renderingOptionsBuilder = FirRenderingOptions.Builder().apply { renderKtText = true }
+
+        builder.forTestsMatching("*/js/*") {
             this.defaultsProviderBuilder.targetPlatform = JsPlatforms.defaultJsPlatform
         }
+        builder.forTestsMatching("*/jvm/*") {
+            this.defaultsProviderBuilder.targetPlatform = JvmPlatforms.defaultJvmPlatform
+        }
+        builder.forTestsMatching("*/metadata/*") {
+            this.defaultsProviderBuilder.targetPlatform = CommonPlatforms.defaultCommonPlatform
+            this.useAdditionalService<TestModuleDecompiler> { TestModuleDecompilerDirectory() }
+        }
+        builder.forTestsMatching("*/containerSource/*") {
+            renderingOptionsBuilder.renderKtFileName = true
+            renderingOptionsBuilder.renderContainerSource = true
+        }
         super.configureTest(builder)
         with(builder) {
             useDirectives(Directives)
-            useAdditionalServices(service(::CompiledLibraryProvider))
+            useAdditionalServices(
+                service(::CompiledLibraryProvider),
+                service<FirRenderingOptions> { renderingOptionsBuilder.build() }
+            )
         }
     }
 
@@ -44,7 +66,7 @@
         val symbolProvider = resolveSession.getSessionFor(ktModule).symbolProvider
         val fir = FirDeclarationForCompiledElementSearcher(symbolProvider).findNonLocalDeclaration(declaration)
 
-        testServices.assertions.assertEqualsToTestDataFileSibling(renderActualFir(fir, declaration, true))
+        testServices.assertions.assertEqualsToTestDataFileSibling(renderActualFir(fir, declaration, testServices.firRenderingOptions))
     }
 
     private fun getElementToSearch(ktFile: KtFile, moduleStructure: TestModuleStructure): KtDeclaration? {
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractStdLibBasedGetOrBuildFirTest.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractStdLibBasedGetOrBuildFirTest.kt
index fd2253c..c6f5d49 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractStdLibBasedGetOrBuildFirTest.kt
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/AbstractStdLibBasedGetOrBuildFirTest.kt
@@ -6,6 +6,7 @@
 package org.jetbrains.kotlin.analysis.low.level.api.fir
 
 import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol
+import org.jetbrains.kotlin.analysis.low.level.api.fir.services.firRenderingOptions
 import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirSourceTestConfigurator
 import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
 import org.jetbrains.kotlin.analysis.test.framework.projectStructure.KtTestModule
@@ -34,6 +35,6 @@
 
         val resolveSession = LLFirResolveSessionService.getInstance(project).getFirResolveSession(mainModule.ktModule)
         val fir = declaration.resolveToFirSymbol(resolveSession).fir
-        testServices.assertions.assertEqualsToTestDataFileSibling(renderActualFir(fir, declaration))
+        testServices.assertions.assertEqualsToTestDataFileSibling(renderActualFir(fir, declaration, testServices.firRenderingOptions))
     }
 }
\ No newline at end of file
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/LibraryGetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/LibraryGetOrBuildFirTestGenerated.java
index c7179f3..cbeb9c7 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/LibraryGetOrBuildFirTestGenerated.java
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/LibraryGetOrBuildFirTestGenerated.java
@@ -97,6 +97,166 @@
   }
 
   @Nested
+  @TestMetadata("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource")
+  @TestDataPath("$PROJECT_ROOT")
+  public class ContainerSource {
+    @Test
+    public void testAllFilesPresentInContainerSource() {
+      KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource"), Pattern.compile("^(.+)\\.kt$"), null, true);
+    }
+
+    @Nested
+    @TestMetadata("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js")
+    @TestDataPath("$PROJECT_ROOT")
+    public class Js {
+      @Test
+      public void testAllFilesPresentInJs() {
+        KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js"), Pattern.compile("^(.+)\\.kt$"), null, true);
+      }
+
+      @Test
+      @TestMetadata("memberFunction.kt")
+      public void testMemberFunction() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberFunction.kt");
+      }
+
+      @Test
+      @TestMetadata("memberProperty.kt")
+      public void testMemberProperty() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/memberProperty.kt");
+      }
+
+      @Test
+      @TestMetadata("primaryConstructor.kt")
+      public void testPrimaryConstructor() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/primaryConstructor.kt");
+      }
+
+      @Test
+      @TestMetadata("secondaryConstructor.kt")
+      public void testSecondaryConstructor() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/secondaryConstructor.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelFunction.kt")
+      public void testTopLevelFunction() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelFunction.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelProperty.kt")
+      public void testTopLevelProperty() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/js/topLevelProperty.kt");
+      }
+    }
+
+    @Nested
+    @TestMetadata("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm")
+    @TestDataPath("$PROJECT_ROOT")
+    public class Jvm {
+      @Test
+      public void testAllFilesPresentInJvm() {
+        KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm"), Pattern.compile("^(.+)\\.kt$"), null, true);
+      }
+
+      @Test
+      @TestMetadata("memberFunction.kt")
+      public void testMemberFunction() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberFunction.kt");
+      }
+
+      @Test
+      @TestMetadata("memberProperty.kt")
+      public void testMemberProperty() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/memberProperty.kt");
+      }
+
+      @Test
+      @TestMetadata("primaryConstructor.kt")
+      public void testPrimaryConstructor() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/primaryConstructor.kt");
+      }
+
+      @Test
+      @TestMetadata("secondaryConstructor.kt")
+      public void testSecondaryConstructor() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/secondaryConstructor.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelFunction.kt")
+      public void testTopLevelFunction() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunction.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelFunctionWithJvmName.kt")
+      public void testTopLevelFunctionWithJvmName() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelFunctionWithJvmName.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelProperty.kt")
+      public void testTopLevelProperty() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelProperty.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelPropertyWithJvmName.kt")
+      public void testTopLevelPropertyWithJvmName() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/jvm/topLevelPropertyWithJvmName.kt");
+      }
+    }
+
+    @Nested
+    @TestMetadata("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata")
+    @TestDataPath("$PROJECT_ROOT")
+    public class Metadata {
+      @Test
+      public void testAllFilesPresentInMetadata() {
+        KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata"), Pattern.compile("^(.+)\\.kt$"), null, true);
+      }
+
+      @Test
+      @TestMetadata("memberFunction.kt")
+      public void testMemberFunction() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberFunction.kt");
+      }
+
+      @Test
+      @TestMetadata("memberProperty.kt")
+      public void testMemberProperty() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/memberProperty.kt");
+      }
+
+      @Test
+      @TestMetadata("primaryConstructor.kt")
+      public void testPrimaryConstructor() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/primaryConstructor.kt");
+      }
+
+      @Test
+      @TestMetadata("secondaryConstructor.kt")
+      public void testSecondaryConstructor() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/secondaryConstructor.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelFunction.kt")
+      public void testTopLevelFunction() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelFunction.kt");
+      }
+
+      @Test
+      @TestMetadata("topLevelProperty.kt")
+      public void testTopLevelProperty() {
+        runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/containerSource/metadata/topLevelProperty.kt");
+      }
+    }
+  }
+
+  @Nested
   @TestMetadata("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js")
   @TestDataPath("$PROJECT_ROOT")
   public class Js {
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/firTestUtils.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/firTestUtils.kt
index c6185d8..32f0734 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/firTestUtils.kt
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/firTestUtils.kt
@@ -5,11 +5,11 @@
 
 package org.jetbrains.kotlin.analysis.low.level.api.fir
 
-import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
-import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator
+import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinProjectStructureProvider
 import org.jetbrains.kotlin.analysis.api.projectStructure.KaLibraryModule
 import org.jetbrains.kotlin.analysis.api.projectStructure.KaLibrarySourceModule
-import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinProjectStructureProvider
+import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
+import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator
 import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager
 import org.jetbrains.kotlin.fir.FirElement
 import org.jetbrains.kotlin.fir.declarations.*
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/services/FirRenderingOptions.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/services/FirRenderingOptions.kt
new file mode 100644
index 0000000..d3dbeaa
--- /dev/null
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/services/FirRenderingOptions.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.analysis.low.level.api.fir.services
+
+import org.jetbrains.kotlin.test.services.TestService
+import org.jetbrains.kotlin.test.services.TestServices
+
+internal class FirRenderingOptions(
+    val renderKtText: Boolean = false,
+    val renderKtFileName: Boolean = false,
+    val renderContainerSource: Boolean = false,
+) : TestService {
+    companion object {
+        val DEFAULT = FirRenderingOptions()
+    }
+
+    class Builder {
+        var renderKtText: Boolean = false
+        var renderKtFileName: Boolean = false
+        var renderContainerSource: Boolean = false
+
+        fun build(): FirRenderingOptions = FirRenderingOptions(
+            renderKtText,
+            renderKtFileName,
+            renderContainerSource,
+        )
+    }
+}
+
+internal val TestServices.firRenderingOptionsIfRegistered: FirRenderingOptions? by TestServices.nullableTestServiceAccessor()
+internal val TestServices.firRenderingOptions: FirRenderingOptions
+    get() = firRenderingOptionsIfRegistered ?: FirRenderingOptions.DEFAULT