[AA] Provide separate non-static and static declared member scopes

- Member scopes already don't contain static callables, only their
  static member scope counterparts. However, declared member scopes
  contained both non-static and static callables, which was confusing to
  users. See for example KT-61255.
- Now declared member scopes only contain non-static callables and
  static declared member scopes only contain static callables.
- In `KtFirScopeProvider`, the new implementation is different for
  Kotlin and Java classes, because the standard declared member scope
  doesn't work for Java. Instead, we have to get the Java *enhancement*
  scopes from `JavaScopeProvider`. Unfortunately, `JavaScopeProvider`
  doesn't have a direct enhancement declared member scope. This results
  in a somewhat complex scope structure with the declared members filter
  scope around the use-site/static Java enhancement scope, but since the
  declared members filtering scope properly reduces the set of callable
  names and scopes in general are cached, this shouldn't be an issue.
- `getCombinedDeclaredMemberScope` is introduced as a separate public
  function because for Kotlin scopes, we don't actually have to create a
  combined scope, as the non-static and static scopes are just filters
  around a combined declared member scope provided by the compiler. It's
  also important to have a convenient function to get the combined
  declared member scope, because some usages explicitly want access to
  all declared members (such as symbol light classes).
- This commit also fixes KT-61901, because
  `getFirJavaDeclaredMemberScope` now provides a proper static scope for
  Java classes, which will be accessible via the combined declared
  member scope as well.

