[AA] Standalone Analysis API: Introduce KtStaticProjectStructureProvider

- `registerServicesForProjectEnvironment` relied on lists of all modules
  and source files, which the standalone project structure providers
  would have already been able to provide with the right interface.
- In `StandaloneAnalysisAPISessionBuilder.build`, to get all modules and
  source files, `projectStructureProvider` had to be hard-casted to
  `KtModuleProviderImpl`.
- This commit introduces `KtStaticProjectStructureProvider`, a shared
  interface for project structure providers with static module
  structures that know about their modules and source files.
This commit is contained in:
Marco Pennekamp
2023-05-04 21:15:44 +02:00
committed by Space Team
parent e3d803de8b
commit 7fd8e4d0fd
9 changed files with 72 additions and 41 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.standalone.base.project.structure
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFileSystemItem
import org.jetbrains.kotlin.analysis.project.structure.*
import org.jetbrains.kotlin.psi.psiUtil.contains
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
@@ -14,10 +15,7 @@ import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
class KtStaticModuleProvider(
private val builtinsModule: KtBuiltinsModule,
private val projectStructure: KtModuleProjectStructure,
) : ProjectStructureProvider() {
val allModules: List<KtModule>
get() = projectStructure.allKtModules()
) : KtStaticProjectStructureProvider() {
@OptIn(KtModuleStructureInternals::class)
override fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule {
val containingFileAsPsiFile = element.containingFile
@@ -38,7 +36,11 @@ class KtStaticModuleProvider(
?: throw KotlinExceptionWithAttachments("Cannot find KtModule; see the attachment for more details.")
.withAttachment(
containingFileAsVirtualFile.path,
allModules.joinToString(separator = System.lineSeparator()) { it.asDebugString() }
allKtModules.joinToString(separator = System.lineSeparator()) { it.asDebugString() }
)
}
override val allKtModules: List<KtModule> = projectStructure.allKtModules()
override val allSourceFiles: List<PsiFileSystemItem> = projectStructure.allSourceFiles()
}
@@ -0,0 +1,28 @@
/*
* 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.api.standalone.base.project.structure
import com.intellij.psi.PsiFileSystemItem
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtNotUnderContentRootModule
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
/**
* A [ProjectStructureProvider] with a static module structure.
*
* Static project structure providers may still create [KtNotUnderContentRootModule]s on the fly, because files which don't belong to any
* of the pre-registered modules are by definition not part of the *static* module structure.
*/
public abstract class KtStaticProjectStructureProvider : ProjectStructureProvider() {
/**
* All [KtModule]s registered with the project structure provider, excluding [KtNotUnderContentRootModule]s and the built-ins module.
*
* [allKtModules] may be used by other services to pre-build caches based on the full module structure.
*/
public abstract val allKtModules: List<KtModule>
public abstract val allSourceFiles: List<PsiFileSystemItem>
}
@@ -162,9 +162,7 @@ object StandaloneProjectFactory {
fun registerServicesForProjectEnvironment(
environment: KotlinCoreProjectEnvironment,
projectStructureProvider: ProjectStructureProvider,
modules: List<KtModule>,
sourceFiles: List<PsiFileSystemItem>,
projectStructureProvider: KtStaticProjectStructureProvider,
languageVersionSettings: LanguageVersionSettings = latestLanguageVersionSettings,
jdkHome: Path? = null,
) {
@@ -181,7 +179,13 @@ object StandaloneProjectFactory {
}
project.registerService(ProjectStructureProvider::class.java, projectStructureProvider)
initialiseVirtualFileFinderServices(environment, modules, sourceFiles, languageVersionSettings, jdkHome)
initialiseVirtualFileFinderServices(
environment,
projectStructureProvider.allKtModules,
projectStructureProvider.allSourceFiles,
languageVersionSettings,
jdkHome,
)
}
private fun initialiseVirtualFileFinderServices(
@@ -12,16 +12,13 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
import com.intellij.psi.search.GlobalSearchScope
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.FirStandaloneServiceRegistrar
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticProjectStructureProvider
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.builder.KtModuleProviderBuilder
import org.jetbrains.kotlin.analysis.project.structure.builder.buildProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.impl.KtModuleProviderImpl
@@ -38,6 +35,9 @@ import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.psi.KtFile
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
public class StandaloneAnalysisAPISessionBuilder(
applicationDisposable: Disposable,
@@ -70,7 +70,7 @@ public class StandaloneAnalysisAPISessionBuilder(
public val project: Project = kotlinCoreProjectEnvironment.project
private lateinit var projectStructureProvider: ProjectStructureProvider
private lateinit var projectStructureProvider: KtStaticProjectStructureProvider
public fun buildKtModuleProvider(init: KtModuleProviderBuilder.() -> Unit) {
projectStructureProvider = buildProjectStructureProvider(init)
@@ -181,18 +181,16 @@ public class StandaloneAnalysisAPISessionBuilder(
public fun build(
withPsiDeclarationFromBinaryModuleProvider: Boolean = false,
): StandaloneAnalysisAPISession {
val ktModuleProviderImpl = projectStructureProvider as KtModuleProviderImpl
val modules = ktModuleProviderImpl.mainModules
val allSourceFiles = ktModuleProviderImpl.allSourceFiles()
StandaloneProjectFactory.registerServicesForProjectEnvironment(
kotlinCoreProjectEnvironment,
projectStructureProvider,
modules,
allSourceFiles,
)
val project = kotlinCoreProjectEnvironment.project
val ktFiles = allSourceFiles.filterIsInstance<KtFile>()
val libraryRoots = StandaloneProjectFactory.getAllBinaryRoots(modules, kotlinCoreProjectEnvironment)
val ktFiles = projectStructureProvider.allSourceFiles.filterIsInstance<KtFile>()
val libraryRoots = StandaloneProjectFactory.getAllBinaryRoots(
projectStructureProvider.allKtModules,
kotlinCoreProjectEnvironment,
)
val createPackagePartProvider =
StandaloneProjectFactory.createPackagePartsProvider(
project,
@@ -7,12 +7,12 @@ package org.jetbrains.kotlin.analysis.project.structure.builder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.impl.KtModuleProviderImpl
import org.jetbrains.kotlin.platform.TargetPlatform
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticProjectStructureProvider
@KtModuleBuilderDsl
public class KtModuleProviderBuilder {
@@ -25,7 +25,7 @@ public class KtModuleProviderBuilder {
public lateinit var platform: TargetPlatform
public lateinit var project: Project
public fun build(): ProjectStructureProvider {
public fun build(): KtStaticProjectStructureProvider {
return KtModuleProviderImpl(
platform,
project,
@@ -35,7 +35,7 @@ public class KtModuleProviderBuilder {
}
@OptIn(ExperimentalContracts::class)
public inline fun buildProjectStructureProvider(init: KtModuleProviderBuilder.() -> Unit): ProjectStructureProvider {
public inline fun buildProjectStructureProvider(init: KtModuleProviderBuilder.() -> Unit): KtStaticProjectStructureProvider {
contract {
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
}
@@ -11,6 +11,7 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileSystemItem
import com.intellij.psi.PsiJavaFile
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticProjectStructureProvider
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory.findJvmRootsForJavaFiles
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirBuiltinsSessionFactory
import org.jetbrains.kotlin.analysis.project.structure.*
@@ -23,8 +24,8 @@ import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
internal class KtModuleProviderImpl(
private val platform: TargetPlatform,
private val project: Project,
internal val mainModules: List<KtModule>,
) : ProjectStructureProvider() {
override val allKtModules: List<KtModule>,
) : KtStaticProjectStructureProvider() {
private val ktNotUnderContentRootModuleWithoutPsiFile by lazy {
KtNotUnderContentRootModuleImpl(
name = "unnamed-outside-content-root",
@@ -65,25 +66,27 @@ internal class KtModuleProviderImpl(
containingFileAsVirtualFile.analysisExtensionFileContextModule?.let { return it }
return mainModules.firstOrNull { module ->
return allKtModules.firstOrNull { module ->
containingFileAsVirtualFile in module.contentScope
}
?: throw KotlinExceptionWithAttachments("Cannot find KtModule; see the attachment for more details.")
.withAttachment(
containingFileAsVirtualFile.path,
mainModules.joinToString(separator = System.lineSeparator()) { it.asDebugString() }
allKtModules.joinToString(separator = System.lineSeparator()) { it.asDebugString() }
)
}
internal val binaryModules: List<KtBinaryModule> by lazy {
mainModules
allKtModules
.flatMap { it.allDirectDependencies() }
.filterIsInstance<KtBinaryModule>()
}
internal fun allSourceFiles(): List<PsiFileSystemItem> = buildList {
val files = mainModules.mapNotNull { (it as? KtSourceModuleImpl)?.sourceRoots }.flatten()
addAll(files)
addAll(findJvmRootsForJavaFiles(files.filterIsInstance<PsiJavaFile>()))
override val allSourceFiles: List<PsiFileSystemItem> by lazy {
buildList {
val files = allKtModules.mapNotNull { (it as? KtSourceModuleImpl)?.sourceRoots }.flatten()
addAll(files)
addAll(findJvmRootsForJavaFiles(files.filterIsInstance<PsiJavaFile>()))
}
}
}
@@ -16,7 +16,6 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.builder.*
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
@@ -44,6 +43,7 @@ import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerService
import java.io.IOException
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticProjectStructureProvider
internal fun TargetPlatform.getAnalyzerServices(): PlatformDependentAnalyzerServices {
return when {
@@ -154,7 +154,7 @@ internal fun buildKtModuleProviderByCompilerConfiguration(
compilerConfig: CompilerConfiguration,
project: Project,
ktFiles: List<KtFile>,
): ProjectStructureProvider = buildProjectStructureProvider {
): KtStaticProjectStructureProvider = buildProjectStructureProvider {
val (scriptFiles, ordinaryFiles) = ktFiles.partition { it.isScript() }
val platform = JvmPlatforms.defaultJvmPlatform
@@ -13,9 +13,7 @@ import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtu
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory
import org.jetbrains.kotlin.analysis.providers.KotlinPsiDeclarationProviderFactory
import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory
import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticPsiDeclarationProviderFactory
import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar
@@ -29,9 +27,9 @@ public object StandaloneModeTestServiceRegistrar : AnalysisApiTestServiceRegistr
}
override fun registerProjectModelServices(project: MockProject, testServices: TestServices) {
val projectStructureProvider = project.getService(ProjectStructureProvider::class.java)
val projectStructureProvider = ProjectStructureProvider.getInstance(project)
val binaryModules =
(projectStructureProvider as? KtStaticModuleProvider)?.allModules?.filterIsInstance<KtBinaryModule>()
(projectStructureProvider as? KtStaticModuleProvider)?.allKtModules?.filterIsInstance<KtBinaryModule>()
?: emptyList()
val projectEnvironment = testServices.environmentManager.getProjectEnvironment()
project.apply {
@@ -67,8 +67,6 @@ class AnalysisApiEnvironmentManagerImpl(
StandaloneProjectFactory.registerServicesForProjectEnvironment(
_projectEnvironment,
KtStaticModuleProvider(builtinsModule, ktModuleProjectStructure),
ktModuleProjectStructure.allKtModules(),
ktModuleProjectStructure.allSourceFiles(),
useSiteCompilerConfiguration.languageVersionSettings,
useSiteCompilerConfiguration.get(JVMConfigurationKeys.JDK_HOME)?.toPath(),
)