Consider platform compatibility in library usage index

Logic of module dependencies is enhanced to filter out incompatible
common -> platform dependencies. However the reversed index for getting
all modules that use libraries doesn't take this filtering into
account and returns all modules based exclusively on order entries.
This leads to incorrect library dependency calculation.

^KT-44638 Verification pending
This commit is contained in:
Pavel Kirpichenkov
2021-02-03 17:54:40 +03:00
parent 27dcd07a5e
commit 9f7d7e55da
8 changed files with 58 additions and 3 deletions
@@ -173,7 +173,7 @@ private fun ideaModelDependencies(
return correctedResult return correctedResult
} }
private fun TargetPlatform.canDependOn(other: IdeaModuleInfo, isHmppEnabled: Boolean): Boolean { internal fun TargetPlatform.canDependOn(other: IdeaModuleInfo, isHmppEnabled: Boolean): Boolean {
if (isHmppEnabled) { if (isHmppEnabled) {
// HACK: allow depending on stdlib even if platforms do not match // HACK: allow depending on stdlib even if platforms do not match
if (isNative() && other is AbstractKlibLibraryInfo && other.libraryRoot.endsWith(KONAN_STDLIB_NAME)) return true if (isNative() && other is AbstractKlibLibraryInfo && other.libraryRoot.endsWith(KONAN_STDLIB_NAME)) return true
@@ -19,6 +19,7 @@ import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.idea.core.util.CachedValue import org.jetbrains.kotlin.idea.core.util.CachedValue
import org.jetbrains.kotlin.idea.core.util.getValue import org.jetbrains.kotlin.idea.core.util.getValue
import org.jetbrains.kotlin.idea.project.isHMPPEnabled
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon import org.jetbrains.kotlin.platform.isCommon
import java.util.* import java.util.*
@@ -62,7 +63,7 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
val platform = libraryInfo.platform val platform = libraryInfo.platform
for (module in getLibraryUsageIndex().modulesLibraryIsUsedIn[libraryInfo.library]) { for (module in getLibraryUsageIndex().getModulesLibraryIsUsedIn(libraryInfo)) {
if (!processedModules.add(module)) continue if (!processedModules.add(module)) continue
ModuleRootManager.getInstance(module).orderEntries().recursively().satisfying(condition).process(object : RootPolicy<Unit>() { ModuleRootManager.getInstance(module).orderEntries().recursively().satisfying(condition).process(object : RootPolicy<Unit>() {
@@ -109,7 +110,7 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
} }
private inner class LibraryUsageIndex { private inner class LibraryUsageIndex {
val modulesLibraryIsUsedIn: MultiMap<Library, Module> = MultiMap.createSet() private val modulesLibraryIsUsedIn: MultiMap<Library, Module> = MultiMap.createSet()
init { init {
for (module in ModuleManager.getInstance(project).modules) { for (module in ModuleManager.getInstance(project).modules) {
@@ -123,5 +124,15 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
} }
} }
} }
fun getModulesLibraryIsUsedIn(libraryInfo: LibraryInfo) = sequence<Module> {
val ideaModelInfosCache = getIdeaModelInfosCache(project)
for (module in modulesLibraryIsUsedIn[libraryInfo.library]) {
val mappedModuleInfos = ideaModelInfosCache.getModuleInfosForModule(module)
if (mappedModuleInfos.any { it.platform.canDependOn(libraryInfo, module.isHMPPEnabled) }) {
yield(module)
}
}
}
} }
} }
@@ -0,0 +1,6 @@
fun checkCloneableIsAbsent(array: Array<String>) {
array.<!UNRESOLVED_REFERENCE!>clone<!>()
}
@<!UNRESOLVED_REFERENCE!>Metadata<!>
class MetaUnresolved
@@ -0,0 +1,10 @@
// See KT-44638
MODULE jvm { platform=[JVM] }
MODULE js { platform=[JS] }
MODULE common { platform=[JVM, JS] }
jvm -> STDLIB_JVM, MOCK_JDK { kind=DEPENDENCY }
js -> STDLIB_JS { kind=DEPENDENCY }
common -> STDLIB_COMMON, STDLIB_JVM { kind=DEPENDENCY }
jvm -> common { kind=DEPENDS_ON }
@@ -0,0 +1,6 @@
fun checkCloneableIsAbsent(array: Array<String>) {
array.<!UNRESOLVED_REFERENCE!>clone<!>()
}
@<!UNRESOLVED_REFERENCE!>Metadata<!>
class MetaUnresolved
@@ -0,0 +1,6 @@
fun checkCloneable(array: Array<String>) {
array.clone()
}
<!EXPLICIT_METADATA_IS_DISALLOWED!>@Metadata<!>
class MetaWithError
@@ -26,10 +26,12 @@ import org.jetbrains.kotlin.idea.caches.project.ModuleTestSourceInfo
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
import org.jetbrains.kotlin.idea.framework.JSLibraryKind import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.framework.platform import org.jetbrains.kotlin.idea.framework.platform
import org.jetbrains.kotlin.idea.stubs.createMultiplatformFacetM3
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.* import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.jetbrains.kotlin.test.util.KtTestUtil import org.jetbrains.kotlin.test.util.KtTestUtil
import org.jetbrains.kotlin.test.util.addDependency import org.jetbrains.kotlin.test.util.addDependency
@@ -316,6 +318,7 @@ class IdeaModuleInfoTest : ModuleTestCase() {
a.addDependency(stdlibJvm) a.addDependency(stdlibJvm)
val b = module("b") val b = module("b")
b.setUpPlatform(JsPlatforms.defaultJsPlatform)
b.addDependency(stdlibCommon) b.addDependency(stdlibCommon)
b.addDependency(stdlibJs) b.addDependency(stdlibJs)
@@ -480,6 +483,14 @@ class IdeaModuleInfoTest : ModuleTestCase() {
kind = JSLibraryKind kind = JSLibraryKind
) )
private fun Module.setUpPlatform(targetPlatform: TargetPlatform) {
createMultiplatformFacetM3(
platformKind = targetPlatform,
dependsOnModuleNames = listOf(),
pureKotlinSourceFolders = listOf(),
)
}
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
@@ -199,6 +199,11 @@ public class MultiplatformAnalysisTestGenerated extends AbstractMultiplatformAna
runTest("idea/testData/multiplatform/overrideExpectWithCompositeType/"); runTest("idea/testData/multiplatform/overrideExpectWithCompositeType/");
} }
@TestMetadata("platformDependencyInCommon")
public void testPlatformDependencyInCommon() throws Exception {
runTest("idea/testData/multiplatform/platformDependencyInCommon/");
}
@TestMetadata("platformSpecificChecksInCommon") @TestMetadata("platformSpecificChecksInCommon")
public void testPlatformSpecificChecksInCommon() throws Exception { public void testPlatformSpecificChecksInCommon() throws Exception {
runTest("idea/testData/multiplatform/platformSpecificChecksInCommon/"); runTest("idea/testData/multiplatform/platformSpecificChecksInCommon/");