Skip to content

Commit 9b6a720

Browse files
committed
Merge conflicting functions and preserve corresponding metadata
see #174
1 parent a111778 commit 9b6a720

File tree

3 files changed

+58
-19
lines changed

3 files changed

+58
-19
lines changed

compiler/test/data/typescript/class/override/conflictingOverloads.d.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ external interface Ping {
2626
fun ping(a: Boolean)
2727
}
2828

29-
external fun addListener(event: String, listener: (worker: Ping) -> Unit): Message
29+
external fun addListener(event: String /* "disconnect" | "online" */, listener: (worker: Ping) -> Unit): Message /* */

compiler/test/data/typescript/misc/literalType.d.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ import org.w3c.performance.*
1515
import org.w3c.workers.*
1616
import org.w3c.xhr.*
1717

18-
external fun foo(s: String /* "number" */): Number
19-
20-
external fun foo(s: String /* "string" */): String
18+
external fun foo(s: String /* "number" | "string" */): dynamic /* Number | String */
2119

2220
external interface I {
2321
fun bar(s: String /* "number" */): Number

model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/removeConflictingOverloads.kt

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.jetbrains.dukat.model.commonLowerings
22

3+
import org.jetbrains.dukat.astCommon.IdentifierEntity
4+
import org.jetbrains.dukat.astCommon.NameEntity
5+
import org.jetbrains.dukat.astCommon.rightMost
36
import org.jetbrains.dukat.astModel.ClassModel
47
import org.jetbrains.dukat.astModel.FunctionModel
58
import org.jetbrains.dukat.astModel.InterfaceModel
@@ -33,13 +36,6 @@ private fun MemberModel.normalize(): MemberModel {
3336
}
3437
}
3538

36-
private fun TopLevelModel.normalize(): TopLevelModel {
37-
return when (this) {
38-
is FunctionModel -> copy(parameters = parameters.map { it.withoutMeta() })
39-
else -> this
40-
}
41-
}
42-
4339
private fun filterOutConflictingOverloads(members: List<MemberModel>): List<MemberModel> {
4440
return members.groupBy { it.normalize() }.map { (_, bucketMembers) ->
4541
if (bucketMembers.size > 1) {
@@ -50,26 +46,71 @@ private fun filterOutConflictingOverloads(members: List<MemberModel>): List<Memb
5046
}
5147
}
5248

53-
private class ConflictingOverloads() : ModelWithOwnerTypeLowering {
49+
private class ConflictingOverloads : TopLevelModelLowering {
5450

55-
override fun lowerInterfaceModel(ownerContext: NodeOwner<InterfaceModel>, parentModule: ModuleModel): InterfaceModel {
51+
override fun lowerInterfaceModel(ownerContext: NodeOwner<InterfaceModel>, parentModule: ModuleModel): InterfaceModel? {
5652
val node = ownerContext.node.copy(members = filterOutConflictingOverloads(ownerContext.node.members))
5753
return super.lowerInterfaceModel(ownerContext.copy(node = node), parentModule)
5854
}
5955

60-
override fun lowerClassModel(ownerContext: NodeOwner<ClassModel>, parentModule: ModuleModel): ClassModel {
56+
override fun lowerClassModel(ownerContext: NodeOwner<ClassModel>, parentModule: ModuleModel): ClassModel? {
6157
val node = ownerContext.node.copy(members = filterOutConflictingOverloads(ownerContext.node.members))
6258
return super.lowerClassModel(ownerContext.copy(node = node), parentModule)
6359
}
6460
}
6561

62+
private fun mergeTypeModels(a: TypeModel, b: TypeModel): TypeModel {
63+
return if ((a is TypeValueModel) && (b is TypeValueModel)) {
64+
a.copy(metaDescription = listOfNotNull(a.metaDescription, b.metaDescription).joinToString(" | "))
65+
} else {
66+
a
67+
}
68+
}
69+
70+
private fun mergeTypeModelsAsReturn(a: TypeModel, b: TypeModel): TypeModel {
71+
return if ((a is TypeValueModel) && (b is TypeValueModel)) {
72+
a.copy(metaDescription = listOfNotNull(a.metaDescription, b.metaDescription).joinToString(" | "))
73+
if (a.withoutMeta() == b.withoutMeta()) {
74+
mergeTypeModels(a, b)
75+
} else {
76+
TypeValueModel(IdentifierEntity("dynamic"), listOf(), listOfNotNull(a.fqName?.rightMost(), b.fqName?.rightMost()).joinToString(" | ") { it.toString() }, null)
77+
}
78+
} else {
79+
TypeValueModel(IdentifierEntity("dynamic"), listOf(), null, null)
80+
}
81+
}
82+
83+
84+
private fun mergeFunctionModels(a: FunctionModel, b: FunctionModel): FunctionModel {
85+
val paramsMerged = a.parameters.zip(b.parameters).map { (paramA, paramB) ->
86+
paramA.copy(type = mergeTypeModels(paramA.type, paramB.type))
87+
}
88+
89+
return a.copy(parameters = paramsMerged, type = mergeTypeModelsAsReturn(a.type, b.type))
90+
}
91+
92+
typealias FunctionModelKey = Triple<NameEntity, List<TypeModel>, List<TypeModel>>
93+
94+
private fun FunctionModel.getKey(): FunctionModelKey {
95+
return Triple(name, parameters.map { it.type.withoutMeta() }, typeParameters.map { it.type.withoutMeta() })
96+
}
97+
6698
class RemoveConflictingOverloads : ModelLowering {
6799
override fun lower(module: ModuleModel): ModuleModel {
68-
val declarationsResolved = module.declarations.groupBy { it.normalize() }.map { (_, bucketMembers) ->
69-
if (bucketMembers.size > 1) {
70-
bucketMembers.first().normalize()
71-
} else {
72-
bucketMembers.first()
100+
val keyCache = mutableMapOf<FunctionModel, FunctionModelKey>()
101+
102+
val functionsBucket = module.declarations.filterIsInstance(FunctionModel::class.java).groupBy { functionModel ->
103+
val key = functionModel.getKey()
104+
keyCache.put(functionModel, key)
105+
key
106+
}.toMutableMap()
107+
108+
val declarationsResolved = module.declarations.mapNotNull { topLevelModel ->
109+
when (topLevelModel) {
110+
is FunctionModel -> {
111+
functionsBucket.remove(keyCache[topLevelModel])?.reduce { a, b -> mergeFunctionModels(a, b) }
112+
}
113+
else -> topLevelModel
73114
}
74115
}
75116

0 commit comments

Comments
 (0)