Always treat all sources as parts of the module being compiled
If a path to the module-info.java file is passed as an argument, we should treat all other source files passed as arguments (either as individual source files or inside a source directory) as members of that module. Previously we treated other source files as members of the unnamed module, and this resulted in incorrect errors when using a member exported with a qualification from another named module, for example #KT-18598 In Fixed
This commit is contained in:
@@ -17,8 +17,10 @@
|
||||
package org.jetbrains.kotlin.cli.jvm.modules
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleGraph
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
@@ -27,10 +29,20 @@ class CliJavaModuleResolver(
|
||||
override val moduleGraph: JavaModuleGraph,
|
||||
private val javaModules: List<JavaModule>
|
||||
) : JavaModuleResolver {
|
||||
override fun findJavaModule(file: VirtualFile): JavaModule? {
|
||||
val isBinary = file.fileType == JavaClassFileType.INSTANCE
|
||||
return javaModules.firstOrNull { module ->
|
||||
module.isBinary == isBinary && VfsUtilCore.isAncestor(module.moduleRoot, file, false)
|
||||
init {
|
||||
assert(javaModules.count { !it.isBinary } <= 1) {
|
||||
"Modules computed by ClasspathRootsResolver cannot have more than one source module: $javaModules"
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceModule = javaModules.firstOrNull { !it.isBinary }
|
||||
|
||||
override fun findJavaModule(file: VirtualFile): JavaModule? =
|
||||
when (file.fileType) {
|
||||
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule
|
||||
JavaClassFileType.INSTANCE -> javaModules.firstOrNull { module ->
|
||||
module.isBinary && VfsUtilCore.isAncestor(module.moduleRoot, file, false)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,9 @@ interface JavaModule {
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* A directory or the root of a .jar file on the module path. In case of an explicit module,
|
||||
* module-info.class file can be found in its children.
|
||||
* A directory or the root of a .jar file on the module path. Can also be the path to the `module-info.java` file if that path
|
||||
* was passed as an explicit argument to the compiler.
|
||||
* In case of an explicit module, module-info.class file can be found in its children.
|
||||
*/
|
||||
val moduleRoot: VirtualFile
|
||||
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo;
|
||||
|
||||
public class Foo {}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module moduleA {
|
||||
exports foo to moduleB;
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package unexported;
|
||||
|
||||
public class Unexported {}
|
||||
@@ -0,0 +1,10 @@
|
||||
compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB/usage.kt:2:19: error: symbol is declared in module 'moduleA' which does not export package 'unexported'
|
||||
import unexported.Unexported
|
||||
^
|
||||
compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB/usage.kt:9:5: error: symbol is declared in module 'moduleA' which does not export package 'unexported'
|
||||
Unexported()
|
||||
^
|
||||
compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB/usage.kt:13:5: error: unresolved reference: Unrelated
|
||||
Unrelated()
|
||||
^
|
||||
COMPILATION_ERROR
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import foo.Foo
|
||||
import unexported.Unexported
|
||||
|
||||
fun usage() {
|
||||
// Should be OK
|
||||
Foo()
|
||||
|
||||
// Should be error
|
||||
Unexported()
|
||||
|
||||
// Should be error: if we're passing the path to a particular module-info.java to the compiler,
|
||||
// we should not treat its location as a root
|
||||
Unrelated()
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
public class Unrelated {}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module moduleB {
|
||||
requires moduleA;
|
||||
}
|
||||
@@ -54,12 +54,14 @@ class Java9ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
||||
}
|
||||
KotlinTestUtils.compileJavaFilesExternallyWithJava9(javaFiles, javaOptions)
|
||||
},
|
||||
checkKotlinOutput = { actual ->
|
||||
KotlinTestUtils.assertEqualsToFile(File(testDataDirectory, "$name.txt"), actual)
|
||||
}
|
||||
checkKotlinOutput = checkKotlinOutput(name)
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkKotlinOutput(moduleName: String): (String) -> Unit = { actual ->
|
||||
KotlinTestUtils.assertEqualsToFile(File(testDataDirectory, "$moduleName.txt"), actual)
|
||||
}
|
||||
|
||||
fun testSimple() {
|
||||
val a = module("moduleA")
|
||||
module("moduleB", listOf(a))
|
||||
@@ -130,4 +132,21 @@ class Java9ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
||||
val c = module("moduleC", listOf(a, b))
|
||||
module("moduleD", listOf(c, b, a))
|
||||
}
|
||||
|
||||
fun testSpecifyPathToModuleInfoInArguments() {
|
||||
val a = module("moduleA")
|
||||
|
||||
val jdk9Home = KotlinTestUtils.getJdk9HomeIfPossible() ?: return
|
||||
val kotlinOptions = mutableListOf(
|
||||
"$testDataDirectory/someOtherDirectoryWithTheActualModuleInfo/module-info.java",
|
||||
"-jdk-home", jdk9Home.path,
|
||||
"-Xmodule-path=${a.path}"
|
||||
)
|
||||
compileLibrary(
|
||||
"moduleB",
|
||||
additionalOptions = kotlinOptions,
|
||||
compileJava = { _, _, _ -> error("No .java files in moduleB in this test") },
|
||||
checkKotlinOutput = checkKotlinOutput("moduleB")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user