[AA] KT-55566 StandaloneProjectFactory: Setup JDK default module roots

- Setup JDK default module roots in `StandaloneProjectFactory` (compare
  with `KotlinCoreEnvironment`). The implementation is a distilled
  version of `ClasspathRootsResolver`'s default module handling.
- This fixes an issue where some LL FIR tests with JDK 17 and 11 had
  mismatched types between Kotlin and Java sources.

^KT-55566 fixed
This commit is contained in:
Marco Pennekamp
2023-01-03 18:44:32 +01:00
committed by Space Team
parent e68111c218
commit a777ffcd8a
12 changed files with 97 additions and 71 deletions
@@ -98,37 +98,42 @@ object StandaloneProjectFactory {
jdkHome: Path?,
) {
val project = environment.project
val javaFileManager = project.getService(JavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.toFile(), null, javaFileManager, project, null)
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
val allSourceFileRoots = sourceFiles.map { JavaRoot(it.virtualFile, JavaRoot.RootType.SOURCE) }
val libraryRoots = getAllBinaryRoots(modules, environment)
libraryRoots.forEach { environment.addSourcesToClasspath(it.file) }
val jdkRoots = getDefaultJdkModuleRoots(javaModuleFinder, javaModuleGraph)
val sourceAndLibraryRoots = buildList {
val rootsWithSingleJavaFileRoots = buildList {
addAll(libraryRoots)
addAll(allSourceFileRoots)
addAll(jdkRoots)
}
val (roots, singleJavaFileRoots) =
sourceAndLibraryRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION }
val javaFileManager = project.getService(JavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.toFile(), null, javaFileManager, project, null)
rootsWithSingleJavaFileRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION }
val corePackageIndex = project.getService(PackageIndex::class.java) as CorePackageIndex
val rootsIndex = JvmDependenciesDynamicCompoundIndex().apply {
addIndex(JvmDependenciesIndexImpl(roots))
indexedRoots.forEach { javaRoot ->
if (javaRoot.file.isDirectory && javaRoot.type == JavaRoot.RootType.SOURCE) {
// NB: [JavaCoreProjectEnvironment#addSourcesToClasspath] calls:
// 1) [CoreJavaFileManager#addToClasspath], which is used to look up Java roots;
// 2) [CorePackageIndex#addToClasspath], which populates [PackageIndex]; and
// 3) [FileIndexFacade#addLibraryRoot], which conflicts with this SOURCE root when generating a library scope.
// Thus, here we manually call first two, which are used to:
// 1) create [PsiPackage] as a package resolution result; and
// 2) find directories by package name.
// With both supports, annotations defined in package-info.java can be properly propagated.
javaFileManager.addToClasspath(javaRoot.file)
corePackageIndex.addToClasspath(javaRoot.file)
if (javaRoot.file.isDirectory) {
if (javaRoot.type == JavaRoot.RootType.SOURCE) {
// NB: [JavaCoreProjectEnvironment#addSourcesToClasspath] calls:
// 1) [CoreJavaFileManager#addToClasspath], which is used to look up Java roots;
// 2) [CorePackageIndex#addToClasspath], which populates [PackageIndex]; and
// 3) [FileIndexFacade#addLibraryRoot], which conflicts with this SOURCE root when generating a library scope.
// Thus, here we manually call first two, which are used to:
// 1) create [PsiPackage] as a package resolution result; and
// 2) find directories by package name.
// With both supports, annotations defined in package-info.java can be properly propagated.
javaFileManager.addToClasspath(javaRoot.file)
corePackageIndex.addToClasspath(javaRoot.file)
} else {
environment.addSourcesToClasspath(javaRoot.file)
}
}
}
}
@@ -136,7 +141,7 @@ object StandaloneProjectFactory {
javaFileManager.initialize(
rootsIndex,
listOf(
createPackagePartsProvider(project, libraryRoots, languageVersionSettings)
createPackagePartsProvider(project, libraryRoots + jdkRoots, languageVersionSettings)
.invoke(ProjectScope.getLibrariesScope(project))
),
SingleJavaFileRootsIndex(singleJavaFileRoots),
@@ -145,7 +150,7 @@ object StandaloneProjectFactory {
project.registerService(
JavaModuleResolver::class.java,
CliJavaModuleResolver(JavaModuleGraph(javaModuleFinder), emptyList(), javaModuleFinder.systemModules.toList(), project)
CliJavaModuleResolver(javaModuleGraph, emptyList(), javaModuleFinder.systemModules.toList(), project)
)
val finderFactory = CliVirtualFileFinderFactory(rootsIndex, false)
@@ -154,6 +159,21 @@ object StandaloneProjectFactory {
project.registerService(VirtualFileFinderFactory::class.java, finderFactory)
}
/**
* Computes the [JavaRoot]s of the JDK's default modules.
*
* @see ClasspathRootsResolver.addModularRoots
*/
private fun getDefaultJdkModuleRoots(javaModuleFinder: CliJavaModuleFinder, javaModuleGraph: JavaModuleGraph): List<JavaRoot> {
// In contrast to `ClasspathRootsResolver.addModularRoots`, we do not need to handle automatic Java modules because JDK modules
// aren't automatic.
return javaModuleGraph.getAllDependencies(javaModuleFinder.computeDefaultRootModules()).flatMap { moduleName ->
val module = javaModuleFinder.findModule(moduleName) ?: return@flatMap emptyList<JavaRoot>()
val result = module.getJavaModuleRoots()
result
}
}
/**
* Note that [findJvmRootsForJavaFiles] parses the given [files] because it needs access to each file's package name. To avoid parsing
* errors, [registerJavaPsiFacade] ensures that the Java language level is configured before [findJvmRootsForJavaFiles] is called.