^KT-61800 fixed
^KT-61901 fixed
^KT-61255 fixed
This commit is contained in:
Marco Pennekamp
2023-09-13 20:58:40 +02:00
committed by Space Team
parent 8b5f87f15f
commit 71017298d9
6 changed files with 181 additions and 55 deletions
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.analysis.api.descriptors.scopes.KtFe10FileScope
import org.jetbrains.kotlin.analysis.api.descriptors.scopes.KtFe10PackageScope
import org.jetbrains.kotlin.analysis.api.descriptors.scopes.KtFe10ScopeLexical
import org.jetbrains.kotlin.analysis.api.descriptors.scopes.KtFe10ScopeMember
import org.jetbrains.kotlin.analysis.api.descriptors.scopes.KtFe10ScopeNonStaticMember
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.KtFe10FileSymbol
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.KtFe10PackageSymbol
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.base.KtFe10Symbol
@@ -63,10 +64,33 @@ internal class KtFe10ScopeProvider(
return KtFe10ScopeMember(descriptor.unsubstitutedMemberScope, descriptor.constructors, analysisContext)
}
override fun getStaticMemberScope(symbol: KtSymbolWithMembers): KtScope {
val descriptor = getDescriptor<ClassDescriptor>(symbol) ?: return getEmptyScope()
return KtFe10ScopeMember(descriptor.staticScope, emptyList(), analysisContext)
}
override fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope {
val descriptor = getDescriptor<ClassDescriptor>(classSymbol)
?: return getEmptyScope()
return KtFe10ScopeNonStaticMember(DeclaredMemberScope(descriptor), descriptor.constructors, analysisContext)
}
override fun getStaticDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope {
val descriptor = getDescriptor<ClassDescriptor>(classSymbol)
?: return getEmptyScope()
return KtFe10ScopeMember(
DeclaredMemberScope(descriptor.staticScope, descriptor, forDelegatedMembersOnly = false),
emptyList(),
analysisContext,
)
}
override fun getCombinedDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope {
val descriptor = getDescriptor<ClassDescriptor>(classSymbol)
?: return getEmptyScope()
return KtFe10ScopeMember(DeclaredMemberScope(descriptor), descriptor.constructors, analysisContext)
}
@@ -157,12 +181,6 @@ internal class KtFe10ScopeProvider(
}
}
override fun getStaticMemberScope(symbol: KtSymbolWithMembers): KtScope {
val descriptor = getDescriptor<ClassDescriptor>(symbol) ?: return getEmptyScope()
return KtFe10ScopeMember(descriptor.staticScope, emptyList(), analysisContext)
}
override fun getEmptyScope(): KtScope {
return KtEmptyScope(token)
}
@@ -16,7 +16,9 @@ import org.jetbrains.kotlin.analysis.api.scopes.KtScopeNameFilter
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtClassifierSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtConstructorSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtPackageSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossiblyNamedSymbol
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.name.Name
@@ -102,6 +104,22 @@ internal open class KtFe10ScopeMember(
}
}
internal open class KtFe10ScopeNonStaticMember(
scope: MemberScope,
constructors: Collection<ConstructorDescriptor>,
analysisContext: Fe10AnalysisContext
) : KtFe10ScopeMember(scope, constructors, analysisContext) {
override fun getCallableSymbols(nameFilter: KtScopeNameFilter): Sequence<KtCallableSymbol> = withValidityAssertion {
super.getCallableSymbols(nameFilter).filter { symbol ->
when (symbol) {
is KtFunctionSymbol -> !symbol.isStatic
is KtPropertySymbol -> !symbol.isStatic
else -> true
}
}
}
}
internal class KtFe10ScopeImporting(
override val scope: ImportingScope,
override val analysisContext: Fe10AnalysisContext
@@ -24,10 +24,8 @@ import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.analysis.utils.errors.unexpectedElementError
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.utils.delegateFields
import org.jetbrains.kotlin.fir.java.JavaScopeProvider
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
@@ -83,23 +81,82 @@ internal class KtFirScopeProvider(
return KtFirDelegatingNamesAwareScope(firScope, builder)
}
override fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope {
val useSiteSession = analysisSession.useSiteSession
if (classSymbol is KtFirScriptSymbol) {
return KtFirDelegatingNamesAwareScope(
FirScriptDeclarationsScope(useSiteSession, classSymbol.firSymbol.fir),
builder,
)
override fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope =
getDeclaredMemberScope(classSymbol, DeclaredMemberScopeKind.NON_STATIC)
override fun getStaticDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope =
getDeclaredMemberScope(classSymbol, DeclaredMemberScopeKind.STATIC)
private enum class DeclaredMemberScopeKind { NON_STATIC, STATIC }
private fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers, kind: DeclaredMemberScopeKind): KtScope {
val firDeclaration = classSymbol.firSymbol.fir
val firScope = when (firDeclaration) {
is FirJavaClass -> getFirJavaDeclaredMemberScope(firDeclaration, kind) ?: return getEmptyScope()
else -> getFirKotlinDeclaredMemberScope(classSymbol, kind)
}
val fir = classSymbol.getFirForScope()
val firScope = when (val regularClass = classSymbol.firSymbol.fir) {
is FirJavaClass -> buildJavaEnhancementDeclaredMemberScope(useSiteSession, regularClass.symbol, getScopeSession())
else -> useSiteSession.declaredMemberScope(fir, memberRequiredPhase = null)
}
return KtFirDelegatingNamesAwareScope(firScope, builder)
}
private fun getFirKotlinDeclaredMemberScope(
classSymbol: KtSymbolWithMembers,
kind: DeclaredMemberScopeKind,
): FirContainingNamesAwareScope {
val combinedScope = getCombinedFirKotlinDeclaredMemberScope(classSymbol)
return when (kind) {
DeclaredMemberScopeKind.NON_STATIC -> FirNonStaticCallablesScope(combinedScope)
DeclaredMemberScopeKind.STATIC -> FirStaticScope(combinedScope)
}
}
private fun getFirJavaDeclaredMemberScope(firJavaClass: FirJavaClass, kind: DeclaredMemberScopeKind): FirContainingNamesAwareScope? {
val useSiteSession = analysisSession.useSiteSession
val scopeSession = getScopeSession()
val firScope = when (kind) {
DeclaredMemberScopeKind.NON_STATIC -> JavaScopeProvider.getUseSiteMemberScope(
firJavaClass,
useSiteSession,
scopeSession,
memberRequiredPhase = FirResolvePhase.TYPES,
)
DeclaredMemberScopeKind.STATIC -> JavaScopeProvider.getStaticScope(firJavaClass, useSiteSession, scopeSession) ?: return null
}
val cacheKey = when (kind) {
DeclaredMemberScopeKind.NON_STATIC -> JAVA_ENHANCEMENT_FOR_DECLARED_MEMBERS
DeclaredMemberScopeKind.STATIC -> JAVA_ENHANCEMENT_FOR_STATIC_DECLARED_MEMBERS
}
return scopeSession.getOrBuild(firJavaClass.symbol, cacheKey) {
JavaClassDeclaredMembersEnhancementScope(analysisSession.useSiteSession, firJavaClass, firScope)
}
}
override fun getCombinedDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope {
val firDeclaration = classSymbol.firSymbol.fir
if (firDeclaration is FirJavaClass) {
// Java enhancement scopes as provided by `JavaScopeProvider` are either use-site or static scopes, so we need to compose them
// to get the combined scope. A base declared member scope with Java enhancement doesn't exist, unfortunately.
return KtCompositeScope.create(listOf(getDeclaredMemberScope(classSymbol), getStaticDeclaredMemberScope(classSymbol)), token)
}
return KtFirDelegatingNamesAwareScope(getCombinedFirKotlinDeclaredMemberScope(classSymbol), builder)
}
/**
* Returns a declared member scope which contains both static and non-static callables, as well as all classifiers. Java classes need to
* be handled specially, because [declaredMemberScope] doesn't handle Java enhancement properly.
*/
private fun getCombinedFirKotlinDeclaredMemberScope(symbolWithMembers: KtSymbolWithMembers): FirContainingNamesAwareScope {
val useSiteSession = analysisSession.useSiteSession
return when (symbolWithMembers) {
is KtFirScriptSymbol -> FirScriptDeclarationsScope(useSiteSession, symbolWithMembers.firSymbol.fir)
else -> useSiteSession.declaredMemberScope(symbolWithMembers.getFirForScope(), memberRequiredPhase = null)
}
}
override fun getDelegatedMemberScope(classSymbol: KtSymbolWithMembers): KtScope {
val declaredScope = (getDeclaredMemberScope(classSymbol) as? KtFirDelegatingNamesAwareScope)?.firScope ?: return getEmptyScope()
@@ -276,30 +333,6 @@ internal class KtFirScopeProvider(
val syntheticPropertiesScope = getFirSyntheticPropertiesScope(coneType, this) ?: return this
return FirTypeScopeWithSyntheticProperties(typeScope = this, syntheticPropertiesScope)
}
private fun buildJavaEnhancementDeclaredMemberScope(
useSiteSession: FirSession,
symbol: FirRegularClassSymbol,
scopeSession: ScopeSession,
): JavaClassDeclaredMembersEnhancementScope {
return scopeSession.getOrBuild(symbol, JAVA_ENHANCEMENT_FOR_DECLARED_MEMBER) {
val firJavaClass = symbol.fir
require(firJavaClass is FirJavaClass) {
"${firJavaClass.classId} is expected to be FirJavaClass, but ${firJavaClass::class} found"
}
JavaClassDeclaredMembersEnhancementScope(
useSiteSession,
firJavaClass,
JavaScopeProvider.getUseSiteMemberScope(
firJavaClass,
useSiteSession,
scopeSession,
memberRequiredPhase = FirResolvePhase.TYPES,
)
)
}
}
}
private class FirTypeScopeWithSyntheticProperties(
@@ -315,4 +348,6 @@ private class FirTypeScopeWithSyntheticProperties(
}
}
private val JAVA_ENHANCEMENT_FOR_DECLARED_MEMBER = scopeSessionKey<FirRegularClassSymbol, JavaClassDeclaredMembersEnhancementScope>()
private val JAVA_ENHANCEMENT_FOR_DECLARED_MEMBERS = scopeSessionKey<FirRegularClassSymbol, FirContainingNamesAwareScope>()
private val JAVA_ENHANCEMENT_FOR_STATIC_DECLARED_MEMBERS = scopeSessionKey<FirRegularClassSymbol, FirContainingNamesAwareScope>()
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.name.SpecialNames
internal class JavaClassDeclaredMembersEnhancementScope(
private val useSiteSession: FirSession,
private val owner: FirJavaClass,
private val useSiteMemberEnhancementScope: FirTypeScope
private val useSiteMemberEnhancementScope: FirContainingNamesAwareScope
) : FirContainingNamesAwareScope() {
private fun FirCallableDeclaration.isDeclared(): Boolean {
return (this.dispatchReceiverType as? ConeLookupTagBasedType)?.lookupTag == owner.symbol.toLookupTag()
@@ -80,12 +80,4 @@ internal class JavaClassDeclaredMembersEnhancementScope(
override fun toString(): String {
return "Java enhancement declared member scope for ${owner.classId}"
}
private fun FirCallableDeclaration.overriddenMembers(): List<FirCallableDeclaration> {
return when (val symbol = this.symbol) {
is FirNamedFunctionSymbol -> useSiteMemberEnhancementScope.getDirectOverriddenMembers(symbol)
is FirPropertySymbol -> useSiteMemberEnhancementScope.getDirectOverriddenProperties(symbol)
else -> emptyList()
}.map { it.fir }
}
}
}
@@ -24,6 +24,10 @@ public abstract class KtScopeProvider : KtAnalysisSessionComponent() {
public abstract fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope
public abstract fun getStaticDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope
public abstract fun getCombinedDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope
public abstract fun getDelegatedMemberScope(classSymbol: KtSymbolWithMembers): KtScope
public abstract fun getStaticMemberScope(symbol: KtSymbolWithMembers): KtScope
@@ -56,9 +60,34 @@ public interface KtScopeProviderMixIn : KtAnalysisSessionMixIn {
public fun KtSymbolWithMembers.getMemberScope(): KtScope =
withValidityAssertion { analysisSession.scopeProvider.getMemberScope(this) }
/**
* Returns a [KtScope] containing the *non-static* callables and all classifiers explicitly declared in the given [KtSymbolWithMembers].
*
* @see getStaticDeclaredMemberScope
*/
public fun KtSymbolWithMembers.getDeclaredMemberScope(): KtScope =
withValidityAssertion { analysisSession.scopeProvider.getDeclaredMemberScope(this) }
/**
* Returns a [KtScope] containing the *static* members explicitly declared in the given [KtSymbolWithMembers].
*
* It is worth noting that, while Java classes may contain declarations of static callables freely, in Kotlin only enum classes define
* static callables. Hence, for non-enum Kotlin classes, it is not expected that the static declared member scope will contain any
* callables.
*
* @see getDeclaredMemberScope
*/
public fun KtSymbolWithMembers.getStaticDeclaredMemberScope(): KtScope =
withValidityAssertion { analysisSession.scopeProvider.getStaticDeclaredMemberScope(this) }
/**
* Returns a [KtScope] containing *all* members explicitly declared in the given [KtSymbolWithMembers].
*
* In contrast to [getDeclaredMemberScope] and [getStaticDeclaredMemberScope], this scope contains both static and non-static members.
*/
public fun KtSymbolWithMembers.getCombinedDeclaredMemberScope(): KtScope =
withValidityAssertion { analysisSession.scopeProvider.getCombinedDeclaredMemberScope(this) }
public fun KtSymbolWithMembers.getDelegatedMemberScope(): KtScope =
withValidityAssertion { analysisSession.scopeProvider.getDelegatedMemberScope(this) }
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2023 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.fir.scopes.impl
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirDelegatingContainingNamesAwareScope
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.Name
/**
* A counterpart to [FirStaticScope] that contains only the *non-static* callables and all classifiers of the [delegate] scope.
*/
class FirNonStaticCallablesScope(private val delegate: FirContainingNamesAwareScope) : FirDelegatingContainingNamesAwareScope(delegate) {
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
delegate.processFunctionsByName(name) {
if (!it.fir.isStatic) {
processor(it)
}
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
delegate.processPropertiesByName(name) {
if (!it.fir.isStatic) {
processor(it)
}
}
}
}