From 71017298d9ca057da5319fdc70c54270788ff36c Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Wed, 13 Sep 2023 20:58:40 +0200 Subject: [PATCH] [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 --- .../components/KtFe10ScopeProvider.kt | 30 ++++- .../scopes/KtFe10ScopeResolution.kt | 18 +++ .../api/fir/components/KtFirScopeProvider.kt | 113 ++++++++++++------ ...avaClassDeclaredMembersEnhancementScope.kt | 12 +- .../api/components/KtScopeProvider.kt | 29 +++++ .../scopes/impl/FirNonStaticCallablesScope.kt | 34 ++++++ 6 files changed, 181 insertions(+), 55 deletions(-) create mode 100644 compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNonStaticCallablesScope.kt 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 c18926072e1..b1350ef281e 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 @@ -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(symbol) ?: return getEmptyScope() + return KtFe10ScopeMember(descriptor.staticScope, emptyList(), analysisContext) + } + override fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope { val descriptor = getDescriptor(classSymbol) ?: return getEmptyScope() + return KtFe10ScopeNonStaticMember(DeclaredMemberScope(descriptor), descriptor.constructors, analysisContext) + } + + override fun getStaticDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope { + val descriptor = getDescriptor(classSymbol) + ?: return getEmptyScope() + + return KtFe10ScopeMember( + DeclaredMemberScope(descriptor.staticScope, descriptor, forDelegatedMembersOnly = false), + emptyList(), + analysisContext, + ) + } + + override fun getCombinedDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtScope { + val descriptor = getDescriptor(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(symbol) ?: return getEmptyScope() - return KtFe10ScopeMember(descriptor.staticScope, emptyList(), analysisContext) - } - override fun getEmptyScope(): KtScope { return KtEmptyScope(token) } diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/scopes/KtFe10ScopeResolution.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/scopes/KtFe10ScopeResolution.kt index 4efe8b8fcb1..89824d79b11 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/scopes/KtFe10ScopeResolution.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/scopes/KtFe10ScopeResolution.kt @@ -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, + analysisContext: Fe10AnalysisContext +) : KtFe10ScopeMember(scope, constructors, analysisContext) { + override fun getCallableSymbols(nameFilter: KtScopeNameFilter): Sequence = 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 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 a3050284b5f..1e10b60e3f1 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 @@ -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() +private val JAVA_ENHANCEMENT_FOR_DECLARED_MEMBERS = scopeSessionKey() + +private val JAVA_ENHANCEMENT_FOR_STATIC_DECLARED_MEMBERS = scopeSessionKey() diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/scopes/JavaClassDeclaredMembersEnhancementScope.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/scopes/JavaClassDeclaredMembersEnhancementScope.kt index 828488fb34e..5344f40207e 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/scopes/JavaClassDeclaredMembersEnhancementScope.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/scopes/JavaClassDeclaredMembersEnhancementScope.kt @@ -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 { - return when (val symbol = this.symbol) { - is FirNamedFunctionSymbol -> useSiteMemberEnhancementScope.getDirectOverriddenMembers(symbol) - is FirPropertySymbol -> useSiteMemberEnhancementScope.getDirectOverriddenProperties(symbol) - else -> emptyList() - }.map { it.fir } - } -} \ No newline at end of file +} 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 9e1aa19a6fb..4d3ccefdf89 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 @@ -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) } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNonStaticCallablesScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNonStaticCallablesScope.kt new file mode 100644 index 00000000000..c44463d81b7 --- /dev/null +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNonStaticCallablesScope.kt @@ -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) + } + } + } +}