[FIR, IR] expect-actual matcher. Make matching static members an explict separate step

^KT-71817 Fixed
Before 2bbc1e7ef75c9874ce9438155ee1364a3a785781, statics matching was an
implicit part of "collectAllMembers" step.

Currently, it affects nothing but enums (static members in enums are the
only static members in Kotlin), and correctness of `KotlinActual`
annotation in Kotlin-to-Java direct actualization.
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirExpectActualMatchingContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirExpectActualMatchingContext.kt
index 9ab9170..9a69172 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirExpectActualMatchingContext.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirExpectActualMatchingContext.kt
@@ -22,6 +22,7 @@
 
     val expectScopeSession: ScopeSession
     override fun RegularClassSymbolMarker.getCallablesForExpectClass(name: Name): List<FirCallableSymbol<*>>
+    fun RegularClassSymbolMarker.getStaticCallablesForExpectClass(name: Name): List<FirCallableSymbol<*>>
 }
 
 interface FirExpectActualMatchingContextFactory : FirSessionComponent {
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt
index de6bdc1..686b265 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt
@@ -191,6 +191,20 @@
         }
     }
 
+    override fun RegularClassSymbolMarker.collectAllStaticCallables(isActualDeclaration: Boolean): List<FirCallableSymbol<*>> {
+        val symbol = asSymbol()
+        val session = when (isActualDeclaration) {
+            true -> actualSession
+            else -> symbol.moduleData.session
+        }
+        val scope = symbol.staticScope(SessionHolderImpl(session, actualScopeSession)) ?: return emptyList()
+        val result = ArrayList<FirCallableSymbol<*>>()
+        for (name in scope.getCallableNames()) {
+            scope.getMembersTo(result, name)
+        }
+        return result
+    }
+
     override fun RegularClassSymbolMarker.getCallablesForExpectClass(name: Name): List<FirCallableSymbol<*>> {
         val symbol = asSymbol()
         val scope = symbol.defaultType().scope(
@@ -205,6 +219,14 @@
         }
     }
 
+    override fun RegularClassSymbolMarker.getStaticCallablesForExpectClass(name: Name): List<FirCallableSymbol<*>> {
+        val symbol = asSymbol()
+        val scope = symbol.staticScope(SessionHolderImpl(symbol.moduleData.session, actualScopeSession)) ?: return emptyList()
+        val result = ArrayList<FirCallableSymbol<*>>()
+        scope.getMembersTo(result, name)
+        return result
+    }
+
     override fun FirClassSymbol<*>.getConstructors(
         scopeSession: ScopeSession,
         session: FirSession,
@@ -225,7 +247,7 @@
         scope.getDeclaredConstructors().mapTo(destination) { it }
     }
 
-    private fun FirTypeScope.getMembersTo(destination: MutableList<in FirCallableSymbol<*>>, name: Name) {
+    private fun FirScope.getMembersTo(destination: MutableList<in FirCallableSymbol<*>>, name: Name) {
         processFunctionsByName(name) { destination.add(it) }
         processPropertiesByName(name) { destination.add(it) }
     }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt
index 7a32127..710b095 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt
@@ -12,6 +12,7 @@
 import org.jetbrains.kotlin.fir.declarations.expectForActual
 import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass
 import org.jetbrains.kotlin.fir.declarations.utils.isExpect
+import org.jetbrains.kotlin.fir.declarations.utils.isStatic
 import org.jetbrains.kotlin.fir.resolve.providers.dependenciesSymbolProvider
 import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
 import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
@@ -19,6 +20,7 @@
 import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
 import org.jetbrains.kotlin.mpp.CallableSymbolMarker
 import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher
@@ -46,8 +48,11 @@
                                 ?.get(ExpectActualMatchingCompatibility.MatchedSuccessfully)
                                 ?.singleOrNull() as? FirRegularClassSymbol
 
-                            when (actualSymbol) {
-                                is FirConstructorSymbol -> expectContainingClass?.getConstructors(expectScopeSession)
+                            when {
+                                actualSymbol is FirConstructorSymbol -> expectContainingClass?.getConstructors(expectScopeSession)
+                                // I disagree that FirEnumEntrySymbol should be a subclass of FirCallableSymbol
+                                actualSymbol !is FirEnumEntrySymbol && actualSymbol.isStatic ->
+                                    expectContainingClass?.getStaticCallablesForExpectClass(actualSymbol.name)
                                 else -> expectContainingClass?.getCallablesForExpectClass(actualSymbol.name)
                             }.orEmpty()
                         }
diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt
index e9dbaff..1643bcc 100644
--- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt
+++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt
@@ -13,6 +13,7 @@
 import org.jetbrains.kotlin.fir.expressions.FirExpression
 import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
 import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
+import org.jetbrains.kotlin.mpp.EnumEntrySymbolMarker
 import org.jetbrains.kotlin.mpp.PropertySymbolMarker
 import org.jetbrains.kotlin.mpp.ValueParameterSymbolMarker
 import org.jetbrains.kotlin.name.CallableId
@@ -115,7 +116,7 @@
         get() = fir.hasConstantInitializer
 }
 
