From 8a76750fd968f32974c59744c3856cb57fa8c276 Mon Sep 17 00:00:00 2001 From: CountBleck Date: Mon, 20 Nov 2023 18:50:29 -0800 Subject: [PATCH] Forbid duplicate static class and/or namespace members This isn't fully TS compatible, but refactors targeting internal names, scoping, merging, etc. are needed to become more compatible. For instance, if namespace members had unique separators in internal names, then a non-exported namespace member would override a static class member, assuming the names are the same. Note that this change doesn't prevent the compiler from attempting to compile the duplicate global, and hence the previous commit is needed for this to work fully. --- src/program.ts | 20 ++++++++++++++++++-- tests/compiler/issues/2793.json | 13 +++++++++++++ tests/compiler/issues/2793.ts | 12 ++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/compiler/issues/2793.json create mode 100644 tests/compiler/issues/2793.ts diff --git a/src/program.ts b/src/program.ts index febeb1227c..1db22d6855 100644 --- a/src/program.ts +++ b/src/program.ts @@ -5058,6 +5058,9 @@ function tryMerge(older: Element, newer: Element): DeclaredElement | null { /** Copies the members of `src` to `dest`. */ function copyMembers(src: Element, dest: Element): void { + let program = src.program; + assert(program == dest.program); + let srcMembers = src.members; if (srcMembers) { let destMembers = dest.members; @@ -5065,8 +5068,21 @@ function copyMembers(src: Element, dest: Element): void { // TODO: for (let [memberName, member] of srcMembers) { for (let _keys = Map_keys(srcMembers), i = 0, k = _keys.length; i < k; ++i) { let memberName = unchecked(_keys[i]); - let member = assert(srcMembers.get(memberName)); - destMembers.set(memberName, member); + let srcMember = assert(srcMembers.get(memberName)); + // This isn't TS compatible in every case, but the logic involved in + // merging, scoping, and making internal names is not currently able to + // handle duplicates well. + if (destMembers.has(memberName)) { + let destMember = assert(destMembers.get(memberName)); + program.errorRelated( + DiagnosticCode.Duplicate_identifier_0, + srcMember.declaration.name.range, destMember.declaration.name.range, + memberName + ); + continue; + } + + destMembers.set(memberName, srcMember); } } } diff --git a/tests/compiler/issues/2793.json b/tests/compiler/issues/2793.json new file mode 100644 index 0000000000..8f275f6155 --- /dev/null +++ b/tests/compiler/issues/2793.json @@ -0,0 +1,13 @@ +{ + "stderr": [ + "Duplicate identifier 'bar'.", + "in issues/2793.ts(8,14)", + "in issues/2793.ts(2,10)", + "Duplicate identifier 'baz'.", + "in issues/2793.ts(9,7)", + "in issues/2793.ts(3,10)", + "Duplicate identifier 'qux'.", + "in issues/2793.ts(10,14)", + "in issues/2793.ts(4,18)" + ] +} \ No newline at end of file diff --git a/tests/compiler/issues/2793.ts b/tests/compiler/issues/2793.ts new file mode 100644 index 0000000000..b9f966bc43 --- /dev/null +++ b/tests/compiler/issues/2793.ts @@ -0,0 +1,12 @@ +class Foo { + static bar: i32 = 2; // errors in TS + static baz: i32 = 2; // does not error in TS + private static qux: i32 = 2; // errors in TS +} + +namespace Foo { + export let bar: i32 = 1; + let baz: string = "baz"; + export let qux: i32 = 1; + let hi: i32 = 1; +}