Skip to content

Commit 115317e

Browse files
committed
Fixes microsoft#8675 by allowing duplicate identifiers across declarations.
Duplicate identifiers *within the same block* are still disallowed.
1 parent 9ffc7a9 commit 115317e

File tree

57 files changed

+676
-622
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+676
-622
lines changed

src/compiler/checker.ts

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12893,6 +12893,90 @@ namespace ts {
1289312893
}
1289412894
}
1289512895

12896+
function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) {
12897+
const getter = 1, setter = 2, property = getter | setter;
12898+
12899+
const instanceNames: Map<number> = {};
12900+
const staticNames: Map<number> = {};
12901+
for (const member of node.members) {
12902+
let memberName: string;
12903+
if (member.name) {
12904+
switch (member.name.kind) {
12905+
case SyntaxKind.StringLiteral:
12906+
case SyntaxKind.NumericLiteral:
12907+
case SyntaxKind.Identifier:
12908+
memberName = (member.name as LiteralExpression|Identifier).text;
12909+
break;
12910+
default:
12911+
continue;
12912+
}
12913+
}
12914+
12915+
const static = forEach(member.modifiers, m => m.kind === SyntaxKind.StaticKeyword);
12916+
const names = static ? staticNames : instanceNames;
12917+
switch (member.kind) {
12918+
case SyntaxKind.Constructor:
12919+
for (const param of (member as ConstructorDeclaration).parameters) {
12920+
if (isParameterPropertyDeclaration(param)) {
12921+
addName(names, param, (param.name as Identifier).text, property);
12922+
}
12923+
}
12924+
break;
12925+
12926+
case SyntaxKind.GetAccessor:
12927+
addName(names, member.name, memberName, getter);
12928+
break;
12929+
12930+
case SyntaxKind.SetAccessor:
12931+
addName(names, member.name, memberName, setter);
12932+
break;
12933+
12934+
case SyntaxKind.PropertyDeclaration:
12935+
addName(names, member.name, memberName, property);
12936+
break;
12937+
}
12938+
}
12939+
12940+
function addName(names: Map<number>, location: Node, name: string, meaning: number) {
12941+
if (hasProperty(names, name)) {
12942+
const prev = names[name];
12943+
if (prev & meaning) {
12944+
error(location, Diagnostics.Duplicate_identifier_0, name);
12945+
} else {
12946+
names[name] = prev | meaning;
12947+
}
12948+
}
12949+
else {
12950+
names[name] = meaning;
12951+
}
12952+
}
12953+
}
12954+
12955+
function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) {
12956+
const names: Map<boolean> = {};
12957+
for (const member of node.members) {
12958+
if (member.kind == SyntaxKind.PropertySignature) {
12959+
let memberName: string;
12960+
switch (member.name.kind) {
12961+
case SyntaxKind.StringLiteral:
12962+
case SyntaxKind.NumericLiteral:
12963+
case SyntaxKind.Identifier:
12964+
memberName = (member.name as LiteralExpression|Identifier).text;
12965+
break;
12966+
default:
12967+
continue;
12968+
}
12969+
12970+
if (hasProperty(names, memberName)) {
12971+
error(member, Diagnostics.Duplicate_identifier_0, memberName);
12972+
}
12973+
else {
12974+
names[memberName] = true;
12975+
}
12976+
}
12977+
}
12978+
}
12979+
1289612980
function checkTypeForDuplicateIndexSignatures(node: Node) {
1289712981
if (node.kind === SyntaxKind.InterfaceDeclaration) {
1289812982
const nodeSymbol = getSymbolOfNode(node);
@@ -13177,6 +13261,7 @@ namespace ts {
1317713261
const type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
1317813262
checkIndexConstraints(type);
1317913263
checkTypeForDuplicateIndexSignatures(node);
13264+
checkObjectTypeForDuplicateDeclarations(node);
1318013265
}
1318113266
}
1318213267

@@ -15176,6 +15261,7 @@ namespace ts {
1517615261
const typeWithThis = getTypeWithThisArgument(type);
1517715262
const staticType = <ObjectType>getTypeOfSymbol(symbol);
1517815263
checkTypeParameterListsIdentical(node, symbol);
15264+
checkClassForDuplicateDeclarations(node);
1517915265

1518015266
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
1518115267
if (baseTypeNode) {
@@ -15453,6 +15539,7 @@ namespace ts {
1545315539
checkIndexConstraints(type);
1545415540
}
1545515541
}
15542+
checkObjectTypeForDuplicateDeclarations(node);
1545615543
}
1545715544
forEach(getInterfaceBaseTypeNodes(node), heritageElement => {
1545815545
if (!isSupportedExpressionWithTypeArguments(heritageElement)) {
@@ -18146,7 +18233,7 @@ namespace ts {
1814618233
else {
1814718234
const existingKind = seen[(<Identifier>name).text];
1814818235
if (currentKind === Property && existingKind === Property) {
18149-
continue;
18236+
grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, (<Identifier>name).text);
1815018237
}
1815118238
else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) {
1815218239
if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) {

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2027,7 +2027,7 @@ namespace ts {
20272027
BlockScopedVariableExcludes = Value,
20282028

20292029
ParameterExcludes = Value,
2030-
PropertyExcludes = Value,
2030+
PropertyExcludes = None,
20312031
EnumMemberExcludes = Value,
20322032
FunctionExcludes = Value & ~(Function | ValueModule),
20332033
ClassExcludes = (Value | Type) & ~(ValueModule | Interface), // class-interface mergability done in checker.ts
Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralError.ts(2,17): error TS1005: ',' expected.
22
tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralError.ts(3,17): error TS1005: ',' expected.
3-
tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralError.ts(6,5): error TS2300: Duplicate identifier '0b11010'.
4-
tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralError.ts(7,5): error TS2300: Duplicate identifier '26'.
5-
tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralError.ts(8,5): error TS2300: Duplicate identifier '"26"'.
63

74

8-
==== tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralError.ts (5 errors) ====
5+
==== tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralError.ts (2 errors) ====
96
// error
107
var bin1 = 0B1102110;
118
~~~~
@@ -16,13 +13,7 @@ tests/cases/conformance/es6/binaryAndOctalIntegerLiteral/binaryIntegerLiteralErr
1613

1714
var obj1 = {
1815
0b11010: "hi",
19-
~~~~~~~
20-
!!! error TS2300: Duplicate identifier '0b11010'.
2116
26: "Hello",
22-
~~
23-
!!! error TS2300: Duplicate identifier '26'.
2417
"26": "world",
25-
~~~~
26-
!!! error TS2300: Duplicate identifier '"26"'.
2718
};
2819

tests/baselines/reference/callSignaturesWithParameterInitializers2.errors.txt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts(4,14): error TS2371: A parameter initializer is only allowed in a function or constructor implementation.
22
tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts(11,9): error TS2371: A parameter initializer is only allowed in a function or constructor implementation.
3-
tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts(20,5): error TS2300: Duplicate identifier 'foo'.
43
tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts(20,9): error TS2371: A parameter initializer is only allowed in a function or constructor implementation.
54
tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts(20,15): error TS1005: '{' expected.
6-
tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts(21,5): error TS2300: Duplicate identifier 'foo'.
75

86

9-
==== tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts (6 errors) ====
7+
==== tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts (4 errors) ====
108
// Optional parameters allow initializers only in implementation signatures
119
// All the below declarations are errors
1210

@@ -31,15 +29,11 @@ tests/cases/conformance/types/objectTypeLiteral/callSignatures/callSignaturesWit
3129

3230
var b = {
3331
foo(x = 1), // error
34-
~~~
35-
!!! error TS2300: Duplicate identifier 'foo'.
3632
~~~~~
3733
!!! error TS2371: A parameter initializer is only allowed in a function or constructor implementation.
3834
~
3935
!!! error TS1005: '{' expected.
4036
foo(x = 1) { }, // error
41-
~~~
42-
!!! error TS2300: Duplicate identifier 'foo'.
4337
}
4438

4539
b.foo();

tests/baselines/reference/classAndInterfaceMergeConflictingMembers.errors.txt

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts ===
2+
declare class C1 {
3+
>C1 : Symbol(C1, Decl(classAndInterfaceMergeConflictingMembers.ts, 0, 0), Decl(classAndInterfaceMergeConflictingMembers.ts, 2, 1))
4+
5+
public x : number;
6+
>x : Symbol(C1.x, Decl(classAndInterfaceMergeConflictingMembers.ts, 0, 18), Decl(classAndInterfaceMergeConflictingMembers.ts, 4, 14))
7+
}
8+
9+
interface C1 {
10+
>C1 : Symbol(C1, Decl(classAndInterfaceMergeConflictingMembers.ts, 0, 0), Decl(classAndInterfaceMergeConflictingMembers.ts, 2, 1))
11+
12+
x : number;
13+
>x : Symbol(C1.x, Decl(classAndInterfaceMergeConflictingMembers.ts, 0, 18), Decl(classAndInterfaceMergeConflictingMembers.ts, 4, 14))
14+
}
15+
16+
declare class C2 {
17+
>C2 : Symbol(C2, Decl(classAndInterfaceMergeConflictingMembers.ts, 6, 1), Decl(classAndInterfaceMergeConflictingMembers.ts, 10, 1))
18+
19+
protected x : number;
20+
>x : Symbol(C2.x, Decl(classAndInterfaceMergeConflictingMembers.ts, 8, 18), Decl(classAndInterfaceMergeConflictingMembers.ts, 12, 14))
21+
}
22+
23+
interface C2 {
24+
>C2 : Symbol(C2, Decl(classAndInterfaceMergeConflictingMembers.ts, 6, 1), Decl(classAndInterfaceMergeConflictingMembers.ts, 10, 1))
25+
26+
x : number;
27+
>x : Symbol(C2.x, Decl(classAndInterfaceMergeConflictingMembers.ts, 8, 18), Decl(classAndInterfaceMergeConflictingMembers.ts, 12, 14))
28+
}
29+
30+
declare class C3 {
31+
>C3 : Symbol(C3, Decl(classAndInterfaceMergeConflictingMembers.ts, 14, 1), Decl(classAndInterfaceMergeConflictingMembers.ts, 18, 1))
32+
33+
private x : number;
34+
>x : Symbol(C3.x, Decl(classAndInterfaceMergeConflictingMembers.ts, 16, 18), Decl(classAndInterfaceMergeConflictingMembers.ts, 20, 14))
35+
}
36+
37+
interface C3 {
38+
>C3 : Symbol(C3, Decl(classAndInterfaceMergeConflictingMembers.ts, 14, 1), Decl(classAndInterfaceMergeConflictingMembers.ts, 18, 1))
39+
40+
x : number;
41+
>x : Symbol(C3.x, Decl(classAndInterfaceMergeConflictingMembers.ts, 16, 18), Decl(classAndInterfaceMergeConflictingMembers.ts, 20, 14))
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts ===
2+
declare class C1 {
3+
>C1 : C1
4+
5+
public x : number;
6+
>x : number
7+
}
8+
9+
interface C1 {
10+
>C1 : C1
11+
12+
x : number;
13+
>x : number
14+
}
15+
16+
declare class C2 {
17+
>C2 : C2
18+
19+
protected x : number;
20+
>x : number
21+
}
22+
23+
interface C2 {
24+
>C2 : C2
25+
26+
x : number;
27+
>x : number
28+
}
29+
30+
declare class C3 {
31+
>C3 : C3
32+
33+
private x : number;
34+
>x : number
35+
}
36+
37+
interface C3 {
38+
>C3 : C3
39+
40+
x : number;
41+
>x : number
42+
}

tests/baselines/reference/classAndInterfaceWithSameName.errors.txt

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/classes/classDeclarations/classAndInterfaceWithSameName.ts ===
2+
class C { foo: string; }
3+
>C : Symbol(C, Decl(classAndInterfaceWithSameName.ts, 0, 0), Decl(classAndInterfaceWithSameName.ts, 0, 24))
4+
>foo : Symbol(C.foo, Decl(classAndInterfaceWithSameName.ts, 0, 9), Decl(classAndInterfaceWithSameName.ts, 1, 13))
5+
6+
interface C { foo: string; }
7+
>C : Symbol(C, Decl(classAndInterfaceWithSameName.ts, 0, 0), Decl(classAndInterfaceWithSameName.ts, 0, 24))
8+
>foo : Symbol(C.foo, Decl(classAndInterfaceWithSameName.ts, 0, 9), Decl(classAndInterfaceWithSameName.ts, 1, 13))
9+
10+
module M {
11+
>M : Symbol(M, Decl(classAndInterfaceWithSameName.ts, 1, 28))
12+
13+
class D {
14+
>D : Symbol(D, Decl(classAndInterfaceWithSameName.ts, 3, 10), Decl(classAndInterfaceWithSameName.ts, 6, 5))
15+
16+
bar: string;
17+
>bar : Symbol(D.bar, Decl(classAndInterfaceWithSameName.ts, 4, 13), Decl(classAndInterfaceWithSameName.ts, 8, 17))
18+
}
19+
20+
interface D {
21+
>D : Symbol(D, Decl(classAndInterfaceWithSameName.ts, 3, 10), Decl(classAndInterfaceWithSameName.ts, 6, 5))
22+
23+
bar: string;
24+
>bar : Symbol(D.bar, Decl(classAndInterfaceWithSameName.ts, 4, 13), Decl(classAndInterfaceWithSameName.ts, 8, 17))
25+
}
26+
}

0 commit comments

Comments
 (0)