Check that kotlin.stdlib is explicitly required in module-info

Writing a Jigsaw-modular Kotlin program which doesn't require
kotlin.stdlib doesn't make sense because it most likely will fail at
runtime, on access to anything from the standard library. Previously, we
checked that kotlin.stdlib was in the module graph, but that's not
enough, we should also check that the source module requires it.
'-Xallow-kotlin-package' can be used to disable the error.

Add a test checking that an indirect (transitive) dependency is also OK
This commit is contained in:
Alexander Udalov
2018-02-14 11:37:28 +01:00
parent 7501d2ad69
commit 0439abba46
17 changed files with 38 additions and 37 deletions
@@ -265,7 +265,7 @@ class ClasspathRootsResolver(
} }
} }
if (requireStdlibModule && sourceModule != null && KOTLIN_STDLIB_MODULE_NAME !in allDependencies) { if (requireStdlibModule && sourceModule != null && !javaModuleGraph.reads(sourceModule.name, KOTLIN_STDLIB_MODULE_NAME)) {
report( report(
ERROR, ERROR,
"The Kotlin standard library is not found in the module graph. " + "The Kotlin standard library is not found in the module graph. " +
@@ -2,4 +2,6 @@ module moduleD {
requires moduleA; requires moduleA;
requires moduleB; requires moduleB;
requires moduleC; requires moduleC;
}
requires kotlin.stdlib;
}
@@ -0,0 +1,3 @@
module namedWithIndirectDependencyViaOtherModule {
requires namedWithExplicitDependency;
}
@@ -0,0 +1,6 @@
import kotlin.text.Regex
fun f1(): List<String?> = emptyList()
fun f2(): Array<Lazy<Unit>> = arrayOf()
fun f3(map: Map<Int, Regex>): Collection<Regex> =
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
@@ -1,34 +1,2 @@
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:1:20: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/module-info.java: error: the Kotlin standard library is not found in the module graph. Please ensure you have the 'requires kotlin.stdlib' clause in your module definition
import kotlin.text.Regex
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:3:27: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f1(): List<String?> = emptyList()
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:4:17: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f2(): Array<Lazy<Unit>> = arrayOf()
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:4:22: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f2(): Array<Lazy<Unit>> = arrayOf()
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:5:22: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f3(map: Map<Int, Regex>): Collection<Regex> =
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:5:42: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f3(map: Map<Int, Regex>): Collection<Regex> =
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:13: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:26: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:31: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:48: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:61: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
COMPILATION_ERROR COMPILATION_ERROR
@@ -2,4 +2,6 @@ module main {
requires java.naming; requires java.naming;
requires jdk.net; requires jdk.net;
requires oracle.desktop; requires oracle.desktop;
requires kotlin.stdlib;
} }
@@ -1,3 +1,5 @@
module main { module main {
requires library; requires library;
requires kotlin.stdlib;
} }
@@ -1,3 +1,5 @@
module moduleC { module moduleC {
requires moduleB; requires moduleB;
requires kotlin.stdlib;
} }
@@ -1,4 +1,6 @@
module moduleD { module moduleD {
requires moduleC; requires moduleC;
requires moduleB; requires moduleB;
requires kotlin.stdlib;
} }
@@ -1,3 +1,5 @@
module main { module main {
requires dependency; requires dependency;
requires kotlin.stdlib;
} }
@@ -1,3 +1,5 @@
module moduleB { module moduleB {
requires moduleA; requires moduleA;
requires kotlin.stdlib;
} }
@@ -1,3 +1,5 @@
module moduleB { module moduleB {
requires moduleA; requires moduleA;
requires kotlin.stdlib;
} }
@@ -1,3 +1,5 @@
module moduleB { module moduleB {
requires moduleA; requires moduleA;
requires kotlin.stdlib;
} }
@@ -227,8 +227,9 @@ class Java9ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
fun testDependencyOnStdlib() { fun testDependencyOnStdlib() {
module("unnamed") module("unnamed")
module("namedWithExplicitDependency") val namedWithExplicitDependency = module("namedWithExplicitDependency")
module("namedWithoutExplicitDependency") module("namedWithoutExplicitDependency")
module("namedWithIndirectDependencyViaOtherModule", listOf(namedWithExplicitDependency))
module("namedWithIndirectDependencyViaReflect", listOf(ForTestCompileRuntime.reflectJarForTests())) module("namedWithIndirectDependencyViaReflect", listOf(ForTestCompileRuntime.reflectJarForTests()))
} }
@@ -1,3 +1,5 @@
module main { module main {
requires library; requires library;
requires kotlin.stdlib;
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.caches.resolve package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.MockLibraryUtil import org.jetbrains.kotlin.test.MockLibraryUtil
@@ -41,9 +42,10 @@ class Java9MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
fun testSimpleLibraryExportsPackage() = doTest { fun testSimpleLibraryExportsPackage() = doTest {
val jdk9Home = KotlinTestUtils.getJdk9HomeIfPossible() ?: return val jdk9Home = KotlinTestUtils.getJdk9HomeIfPossible() ?: return
// -Xallow-kotlin-package to avoid "require kotlin.stdlib" in module-info.java
val library = MockLibraryUtil.compileJvmLibraryToJar( val library = MockLibraryUtil.compileJvmLibraryToJar(
testDataPath + "${getTestName(true)}/library", "library", testDataPath + "${getTestName(true)}/library", "library",
extraOptions = listOf("-jdk-home", jdk9Home.path), extraOptions = listOf("-jdk-home", jdk9Home.path, "-Xallow-kotlin-package"),
useJava9 = true useJava9 = true
) )