Skip to content

Commit 681954b

Browse files
committed
Merge pull request microsoft#3048 from Microsoft/duplicateIdentifierBindingElement
Duplicate identifier binding element
2 parents e0931d8 + b702765 commit 681954b

20 files changed

+439
-866
lines changed

src/compiler/binder.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,18 @@ module ts {
440440
else if (isBlockOrCatchScoped(<Declaration>node)) {
441441
bindBlockScopedVariableDeclaration(<Declaration>node);
442442
}
443+
else if (isParameterDeclaration(<VariableLikeDeclaration>node)) {
444+
// It is safe to walk up parent chain to find whether the node is a destructing parameter declaration
445+
// because its parent chain has already been set up, since parents are set before descending into children.
446+
//
447+
// If node is a binding element in parameter declaration, we need to use ParameterExcludes.
448+
// Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration
449+
// For example:
450+
// function foo([a,a]) {} // Duplicate Identifier error
451+
// function bar(a,a) {} // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter
452+
// // which correctly set excluded symbols
453+
bindDeclaration(<Declaration>node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false);
454+
}
443455
else {
444456
bindDeclaration(<Declaration>node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false);
445457
}

src/compiler/checker.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,13 +2051,6 @@ module ts {
20512051
return resolutionResults.pop();
20522052
}
20532053

2054-
function getRootDeclaration(node: Node): Node {
2055-
while (node.kind === SyntaxKind.BindingElement) {
2056-
node = node.parent.parent;
2057-
}
2058-
return node;
2059-
}
2060-
20612054
function getDeclarationContainer(node: Node): Node {
20622055
node = getRootDeclaration(node);
20632056

@@ -9206,13 +9199,6 @@ module ts {
92069199
}
92079200
}
92089201

9209-
function isParameterDeclaration(node: VariableLikeDeclaration) {
9210-
while (node.kind === SyntaxKind.BindingElement) {
9211-
node = <VariableLikeDeclaration>node.parent.parent;
9212-
}
9213-
return node.kind === SyntaxKind.Parameter;
9214-
}
9215-
92169202
// Check that a parameter initializer contains no references to parameters declared to the right of itself
92179203
function checkParameterInitializer(node: VariableLikeDeclaration): void {
92189204
if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) {

src/compiler/utilities.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,18 @@ module ts {
11461146
}
11471147
return false;
11481148
}
1149+
1150+
export function isParameterDeclaration(node: VariableLikeDeclaration) {
1151+
let root = getRootDeclaration(node);
1152+
return root.kind === SyntaxKind.Parameter;
1153+
}
1154+
1155+
export function getRootDeclaration(node: Node): Node {
1156+
while (node.kind === SyntaxKind.BindingElement) {
1157+
node = node.parent.parent;
1158+
}
1159+
return node;
1160+
}
11491161

11501162
export function nodeStartsNewLexicalEnvironment(n: Node): boolean {
11511163
return isFunctionLike(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tests/cases/compiler/declarationEmitDestructuring2.ts(3,13): error TS2300: Duplicate identifier 'a'.
2+
tests/cases/compiler/declarationEmitDestructuring2.ts(3,17): error TS2300: Duplicate identifier 'b'.
3+
tests/cases/compiler/declarationEmitDestructuring2.ts(3,23): error TS2300: Duplicate identifier 'c'.
4+
tests/cases/compiler/declarationEmitDestructuring2.ts(3,41): error TS2300: Duplicate identifier 'a'.
5+
tests/cases/compiler/declarationEmitDestructuring2.ts(3,44): error TS2300: Duplicate identifier 'b'.
6+
tests/cases/compiler/declarationEmitDestructuring2.ts(3,47): error TS2300: Duplicate identifier 'c'.
7+
8+
9+
==== tests/cases/compiler/declarationEmitDestructuring2.ts (6 errors) ====
10+
function f({x = 10, y: [a, b, c, d] = [1, 2, 3, 4]} = { x: 10, y: [2, 4, 6, 8] }) { }
11+
function g([a, b, c, d] = [1, 2, 3, 4]) { }
12+
function h([a, [b], [[c]], {x = 10, y: [a, b, c], z: {a1, b1}}]){ }
13+
~
14+
!!! error TS2300: Duplicate identifier 'a'.
15+
~
16+
!!! error TS2300: Duplicate identifier 'b'.
17+
~
18+
!!! error TS2300: Duplicate identifier 'c'.
19+
~
20+
!!! error TS2300: Duplicate identifier 'a'.
21+
~
22+
!!! error TS2300: Duplicate identifier 'b'.
23+
~
24+
!!! error TS2300: Duplicate identifier 'c'.
25+
function h1([a, [b], [[c]], {x = 10, y = [1, 2, 3], z: {a1, b1}}]){ }

tests/baselines/reference/declarationEmitDestructuring2.symbols

Lines changed: 0 additions & 40 deletions
This file was deleted.

tests/baselines/reference/declarationEmitDestructuring2.types

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES6.ts(96,18): error TS2300: Duplicate identifier 'number'.
2+
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES6.ts(96,26): error TS2300: Duplicate identifier 'number'.
3+
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES6.ts(96,34): error TS2300: Duplicate identifier 'number'.
4+
5+
6+
==== tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES6.ts (3 errors) ====
7+
// Conformance for emitting ES6
8+
9+
// A parameter declaration may specify either an identifier or a binding pattern.
10+
// The identifiers specified in parameter declarations and binding patterns
11+
// in a parameter list must be unique within that parameter list.
12+
13+
// If the declaration includes a type annotation, the parameter is of that type
14+
function a1([a, b, [[c]]]: [number, number, string[][]]) { }
15+
function a2(o: { x: number, a: number }) { }
16+
function a3({j, k, l: {m, n}, q: [a, b, c]}: { j: number, k: string, l: { m: boolean, n: number }, q: (number|string)[] }) { };
17+
function a4({x, a}: { x: number, a: number }) { }
18+
19+
a1([1, 2, [["world"]]]);
20+
a1([1, 2, [["world"]], 3]);
21+
22+
23+
// If the declaration includes an initializer expression (which is permitted only
24+
// when the parameter list occurs in conjunction with a function body),
25+
// the parameter type is the widened form (section 3.11) of the type of the initializer expression.
26+
27+
function b1(z = [undefined, null]) { };
28+
function b2(z = null, o = { x: 0, y: undefined }) { }
29+
function b3({z: {x, y: {j}}} = { z: { x: "hi", y: { j: 1 } } }) { }
30+
31+
interface F1 {
32+
b5(z, y, [, a, b], {p, m: { q, r}});
33+
}
34+
35+
function b6([a, z, y] = [undefined, null, undefined]) { }
36+
function b7([[a], b, [[c, d]]] = [[undefined], undefined, [[undefined, undefined]]]) { }
37+
38+
b1([1, 2, 3]); // z is widen to the type any[]
39+
b2("string", { x: 200, y: "string" });
40+
b2("string", { x: 200, y: true });
41+
42+
43+
// If the declaration specifies a binding pattern, the parameter type is the implied type of that binding pattern (section 5.1.3)
44+
enum Foo { a }
45+
function c0({z: {x, y: {j}}}) { }
46+
function c1({z} = { z: 10 }) { }
47+
function c2({z = 10}) { }
48+
function c3({b}: { b: number|string} = { b: "hello" }) { }
49+
function c5([a, b, [[c]]]) { }
50+
function c6([a, b, [[c=1]]]) { }
51+
52+
c0({z : { x: 1, y: { j: "world" } }}); // Implied type is { z: {x: any, y: {j: any}} }
53+
c0({z : { x: "string", y: { j: true } }}); // Implied type is { z: {x: any, y: {j: any}} }
54+
55+
c1(); // Implied type is {z:number}?
56+
c1({ z: 1 }) // Implied type is {z:number}?
57+
58+
c2({}); // Implied type is {z?: number}
59+
c2({z:1}); // Implied type is {z?: number}
60+
61+
c3({ b: 1 }); // Implied type is { b: number|string }.
62+
63+
c5([1, 2, [["string"]]]); // Implied type is is [any, any, [[any]]]
64+
c5([1, 2, [["string"]], false, true]); // Implied type is is [any, any, [[any]]]
65+
66+
67+
// A parameter can be marked optional by following its name or binding pattern with a question mark (?)
68+
// or by including an initializer.
69+
70+
interface F2 {
71+
d3([a, b, c]?);
72+
d4({x, y, z}?);
73+
e0([a, b, c]);
74+
}
75+
76+
class C2 implements F2 {
77+
constructor() { }
78+
d3() { }
79+
d4() { }
80+
e0([a, b, c]) { }
81+
}
82+
83+
class C3 implements F2 {
84+
d3([a, b, c]) { }
85+
d4({x, y, z}) { }
86+
e0([a, b, c]) { }
87+
}
88+
89+
function d5({x, y} = { x: 1, y: 2 }) { }
90+
d5(); // Parameter is optional as its declaration included an initializer
91+
92+
// Destructuring parameter declarations do not permit type annotations on the individual binding patterns,
93+
// as such annotations would conflict with the already established meaning of colons in object literals.
94+
// Type annotations must instead be written on the top- level parameter declaration
95+
96+
function e1({x: number}) { } // x has type any NOT number
97+
function e2({x}: { x: number }) { } // x is type number
98+
function e3({x}: { x?: number }) { } // x is an optional with type number
99+
function e4({x: [number,string,any] }) { } // x has type [any, any, any]
100+
function e5({x: [a, b, c]}: { x: [number, number, number] }) { } // x has type [any, any, any]
101+
102+
function e6({x: [number, number, number]}) { } // error, duplicate identifier;
103+
~~~~~~
104+
!!! error TS2300: Duplicate identifier 'number'.
105+
~~~~~~
106+
!!! error TS2300: Duplicate identifier 'number'.
107+
~~~~~~
108+
!!! error TS2300: Duplicate identifier 'number'.
109+
110+
111+

tests/baselines/reference/destructuringParameterDeclaration1ES6.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function e3({x}: { x?: number }) { } // x is an optional with type number
9494
function e4({x: [number,string,any] }) { } // x has type [any, any, any]
9595
function e5({x: [a, b, c]}: { x: [number, number, number] }) { } // x has type [any, any, any]
9696

97-
function e6({x: [number, number, number]}) { } // should be an error, duplicate identifier;
97+
function e6({x: [number, number, number]}) { } // error, duplicate identifier;
9898

9999

100100

@@ -166,4 +166,4 @@ function e2({ x }) { } // x is type number
166166
function e3({ x }) { } // x is an optional with type number
167167
function e4({ x: [number, string, any] }) { } // x has type [any, any, any]
168168
function e5({ x: [a, b, c] }) { } // x has type [any, any, any]
169-
function e6({ x: [number, number, number] }) { } // should be an error, duplicate identifier;
169+
function e6({ x: [number, number, number] }) { } // error, duplicate identifier;

0 commit comments

Comments
 (0)