[Analysis API] Add API for obtaining scope with synthetic properties

^KTIJ-22359 Fixed
diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ScopeProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ScopeProvider.kt
index 7d8383d..10e7d81 100644
--- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ScopeProvider.kt
+++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ScopeProvider.kt
@@ -72,7 +72,7 @@
         return KtFe10ScopeMember(DeclaredMemberScope(descriptor), descriptor.constructors, analysisContext)
     }
 
-    override fun getDelegatedMemberScope(classSymbol: KtSymbolWithMembers): KtScope  {
+    override fun getDelegatedMemberScope(classSymbol: KtSymbolWithMembers): KtScope {
         val descriptor = getDescriptor<ClassDescriptor>(classSymbol)
             ?: return getEmptyScope()
 
@@ -191,6 +191,11 @@
         TODO()
     }
 
+    override fun getSyntheticJavaPropertiesScope(type: KtType): KtTypeScope {
+        require(type is KtFe10Type)
+        TODO()
+    }
+
     override fun getScopeContextForPosition(originalFile: KtFile, positionInFakeFile: KtElement): KtScopeContext {
         val elementToAnalyze = positionInFakeFile.containingNonLocalDeclaration() ?: originalFile
         val bindingContext = analysisContext.analyze(elementToAnalyze)
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt
index b679308..3738854 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt
@@ -6,7 +6,6 @@
 package org.jetbrains.kotlin.analysis.api.fir.components
 
 import com.intellij.openapi.project.Project
-import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
 import org.jetbrains.kotlin.analysis.api.components.KtImplicitReceiver
 import org.jetbrains.kotlin.analysis.api.components.KtScopeContext
 import org.jetbrains.kotlin.analysis.api.components.KtScopeProvider
@@ -19,7 +18,6 @@
 import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirNamedClassOrObjectSymbol
 import org.jetbrains.kotlin.analysis.api.fir.types.KtFirType
 import org.jetbrains.kotlin.analysis.api.impl.base.scopes.KtCompositeScope
-import org.jetbrains.kotlin.analysis.api.impl.base.scopes.KtCompositeTypeScope
 import org.jetbrains.kotlin.analysis.api.impl.base.scopes.KtEmptyScope
 import org.jetbrains.kotlin.analysis.api.scopes.KtScope
 import org.jetbrains.kotlin.analysis.api.scopes.KtTypeScope
@@ -38,11 +36,8 @@
 import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertiesScope
 import org.jetbrains.kotlin.fir.resolve.scope
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
-import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
-import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
-import org.jetbrains.kotlin.fir.scopes.FirScope
+import org.jetbrains.kotlin.fir.scopes.*
 import org.jetbrains.kotlin.fir.scopes.impl.*
-import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
 import org.jetbrains.kotlin.fir.symbols.impl.*
 import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
 import org.jetbrains.kotlin.name.FqName
@@ -165,23 +160,16 @@
         return KtCompositeScope(subScopes, token)
     }
 
-    @OptIn(KtAnalysisApiInternals::class)
-    override fun getTypeScope(type: KtType): KtTypeScope? {
+    override fun getTypeScope(type: KtType): KtTypeScope? = getFirTypeScope(type)?.let { convertToKtTypeScope(it) }
+
+    override fun getSyntheticJavaPropertiesScope(type: KtType): KtTypeScope? {
         check(type is KtFirType) { "KtFirScopeProvider can only work with KtFirType, but ${type::class} was provided" }
-        val firSession = firResolveSession.useSiteFirSession
-        val firTypeScope = type.coneType.scope(
-            firSession,
-            scopeSession,
-            FakeOverrideTypeCalculator.Forced,
-            requiredPhase = FirResolvePhase.STATUS,
-        ) ?: return null
-        return KtCompositeTypeScope(
-            listOfNotNull(
-                convertToKtTypeScope(firTypeScope),
-                FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(firSession, type.coneType, firTypeScope)?.let { convertToKtTypeScope(it) }
-            ),
-            token
-        )
+        val firTypeScope = getFirTypeScope(type) ?: return null
+        return FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(
+            firResolveSession.useSiteFirSession,
+            type.coneType,
+            firTypeScope
+        )?.let { convertToKtTypeScope(it) }
     }
 
     override fun getScopeContextForPosition(
@@ -246,6 +234,16 @@
             else -> TODO(firScope::class.toString())
         }
     }
+
+    private fun getFirTypeScope(type: KtType): FirTypeScope? {
+        check(type is KtFirType) { "KtFirScopeProvider can only work with KtFirType, but ${type::class} was provided" }
+        return type.coneType.scope(
+            firResolveSession.useSiteFirSession,
+            scopeSession,
+            FakeOverrideTypeCalculator.Forced,
+            requiredPhase = FirResolvePhase.STATUS,
+        )
+    }
 }
 
 private class EnumEntryContainingNamesAwareScope(private val originalScope: FirContainingNamesAwareScope) : FirContainingNamesAwareScope() {
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtScopeProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtScopeProvider.kt
index aa2fb3a..fa87e38 100644
--- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtScopeProvider.kt
+++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtScopeProvider.kt
@@ -15,6 +15,7 @@
 import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
 import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithMembers
 import org.jetbrains.kotlin.analysis.api.types.KtType
+import org.jetbrains.kotlin.psi.KtDeclaration
 import org.jetbrains.kotlin.psi.KtElement
 import org.jetbrains.kotlin.psi.KtFile
 
@@ -37,6 +38,8 @@
 
     public abstract fun getTypeScope(type: KtType): KtTypeScope?
 
+    public abstract fun getSyntheticJavaPropertiesScope(type: KtType): KtTypeScope?
+
     public abstract fun getScopeContextForPosition(
         originalFile: KtFile,
         positionInFakeFile: KtElement
@@ -44,6 +47,10 @@
 }
 
 public interface KtScopeProviderMixIn : KtAnalysisSessionMixIn {
+    /**
+     * Creates [KtScope] containing members of [KtDeclaration].
+     * Returned [KtScope] doesn't include synthetic Java properties. To get such properties use [getSyntheticJavaPropertiesScope].
+     */
     public fun KtSymbolWithMembers.getMemberScope(): KtScope =
         withValidityAssertion { analysisSession.scopeProvider.getMemberScope(this) }
 
@@ -81,6 +88,7 @@
      * Inside the `LIST_KT_ELEMENT.getKtType().getTypeScope()` would contain the `get(i: Int): String` method with substituted type `T = String`
      *
      * @return type scope for the given type if given `KtType` is not error type, `null` otherwise.
+     * Returned [KtTypeScope] doesn't include synthetic Java properties. To get such properties use [getSyntheticJavaPropertiesScope].
      *
      * @see KtTypeScope
      * @see KtTypeProviderMixIn.getKtType
@@ -88,6 +96,16 @@
     public fun KtType.getTypeScope(): KtTypeScope? =
         withValidityAssertion { analysisSession.scopeProvider.getTypeScope(this) }
 
+    /**
+     * Returns a [KtTypeScope] with synthetic Java properties created for a given [KtType].
+     */
+    public fun KtType.getSyntheticJavaPropertiesScope(): KtTypeScope? =
+        withValidityAssertion { analysisSession.scopeProvider.getSyntheticJavaPropertiesScope(this) }
+
+    /**
+     * Scopes in returned [KtScopeContext] don't include synthetic Java properties.
+     * To get such properties use [getSyntheticJavaPropertiesScope].
+     */
     public fun KtFile.getScopeContextForPosition(positionInFakeFile: KtElement): KtScopeContext =
         withValidityAssertion { analysisSession.scopeProvider.getScopeContextForPosition(this, positionInFakeFile) }