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
}
private fun TargetPlatform.canDependOn(other: IdeaModuleInfo, isHmppEnabled: Boolean): Boolean {
internal fun TargetPlatform.canDependOn(other: IdeaModuleInfo, isHmppEnabled: Boolean): Boolean {
if (isHmppEnabled) {
// HACK: allow depending on stdlib even if platforms do not match
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 org.jetbrains.kotlin.idea.core.util.CachedValue
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.isCommon
import java.util.*
@@ -62,7 +63,7 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
val platform = libraryInfo.platform
for (module in getLibraryUsageIndex().modulesLibraryIsUsedIn[libraryInfo.library]) {
for (module in getLibraryUsageIndex().getModulesLibraryIsUsedIn(libraryInfo)) {
if (!processedModules.add(module)) continue
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 {
val modulesLibraryIsUsedIn: MultiMap<Library, Module> = MultiMap.createSet()
private val modulesLibraryIsUsedIn: MultiMap<Library, Module> = MultiMap.createSet()
init {
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.JSLibraryKind
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.util.application.runWriteAction
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.jetbrains.kotlin.test.util.KtTestUtil
import org.jetbrains.kotlin.test.util.addDependency
@@ -316,6 +318,7 @@ class IdeaModuleInfoTest : ModuleTestCase() {
a.addDependency(stdlibJvm)
val b = module("b")
b.setUpPlatform(JsPlatforms.defaultJsPlatform)
b.addDependency(stdlibCommon)
b.addDependency(stdlibJs)
@@ -480,6 +483,14 @@ class IdeaModuleInfoTest : ModuleTestCase() {
kind = JSLibraryKind
)
private fun Module.setUpPlatform(targetPlatform: TargetPlatform) {
createMultiplatformFacetM3(
platformKind = targetPlatform,
dependsOnModuleNames = listOf(),
pureKotlinSourceFolders = listOf(),
)
}
override fun setUp() {
super.setUp()
@@ -199,6 +199,11 @@ public class MultiplatformAnalysisTestGenerated extends AbstractMultiplatformAna
runTest("idea/testData/multiplatform/overrideExpectWithCompositeType/");
}
@TestMetadata("platformDependencyInCommon")
public void testPlatformDependencyInCommon() throws Exception {
runTest("idea/testData/multiplatform/platformDependencyInCommon/");
}
@TestMetadata("platformSpecificChecksInCommon")
public void testPlatformSpecificChecksInCommon() throws Exception {
runTest("idea/testData/multiplatform/platformSpecificChecksInCommon/");