diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt index 38f0f0e032a..bcc54f612e0 100644 --- a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt @@ -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 { + // 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() + 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. diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt index fe1ecfd5abd..2abfc9b6a33 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt @@ -260,7 +260,7 @@ class ClasspathRootsResolver( val rootModules = when { sourceModule != null -> listOf(sourceModule.name) + additionalModules addAllModulePathToRoots -> modules.map(JavaModule::name) - else -> computeDefaultRootModules() + additionalModules + else -> javaModuleFinder.computeDefaultRootModules() + additionalModules } val allDependencies = javaModuleGraph.getAllDependencies(rootModules) @@ -282,14 +282,7 @@ class ClasspathRootsResolver( if (module == null) { report(ERROR, "Module $moduleName cannot be found in the module graph") } else { - module.moduleRoots.mapTo(result) { (root, isBinary, isBinarySignature) -> - val type = when { - isBinarySignature -> JavaRoot.RootType.BINARY_SIG - isBinary -> JavaRoot.RootType.BINARY - else -> JavaRoot.RootType.SOURCE - } - JavaRoot(root, type) - } + result.addAll(module.getJavaModuleRoots()) } } @@ -303,40 +296,6 @@ class ClasspathRootsResolver( } } - // See http://openjdk.java.net/jeps/261 - private fun computeDefaultRootModules(): List { - val result = arrayListOf() - - val systemModules = javaModuleFinder.systemModules.associateBy(JavaModule::name) - val javaSeExists = "java.se" in systemModules - if (javaSeExists) { - // The java.se module is a root, if it exists. - result.add("java.se") - } - - fun JavaModule.Explicit.exportsAtLeastOnePackageUnqualified(): Boolean = moduleInfo.exports.any { it.toModules.isEmpty() } - - if (!javaSeExists) { - // If it does not exist then every java.* module on the upgrade module path or among the system modules - // that exports at least one package, without qualification, is a root. - for ((name, module) in systemModules) { - if (name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) { - result.add(name) - } - } - } - - for ((name, module) in systemModules) { - // Every non-java.* module on the upgrade module path or among the system modules that exports at least one package, - // without qualification, is also a root. - if (!name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) { - result.add(name) - } - } - - return result - } - private fun report(severity: CompilerMessageSeverity, message: String, file: VirtualFile? = null) { if (messageCollector == null) { throw IllegalStateException("${if (file != null) file.path + ":" else ""}$severity: $message (no MessageCollector configured)") diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/cliJavaModuleUtils.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/cliJavaModuleUtils.kt new file mode 100644 index 00000000000..b89ba085233 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/cliJavaModuleUtils.kt @@ -0,0 +1,56 @@ +/* + * 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.cli.jvm.compiler + +import org.jetbrains.kotlin.cli.jvm.index.JavaRoot +import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule + +fun JavaModule.getJavaModuleRoots(): List = + moduleRoots.map { (root, isBinary, isBinarySignature) -> + val type = when { + isBinarySignature -> JavaRoot.RootType.BINARY_SIG + isBinary -> JavaRoot.RootType.BINARY + else -> JavaRoot.RootType.SOURCE + } + JavaRoot(root, type) + } + +/** + * Computes the JDK's default root modules. See [JEP 261: Module System](http://openjdk.java.net/jeps/261). + */ +fun CliJavaModuleFinder.computeDefaultRootModules(): List { + val result = arrayListOf() + + val systemModules = systemModules.associateBy(JavaModule::name) + val javaSeExists = "java.se" in systemModules + if (javaSeExists) { + // The java.se module is a root, if it exists. + result.add("java.se") + } + + fun JavaModule.Explicit.exportsAtLeastOnePackageUnqualified(): Boolean = moduleInfo.exports.any { it.toModules.isEmpty() } + + if (!javaSeExists) { + // If it does not exist then every java.* module on the upgrade module path or among the system modules + // that exports at least one package, without qualification, is a root. + for ((name, module) in systemModules) { + if (name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) { + result.add(name) + } + } + } + + for ((name, module) in systemModules) { + // Every non-java.* module on the upgrade module path or among the system modules that exports at least one package, + // without qualification, is also a root. + if (!name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) { + result.add(name) + } + } + + return result +} diff --git a/compiler/testData/diagnostics/tests/kt11167.kt b/compiler/testData/diagnostics/tests/kt11167.kt index e9b8252293c..3d29751ab9e 100644 --- a/compiler/testData/diagnostics/tests/kt11167.kt +++ b/compiler/testData/diagnostics/tests/kt11167.kt @@ -1,4 +1,3 @@ -// FIR_IDE_IGNORE // FIR_IDENTICAL // JDK_KIND: FULL_JDK_11 // WITH_STDLIB diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.fir.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.fir.kt index d1c29f2929d..8e4fe958b34 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.fir.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.fir.kt @@ -1,5 +1,4 @@ // FIR_DISABLE_LAZY_RESOLVE_CHECKS -// FIR_IDE_IGNORE // !API_VERSION: 1.5 // !LANGUAGE: -JvmRecordSupport // SKIP_TXT diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.kt index 2de527217c6..52124f4b8a2 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/disabledFeature.kt @@ -1,5 +1,4 @@ // FIR_DISABLE_LAZY_RESOLVE_CHECKS -// FIR_IDE_IGNORE // !API_VERSION: 1.5 // !LANGUAGE: -JvmRecordSupport // SKIP_TXT diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt index 54fc17fc227..78fb87d3683 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt @@ -1,5 +1,4 @@ // FIR_IDENTICAL -// FIR_IDE_IGNORE // API_VERSION: 1.5 // LANGUAGE: +JvmRecordSupport // SCOPE_DUMP: MyRecord:x diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.fir.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.fir.kt index 7c05f29c2fd..269ad20acaa 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.fir.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.fir.kt @@ -1,5 +1,4 @@ // FIR_DISABLE_LAZY_RESOLVE_CHECKS -// FIR_IDE_IGNORE // !API_VERSION: 1.5 // !LANGUAGE: +JvmRecordSupport // JVM_TARGET: 17 diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.kt index 58ac0b84f31..9c4ea35e508 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/jvmRecordDescriptorStructure.kt @@ -1,5 +1,4 @@ // FIR_DISABLE_LAZY_RESOLVE_CHECKS -// FIR_IDE_IGNORE // !API_VERSION: 1.5 // !LANGUAGE: +JvmRecordSupport // JVM_TARGET: 17 diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/simpleRecords.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/simpleRecords.kt index 4359a58ba1f..bcf5d6fbe94 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/simpleRecords.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/simpleRecords.kt @@ -1,5 +1,4 @@ // FIR_IDENTICAL -// FIR_IDE_IGNORE // API_VERSION: 1.5 // LANGUAGE: +JvmRecordSupport // SCOPE_DUMP: MyRecord:x;y;z diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.fir.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.fir.kt index 27029e020c8..39dcba593bb 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.fir.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.fir.kt @@ -1,5 +1,4 @@ // FIR_DISABLE_LAZY_RESOLVE_CHECKS -// FIR_IDE_IGNORE // !API_VERSION: 1.5 // !LANGUAGE: +JvmRecordSupport // JVM_TARGET: 17 diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.kt b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.kt index cd079cf431f..b5b20995bfa 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/supertypesCheck.kt @@ -1,5 +1,4 @@ // FIR_DISABLE_LAZY_RESOLVE_CHECKS -// FIR_IDE_IGNORE // !API_VERSION: 1.5 // !LANGUAGE: +JvmRecordSupport // JVM_TARGET: 17