[LL] Sort modules from the same KMP project in symbol providers
Sort dependency modules topologically if they belong to the same KMP project, preserving their relative positions. Sorting all modules and changing positions of unrelated modules can be harmful: in the case of a classpath hell, IDE results can become different from runtime behavior. Sorting in place can help to avoid this problem, because conflicting declarations shouldn't be allowed in different source sets of the same multiplatform project. KTIJ-27569
This commit is contained in:
committed by
Space Team
parent
16b1caff7e
commit
ce3c05500e
-33
@@ -1,33 +0,0 @@
|
||||
// LL_FIR_DIVERGENCE
|
||||
// False positive reports of ARGUMENT_TYPE_MISMATCH and UNRESOLVED_REFERENCE are due to bug KT-63382.
|
||||
// LL_FIR_DIVERGENCE
|
||||
// ISSUE: KT-57369
|
||||
|
||||
// MODULE: common
|
||||
// TARGET_PLATFORM: Common
|
||||
interface CompletionHandler {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
expect class CompletionHandlerBase()
|
||||
|
||||
fun invokeOnCompletion(handler: CompletionHandler) {}
|
||||
|
||||
// MODULE: intermediate()()(common)
|
||||
// TARGET_PLATFORM: Common
|
||||
// actual has an additional super type
|
||||
actual class CompletionHandlerBase : CompletionHandler {
|
||||
override fun foo() {}
|
||||
}
|
||||
|
||||
fun cancelFutureOnCompletionAlt(handlerBase: CompletionHandlerBase) {
|
||||
invokeOnCompletion(handlerBase)
|
||||
handlerBase.foo()
|
||||
}
|
||||
|
||||
// MODULE: main()()(common, intermediate)
|
||||
// the order of dependencies is important to reproduce KT-57369
|
||||
fun cancelFutureOnCompletion(handlerBase: CompletionHandlerBase) {
|
||||
invokeOnCompletion(<!ARGUMENT_TYPE_MISMATCH!>handlerBase<!>)
|
||||
handlerBase.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.utils
|
||||
|
||||
import junit.framework.TestCase
|
||||
import kotlin.test.assertFails
|
||||
|
||||
class TopologicalSortTest : TestCase() {
|
||||
fun testEmptyGraph() {
|
||||
assertEquals("", emptyList<Any>(), topologicalSort(emptyList<Any>()) { emptyList<Any>() })
|
||||
}
|
||||
|
||||
fun testSingleNode() {
|
||||
checkGraph(
|
||||
"""
|
||||
A
|
||||
|
||||
""".trimIndent(),
|
||||
listOf("A")
|
||||
)
|
||||
}
|
||||
|
||||
fun testSimpleGraph() {
|
||||
checkGraph(
|
||||
"""
|
||||
A; B; C
|
||||
A > B; B > C; A > C
|
||||
""".trimIndent(),
|
||||
listOf("A", "B", "C")
|
||||
)
|
||||
}
|
||||
|
||||
fun testSimpleGraphShuffledNodes() {
|
||||
checkGraph(
|
||||
"""
|
||||
C; A; B
|
||||
A > B; B > C; A > C
|
||||
""".trimIndent(),
|
||||
listOf("A", "B", "C")
|
||||
)
|
||||
}
|
||||
|
||||
fun testBamboo() {
|
||||
checkGraph(
|
||||
"""
|
||||
A; B; C
|
||||
C > B; B > A
|
||||
""".trimIndent(),
|
||||
listOf("C", "B", "A")
|
||||
)
|
||||
}
|
||||
|
||||
fun testLongerPath() {
|
||||
checkGraph(
|
||||
"""
|
||||
A; B; C; D; E
|
||||
E > D; E > B; D > C; C > B; C > A; B > A
|
||||
""".trimIndent(),
|
||||
listOf("E", "D", "C", "B", "A")
|
||||
)
|
||||
}
|
||||
|
||||
fun testRepeatedEdges() {
|
||||
checkGraph(
|
||||
"""
|
||||
A; B; C
|
||||
C > B; C > B; B > A; C > A
|
||||
""".trimIndent(),
|
||||
listOf("C", "B", "A")
|
||||
)
|
||||
}
|
||||
|
||||
fun testSelfLoopReport() {
|
||||
val graph = parseFromString(
|
||||
"""
|
||||
A
|
||||
A > A
|
||||
""".trimIndent()
|
||||
)
|
||||
assertFails { topologicalSort(graph.nodes, dependencies = { graph.edges[this].orEmpty() }) }
|
||||
}
|
||||
|
||||
fun testDirectLoopReport() {
|
||||
val graph = parseFromString(
|
||||
"""
|
||||
A; B
|
||||
A > B; B > A
|
||||
""".trimIndent()
|
||||
)
|
||||
assertFails { topologicalSort(graph.nodes, dependencies = { graph.edges[this].orEmpty() }) }
|
||||
}
|
||||
|
||||
fun testLongLoopReport() {
|
||||
val graph = parseFromString(
|
||||
"""
|
||||
A; B; C; D
|
||||
D > C; C > B; C > A; A > D
|
||||
""".trimIndent()
|
||||
)
|
||||
assertFails { topologicalSort(graph.nodes, dependencies = { graph.edges[this].orEmpty() }) }
|
||||
}
|
||||
|
||||
private fun checkGraph(description: String, expected: List<String>) {
|
||||
val graph = parseFromString(description)
|
||||
assertEquals("Incorrect order of sorted nodes", expected, sortedNodes(graph))
|
||||
}
|
||||
|
||||
private fun <T> sortedNodes(graph: Graph<T>): List<T> {
|
||||
return topologicalSort(graph.nodes) { graph.edges[this].orEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
private class Graph<T>(
|
||||
val nodes: Set<T>,
|
||||
val edges: Map<T, List<T>>
|
||||
)
|
||||
|
||||
/**
|
||||
* Expected format: line with the list of nodes, line with the list of edges.
|
||||
* [node[;...]]
|
||||
* [node > node[;...]]
|
||||
*/
|
||||
private fun parseFromString(description: String): Graph<String> {
|
||||
val (nodeStrings, edgeStrings) = description.lines()
|
||||
val nodes = nodeStrings.split(";").map(String::trim).toSet()
|
||||
val edges = buildMap<String, MutableList<String>> {
|
||||
edgeStrings.takeIf { it.isNotBlank() }?.split(";")?.forEach { edgeDescription ->
|
||||
val (from, to) = edgeDescription.split(">").map(String::trim)
|
||||
getOrPut(from) { mutableListOf() }.add(to)
|
||||
}
|
||||
}
|
||||
|
||||
return Graph(nodes, edges)
|
||||
}
|
||||
Reference in New Issue
Block a user