Skip to content

Commit 3dde134

Browse files
committed
Merge pull request #8696 from RyanCavanaugh/fix8675
Allow duplicate identifiers across declarations.
2 parents 1ee9cb0 + 675d176 commit 3dde134

File tree

64 files changed

+731
-487
lines changed

Some content is hidden

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

64 files changed

+731
-487
lines changed

src/compiler/checker.ts

+108-6
Original file line numberDiff line numberDiff line change
@@ -12954,6 +12954,82 @@ namespace ts {
1295412954
}
1295512955
}
1295612956

12957+
function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) {
12958+
const getter = 1, setter = 2, property = getter | setter;
12959+
12960+
const instanceNames: Map<number> = {};
12961+
const staticNames: Map<number> = {};
12962+
for (const member of node.members) {
12963+
if (member.kind === SyntaxKind.Constructor) {
12964+
for (const param of (member as ConstructorDeclaration).parameters) {
12965+
if (isParameterPropertyDeclaration(param)) {
12966+
addName(instanceNames, param.name, (param.name as Identifier).text, property);
12967+
}
12968+
}
12969+
}
12970+
else {
12971+
const static = forEach(member.modifiers, m => m.kind === SyntaxKind.StaticKeyword);
12972+
const names = static ? staticNames : instanceNames;
12973+
12974+
const memberName = member.name && getPropertyNameForPropertyNameNode(member.name);
12975+
switch (member.kind) {
12976+
case SyntaxKind.GetAccessor:
12977+
addName(names, member.name, memberName, getter);
12978+
break;
12979+
12980+
case SyntaxKind.SetAccessor:
12981+
addName(names, member.name, memberName, setter);
12982+
break;
12983+
12984+
case SyntaxKind.PropertyDeclaration:
12985+
addName(names, member.name, memberName, property);
12986+
break;
12987+
}
12988+
}
12989+
}
12990+
12991+
function addName(names: Map<number>, location: Node, name: string, meaning: number) {
12992+
if (hasProperty(names, name)) {
12993+
const prev = names[name];
12994+
if (prev & meaning) {
12995+
error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location));
12996+
}
12997+
else {
12998+
names[name] = prev | meaning;
12999+
}
13000+
}
13001+
else {
13002+
names[name] = meaning;
13003+
}
13004+
}
13005+
}
13006+
13007+
function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) {
13008+
const names: Map<boolean> = {};
13009+
for (const member of node.members) {
13010+
if (member.kind == SyntaxKind.PropertySignature) {
13011+
let memberName: string;
13012+
switch (member.name.kind) {
13013+
case SyntaxKind.StringLiteral:
13014+
case SyntaxKind.NumericLiteral:
13015+
case SyntaxKind.Identifier:
13016+
memberName = (member.name as LiteralExpression | Identifier).text;
13017+
break;
13018+
default:
13019+
continue;
13020+
}
13021+
13022+
if (hasProperty(names, memberName)) {
13023+
error(member.symbol.valueDeclaration.name, Diagnostics.Duplicate_identifier_0, memberName);
13024+
error(member.name, Diagnostics.Duplicate_identifier_0, memberName);
13025+
}
13026+
else {
13027+
names[memberName] = true;
13028+
}
13029+
}
13030+
}
13031+
}
13032+
1295713033
function checkTypeForDuplicateIndexSignatures(node: Node) {
1295813034
if (node.kind === SyntaxKind.InterfaceDeclaration) {
1295913035
const nodeSymbol = getSymbolOfNode(node);
@@ -13238,6 +13314,7 @@ namespace ts {
1323813314
const type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
1323913315
checkIndexConstraints(type);
1324013316
checkTypeForDuplicateIndexSignatures(node);
13317+
checkObjectTypeForDuplicateDeclarations(node);
1324113318
}
1324213319
}
1324313320

@@ -14430,6 +14507,10 @@ namespace ts {
1443014507
if (node.initializer) {
1443114508
checkTypeAssignableTo(checkExpressionCached(node.initializer), declarationType, node, /*headMessage*/ undefined);
1443214509
}
14510+
if (!areDeclarationFlagsIdentical(node, symbol.valueDeclaration)) {
14511+
error(symbol.valueDeclaration.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name));
14512+
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name));
14513+
}
1443314514
}
1443414515
if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature) {
1443514516
// We know we don't have a binding pattern or computed name here
@@ -14444,6 +14525,21 @@ namespace ts {
1444414525
}
1444514526
}
1444614527

