From 2ab9e7edd9ba5daaa42a673892608f42f07f890d Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Thu, 9 Mar 2023 19:47:49 +0200 Subject: [PATCH] fix(53011): Illegal declaration file can be emitted (duplicate parameter names) when spreading tuples into parameter positions (#53028) --- src/compiler/checker.ts | 22 +++++++-- .../reference/spreadParameterTupleType.js | 40 ++++++++++++++++ .../spreadParameterTupleType.symbols | 47 +++++++++++++++++++ .../reference/spreadParameterTupleType.types | 37 +++++++++++++++ .../compiler/spreadParameterTupleType.ts | 17 +++++++ 5 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/spreadParameterTupleType.js create mode 100644 tests/baselines/reference/spreadParameterTupleType.symbols create mode 100644 tests/baselines/reference/spreadParameterTupleType.types create mode 100644 tests/cases/compiler/spreadParameterTupleType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d5c90db5d61..5359974ff87 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12644,11 +12644,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number) { const elementTypes = getTypeArguments(restType); - const associatedNames = restType.target.labeledElementDeclarations; + const associatedNames = getUniqAssociatedNamesFromTupleType(restType); const restParams = map(elementTypes, (t, i) => { // Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name - const tupleLabelName = !!associatedNames && getTupleElementLabel(associatedNames[i]); - const name = tupleLabelName || getParameterNameAtPosition(sig, restIndex + i, restType); + const name = associatedNames && associatedNames[i] ? associatedNames[i] : + getParameterNameAtPosition(sig, restIndex + i, restType); const flags = restType.target.elementFlags[i]; const checkFlags = flags & ElementFlags.Variable ? CheckFlags.RestParameter : flags & ElementFlags.Optional ? CheckFlags.OptionalParameter : 0; @@ -12658,6 +12658,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { }); return concatenate(sig.parameters.slice(0, restIndex), restParams); } + + function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference) { + const associatedNamesMap = new Map<__String, number>(); + return map(type.target.labeledElementDeclarations, labeledElement => { + const name = getTupleElementLabel(labeledElement); + const prevCounter = associatedNamesMap.get(name); + if (prevCounter === undefined) { + associatedNamesMap.set(name, 1); + return name; + } + else { + associatedNamesMap.set(name, prevCounter + 1); + return `${name}_${prevCounter}` as __String; + } + }); + } } function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { diff --git a/tests/baselines/reference/spreadParameterTupleType.js b/tests/baselines/reference/spreadParameterTupleType.js new file mode 100644 index 00000000000..3e79137259d --- /dev/null +++ b/tests/baselines/reference/spreadParameterTupleType.js @@ -0,0 +1,40 @@ +//// [spreadParameterTupleType.ts] +function f1() { + type A = [s: string]; + type C = [...A, ...A]; + + return function fn(...args: C) { } +} + +function f2() { + type A = [a: string]; + type B = [b: string]; + type C = [c: string]; + type D = [...A, ...A, ...B, ...A, ...B, ...B, ...A, ...C]; + + return function fn(...args: D) { } +} + + +//// [spreadParameterTupleType.js] +function f1() { + return function fn() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + }; +} +function f2() { + return function fn() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + }; +} + + +//// [spreadParameterTupleType.d.ts] +declare function f1(): (s: string, s_1: string) => void; +declare function f2(): (a: string, a_1: string, b: string, a_2: string, b_1: string, b_2: string, a_3: string, c: string) => void; diff --git a/tests/baselines/reference/spreadParameterTupleType.symbols b/tests/baselines/reference/spreadParameterTupleType.symbols new file mode 100644 index 00000000000..58dec412b9c --- /dev/null +++ b/tests/baselines/reference/spreadParameterTupleType.symbols @@ -0,0 +1,47 @@ +=== tests/cases/compiler/spreadParameterTupleType.ts === +function f1() { +>f1 : Symbol(f1, Decl(spreadParameterTupleType.ts, 0, 0)) + + type A = [s: string]; +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 0, 15)) + + type C = [...A, ...A]; +>C : Symbol(C, Decl(spreadParameterTupleType.ts, 1, 25)) +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 0, 15)) +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 0, 15)) + + return function fn(...args: C) { } +>fn : Symbol(fn, Decl(spreadParameterTupleType.ts, 4, 10)) +>args : Symbol(args, Decl(spreadParameterTupleType.ts, 4, 23)) +>C : Symbol(C, Decl(spreadParameterTupleType.ts, 1, 25)) +} + +function f2() { +>f2 : Symbol(f2, Decl(spreadParameterTupleType.ts, 5, 1)) + + type A = [a: string]; +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 7, 15)) + + type B = [b: string]; +>B : Symbol(B, Decl(spreadParameterTupleType.ts, 8, 25)) + + type C = [c: string]; +>C : Symbol(C, Decl(spreadParameterTupleType.ts, 9, 25)) + + type D = [...A, ...A, ...B, ...A, ...B, ...B, ...A, ...C]; +>D : Symbol(D, Decl(spreadParameterTupleType.ts, 10, 25)) +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 7, 15)) +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 7, 15)) +>B : Symbol(B, Decl(spreadParameterTupleType.ts, 8, 25)) +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 7, 15)) +>B : Symbol(B, Decl(spreadParameterTupleType.ts, 8, 25)) +>B : Symbol(B, Decl(spreadParameterTupleType.ts, 8, 25)) +>A : Symbol(A, Decl(spreadParameterTupleType.ts, 7, 15)) +>C : Symbol(C, Decl(spreadParameterTupleType.ts, 9, 25)) + + return function fn(...args: D) { } +>fn : Symbol(fn, Decl(spreadParameterTupleType.ts, 13, 10)) +>args : Symbol(args, Decl(spreadParameterTupleType.ts, 13, 23)) +>D : Symbol(D, Decl(spreadParameterTupleType.ts, 10, 25)) +} + diff --git a/tests/baselines/reference/spreadParameterTupleType.types b/tests/baselines/reference/spreadParameterTupleType.types new file mode 100644 index 00000000000..26b3932c954 --- /dev/null +++ b/tests/baselines/reference/spreadParameterTupleType.types @@ -0,0 +1,37 @@ +=== tests/cases/compiler/spreadParameterTupleType.ts === +function f1() { +>f1 : () => (s: string, s_1: string) => void + + type A = [s: string]; +>A : [s: string] + + type C = [...A, ...A]; +>C : [s: string, s: string] + + return function fn(...args: C) { } +>function fn(...args: C) { } : (s: string, s_1: string) => void +>fn : (s: string, s_1: string) => void +>args : [s: string, s: string] +} + +function f2() { +>f2 : () => (a: string, a_1: string, b: string, a_2: string, b_1: string, b_2: string, a_3: string, c: string) => void + + type A = [a: string]; +>A : [a: string] + + type B = [b: string]; +>B : [b: string] + + type C = [c: string]; +>C : [c: string] + + type D = [...A, ...A, ...B, ...A, ...B, ...B, ...A, ...C]; +>D : [a: string, a: string, b: string, a: string, b: string, b: string, a: string, c: string] + + return function fn(...args: D) { } +>function fn(...args: D) { } : (a: string, a_1: string, b: string, a_2: string, b_1: string, b_2: string, a_3: string, c: string) => void +>fn : (a: string, a_1: string, b: string, a_2: string, b_1: string, b_2: string, a_3: string, c: string) => void +>args : [a: string, a: string, b: string, a: string, b: string, b: string, a: string, c: string] +} + diff --git a/tests/cases/compiler/spreadParameterTupleType.ts b/tests/cases/compiler/spreadParameterTupleType.ts new file mode 100644 index 00000000000..f626df3e7e1 --- /dev/null +++ b/tests/cases/compiler/spreadParameterTupleType.ts @@ -0,0 +1,17 @@ +// @declaration: true + +function f1() { + type A = [s: string]; + type C = [...A, ...A]; + + return function fn(...args: C) { } +} + +function f2() { + type A = [a: string]; + type B = [b: string]; + type C = [c: string]; + type D = [...A, ...A, ...B, ...A, ...B, ...B, ...A, ...C]; + + return function fn(...args: D) { } +}