From 8df759b05c175ce721c6243a2ea512434c7a2299 Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Mon, 3 Jun 2019 09:33:38 +0300 Subject: [PATCH] [Expect/Actual] Adjust IdeaModuleStructureOracle to old IDE Resolve The main issue is that we have CombinedModuleInfo in old IDE Resolve, which essentially merges platfor-modules together with its dependsOn-parents. It conceals real module structure, which is fine for practically all clients except for ExpectedActualDeclarationChecker. In particular, it is nearly impossible to check expect/actual via ModuleDescriptors, because you'll always get one and the same 'merged'-module descriptor. So, this commit rewrites IdeaModuleStructureOracle in the following way: - use ModuleInfos instead of ModuleDescriptor, as they allow more fine-grained way to work with modules - use 'unwrapModuleSourceInfo' in seemingly random places in order to get real, non-merged moduleInfos - carefully convert them back to ModuleDescriptors, because EADC work with descriptors This is still not an ideal solution. In particular, last step is flawed: conversion back to ModuleDescriptors will always return merged moduleDescriptor for platform-modules. There's no easy way to fix that except for removing CombinedModuleInfo altogether, so we leave it as known issue. Also, there are some weird changes in testdata (again, because sometimes we see several modules merged together), but they are mostly confusing rather than plain incorrect. --- .../idea/caches/project/multiplatformUtil.kt | 4 +- .../idea/project/IdeaModuleStructureOracle.kt | 96 ++++++++++++++++--- .../diamondDuplicateActuals/bottom/bottom.kt | 6 +- .../diamondDuplicateActuals/top/top.kt | 4 +- .../diamondSeesTwoActuals/top/top.kt | 4 +- .../duplicateActualsExplicit/bottom/bottom.kt | 2 +- .../duplicateActualsExplicit/top/top.kt | 2 +- .../duplicateActualsImplicit/bottom/bottom.kt | 6 +- .../duplicateActualsImplicit/top/top.kt | 6 +- .../bottom/bottom.kt | 2 +- .../top/top.kt | 2 +- .../duplicateExpectsExplicit/bottom/bottom.kt | 2 +- .../duplicateExpectsImplicit/bottom/bottom.kt | 2 +- 13 files changed, 103 insertions(+), 35 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt index 0a26110ca2d..4f6bbfb738e 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt @@ -96,7 +96,7 @@ val ModuleDescriptor.implementingDescriptors: List return implementingModuleInfos.mapNotNull { it.toDescriptor() } } -private fun Module.toInfo(type: SourceType): ModuleSourceInfo? = when (type) { +fun Module.toInfo(type: SourceType): ModuleSourceInfo? = when (type) { PRODUCTION -> productionSourceInfo() TEST -> testSourceInfo() } @@ -115,7 +115,7 @@ val ModuleDescriptor.implementedDescriptors: List return moduleSourceInfo.expectedBy.mapNotNull { it.toDescriptor() } } -private fun ModuleSourceInfo.toDescriptor() = KotlinCacheService.getInstance(module.project) +fun ModuleSourceInfo.toDescriptor() = KotlinCacheService.getInstance(module.project) .getResolutionFacadeByModuleInfo(this, platform)?.moduleDescriptor fun PsiElement.getPlatformModuleInfo(desiredPlatform: TargetPlatform): PlatformModuleInfo? { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaModuleStructureOracle.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaModuleStructureOracle.kt index d1495657a70..8b1a440f28f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaModuleStructureOracle.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaModuleStructureOracle.kt @@ -5,9 +5,12 @@ package org.jetbrains.kotlin.idea.project +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors -import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors +import org.jetbrains.kotlin.idea.caches.project.* +import org.jetbrains.kotlin.idea.caches.project.sourceType +import org.jetbrains.kotlin.idea.core.unwrapModuleSourceInfo import org.jetbrains.kotlin.resolve.ModulePath import org.jetbrains.kotlin.resolve.ModuleStructureOracle import java.util.* @@ -18,31 +21,53 @@ class IdeaModuleStructureOracle : ModuleStructureOracle { } override fun findAllReversedDependsOnPaths(module: ModuleDescriptor): List { - val currentPath: Stack = Stack() + val currentPath: Stack = Stack() - return sequence { - yieldPathsFromSubgraph(module, currentPath, getChilds = { it.implementingDescriptors }) + return sequence { + val root = module.moduleInfo + if (root != null) { + yieldPathsFromSubgraph( + root, + currentPath, + getChilds = { + with(DependsOnGraphHelper) { it.unwrapModuleSourceInfo()?.predecessorsInDependsOnGraph() ?: emptyList() } + } + ) + } + }.map { + it.toModulePath() }.toList() } override fun findAllDependsOnPaths(module: ModuleDescriptor): List { - val currentPath: Stack = Stack() + val currentPath: Stack = Stack() - return sequence { - yieldPathsFromSubgraph(module, currentPath, getChilds = { it.implementedDescriptors }) + return sequence { + val root = module.moduleInfo + if (root != null) { + yieldPathsFromSubgraph( + root, + currentPath, + getChilds = { + with(DependsOnGraphHelper) { it.unwrapModuleSourceInfo()?.successorsInDependsOnGraph() ?: emptyList() } + } + ) + } + }.map { + it.toModulePath() }.toList() } - private suspend fun SequenceScope.yieldPathsFromSubgraph( - root: ModuleDescriptor, - currentPath: Stack, - getChilds: (ModuleDescriptor) -> List + private suspend fun SequenceScope.yieldPathsFromSubgraph( + root: ModuleInfo, + currentPath: Stack, + getChilds: (ModuleInfo) -> List ) { currentPath.push(root) val childs = getChilds(root) if (childs.isEmpty()) { - yield(ModulePath(currentPath.toList())) + yield(ModuleInfoPath(currentPath.toList())) } else { childs.forEach { yieldPathsFromSubgraph(it, currentPath, getChilds) @@ -51,4 +76,47 @@ class IdeaModuleStructureOracle : ModuleStructureOracle { currentPath.pop() } -} \ No newline at end of file + + private class ModuleInfoPath(val nodes: List) + + private fun ModuleInfoPath.toModulePath(): ModulePath = + ModulePath(nodes.mapNotNull { it.unwrapModuleSourceInfo()?.toDescriptor() }) +} + +object DependsOnGraphHelper { + fun ModuleDescriptor.predecessorsInDependsOnGraph(): List { + return moduleSourceInfo + ?.predecessorsInDependsOnGraph() + ?.mapNotNull { it.toDescriptor() } + ?: emptyList() + } + + fun ModuleSourceInfo.predecessorsInDependsOnGraph(): List { + return this.module.predecessorsInDependsOnGraph().mapNotNull { it.toInfo(sourceType) } + } + + fun Module.predecessorsInDependsOnGraph(): List { + return implementingModules + } + + fun ModuleDescriptor.successorsInDependsOnGraph(): List { + return moduleSourceInfo + ?.successorsInDependsOnGraph() + ?.mapNotNull { it.toDescriptor() } + ?: emptyList() + } + + fun ModuleSourceInfo.successorsInDependsOnGraph(): List { + return module.successorsInDependsOnGraph().mapNotNull { it.toInfo(sourceType) } + } + + fun Module.successorsInDependsOnGraph(): List { + return implementedModules + } +} + +private val ModuleDescriptor.moduleInfo: ModuleInfo? + get() = getCapability(ModuleInfo.Capability)?.unwrapModuleSourceInfo() + +private val ModuleDescriptor.moduleSourceInfo: ModuleSourceInfo? + get() = moduleInfo as? ModuleSourceInfo \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/bottom/bottom.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/bottom/bottom.kt index 51e1f53ea3a..668cc774835 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/bottom/bottom.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/bottom/bottom.kt @@ -1,6 +1,6 @@ package sample -actual class A { +actual class A { actual fun foo(): Int = 45 fun fromBottom(): Int = 0 } @@ -11,6 +11,6 @@ fun main() { // Any behaviour is acceptable, as the code is erroneous. // At the time of writing this test, we resolve to nearest A, i.e. // 'fromBottom' is resolved, and 'fromLeft' is not. - A().fromLeft() - A().fromBottom() + A().fromLeft() + A().fromBottom() } \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/top/top.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/top/top.kt index 73b99064d04..f4efb6f71fb 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/top/top.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondDuplicateActuals/top/top.kt @@ -1,5 +1,5 @@ package sample -expect class A { - fun foo(): Int +expect class A { + fun foo(): Int } \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondSeesTwoActuals/top/top.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondSeesTwoActuals/top/top.kt index a359748ada8..c3c51289f62 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondSeesTwoActuals/top/top.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/diamondSeesTwoActuals/top/top.kt @@ -1,5 +1,5 @@ package sample -expect class A { - fun foo(): Int +expect class A { + fun foo(): Int } \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/bottom/bottom.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/bottom/bottom.kt index 678301c2c64..28c702441a3 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/bottom/bottom.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/bottom/bottom.kt @@ -1,3 +1,3 @@ package foo -actual class A +actual class A diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/top/top.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/top/top.kt index f38997819fd..84366910dfb 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/top/top.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsExplicit/top/top.kt @@ -1,3 +1,3 @@ package foo -expect class A \ No newline at end of file +expect class A \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/bottom/bottom.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/bottom/bottom.kt index e67a624a9a4..f446f5fd76b 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/bottom/bottom.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/bottom/bottom.kt @@ -1,6 +1,6 @@ package foo -class ActualInMiddleCompatibleInBottom -actual class CompatibleInMiddleActualInBottom +class ActualInMiddleCompatibleInBottom +actual class CompatibleInMiddleActualInBottom -class CompatibleInMiddleAndBottom +class CompatibleInMiddleAndBottom diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/top/top.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/top/top.kt index 986d4c60a35..f181e7bdbfa 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/top/top.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsImplicit/top/top.kt @@ -1,6 +1,6 @@ package foo -expect class ActualInMiddleCompatibleInBottom -expect class CompatibleInMiddleActualInBottom +expect class ActualInMiddleCompatibleInBottom +expect class CompatibleInMiddleActualInBottom -expect class CompatibleInMiddleAndBottom \ No newline at end of file +expect class CompatibleInMiddleAndBottom \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/bottom/bottom.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/bottom/bottom.kt index d6d3776a3d4..b3de2e61b79 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/bottom/bottom.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/bottom/bottom.kt @@ -1,3 +1,3 @@ package foo -private class A \ No newline at end of file +private class A \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/top/top.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/top/top.kt index 4daacb52744..84366910dfb 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/top/top.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateActualsOneWeaklyIncompatible/top/top.kt @@ -1,3 +1,3 @@ package foo -expect class A \ No newline at end of file +expect class A \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsExplicit/bottom/bottom.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsExplicit/bottom/bottom.kt index 52b8666f932..0f462fb0178 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsExplicit/bottom/bottom.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsExplicit/bottom/bottom.kt @@ -1 +1 @@ -actual class A \ No newline at end of file +actual class A \ No newline at end of file diff --git a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsImplicit/bottom/bottom.kt b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsImplicit/bottom/bottom.kt index 72e8e852980..97858c1e487 100644 --- a/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsImplicit/bottom/bottom.kt +++ b/idea/testData/multiModuleHighlighting/hierarchicalExpectActualMatching/duplicateExpectsImplicit/bottom/bottom.kt @@ -1,3 +1,3 @@ // Note that here we have no ambiguity, becuase we don't consider // declarations without 'expect' as potential weakly-compatible 'expect'-counterpart -actual class A \ No newline at end of file +actual class A \ No newline at end of file