14528+
function areDeclarationFlagsIdentical(left: Declaration, right: Declaration) {
14529+
if (hasQuestionToken(left) !== hasQuestionToken(right)) {
14530+
return false;
14531+
}
14532+
14533+
const interestingFlags = NodeFlags.Private |
14534+
NodeFlags.Protected |
14535+
NodeFlags.Async |
14536+
NodeFlags.Abstract |
14537+
NodeFlags.Readonly |
14538+
NodeFlags.Static;
14539+
14540+
return (left.flags & interestingFlags) === (right.flags & interestingFlags);
14541+
}
14542+
1444714543
function checkVariableDeclaration(node: VariableDeclaration) {
1444814544
checkGrammarVariableDeclaration(node);
1444914545
return checkVariableLikeDeclaration(node);
@@ -15237,6 +15333,7 @@ namespace ts {
1523715333
const typeWithThis = getTypeWithThisArgument(type);
1523815334
const staticType = <ObjectType>getTypeOfSymbol(symbol);
1523915335
checkTypeParameterListsIdentical(node, symbol);
15336+
checkClassForDuplicateDeclarations(node);
1524015337

1524115338
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
1524215339
if (baseTypeNode) {
@@ -15514,6 +15611,7 @@ namespace ts {
1551415611
checkIndexConstraints(type);
1551515612
}
1551615613
}
15614+
checkObjectTypeForDuplicateDeclarations(node);
1551715615
}
1551815616
forEach(getInterfaceBaseTypeNodes(node), heritageElement => {
1551915617
if (!isSupportedExpressionWithTypeArguments(heritageElement)) {
@@ -18152,7 +18250,6 @@ namespace ts {
1815218250
name.kind === SyntaxKind.ComputedPropertyName) {
1815318251
// If the name is not a ComputedPropertyName, the grammar checking will skip it
1815418252
checkGrammarComputedPropertyName(<ComputedPropertyName>name);
18155-
continue;
1815618253
}
1815718254

1815818255
if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && (<ShorthandPropertyAssignment>prop).objectAssignmentInitializer) {
@@ -18198,17 +18295,22 @@ namespace ts {
1819818295
Debug.fail("Unexpected syntax kind:" + prop.kind);
1819918296
}
1820018297

18201-
if (!hasProperty(seen, (<Identifier>name).text)) {
18202-
seen[(<Identifier>name).text] = currentKind;
18298+
const effectiveName = getPropertyNameForPropertyNameNode(name);
18299+
if (effectiveName === undefined) {
18300+
continue;
18301+
}
18302+
18303+
if (!hasProperty(seen, effectiveName)) {
18304+
seen[effectiveName] = currentKind;
1820318305
}
1820418306
else {
18205-
const existingKind = seen[(<Identifier>name).text];
18307+
const existingKind = seen[effectiveName];
1820618308
if (currentKind === Property && existingKind === Property) {
18207-
continue;
18309+
grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name));
1820818310
}
1820918311
else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) {
1821018312
if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) {
18211-
seen[(<Identifier>name).text] = currentKind | existingKind;
18313+
seen[effectiveName] = currentKind | existingKind;
1821218314
}
1821318315
else {
1821418316
return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name);

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,10 @@
19271927
"category": "Error",
19281928
"code": 2686
19291929
},
1930+
"All declarations of '{0}' must have identical modifiers.": {
1931+
"category": "Error",
1932+
"code": 2687
1933+
},
19301934
"Import declaration '{0}' is using private name '{1}'.": {
19311935
"category": "Error",
19321936
"code": 4000

src/compiler/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2025,7 +2025,7 @@ namespace ts {
20252025
BlockScopedVariableExcludes = Value,
20262026

20272027
ParameterExcludes = Value,
2028-
PropertyExcludes = Value,
2028+
PropertyExcludes = None,
20292029
EnumMemberExcludes = Value,
20302030
FunctionExcludes = Value & ~(Function | ValueModule),
20312031
ClassExcludes = (Value | Type) & ~(ValueModule | Interface), // class-interface mergability done in checker.ts

src/compiler/utilities.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@ namespace ts {
17741774
}
17751775

17761776
export function getPropertyNameForPropertyNameNode(name: DeclarationName): string {
1777-
if (name.kind === SyntaxKind.Identifier || name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral) {
1777+
if (name.kind === SyntaxKind.Identifier || name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral || name.kind === SyntaxKind.Parameter) {
17781778
return (<Identifier | LiteralExpression>name).text;
17791779
}
17801780
if (name.kind === SyntaxKind.ComputedPropertyName) {
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

+1-7
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();
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,38 @@
1-
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(2,12): error TS2300: Duplicate identifier 'x'.
2-
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(6,5): error TS2300: Duplicate identifier 'x'.
3-
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(10,15): error TS2300: Duplicate identifier 'x'.
4-
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(14,5): error TS2300: Duplicate identifier 'x'.
5-
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(18,13): error TS2300: Duplicate identifier 'x'.
6-
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(22,5): error TS2300: Duplicate identifier 'x'.
1+
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(10,15): error TS2686: All declarations of 'x' must have identical modifiers.
2+
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(14,5): error TS2686: All declarations of 'x' must have identical modifiers.
3+
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(18,13): error TS2686: All declarations of 'x' must have identical modifiers.
4+
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(22,5): error TS2686: All declarations of 'x' must have identical modifiers.
75

86

9-
==== tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts (6 errors) ====
7+
==== tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts (4 errors) ====
108
declare class C1 {
119
public x : number;
12-
~
13-
!!! error TS2300: Duplicate identifier 'x'.
1410
}
1511

1612
interface C1 {
1713
x : number;
18-
~
19-
!!! error TS2300: Duplicate identifier 'x'.
2014
}
2115

2216
declare class C2 {
2317
protected x : number;
2418
~
25-
!!! error TS2300: Duplicate identifier 'x'.
19+
!!! error TS2686: All declarations of 'x' must have identical modifiers.
2620
}
2721

2822
interface C2 {
2923
x : number;
3024
~
31-
!!! error TS2300: Duplicate identifier 'x'.
25+
!!! error TS2686: All declarations of 'x' must have identical modifiers.
3226
}
3327

3428
declare class C3 {
3529
private x : number;
3630
~
37-
!!! error TS2300: Duplicate identifier 'x'.
31+
!!! error TS2686: All declarations of 'x' must have identical modifiers.
3832
}
3933

4034
interface C3 {
4135
x : number;
4236
~
43-
!!! error TS2300: Duplicate identifier 'x'.
37+
!!! error TS2686: All declarations of 'x' must have identical modifiers.
4438
}

tests/baselines/reference/classAndInterfaceWithSameName.errors.txt

-27
This file was deleted.
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)