diff --git a/compiler/test/data/typescript/node_modules/overloads/overloadAmbiguities.d.kt b/compiler/test/data/typescript/node_modules/overloads/overloadAmbiguities.d.kt new file mode 100644 index 000000000..d683d414e --- /dev/null +++ b/compiler/test/data/typescript/node_modules/overloads/overloadAmbiguities.d.kt @@ -0,0 +1,80 @@ +// [test] overloadAmbiguities.module_resolved_name.kt +@file:Suppress("INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS") + +import kotlin.js.* +import org.khronos.webgl.* +import org.w3c.dom.* +import org.w3c.dom.events.* +import org.w3c.dom.parsing.* +import org.w3c.dom.svg.* +import org.w3c.dom.url.* +import org.w3c.fetch.* +import org.w3c.files.* +import org.w3c.notifications.* +import org.w3c.performance.* +import org.w3c.workers.* +import org.w3c.xhr.* + +external interface A + +external interface B + +external interface C + +external interface StaticApi { + @nativeInvoke + operator fun invoke(selector: String, context: A = definedExternally): A + @nativeInvoke + operator fun invoke(selector: String): A + @nativeInvoke + operator fun invoke(selector: String, context: B = definedExternally): A + @nativeInvoke + operator fun invoke(element: B): A + @nativeInvoke + operator fun invoke(): A + @nativeInvoke + operator fun invoke(html: String, ownerDocument: C = definedExternally): A + @nativeInvoke + operator fun invoke(html: String, attributes: Any): A +} + +external fun ping(a: Number, b: Number, c: Number, d: Number = definedExternally, e: Number = definedExternally, f: Number = definedExternally) + +external fun ping(a: Number, b: Number, c: Number) + +external fun ping(a: Number, b: Number, c: Number, d: Number = definedExternally) + +external fun ping(a: Number, b: Number, c: Number, d: Number = definedExternally, e: Number = definedExternally) + +external fun ping(id: String) + +external interface Extreme { + fun pong(o: A = definedExternally) + fun pong() + fun pong(o: B = definedExternally) + fun ping(a: String, b: A = definedExternally) + fun ping(a: String) + fun ping(a: String, b: B = definedExternally) + fun foo(a: Number, b: Number, c: Number, d: Number = definedExternally, e: Number = definedExternally, f: Number = definedExternally) + fun foo(a: Number, b: Number, c: Number) + fun foo(a: Number, b: Number, c: Number, d: Number = definedExternally) + fun foo(a: Number, b: Number, c: Number, d: Number = definedExternally, e: Number = definedExternally) + fun foo(a: String) +} + +external interface Simple { + fun ping(a: String, b: A = definedExternally) + fun foo(a: Number, b: Number, c: Number, d: Number = definedExternally, e: Number = definedExternally, f: Number = definedExternally) +} + +external open class BaseApi { + open fun register(preference: String /* "A" | "B" | "C" */ = definedExternally) + open fun completelyOptional(a: Number = definedExternally, b: Number = definedExternally, c: Number = definedExternally) + open fun completelyOptional() + open fun completelyOptional(a: Number = definedExternally) + open fun completelyOptional(a: Number = definedExternally, b: Number = definedExternally) + open fun completelyOptional(a: Number = definedExternally, b: Number = definedExternally, c: String = definedExternally) + open fun completelyOptional(a: Number = definedExternally, b: Number = definedExternally, c: Boolean = definedExternally) +} + +external open class SimpleApi(a: String = definedExternally) : BaseApi \ No newline at end of file diff --git a/compiler/test/data/typescript/node_modules/overloads/overloadAmbiguities.d.ts b/compiler/test/data/typescript/node_modules/overloads/overloadAmbiguities.d.ts new file mode 100644 index 000000000..183b87180 --- /dev/null +++ b/compiler/test/data/typescript/node_modules/overloads/overloadAmbiguities.d.ts @@ -0,0 +1,38 @@ + +interface A {} +interface B {} +interface C {} + +declare interface StaticApi { + (selector: string, context?: A | B): A; + (element: B): A; + (): A; + + (html: string, ownerDocument?: C): A; + (html: string, attributes: Object): A; +} + +declare function ping(a: number, b: number, c: number, d?:number, e?: number, f?: number); +declare function ping(id: string); + +declare interface Extreme { + pong(o?: A | B); + ping(a: string, b?: A | B); + foo(a: number, b: number, c: number, d?:number, e?: number, f?: number); + foo(a: string); +} + +declare interface Simple { + ping(a: string, b?: A); + foo(a: number, b: number, c: number, d?:number, e?: number, f?: number); +} + + +declare class BaseApi { + register(preference?: "A" | "B" | "C"); + completelyOptional(a?: number, b?: number, c?: number | string | boolean) +} + +declare class SimpleApi extends BaseApi { + constructor(a?: string); +} \ No newline at end of file diff --git a/model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/resolveOverloadResolutionAmbiguity.kt b/model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/resolveOverloadResolutionAmbiguity.kt new file mode 100644 index 000000000..04813f0cb --- /dev/null +++ b/model-lowerings-common/src/org/jetbrains/dukat/model/commonLowerings/resolveOverloadResolutionAmbiguity.kt @@ -0,0 +1,132 @@ +package org.jetbrains.dukat.model.commonLowerings + +import org.jetbrains.dukat.astCommon.NameEntity +import org.jetbrains.dukat.astModel.ClassLikeModel +import org.jetbrains.dukat.astModel.ClassModel +import org.jetbrains.dukat.astModel.ConstructorModel +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.ParametersOwnerModel +import org.jetbrains.dukat.astModel.TopLevelModel +import org.jetbrains.dukat.ownerContext.NodeOwner + +private fun List.paramPrefixes(): List> { + val head = takeWhile { it.initializer == null } + val tail = subList(head.size, size) + + return tail.indices.map { head + tail.subList(0, it) } +} + +private fun > unrollDefaults(params: List, copy: (List) -> T): List { + return params.paramPrefixes().map { copy(it) } +} + +private fun > resolveDefaults(params: List, copy: (List) -> T): List { + return if ((params.lastOrNull()?.initializer != null)) { + unrollDefaults(params.dropLast(0), copy) + } else { + emptyList() + } +} + +private fun MemberModel.resolveDefaults(methodsMap: Map, hasManyConstructors: Boolean?): List { + return when (this) { + is MethodModel -> { + if (methodsMap[name] == true) { + listOf(this) + resolveDefaults(parameters) { copy(parameters = it) } + } else { + listOf(this) + } + } + is ConstructorModel -> { + if (hasManyConstructors == true) { + listOf(this) + resolveDefaults(parameters) { copy(parameters = it) } + } else { + listOf(this) + } + } + else -> listOf(this) + } +} + +private fun ClassLikeModel.processMembers(): List { + val methodsData = mutableMapOf() + var hasManyConstructors: Boolean? = null + + members.forEach { + if (it is MethodModel) { + val key = it.name + if (methodsData[key] == null) { + methodsData[key] = false + } else if (methodsData[key] == false) { + methodsData[key] = true + } + } else if (it is ConstructorModel) { + if (hasManyConstructors == null) { + hasManyConstructors = false + } else if (hasManyConstructors == false) { + hasManyConstructors = true + } + } + } + return members.flatMap { member -> + member.resolveDefaults(methodsData, hasManyConstructors) + } +} + + +private fun TopLevelModel.resolveDefaults(topLevelMap: Map): List { + return when (this) { + is FunctionModel -> { + if (topLevelMap[name] == true) { + listOf(this) + resolveDefaults(parameters) { copy(parameters = it) } + } else { + listOf(this) + } + } + else -> listOf(this) + } +} + +private fun ModuleModel.processTopLevelDeclarations(topLevelMap: Map): List { + return declarations.flatMap { declaration -> + declaration.resolveDefaults(topLevelMap) + } +} + +private class ResolveOverloadResolutionAmbiguityLowering : ModelWithOwnerTypeLowering { + override fun lowerInterfaceModel(ownerContext: NodeOwner, parentModule: ModuleModel): InterfaceModel { + val model = ownerContext.node + return super.lowerInterfaceModel(ownerContext.copy(node = model.copy(members = model.processMembers())), parentModule) + } + + override fun lowerClassModel(ownerContext: NodeOwner, parentModule: ModuleModel): ClassModel { + val model = ownerContext.node + return super.lowerClassModel(ownerContext.copy(node = model.copy(members = model.processMembers())), parentModule) + } + + override fun lowerRoot(moduleModel: ModuleModel, ownerContext: NodeOwner): ModuleModel { + val topLevelMap = mutableMapOf() + moduleModel.declarations.forEach { + if (it is FunctionModel) { + val key = it.name + if (topLevelMap[key] == null) { + topLevelMap[key] = false + } else if(topLevelMap[key] == false) { + topLevelMap[key] = true + } + } + } + return super.lowerRoot(moduleModel.copy(declarations = moduleModel.processTopLevelDeclarations(topLevelMap)), ownerContext) + } +} + +class ResolveOverloadResolutionAmbiguity : ModelLowering { + override fun lower(module: ModuleModel): ModuleModel { + return ResolveOverloadResolutionAmbiguityLowering().lowerRoot(module, NodeOwner(module, null)) + } +} \ No newline at end of file diff --git a/typescript/ts-translator/src/org/jetbrains/dukat/ts/translator/TypescriptLowerer.kt b/typescript/ts-translator/src/org/jetbrains/dukat/ts/translator/TypescriptLowerer.kt index 8c448ea69..c1586512a 100644 --- a/typescript/ts-translator/src/org/jetbrains/dukat/ts/translator/TypescriptLowerer.kt +++ b/typescript/ts-translator/src/org/jetbrains/dukat/ts/translator/TypescriptLowerer.kt @@ -26,6 +26,7 @@ import org.jetbrains.dukat.model.commonLowerings.RearrangeConstructors import org.jetbrains.dukat.model.commonLowerings.RemoveConflictingOverloads import org.jetbrains.dukat.model.commonLowerings.RemoveKotlinBuiltIns import org.jetbrains.dukat.model.commonLowerings.RemoveRedundantTypeParams +import org.jetbrains.dukat.model.commonLowerings.ResolveOverloadResolutionAmbiguity import org.jetbrains.dukat.model.commonLowerings.lower import org.jetbrains.dukat.moduleNameResolver.ModuleNameResolver import org.jetbrains.dukat.nodeIntroduction.introduceModels @@ -96,6 +97,7 @@ open class TypescriptLowerer( val models = declarations .introduceModels(moduleNameResolver) .lower( + ResolveOverloadResolutionAmbiguity(), RearrangeConstructors(), RemoveRedundantTypeParams(), RemoveConflictingOverloads(),