-class FirEnumEntrySymbol(callableId: CallableId) : FirVariableSymbol<FirEnumEntry>(callableId) {
+class FirEnumEntrySymbol(callableId: CallableId) : FirVariableSymbol<FirEnumEntry>(callableId), EnumEntrySymbolMarker {
     val initializerObjectSymbol: FirAnonymousObjectSymbol?
         get() = (fir.initializer as? FirAnonymousObjectExpression)?.anonymousObject?.symbol
 }
diff --git a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt
index b573232..f7b22d7 100644
--- a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt
+++ b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt
@@ -257,6 +257,10 @@
         return asIr().declarations.filter { it !is IrAnonymousInitializer && !it.isStaticFun() }.map { it.symbol }
     }
 
+    override fun RegularClassSymbolMarker.collectAllStaticCallables(isActualDeclaration: Boolean): List<CallableSymbolMarker> {
+        return asIr().declarations.filter { it.isStaticFun() }.mapNotNull { it.symbol as? CallableSymbolMarker }
+    }
+
     override fun RegularClassSymbolMarker.getCallablesForExpectClass(name: Name): List<CallableSymbolMarker> {
         return asIr().declarations.filter { it.getNameWithAssert() == name }.mapNotNull { it.symbol as? CallableSymbolMarker }
     }
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt
index a05a149..f003d8a 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt
@@ -203,6 +203,19 @@
             outToIncompatibleMembers = incompatibleMembers
         )
 
+        val actualStaticMembersByName = actualClassSymbol.collectAllStaticCallables(isActualDeclaration = true).groupBy { nameOf(it) }
+        val expectStaticMembers = expectClassSymbol.collectAllStaticCallables(isActualDeclaration = false)
+        matchAndCheckExpectMembersAgainstPotentialActuals(
+            expectClassSymbol,
+            actualClassSymbol,
+            substitutor,
+            languageVersionSettings,
+            expectStaticMembers,
+            actualStaticMembersByName,
+            outToMismatchedMembers = mismatchedMembers,
+            outToIncompatibleMembers = incompatibleMembers
+        )
+
         if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
             val aEntries = expectClassSymbol.collectEnumEntryNames()
             val bEntries = actualClassSymbol.collectEnumEntryNames()
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt
index b1aefc9..9d7954d 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt
@@ -86,6 +86,7 @@
     ): TypeSubstitutorMarker
 
     fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List<DeclarationSymbolMarker>
+    fun RegularClassSymbolMarker.collectAllStaticCallables(isActualDeclaration: Boolean): List<CallableSymbolMarker>
     fun RegularClassSymbolMarker.getCallablesForExpectClass(name: Name): List<CallableSymbolMarker>
 
     fun RegularClassSymbolMarker.collectEnumEntryNames(): List<Name>
diff --git a/compiler/testData/codegen/box/multiplatform/k2/enumStaticMethods.kt b/compiler/testData/codegen/box/multiplatform/k2/enumStaticMethods.kt
index 6d0fe91..081bdb7 100644
--- a/compiler/testData/codegen/box/multiplatform/k2/enumStaticMethods.kt
+++ b/compiler/testData/codegen/box/multiplatform/k2/enumStaticMethods.kt
@@ -1,4 +1,3 @@
-// IGNORE_BACKEND: ANY
 // LANGUAGE: +MultiPlatformProjects
 
 // MODULE: common