[Analysis API] Add source shadowing for resolve extensions.

KtResolveExtensions are designed to handle IDE analysis use cases where
source might not be available at analysis time, because that source is
generated by an external source generator, such as an annotation
processor or resource compiler. The sources generated by those external
generators can appear in the analysis scope, and cause issues with
source clash - resolution may find the virtual source from the
KtResolveExtension, the on-disk generated source from the external
generator, or both. This can cause issues, because that on-disk
generated source may be stale, and may not have symbols that will exist
the next time the generator is run (or, conversely, may have symbols
that will disappear on the next build).

To solve this, add a `getShadowedScope(): GlobalSearchScope` to
`KtResolveExtension`. Any files in the module that are included in that
scope will be hidden from resolution, allowing the resolve extension to
cleanly replace those files.

^KT-58834 fixed
This commit is contained in:
Justin Paupore
2023-05-22 17:59:47 -07:00
committed by Ilya Kirillov
parent 7c87eb098d
commit f590e88bdd
30 changed files with 357 additions and 82 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.descriptors
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.components.*
@@ -61,7 +62,8 @@ class KtFe10AnalysisSession(
override val importOptimizerImpl: KtImportOptimizer = KtFe10ImportOptimizer(this)
override val jvmTypeMapperImpl: KtJvmTypeMapper = KtFe10JvmTypeMapper(this)
override val symbolInfoProviderImpl: KtSymbolInfoProvider = KtFe10SymbolInfoProvider(this)
override val analysisScopeProviderImpl: KtAnalysisScopeProvider = KtAnalysisScopeProviderImpl(this, token)
override val analysisScopeProviderImpl: KtAnalysisScopeProvider =
KtAnalysisScopeProviderImpl(this, token, shadowedScope = GlobalSearchScope.EMPTY_SCOPE)
override val referenceResolveProviderImpl: KtReferenceResolveProvider = KtFe10ReferenceResolveProvider(this)
override val signatureSubstitutorImpl: KtSignatureSubstitutor = KtFe10SignatureSubstitutor(this)
override val scopeSubstitutionImpl: KtScopeSubstitution = KtFe10ScopeSubstitution(this)
@@ -114,7 +114,7 @@ private constructor(
override val typesCreatorImpl: KtTypeCreator = KtFirTypeCreator(this, token)
override val analysisScopeProviderImpl: KtAnalysisScopeProvider = KtAnalysisScopeProviderImpl(this, token)
override val analysisScopeProviderImpl: KtAnalysisScopeProvider
override val referenceResolveProviderImpl: KtReferenceResolveProvider = KtFirReferenceResolveProvider(this)
@@ -153,9 +153,10 @@ private constructor(
internal val firSymbolProvider: FirSymbolProvider get() = useSiteSession.symbolProvider
internal val targetPlatform: TargetPlatform get() = useSiteSession.moduleData.platform
val useSiteAnalysisScope: GlobalSearchScope = analysisScopeProviderImpl.getAnalysisScope()
val extensionTools: List<LLFirResolveExtensionTool>
val useSiteAnalysisScope: GlobalSearchScope
val useSiteScopeDeclarationProvider: KotlinDeclarationProvider
val useSitePackageProvider: KotlinPackageProvider
@@ -167,6 +168,20 @@ private constructor(
firResolveSession.getSessionFor(dependency).llResolveExtensionTool
}
}
val shadowedScope = GlobalSearchScope.union(
buildSet {
// Add an empty scope to the shadowed set to give GlobalSearchScope.union something
// to work with if there are no extension tools.
// If there are extension tools, any empty scopes, whether from shadowedSearchScope
// on the extension tools or from this add() call, will be ignored.
add(GlobalSearchScope.EMPTY_SCOPE)
extensionTools.mapTo(this) { it.shadowedSearchScope }
}
)
analysisScopeProviderImpl = KtAnalysisScopeProviderImpl(this, token, shadowedScope)
useSiteAnalysisScope = analysisScopeProviderImpl.getAnalysisScope()
useSiteScopeDeclarationProvider = CompositeKotlinDeclarationProvider.create(
buildList {
add(project.createDeclarationProvider(useSiteAnalysisScope, useSiteModule))
@@ -67,6 +67,24 @@ public class FirIdeNormalAnalysisSourceModuleMultiModuleReferenceResolveWithReso
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/extensionFunction.kt");
}
@Test
@TestMetadata("shadowedDeclaration.kt")
public void testShadowedDeclaration() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/shadowedDeclaration.kt");
}
@Test
@TestMetadata("shadowedJava.kt")
public void testShadowedJava() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/shadowedJava.kt");
}
@Test
@TestMetadata("shadowedOverload.kt")
public void testShadowedOverload() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/shadowedOverload.kt");
}
@Test
@TestMetadata("topLevelFunction.kt")
public void testTopLevelFunction() throws Exception {
@@ -58,6 +58,24 @@ public class FirIdeNormalAnalysisSourceModuleSingleModuleReferenceResolveWithRes
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/extensionFunction.kt");
}
@Test
@TestMetadata("shadowedDeclaration.kt")
public void testShadowedDeclaration() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/shadowedDeclaration.kt");
}
@Test
@TestMetadata("shadowedJava.kt")
public void testShadowedJava() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/shadowedJava.kt");
}
@Test
@TestMetadata("shadowedOverload.kt")
public void testShadowedOverload() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/shadowedOverload.kt");
}
@Test
@TestMetadata("topLevelFunction.kt")
public void testTopLevelFunction() throws Exception {
@@ -23,7 +23,8 @@ import org.jetbrains.kotlin.psi.psiUtil.contains
class KtAnalysisScopeProviderImpl(
override val analysisSession: KtAnalysisSession,
override val token: KtLifetimeToken
override val token: KtLifetimeToken,
private val shadowedScope: GlobalSearchScope
) : KtAnalysisScopeProvider() {
private val baseResolveScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
@@ -31,13 +32,13 @@ class KtAnalysisScopeProviderImpl(
}
private val resolveScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
KtAnalysisScopeProviderResolveScope(baseResolveScope, analysisSession.useSiteModule)
KtAnalysisScopeProviderResolveScope(baseResolveScope, analysisSession.useSiteModule, shadowedScope)
}
override fun getAnalysisScope(): GlobalSearchScope = resolveScope
override fun canBeAnalysed(psi: PsiElement): Boolean {
return baseResolveScope.contains(psi)
return (baseResolveScope.contains(psi) && !shadowedScope.contains(psi))
|| psi.isFromGeneratedModule()
}
@@ -49,12 +50,17 @@ class KtAnalysisScopeProviderImpl(
private class KtAnalysisScopeProviderResolveScope(
private val base: GlobalSearchScope,
private val useSiteModule: KtModule
private val useSiteModule: KtModule,
private val shadowed: GlobalSearchScope,
) : GlobalSearchScope() {
override fun getProject(): Project? = base.project
override fun isSearchInModuleContent(aModule: Module): Boolean = base.isSearchInModuleContent(aModule)
override fun isSearchInLibraries(): Boolean = base.isSearchInLibraries
override fun contains(file: VirtualFile): Boolean = base.contains(file) || file.isFromGeneratedModule(useSiteModule)
override fun contains(file: VirtualFile): Boolean =
(base.contains(file) && !shadowed.contains(file)) || file.isFromGeneratedModule(useSiteModule)
override fun toString() =
"Analysis scope for $useSiteModule (base: $base, shadowed: $shadowed)"
}
@OptIn(KtModuleStructureInternals::class)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtMultiModuleResolveExtensionProviderForTest
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
@@ -12,6 +13,12 @@ import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.name.FqName
abstract class AbstractMultiModuleReferenceResolveWithResolveExtensionTest : AbstractReferenceResolveWithResolveExtensionTest() {
override fun createResolveExtensionProvider(files: List<KtResolveExtensionFile>, packages: Set<FqName>): KtResolveExtensionProvider =
KtMultiModuleResolveExtensionProviderForTest(files, packages) { it is KtSourceModule && it.moduleName == "extendedModule" }
override fun createResolveExtensionProvider(
files: List<KtResolveExtensionFile>,
packages: Set<FqName>,
shadowedScope: GlobalSearchScope,
): KtResolveExtensionProvider =
KtMultiModuleResolveExtensionProviderForTest(files, packages, shadowedScope) { module ->
module is KtSourceModule && module.moduleName == "extendedModule"
}
}
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references
import com.intellij.openapi.module.Module
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtResolveExtensionFileForTests
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtResolveExtensionProviderForTestPreAnalysisHandler
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
@@ -17,17 +20,22 @@ abstract class AbstractReferenceResolveWithResolveExtensionTest : AbstractRefere
abstract fun createResolveExtensionProvider(
files: List<KtResolveExtensionFile>,
packages: Set<FqName>,
shadowedScope: GlobalSearchScope,
): KtResolveExtensionProvider
override fun configureTest(builder: TestConfigurationBuilder) {
super.configureTest(builder)
val provider = createResolveExtensionProvider(
listOf(
files = listOf(
KtResolveExtensionFileForTests(
"extension1.kt",
packageName = FqName("generated"),
topLevelClassifiersNames = setOf("GeneratedClass1"),
topLevelCallableNames = setOf("generatedTopLevelFunction1", "generatedTopLevelExtensionFunction1"),
topLevelCallableNames = setOf(
"generatedTopLevelFunction1",
"generatedTopLevelExtensionFunction1",
"generatedOverloadedExtensionFunction",
),
fileText = """|package generated
|
|class GeneratedClass1 {
@@ -37,6 +45,8 @@ abstract class AbstractReferenceResolveWithResolveExtensionTest : AbstractRefere
|fun generatedTopLevelFunction1(): GeneratedClass2
|
|fun String.generatedTopLevelExtensionFunction1(boolean: Boolean): Int
|
|fun Any.generatedOverloadedExtensionFunction(): Int
""".trimMargin()
),
KtResolveExtensionFileForTests(
@@ -52,7 +62,14 @@ abstract class AbstractReferenceResolveWithResolveExtensionTest : AbstractRefere
""".trimMargin(),
)
),
setOf(FqName("generated"))
packages = setOf(FqName("generated")),
shadowedScope = object : GlobalSearchScope() {
override fun contains(file: VirtualFile): Boolean = ".hidden." in file.name
override fun isSearchInModuleContent(aModule: Module): Boolean = false
override fun isSearchInLibraries(): Boolean = false
}
)
with(builder) {
usePreAnalysisHandlers(::KtResolveExtensionProviderForTestPreAnalysisHandler.bind(listOf(provider)))
@@ -5,12 +5,17 @@
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtSingleModuleResolveExtensionProviderForTest
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
import org.jetbrains.kotlin.name.FqName
abstract class AbstractSingleModuleReferenceResolveWithResolveExtensionTest : AbstractReferenceResolveWithResolveExtensionTest() {
override fun createResolveExtensionProvider(files: List<KtResolveExtensionFile>, packages: Set<FqName>): KtResolveExtensionProvider =
KtSingleModuleResolveExtensionProviderForTest(files, packages)
override fun createResolveExtensionProvider(
files: List<KtResolveExtensionFile>,
packages: Set<FqName>,
shadowedScope: GlobalSearchScope,
): KtResolveExtensionProvider =
KtSingleModuleResolveExtensionProviderForTest(files, packages, shadowedScope)
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.util
import com.intellij.mock.MockProject
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
@@ -25,20 +26,22 @@ import org.jetbrains.kotlin.test.services.TestServices
class KtSingleModuleResolveExtensionProviderForTest(
private val files: List<KtResolveExtensionFile>,
private val packages: Set<FqName>,
private val shadowedScope: GlobalSearchScope,
) : KtResolveExtensionProvider() {
override fun provideExtensionsFor(module: KtModule): List<KtResolveExtension> {
return listOf(KtResolveExtensionForTest(files, packages))
return listOf(KtResolveExtensionForTest(files, packages, shadowedScope))
}
}
class KtMultiModuleResolveExtensionProviderForTest(
private val files: List<KtResolveExtensionFile>,
private val packages: Set<FqName>,
private val shadowedScope: GlobalSearchScope,
private val hasResolveExtension: (KtModule) -> Boolean,
) : KtResolveExtensionProvider() {
override fun provideExtensionsFor(module: KtModule): List<KtResolveExtension> {
if (!hasResolveExtension(module)) return emptyList()
return listOf(KtResolveExtensionForTest(files, packages))
return listOf(KtResolveExtensionForTest(files, packages, shadowedScope))
}
}
@@ -59,10 +62,12 @@ class KtResolveExtensionProviderForTestPreAnalysisHandler(
class KtResolveExtensionForTest(
private val files: List<KtResolveExtensionFile>,
private val packages: Set<FqName>,
private val shadowedScope: GlobalSearchScope,
) : KtResolveExtension() {
override fun getKtFiles(): List<KtResolveExtensionFile> = files
override fun getModificationTracker(): ModificationTracker = ModificationTracker.NEVER_CHANGED
override fun getContainedPackages(): Set<FqName> = packages
override fun getShadowedScope(): GlobalSearchScope = shadowedScope
}
class KtResolveExtensionFileForTests(
@@ -67,6 +67,24 @@ public class FirStandaloneNormalAnalysisSourceModuleMultiModuleReferenceResolveW
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/extensionFunction.kt");
}
@Test
@TestMetadata("shadowedDeclaration.kt")
public void testShadowedDeclaration() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/shadowedDeclaration.kt");
}
@Test
@TestMetadata("shadowedJava.kt")
public void testShadowedJava() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/shadowedJava.kt");
}
@Test
@TestMetadata("shadowedOverload.kt")
public void testShadowedOverload() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/multiModule/referenceResolve/extendedModuleDependency/shadowedOverload.kt");
}
@Test
@TestMetadata("topLevelFunction.kt")
public void testTopLevelFunction() throws Exception {
@@ -58,6 +58,24 @@ public class FirStandaloneNormalAnalysisSourceModuleSingleModuleReferenceResolve
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/extensionFunction.kt");
}
@Test
@TestMetadata("shadowedDeclaration.kt")
public void testShadowedDeclaration() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/shadowedDeclaration.kt");
}
@Test
@TestMetadata("shadowedJava.kt")
public void testShadowedJava() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/shadowedJava.kt");
}
@Test
@TestMetadata("shadowedOverload.kt")
public void testShadowedOverload() throws Exception {
runTest("analysis/analysis-api/testData/resolveExtensions/referenceResolve/shadowedOverload.kt");
}
@Test
@TestMetadata("topLevelFunction.kt")
public void testTopLevelFunction() throws Exception {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.resolve.extensions
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.name.FqName
/**
@@ -53,4 +54,17 @@ public abstract class KtResolveExtension {
* @see KtResolveExtension
*/
public abstract fun getContainedPackages(): Set<FqName>
/**
* Returns the scope of files that should be shadowed by the files provided by [getKtFiles].
*
* Any files in the module that are included in this scope will be removed from analysis results. This allows the files provided by
* [getKtFiles] to cleanly replace those files from the module.
*
* If this resolve extension is being used to generate declarations that would normally be provided by sources generated by an external
* build task, such as a resource compiler or annotation processor, the resolve extension should provide a scope here that covers those
* externally generated sources. This will prevent collisions between the definitions provided by [getKtFiles] and those provided by the
* (potentially stale) externally generated sources.
*/
public open fun getShadowedScope(): GlobalSearchScope = GlobalSearchScope.EMPTY_SCOPE
}
@@ -0,0 +1,15 @@
// UNRESOLVED_REFERENCE
// MODULE: extendedModule
// FILE: declarations.hidden.kt
package foo
fun bar() = "baz"
// MODULE: dependency2
// MODULE: main(extendedModule, dependency2)()()
// FILE: main.kt
fun main() {
val x = foo.<caret>bar()
}
@@ -0,0 +1,19 @@
// UNRESOLVED_REFERENCE
// MODULE: extendedModule
// FILE: TestClass.hidden.java
package foo;
public class TestClass {
public TestClass() {}
}
// MODULE: dependency2
// MODULE: main(extendedModule, dependency2)()()
// FILE: main.kt
package foo
fun main() {
val x = <caret>TestClass()
}
@@ -0,0 +1,15 @@
// MODULE: extendedModule
// FILE: generated.hidden.kt
package generated
fun String.generatedOverloadedExtensionFunction(): Int = TODO()
// MODULE: dependency2
// MODULE: main(extendedModule, dependency2)()()
// FILE: main.kt
import generated.*
fun main() {
"string".generatedOverloadedExtension<caret>Function()
}
@@ -0,0 +1,2 @@
Resolved to:
0: (in generated) fun kotlin.Any.generatedOverloadedExtensionFunction(): kotlin.Int
@@ -0,0 +1,11 @@
// UNRESOLVED_REFERENCE
// FILE: declarations.hidden.kt
package foo
fun bar() = "baz"
// FILE: main.kt
fun main() {
val x = foo.<caret>bar()
}
@@ -0,0 +1,15 @@
// UNRESOLVED_REFERENCE
// FILE: TestClass.hidden.java
package foo;
public class TestClass {
public TestClass() {}
}
// FILE: main.kt
package foo
fun main() {
val x = <caret>TestClass()
}
@@ -0,0 +1,11 @@
// FILE: generated.hidden.kt
package generated
fun String.generatedOverloadedExtensionFunction(): Int = TODO()
// FILE: main.kt
import generated.*
fun main() {
"string".generatedOverloadedExtension<caret>Function()
}
@@ -0,0 +1,2 @@
Resolved to:
0: (in generated) fun kotlin.Any.generatedOverloadedExtensionFunction(): kotlin.Int
@@ -92,15 +92,3 @@ internal fun LLFirSourcesSession.registerCompilerPluginServices(
)
}
internal fun createJavaSymbolProvider(
firSession: FirSession,
moduleData: LLFirModuleData,
project: Project,
contentScope: GlobalSearchScope
): JavaSymbolProvider {
return JavaSymbolProvider(
firSession,
FirJavaFacadeForSource(firSession, moduleData, project.createJavaClassFinder(contentScope))
)
}
@@ -90,9 +90,9 @@ internal class LLFirCombinedJavaSymbolProvider private constructor(
override fun getPackage(fqName: FqName): FqName? = providers.firstNotNullOfOrNull { it.getPackage(fqName) }
companion object {
fun merge(session: FirSession, project: Project, providers: List<JavaSymbolProvider>): FirSymbolProvider? =
fun merge(session: FirSession, project: Project, providers: List<LLFirJavaSymbolProvider>): FirSymbolProvider? =
if (providers.size > 1) {
val combinedScope = GlobalSearchScope.union(providers.map { it.session.llFirModuleData.ktModule.contentScope })
val combinedScope = GlobalSearchScope.union(providers.map { it.searchScope })
val javaClassFinder = project.createJavaClassFinder(combinedScope)
LLFirCombinedJavaSymbolProvider(session, project, providers, javaClassFinder)
} else providers.singleOrNull()
@@ -0,0 +1,24 @@
/*
* 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.analysis.low.level.api.fir.providers
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.java.FirJavaFacadeForSource
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
import org.jetbrains.kotlin.load.java.createJavaClassFinder
internal class LLFirJavaSymbolProvider(
firSession: FirSession,
moduleData: LLFirModuleData,
project: Project,
val searchScope: GlobalSearchScope
) : JavaSymbolProvider(
firSession,
FirJavaFacadeForSource(firSession, moduleData, project.createJavaClassFinder(searchScope))
)
@@ -5,11 +5,11 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.SyntheticFirClassProvider
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
@@ -29,22 +29,23 @@ import org.jetbrains.kotlin.psi.KtProperty
@ThreadSafeMutableState
internal class LLFirProvider(
val session: FirSession,
val session: LLFirSession,
private val moduleComponents: LLFirModuleResolveComponents,
private val declarationProvider: KotlinDeclarationProvider,
packageProvider: KotlinPackageProvider,
canContainKotlinPackage: Boolean,
declarationProviderFactory: (GlobalSearchScope) -> KotlinDeclarationProvider?,
) : FirProvider() {
override val symbolProvider: FirSymbolProvider = SymbolProvider()
private val providerHelper = LLFirProviderHelper(
session,
moduleComponents.firFileBuilder,
declarationProvider,
packageProvider,
canContainKotlinPackage,
declarationProviderFactory,
)
val searchScope: GlobalSearchScope
get() = providerHelper.searchScope
override val isPhasedFirAllowed: Boolean get() = true
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration? =
@@ -103,9 +104,7 @@ internal class LLFirProvider(
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> = error("Should not be called in FIR IDE")
override fun getClassNamesInPackage(fqName: FqName): Set<Name> =
declarationProvider.getTopLevelKotlinClassLikeDeclarationNamesInPackage(fqName)
override fun getClassNamesInPackage(fqName: FqName): Set<Name> = providerHelper.getTopLevelClassNamesInPackage(fqName)
@NoMutableState
internal inner class SymbolProvider : LLFirKotlinSymbolProvider(session) {
@@ -5,18 +5,19 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.CompositeKotlinPackageProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.LLFirResolveExtensionTool
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.llResolveExtensionTool
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirElementFinder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirKotlinSymbolNamesProvider
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider
import org.jetbrains.kotlin.analysis.providers.createPackageProvider
import org.jetbrains.kotlin.analysis.providers.impl.declarationProviders.CompositeKotlinDeclarationProvider
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
@@ -35,27 +36,37 @@ import org.jetbrains.kotlin.psi.KtClassLikeDeclaration
import org.jetbrains.kotlin.psi.KtFile
internal class LLFirProviderHelper(
firSession: FirSession,
firSession: LLFirSession,
private val firFileBuilder: LLFirFileBuilder,
mainDeclarationProvider: KotlinDeclarationProvider,
mainPackageProvider: KotlinPackageProvider,
canContainKotlinPackage: Boolean,
declarationProviderFactory: (GlobalSearchScope) -> KotlinDeclarationProvider?
) {
private val extensionTool: LLFirResolveExtensionTool? = firSession.llResolveExtensionTool
val searchScope: GlobalSearchScope =
firSession.ktModule.contentScope.run {
val notShadowedScope = extensionTool?.shadowedSearchScope?.let { GlobalSearchScope.notScope(it) }
if (notShadowedScope != null) {
this.intersectWith(notShadowedScope)
} else {
this
}
}
val declarationProvider = CompositeKotlinDeclarationProvider.create(
listOfNotNull(
mainDeclarationProvider,
declarationProviderFactory(searchScope),
extensionTool?.declarationProvider,
)
)
private val packageProvider = CompositeKotlinPackageProvider.create(
listOfNotNull(
mainPackageProvider,
firSession.project.createPackageProvider(searchScope),
extensionTool?.packageProvider,
)
)
private val allowKotlinPackage = canContainKotlinPackage ||
firSession.languageVersionSettings.getFlag(AnalysisFlags.allowKotlinPackage)
@@ -100,6 +111,11 @@ internal class LLFirProviderHelper(
return classifierByClassId.getValue(classId, classLikeDeclaration)
}
fun getTopLevelClassNamesInPackage(packageFqName: FqName): Set<Name> {
if (!allowKotlinPackage && packageFqName.isKotlinPackage()) return emptySet()
return declarationProvider.getTopLevelKotlinClassLikeDeclarationNamesInPackage(packageFqName)
}
fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
if (!allowKotlinPackage && packageFqName.isKotlinPackage()) return emptyList()
val callableId = CallableId(packageFqName, name)
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.ModificationTracker
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.KtAnalysisAllowanceManager
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
@@ -40,6 +41,7 @@ abstract class LLFirResolveExtensionTool : FirSessionComponent {
abstract val declarationProvider: LLFirResolveExtensionToolDeclarationProvider
abstract val packageProvider: KotlinPackageProvider
abstract val packageFilter: LLFirResolveExtensionToolPackageFilter
abstract val shadowedSearchScope: GlobalSearchScope
internal abstract val symbolNamesProvider: FirSymbolNamesProvider
}
@@ -57,13 +59,19 @@ internal class LLFirNonEmptyResolveExtensionTool(
override val packageFilter = LLFirResolveExtensionToolPackageFilter(extensions)
override val modificationTrackers by lazy { extensions.map { it.getModificationTracker() } }
override val modificationTrackers by lazy { forbidAnalysis { extensions.map { it.getModificationTracker() } } }
override val declarationProvider: LLFirResolveExtensionToolDeclarationProvider =
LLFirResolveExtensionToolDeclarationProvider(fileProvider, session.ktModule)
override val packageProvider: KotlinPackageProvider = LLFirResolveExtensionToolPackageProvider(packageFilter)
override val shadowedSearchScope by lazy {
forbidAnalysis {
GlobalSearchScope.union(extensions.mapTo(mutableSetOf()) { it.getShadowedScope() })
}
}
override val symbolNamesProvider: FirSymbolNamesProvider = LLFirResolveExtensionToolSymbolNamesProvider(packageFilter, fileProvider)
}
@@ -310,7 +318,7 @@ internal class LLFirResolveExtensionsFileProvider(
.filter { it.getFilePackageName() == packageFqName }
}
fun getAllFiles(): Sequence<KtResolveExtensionFile> {
fun getAllFiles(): Sequence<KtResolveExtensionFile> = forbidAnalysis {
return extensions
.asSequence()
.flatMap { it.getKtFiles() }
@@ -15,11 +15,11 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveCompone
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.*
import org.jetbrains.kotlin.analysis.project.structure.*
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.createAnnotationResolver
import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.createPackageProvider
import org.jetbrains.kotlin.analysis.providers.impl.declarationProviders.EmptyKotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.impl.declarationProviders.FileBasedKotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.impl.util.mergeInto
import org.jetbrains.kotlin.analysis.utils.trackers.CompositeModificationTracker
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
@@ -48,7 +48,6 @@ import org.jetbrains.kotlin.scripting.compiler.plugin.FirScriptingSamWithReceive
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import org.jetbrains.kotlin.analysis.providers.impl.util.mergeInto
@OptIn(PrivateSessionConstructor::class, SessionConfiguration::class)
internal abstract class LLFirAbstractSessionFactory(protected val project: Project) {
@@ -66,7 +65,6 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
val scopeProvider = FirKotlinScopeProvider(::wrapScopeWithJvmMapped)
val components = LLFirModuleResolveComponents(module, globalResolveComponents, scopeProvider)
val contentScope = module.contentScope
val dependencies = collectSourceModuleDependencies(module)
val dependencyTracker = createSourceModuleDependencyTracker(module, dependencies)
@@ -90,10 +88,10 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
val provider = LLFirProvider(
this,
components,
FileBasedKotlinDeclarationProvider(module.file),
project.createPackageProvider(contentScope),
canContainKotlinPackage = true,
)
) { scope ->
scope.createScopedDeclarationProviderForFile(module.file)
}
register(FirProvider::class, provider)
register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver())
@@ -103,7 +101,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
add(builtinsSession.symbolProvider)
})
val javaSymbolProvider = createJavaSymbolProvider(this, moduleData, project, contentScope)
val javaSymbolProvider = LLFirJavaSymbolProvider(this, moduleData, project, provider.searchScope)
register(JavaSymbolProvider::class, javaSymbolProvider)
register(
@@ -170,10 +168,10 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
val provider = LLFirProvider(
this,
components,
if (ktFile != null) FileBasedKotlinDeclarationProvider(ktFile) else EmptyKotlinDeclarationProvider,
project.createPackageProvider(module.contentScope),
canContainKotlinPackage = true,
)
) { scope ->
ktFile?.let { scope.createScopedDeclarationProviderForFile(it) }
}
register(FirProvider::class, provider)
register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver())
@@ -226,8 +224,6 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
components.session = session
val moduleData = createModuleData(session)
val contentScope = module.contentScope
return session.apply {
registerModuleData(moduleData)
@@ -240,16 +236,16 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
val firProvider = LLFirProvider(
this,
components,
project.createDeclarationProvider(contentScope, module),
project.createPackageProvider(contentScope),
/* Source modules can contain `kotlin` package only if `-Xallow-kotlin-package` is specified, this is handled in LLFirProvider */
canContainKotlinPackage = false,
)
) { scope ->
project.createDeclarationProvider(scope, module)
}
register(FirProvider::class, firProvider)
register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver())
registerCompilerPluginServices(contentScope, project, module)
registerCompilerPluginServices(firProvider.searchScope, project, module)
registerCompilerPluginExtensions(project, module)
registerCommonComponentsAfterExtensionsAreConfigured()
@@ -273,7 +269,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
}
val context = SourceSessionCreationContext(
moduleData, contentScope, firProvider, dependencyProvider, syntheticFunctionInterfaceProvider,
moduleData, firProvider.searchScope, firProvider, dependencyProvider, syntheticFunctionInterfaceProvider,
switchableExtensionDeclarationsSymbolProvider,
)
additionalSessionConfiguration(context)
@@ -320,22 +316,20 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
registerCommonComponentsAfterExtensionsAreConfigured()
registerResolveComponents()
val contentScope = module.contentScope
val firProvider = LLFirProvider(
this,
components,
project.createDeclarationProvider(contentScope, module),
project.createPackageProvider(contentScope),
canContainKotlinPackage = true,
)
) { scope ->
project.createDeclarationProvider(scope, module)
}
register(FirProvider::class, firProvider)
register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver())
// We need FirRegisteredPluginAnnotations during extensions' registration process
val annotationsResolver = project.createAnnotationResolver(contentScope)
val annotationsResolver = project.createAnnotationResolver(firProvider.searchScope)
register(FirRegisteredPluginAnnotations::class, LLFirIdeRegisteredPluginAnnotations(this, annotationsResolver))
register(FirPredicateBasedProvider::class, FirEmptyPredicateBasedProvider)
@@ -360,7 +354,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
register(DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY, dependencyProvider)
register(LLFirFirClassByPsiClassProvider::class, LLFirFirClassByPsiClassProvider(this))
val context = LibrarySessionCreationContext(moduleData, contentScope, firProvider, dependencyProvider)
val context = LibrarySessionCreationContext(moduleData, firProvider.searchScope, firProvider, dependencyProvider)
additionalSessionConfiguration(context)
LLFirSessionConfigurator.configure(this)
@@ -504,8 +498,21 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
) {
mergeInto(destination) {
merge<LLFirProvider.SymbolProvider> { LLFirCombinedKotlinSymbolProvider.merge(session, project, it) }
merge<JavaSymbolProvider> { LLFirCombinedJavaSymbolProvider.merge(session, project, it) }
merge<LLFirJavaSymbolProvider> { LLFirCombinedJavaSymbolProvider.merge(session, project, it) }
merge<FirExtensionSyntheticFunctionInterfaceProvider> { LLFirCombinedSyntheticFunctionSymbolProvider.merge(session, it) }
}
}
/**
* Creates a single-file [KotlinDeclarationProvider] for the provided file, if it is in the search scope.
*
* Otherwise, returns `null`.
*/
private fun GlobalSearchScope.createScopedDeclarationProviderForFile(file: KtFile): KotlinDeclarationProvider? =
// KtFiles without a backing VirtualFile can't be covered by a shadow scope, and are thus assumed in-scope.
if (file.virtualFile == null || contains(file.virtualFile)) {
FileBasedKotlinDeclarationProvider(file)
} else {
null
}
}
@@ -25,7 +25,7 @@ internal class LLFirJvmSessionFactory(project: Project) : LLFirAbstractSessionFa
return doCreateSourcesSession(module, FirKotlinScopeProvider(::wrapScopeWithJvmMapped)) { context ->
registerCommonJavaComponents(JavaModuleResolver.getInstance(project))
registerJavaSpecificResolveComponents()
val javaSymbolProvider = createJavaSymbolProvider(this, context.moduleData, project, context.contentScope)
val javaSymbolProvider = LLFirJavaSymbolProvider(this, context.moduleData, project, context.contentScope)
register(JavaSymbolProvider::class, javaSymbolProvider)
register(
@@ -50,7 +50,7 @@ internal class LLFirJvmSessionFactory(project: Project) : LLFirAbstractSessionFa
return doCreateLibrarySession(module) { context ->
registerCommonJavaComponents(JavaModuleResolver.getInstance(project))
registerJavaSpecificResolveComponents()
val javaSymbolProvider = createJavaSymbolProvider(this, context.moduleData, project, context.contentScope)
val javaSymbolProvider = LLFirJavaSymbolProvider(this, context.moduleData, project, context.contentScope)
register(
FirSymbolProvider::class,
LLFirModuleWithDependenciesSymbolProvider(
@@ -13,7 +13,7 @@ import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.createJavaSymbolProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirJavaSymbolProvider
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider
@@ -56,7 +56,7 @@ class JvmStubBasedDeserializedSymbolProviderFactory : JvmFirDeserializedSymbolPr
FirDeclarationOrigin.Library
)
)
add(createJavaSymbolProvider(session, moduleData, project, scope))
add(LLFirJavaSymbolProvider(session, moduleData, project, scope))
}
}
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.name.Name
// For library and incremental compilation sessions use `KotlinDeserializedJvmSymbolsProvider`
// in order to load Kotlin classes as well.
//Also used in IDE for loading java classes separately from stub based kotlin classes
class JavaSymbolProvider(
open class JavaSymbolProvider(
session: FirSession,
private val javaFacade: FirJavaFacade,
) : FirSymbolProvider(session) {