diff --git a/compiler/tests-common/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java b/compiler/tests-common/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java index 7415efdf915..a1e6f676a5f 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java +++ b/compiler/tests-common/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java @@ -66,6 +66,11 @@ public class ForTestCompileRuntime { return assertExists(new File("dist/common/kotlin-stdlib-common.jar")); } + @NotNull + public static File stdlibJsForTests() { + return assertExists(new File("dist/kotlinc/lib/kotlin-stdlib-js.jar")); + } + // TODO: Do not use these classes, remove them after stdlib tests are merged in the same build as the compiler @NotNull @Deprecated diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt index 8e3fe25c434..d576e89d654 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt @@ -32,15 +32,12 @@ import com.intellij.util.SmartList import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.idea.framework.CommonLibraryDetectionUtil -import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil +import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected -import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.jvm.GlobalSearchScopeWithModuleSources -import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform import java.util.* interface IdeaModuleInfo : ModuleInfo { @@ -205,11 +202,7 @@ class LibraryInfo(val project: Project, val library: Library) : IdeaModuleInfo, } override val libraryPlatform: TargetPlatform - get() = when { - KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library) -> JsPlatform - CommonLibraryDetectionUtil.isCommonLibrary(library) -> TargetPlatform.Default - else -> JvmPlatform - } + get() = TargetPlatformDetector.getPlatform(library) override val sourcesModuleInfo: SourceForBinaryModuleInfo get() = LibrarySourceInfo(project, library) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/LibraryDependenciesCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/LibraryDependenciesCache.kt index 132b065e61f..d333d9d6751 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/LibraryDependenciesCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/LibraryDependenciesCache.kt @@ -26,6 +26,8 @@ import com.intellij.openapi.util.Condition import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.idea.project.TargetPlatformDetector +import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* @@ -47,6 +49,8 @@ class LibraryDependenciesCache(private val project: Project) { val libraries = LinkedHashSet() val sdks = LinkedHashSet() + val platform = TargetPlatformDetector.getPlatform(library) + for (module in getLibraryUsageIndex().modulesLibraryIsUsedIn[library]) { if (!processedModules.add(module)) continue @@ -56,7 +60,10 @@ class LibraryDependenciesCache(private val project: Project) { } override fun visitLibraryOrderEntry(libraryOrderEntry: LibraryOrderEntry, value: Unit) { - libraries.addIfNotNull(libraryOrderEntry.library) + val otherLibrary = libraryOrderEntry.library + if (otherLibrary != null && compatiblePlatforms(platform, TargetPlatformDetector.getPlatform(otherLibrary))) { + libraries.add(otherLibrary) + } } override fun visitJdkOrderEntry(jdkOrderEntry: JdkOrderEntry, value: Unit) { @@ -68,6 +75,13 @@ class LibraryDependenciesCache(private val project: Project) { return Pair(libraries.toList(), sdks.toList()) } + /** + * @return true if it's OK to add a dependency from a library with platform [from] to a library with platform [to] + */ + private fun compatiblePlatforms(from: TargetPlatform, to: TargetPlatform): Boolean { + return from == to || to == TargetPlatform.Default + } + private fun getLibraryUsageIndex(): LibraryUsageIndex { return CachedValuesManager.getManager(project).getCachedValue(project) { CachedValueProvider.Result(LibraryUsageIndex(), ProjectRootModificationTracker.getInstance(project)) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/framework/CommonLibraryDetectionUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/framework/CommonLibraryDetectionUtil.kt index 3bce68ca08c..0a00747cfab 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/framework/CommonLibraryDetectionUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/framework/CommonLibraryDetectionUtil.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.idea.caches.JarUserDataManager import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment object CommonLibraryDetectionUtil { + @JvmStatic fun isCommonLibrary(library: Library): Boolean { if (library is LibraryEx && library.isDisposed) return false diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/TargetPlatformDetector.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/TargetPlatformDetector.java index 785beae59b6..adc6dc36cb0 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/TargetPlatformDetector.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/TargetPlatformDetector.java @@ -19,10 +19,14 @@ package org.jetbrains.kotlin.idea.project; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.idea.framework.CommonLibraryDetectionUtil; +import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil; +import org.jetbrains.kotlin.js.resolve.JsPlatform; import org.jetbrains.kotlin.psi.KtCodeFragment; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtPsiFactoryKt; @@ -70,4 +74,15 @@ public class TargetPlatformDetector { public static TargetPlatform getPlatform(@NotNull Module module) { return ProjectStructureUtil.getCachedPlatformForModule(module); } + + @NotNull + public static TargetPlatform getPlatform(@NotNull Library library) { + if (KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library)) { + return JsPlatform.INSTANCE; + } + if (CommonLibraryDetectionUtil.isCommonLibrary(library)) { + return TargetPlatform.Default.INSTANCE; + } + return JvmPlatform.INSTANCE; + } } diff --git a/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/common/common.kt b/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/common/common.kt new file mode 100644 index 00000000000..46b1fdfc68f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/common/common.kt @@ -0,0 +1,3 @@ +package test + +fun getEmptyList() = emptyList() diff --git a/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/js/js.kt b/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/js/js.kt new file mode 100644 index 00000000000..8d59a2baf6f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/js/js.kt @@ -0,0 +1,6 @@ +package test + +fun testJs() { + // This is a JavaScript module, so `List.stream` should be unresolved + getEmptyList().stream() +} diff --git a/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/jvm/jvm.kt b/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/jvm/jvm.kt new file mode 100644 index 00000000000..da89d111bbd --- /dev/null +++ b/idea/testData/multiModuleHighlighting/multiplatform/useCorrectBuiltInsForCommonModule/jvm/jvm.kt @@ -0,0 +1,6 @@ +package test + +fun testJvm() { + // This is a JVM 8 module, so `List.stream` should be resolved correctly + getEmptyList().stream() +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfoTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfoTest.kt index 9c05e2be9fe..2906a22362d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfoTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfoTest.kt @@ -16,16 +16,20 @@ package org.jetbrains.kotlin.idea.caches.resolve -import com.intellij.testFramework.ModuleTestCase -import com.intellij.openapi.module.StdModuleTypes +import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.module.Module -import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.module.StdModuleTypes import com.intellij.openapi.roots.DependencyScope -import org.junit.Assert +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.roots.OrderRootType import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable import com.intellij.openapi.roots.libraries.Library -import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.testFramework.ModuleTestCase import com.intellij.testFramework.UsefulTestCase +import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime +import org.junit.Assert +import java.io.File class IdeaModuleInfoTest : ModuleTestCase() { @@ -267,6 +271,24 @@ class IdeaModuleInfoTest : ModuleTestCase() { lib3.classes.assertAdditionalLibraryDependencies(lib1.classes, lib2.classes) } + fun testCommonLibraryDoesNotDependOnPlatform() { + val stdlibCommon = stdlibCommon() + val stdlibJvm = stdlibJvm() + val stdlibJs = stdlibJs() + + val a = module("a") + a.addDependency(stdlibCommon) + a.addDependency(stdlibJvm) + + val b = module("b") + b.addDependency(stdlibCommon) + b.addDependency(stdlibJs) + + stdlibCommon.classes.assertAdditionalLibraryDependencies() + stdlibJvm.classes.assertAdditionalLibraryDependencies(stdlibCommon.classes) + stdlibJs.classes.assertAdditionalLibraryDependencies(stdlibCommon.classes) + } + private fun Module.addDependency( other: Module, dependencyScope: DependencyScope = DependencyScope.COMPILE, @@ -308,10 +330,22 @@ class IdeaModuleInfoTest : ModuleTestCase() { UsefulTestCase.assertSameElements(this.getDependentModules(), expected.toList()) } - private fun projectLibrary(name: String = "lib"): Library { + private fun projectLibrary(name: String = "lib", vararg roots: File): Library { val libraryTable = ProjectLibraryTable.getInstance(myProject)!! return WriteCommandAction.runWriteCommandAction(myProject) { - libraryTable.createLibrary(name) + libraryTable.createLibrary(name).apply { + for (root in roots) { + val model = modifiableModel + model.addRoot(VfsUtil.getUrlForLibraryRoot(root), OrderRootType.CLASSES) + model.commit() + } + } }!! } + + private fun stdlibCommon(): Library = projectLibrary("kotlin-stdlib-common", ForTestCompileRuntime.stdlibCommonForTests()) + + private fun stdlibJvm(): Library = projectLibrary("kotlin-stdlib", ForTestCompileRuntime.runtimeJarForTests()) + + private fun stdlibJs(): Library = projectLibrary("kotlin-stdlib-js", ForTestCompileRuntime.runtimeJarForTests()) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt index 14111386d6b..3df79661dd8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.caches.resolve +import com.intellij.openapi.module.Module import com.intellij.openapi.roots.DependencyScope import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.config.JvmTarget @@ -65,8 +66,13 @@ class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { class MultiPlatform : AbstractMultiModuleHighlightingTest() { override val testPath get() = super.testPath + "multiplatform/" - private fun doTest(vararg platforms: TargetPlatformKind<*>, withStdlibCommon: Boolean = false) { - val commonModule = module("common") + private fun doTest( + vararg platforms: TargetPlatformKind<*>, + withStdlibCommon: Boolean = false, + configureModule: (Module, TargetPlatformKind<*>) -> Unit = { _, _ -> }, + useFullJdk: Boolean = false + ) { + val commonModule = module("common", useFullJdk = useFullJdk) commonModule.createFacet(TargetPlatformKind.Common) if (withStdlibCommon) { commonModule.addLibrary(ForTestCompileRuntime.stdlibCommonForTests()) @@ -78,10 +84,11 @@ class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { is TargetPlatformKind.JavaScript -> "js" else -> error("Unsupported platform: $platform") } - val platformModule = module(path) + val platformModule = module(path, useFullJdk = useFullJdk) platformModule.createFacet(platform) platformModule.enableMultiPlatform() platformModule.addDependency(commonModule) + configureModule(platformModule, platform) } checkHighlightingInAllFiles() @@ -114,5 +121,15 @@ class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { fun testCatchHeaderExceptionInPlatformModule() { doTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], withStdlibCommon = true) } + + fun testUseCorrectBuiltInsForCommonModule() { + doTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], TargetPlatformKind.JavaScript, + withStdlibCommon = true, useFullJdk = true, configureModule = { module, platform -> + if (platform == TargetPlatformKind.JavaScript) { + module.addLibrary(ForTestCompileRuntime.stdlibJsForTests()) + module.addLibrary(ForTestCompileRuntime.stdlibCommonForTests()) + } + }) + } } }