AA: migrate PSI declaration provider (from IJ)
diff --git a/analysis/decompiled/light-classes-for-decompiled/build.gradle.kts b/analysis/decompiled/light-classes-for-decompiled/build.gradle.kts
index 6a376e3..6be903c 100644
--- a/analysis/decompiled/light-classes-for-decompiled/build.gradle.kts
+++ b/analysis/decompiled/light-classes-for-decompiled/build.gradle.kts
@@ -5,6 +5,9 @@
 
 dependencies {
     api(project(":compiler:psi"))
+    api(project(":analysis:analysis-api"))
+    api(project(":analysis:analysis-api-providers"))
+    api(project(":analysis:project-structure"))
     api(project(":analysis:decompiled:decompiler-to-psi"))
     api(project(":compiler:light-classes"))
     implementation(intellijCore())
diff --git a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/DecompiledPsiDeclarationProvider.kt b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/DecompiledPsiDeclarationProvider.kt
new file mode 100644
index 0000000..315c853
--- /dev/null
+++ b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/DecompiledPsiDeclarationProvider.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2010-2022 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.providers
+
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElement
+import com.intellij.psi.search.GlobalSearchScope
+import org.jetbrains.kotlin.analysis.api.symbols.*
+
+public object DecompiledPsiDeclarationProvider {
+    public fun findPsi(ktSymbol: KtSymbol, project: Project): PsiElement? {
+        return when (ktSymbol) {
+            is KtConstructorSymbol -> providePsiForConstructor(ktSymbol, project)
+            is KtFunctionLikeSymbol -> providePsiForFunction(ktSymbol, project)
+            is KtEnumEntrySymbol -> providePsiForEnumEntry(ktSymbol, project)
+            is KtVariableLikeSymbol -> providePsiForProperty(ktSymbol, project)
+            is KtClassLikeSymbol -> providePsiForClass(ktSymbol, project)
+            else -> null
+        }
+    }
+
+    private fun providePsiForConstructor(
+        constructorSymbol: KtConstructorSymbol,
+        project: Project
+    ): PsiElement? {
+        val classId = constructorSymbol.containingClassIdIfNonLocal ?: return null
+        val psiClass = project.createPsiDeclarationProvider(constructorSymbol.scope(project))
+            ?.getClassesByClassId(classId)
+            ?.firstOrNull() ?: return null
+        return psiClass.constructors.firstOrNull()
+    }
+
+    private fun providePsiForFunction(
+        functionLikeSymbol: KtFunctionLikeSymbol,
+        project: Project
+    ): PsiElement? {
+        return functionLikeSymbol.callableIdIfNonLocal?.let {
+            project.createPsiDeclarationProvider(functionLikeSymbol.scope(project))
+                ?.getFunctions(it)
+                ?.firstOrNull()
+        }
+    }
+
+    private fun providePsiForProperty(
+        variableLikeSymbol: KtVariableLikeSymbol,
+        project: Project
+    ): PsiElement? {
+        return variableLikeSymbol.callableIdIfNonLocal?.let {
+            project.createPsiDeclarationProvider(variableLikeSymbol.scope(project))
+                ?.getProperties(it)
+                // TODO: needs to pick field/getter/setter accordingly?
+                ?.firstOrNull()
+        }
+    }
+
+    private fun providePsiForClass(
+        classLikeSymbol: KtClassLikeSymbol,
+        project: Project
+    ): PsiElement? {
+        return classLikeSymbol.classIdIfNonLocal?.let {
+            project.createPsiDeclarationProvider(classLikeSymbol.scope(project))
+                ?.getClassesByClassId(it)
+                ?.firstOrNull()
+        }
+    }
+
+    private fun providePsiForEnumEntry(
+        enumEntrySymbol: KtEnumEntrySymbol,
+        project: Project
+    ): PsiElement? {
+        val classId = enumEntrySymbol.containingEnumClassIdIfNonLocal ?: return null
+        val psiClass = project.createPsiDeclarationProvider(enumEntrySymbol.scope(project))
+            ?.getClassesByClassId(classId)
+            ?.firstOrNull() ?: return null
+        return psiClass.fields.find {
+            it.name == enumEntrySymbol.name.asString()
+        }
+    }
+
+    private fun KtSymbol.scope(project: Project): GlobalSearchScope {
+        // TODO: finding containing module and use a narrower scope?
+        return GlobalSearchScope.allScope(project)
+    }
+}
\ No newline at end of file
diff --git a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/KotlinPsiDeclarationProvider.kt b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/KotlinPsiDeclarationProvider.kt
new file mode 100644
index 0000000..75966c8
--- /dev/null
+++ b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/KotlinPsiDeclarationProvider.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010-2022 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.providers
+
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiMember
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.search.GlobalSearchScope
+import org.jetbrains.kotlin.name.CallableId
+import org.jetbrains.kotlin.name.ClassId
+
+/**
+ * A [PsiMember] declaration provider for a given scope. Can be created via [KotlinPsiDeclarationProviderFactory].
+ */
+public abstract class KotlinPsiDeclarationProvider {
+    /**
+     * Gets a collection of [PsiClass] by [ClassId]
+     *
+     * In standalone mode, this is simply [PsiClassStub]-based [PsiClass]
+     */
+    public abstract fun getClassesByClassId(classId: ClassId): Collection<PsiClass>
+
+    public abstract fun getProperties(callableId: CallableId): Collection<PsiMember>
+    public abstract fun getFunctions(callableId: CallableId): Collection<PsiMethod>
+}
+
+public abstract class KotlinPsiDeclarationProviderFactory {
+    public abstract fun createPsiDeclarationProvider(searchScope: GlobalSearchScope): KotlinPsiDeclarationProvider
+}
+
+public fun Project.createPsiDeclarationProvider(searchScope: GlobalSearchScope): KotlinPsiDeclarationProvider? =
+    // TODO: avoid using fail-safe service loading once the factory has an easy-to-register ctor.
+    getServiceIfCreated(KotlinPsiDeclarationProviderFactory::class.java)
+        ?.createPsiDeclarationProvider(searchScope)
diff --git a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticPsiDeclarationFromBinaryModuleProvider.kt b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticPsiDeclarationFromBinaryModuleProvider.kt
new file mode 100644
index 0000000..39a8a91
--- /dev/null
+++ b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticPsiDeclarationFromBinaryModuleProvider.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010-2022 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.providers.impl
+
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
+import com.intellij.psi.*
+import com.intellij.psi.impl.compiled.ClsClassImpl
+import com.intellij.psi.impl.compiled.ClsFileImpl
+import com.intellij.psi.search.GlobalSearchScope
+import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache
+import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
+import org.jetbrains.kotlin.analysis.providers.KotlinPsiDeclarationProvider
+import org.jetbrains.kotlin.analysis.providers.KotlinPsiDeclarationProviderFactory
+import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
+import org.jetbrains.kotlin.name.CallableId
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
+
+private class KotlinStaticPsiDeclarationFromBinaryModuleProvider(
+    private val project: Project,
+    override val scope: GlobalSearchScope,
+    private val binaryModules: Collection<KtBinaryModule>,
+    override val jarFileSystem: CoreJarFileSystem,
+) : KotlinPsiDeclarationProvider(), AbstractDeclarationFromBinaryModuleProvider {
+    private val psiManager by lazy { PsiManager.getInstance(project) }
+
+    private fun clsClassImplsByFqName(
+        fqName: FqName,
+        isPackageName: Boolean = true,
+    ): Collection<ClsClassImpl> {
+        return binaryModules
+            .flatMap {
+                virtualFilesFromModule(it, fqName, isPackageName)
+            }
+            .mapNotNull {
+                createClsJavaClassFromVirtualFile(it)
+            }
+    }
+
+    private fun createClsJavaClassFromVirtualFile(
+        classFile: VirtualFile,
+    ): ClsClassImpl? {
+        val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null
+        javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
+        val fakeFile = object : ClsFileImpl(ClassFileViewProvider(psiManager, classFile)) {
+            override fun getStub() = javaFileStub
+
+            override fun isPhysical() = false
+        }
+        javaFileStub.psi = fakeFile
+        return fakeFile.classes.single() as ClsClassImpl
+    }
+
+    override fun getClassesByClassId(classId: ClassId): Collection<ClsClassImpl> {
+        return clsClassImplsByFqName(classId.asSingleFqName(), isPackageName = false)
+    }
+
+    override fun getProperties(callableId: CallableId): Collection<PsiMember> {
+        val classes = callableId.classId?.let { classId ->
+            getClassesByClassId(classId)
+        } ?: clsClassImplsByFqName(callableId.packageName)
+        return classes.flatMap { psiClass ->
+            psiClass.children
+                .filterIsInstance<PsiMember>()
+                .filter { psiMember ->
+                    if (psiMember !is PsiMethod && psiMember !is PsiField) return@filter false
+                    val name = psiMember.name ?: return@filter false
+                    // PsiField a.k.a. backing field
+                    name == callableId.callableName.identifier ||
+                            // PsiMethod, i.e., accessors
+                            (name.startsWith("get") || name.startsWith("set")) &&
+                            // E.g., getFooBar -> FooBar -> fooBar
+                            (name.substring(3).decapitalizeSmart().endsWith(callableId.callableName.identifier))
+
+                }
+        }.toList()
+    }
+
+    override fun getFunctions(callableId: CallableId): Collection<PsiMethod> {
+        val classes = callableId.classId?.let { classId ->
+            getClassesByClassId(classId)
+        } ?: clsClassImplsByFqName(callableId.packageName)
+        return classes.flatMap { psiClass ->
+            psiClass.methods.filter { psiMethod ->
+                psiMethod.name == callableId.callableName.identifier
+            }
+        }.toList()
+    }
+}
+
+// TODO: we can't register this in IDE yet due to non-trivial parameters: lib modules and jar file system.
+//  We need a session or facade that maintains such information
+public class KotlinStaticPsiDeclarationProviderFactory(
+    private val project: Project,
+    private val binaryModules: Collection<KtBinaryModule>,
+    private val jarFileSystem: CoreJarFileSystem,
+) : KotlinPsiDeclarationProviderFactory() {
+    override fun createPsiDeclarationProvider(searchScope: GlobalSearchScope): KotlinPsiDeclarationProvider {
+        return KotlinStaticPsiDeclarationFromBinaryModuleProvider(project, searchScope, binaryModules, jarFileSystem)
+    }
+}