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:
Alexander Udalov
2017-06-28 19:03:43 +03:00
parent 5192f946c5
commit 8c21ff66a4
10 changed files with 78 additions and 9 deletions
@@ -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
@@ -0,0 +1,3 @@
package foo;
public class Foo {}
@@ -0,0 +1,3 @@
module moduleA {
exports foo to moduleB;
}
@@ -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
@@ -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()
}
@@ -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")
)
}
}