From a4fb23694db6dc59266e45a49d70c8ecde72181b Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Thu, 4 Jun 2020 03:45:45 +0200 Subject: [PATCH] Merge conflicting methods and preserve corresponding metadata --- .../class/override/conflictingOverloads.d.kt | 4 +- .../resolveClashingNames.d.kt | 1 - .../data/typescript/misc/literalType.d.kt | 6 +- .../removeConflictingOverloads.kt | 80 +++++++++++-------- 4 files changed, 49 insertions(+), 42 deletions(-) diff --git a/compiler/test/data/typescript/class/override/conflictingOverloads.d.kt b/compiler/test/data/typescript/class/override/conflictingOverloads.d.kt index 6a2239a09..9dc72070c 100644 --- a/compiler/test/data/typescript/class/override/conflictingOverloads.d.kt +++ b/compiler/test/data/typescript/class/override/conflictingOverloads.d.kt @@ -16,7 +16,7 @@ import org.w3c.workers.* import org.w3c.xhr.* external open class Message { - open fun once(event: String, listener: () -> Unit): Message /* this */ + open fun once(event: String /* 'abort' | 'timeout' | 'close' | 'drain' | "cancel" | "submit" | "start" | 'end' | 'finish' | 'basta' */, listener: () -> Unit): Message /* this */ open fun once(event: Any, listener: () -> Unit): Message /* this */ } @@ -26,4 +26,4 @@ external interface Ping { fun ping(a: Boolean) } -external fun addListener(event: String /* "disconnect" | "online" */, listener: (worker: Ping) -> Unit): Message /* */ \ No newline at end of file +external fun addListener(event: String /* "disconnect" | "online" */, listener: (worker: Ping) -> Unit): Message \ No newline at end of file diff --git a/compiler/test/data/typescript/extendExternalDeclarations/resolveClashingNames/resolveClashingNames.d.kt b/compiler/test/data/typescript/extendExternalDeclarations/resolveClashingNames/resolveClashingNames.d.kt index 7b6dc7a0f..17efd116c 100644 --- a/compiler/test/data/typescript/extendExternalDeclarations/resolveClashingNames/resolveClashingNames.d.kt +++ b/compiler/test/data/typescript/extendExternalDeclarations/resolveClashingNames/resolveClashingNames.d.kt @@ -39,7 +39,6 @@ external interface API { fun ping(): Boolean fun pong(a: String): Boolean fun foo(a: String) - fun pong(b: String): Boolean fun pong(c: Number): Boolean fun foo(a: String, b: Boolean = definedExternally) fun foo(a: Number, b: Boolean = definedExternally) diff --git a/compiler/test/data/typescript/misc/literalType.d.kt b/compiler/test/data/typescript/misc/literalType.d.kt index 9913eeedf..fd5bca9df 100644 --- a/compiler/test/data/typescript/misc/literalType.d.kt +++ b/compiler/test/data/typescript/misc/literalType.d.kt @@ -18,13 +18,11 @@ import org.w3c.xhr.* external fun foo(s: String /* "number" | "string" */): dynamic /* Number | String */ external interface I { - fun bar(s: String /* "number" */): Number - fun bar(s: String /* "string" */): String + fun bar(s: String /* "number" | "string" */): dynamic /* Number | String */ } external open class C { - open fun baz(s: String /* "number" */): Number - open fun baz(s: String /* "string" */): String + open fun baz(s: String /* "number" | "string" */): dynamic /* Number | String */ } external interface Foo { diff --git a/model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/removeConflictingOverloads.kt b/model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/removeConflictingOverloads.kt index 103d60271..13909cfa1 100644 --- a/model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/removeConflictingOverloads.kt +++ b/model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/removeConflictingOverloads.kt @@ -3,14 +3,13 @@ package org.jetbrains.dukat.model.commonLowerings import org.jetbrains.dukat.astCommon.IdentifierEntity import org.jetbrains.dukat.astCommon.NameEntity import org.jetbrains.dukat.astCommon.rightMost +import org.jetbrains.dukat.astModel.ClassLikeModel import org.jetbrains.dukat.astModel.ClassModel import org.jetbrains.dukat.astModel.FunctionModel import org.jetbrains.dukat.astModel.InterfaceModel import org.jetbrains.dukat.astModel.MemberModel import org.jetbrains.dukat.astModel.MethodModel import org.jetbrains.dukat.astModel.ModuleModel -import org.jetbrains.dukat.astModel.ParameterModel -import org.jetbrains.dukat.astModel.TopLevelModel import org.jetbrains.dukat.astModel.TypeModel import org.jetbrains.dukat.astModel.TypeValueModel import org.jetbrains.dukat.ownerContext.NodeOwner @@ -22,46 +21,28 @@ private fun TypeModel.withoutMeta(): TypeModel { } } -private fun ParameterModel.withoutMeta(): ParameterModel { - return copy(type = type.withoutMeta()) -} - -private fun MemberModel.normalize(): MemberModel { - return when (this) { - is MethodModel -> copy( - parameters = parameters.map { it.withoutMeta() }, - override = null - ) - else -> this - } -} - -private fun filterOutConflictingOverloads(members: List): List { - return members.groupBy { it.normalize() }.map { (_, bucketMembers) -> - if (bucketMembers.size > 1) { - bucketMembers.first().normalize() - } else { - bucketMembers.first() - } - } -} - private class ConflictingOverloads : TopLevelModelLowering { - override fun lowerInterfaceModel(ownerContext: NodeOwner, parentModule: ModuleModel): InterfaceModel? { - val node = ownerContext.node.copy(members = filterOutConflictingOverloads(ownerContext.node.members)) + val node = ownerContext.node.copy(members = ownerContext.node.resolveMembers()) return super.lowerInterfaceModel(ownerContext.copy(node = node), parentModule) } override fun lowerClassModel(ownerContext: NodeOwner, parentModule: ModuleModel): ClassModel? { - val node = ownerContext.node.copy(members = filterOutConflictingOverloads(ownerContext.node.members)) + val node = ownerContext.node.copy(members = ownerContext.node.resolveMembers()) return super.lowerClassModel(ownerContext.copy(node = node), parentModule) } } private fun mergeTypeModels(a: TypeModel, b: TypeModel): TypeModel { return if ((a is TypeValueModel) && (b is TypeValueModel)) { - a.copy(metaDescription = listOfNotNull(a.metaDescription, b.metaDescription).joinToString(" | ")) + val metaDescription = listOfNotNull(a.metaDescription, b.metaDescription).distinct().let { + if (it.isEmpty()) { + null + } else { + it.joinToString(" | ") + } + } + a.copy(metaDescription = metaDescription) } else { a } @@ -69,7 +50,6 @@ private fun mergeTypeModels(a: TypeModel, b: TypeModel): TypeModel { private fun mergeTypeModelsAsReturn(a: TypeModel, b: TypeModel): TypeModel { return if ((a is TypeValueModel) && (b is TypeValueModel)) { - a.copy(metaDescription = listOfNotNull(a.metaDescription, b.metaDescription).joinToString(" | ")) if (a.withoutMeta() == b.withoutMeta()) { mergeTypeModels(a, b) } else { @@ -80,6 +60,13 @@ private fun mergeTypeModelsAsReturn(a: TypeModel, b: TypeModel): TypeModel { } } +private fun mergeMethodModels(a: MethodModel, b: MethodModel): MethodModel { + val paramsMerged = a.parameters.zip(b.parameters).map { (paramA, paramB) -> + paramA.copy(type = mergeTypeModels(paramA.type, paramB.type)) + } + + return a.copy(parameters = paramsMerged, type = mergeTypeModelsAsReturn(a.type, b.type)) +} private fun mergeFunctionModels(a: FunctionModel, b: FunctionModel): FunctionModel { val paramsMerged = a.parameters.zip(b.parameters).map { (paramA, paramB) -> @@ -89,21 +76,44 @@ private fun mergeFunctionModels(a: FunctionModel, b: FunctionModel): FunctionMod return a.copy(parameters = paramsMerged, type = mergeTypeModelsAsReturn(a.type, b.type)) } -typealias FunctionModelKey = Triple, List> +typealias CallableKey = Triple, List> + +private fun ClassLikeModel.resolveMembers(): List { + val keyCache = mutableMapOf() + + val methodsBucket = members.filterIsInstance(MethodModel::class.java).groupBy { methodModel -> + val key = methodModel.getKey() + keyCache.put(methodModel, key) + key + }.toMutableMap() + + return members.mapNotNull { memberModel -> + when (memberModel) { + is MethodModel -> { + methodsBucket.remove(keyCache[memberModel])?.reduce { a, b -> mergeMethodModels(a, b) } + } + else -> memberModel + } + } +} + +private fun FunctionModel.getKey(): CallableKey { + return Triple(name, parameters.map { it.type.withoutMeta() }, typeParameters.map { it.type.withoutMeta() }) +} -private fun FunctionModel.getKey(): FunctionModelKey { +private fun MethodModel.getKey(): CallableKey { return Triple(name, parameters.map { it.type.withoutMeta() }, typeParameters.map { it.type.withoutMeta() }) } class RemoveConflictingOverloads : ModelLowering { override fun lower(module: ModuleModel): ModuleModel { - val keyCache = mutableMapOf() + val keyCache = mutableMapOf() val functionsBucket = module.declarations.filterIsInstance(FunctionModel::class.java).groupBy { functionModel -> val key = functionModel.getKey() keyCache.put(functionModel, key) key - }.toMutableMap() + }.toMutableMap() val declarationsResolved = module.declarations.mapNotNull { topLevelModel -> when (topLevelModel) {