diff --git a/packages/jsii/lib/assembler.ts b/packages/jsii/lib/assembler.ts index 2ad44b60da..c32c9a295b 100644 --- a/packages/jsii/lib/assembler.ts +++ b/packages/jsii/lib/assembler.ts @@ -1744,6 +1744,9 @@ export class Assembler implements Emitter { return Promise.resolve(undefined); } + // check the enum to see if there are duplicate enum values + this.assertNoDuplicateEnumValues(decl); + this._warnAboutReservedWords(symbol); const flags = ts.getCombinedModifierFlags(decl); @@ -1788,6 +1791,60 @@ export class Assembler implements Emitter { return Promise.resolve(jsiiType); } + private assertNoDuplicateEnumValues(decl: ts.EnumDeclaration): void { + type EnumValue = { + name: string; + value: string; + decl: ts.DeclarationName | undefined; + }; + + const enumValues = decl.members + .filter((m) => m.initializer) + .map((member): EnumValue => { + return { + value: member.initializer!.getText(), + name: member.name.getText(), + decl: ts.getNameOfDeclaration(member), + }; + }); + + const hasDuplicateEnumValues = enumValues.some( + (val, _, arr) => arr.filter((e) => val.value === e.value).length > 1, + ); + + if (hasDuplicateEnumValues) { + const enumValueMap = enumValues.reduce>( + (acc, val) => { + if (!acc[val.value]) { + acc[val.value] = []; + } + acc[val.value].push(val); + return acc; + }, + {}, + ); + for (const duplicateValue of Object.keys(enumValueMap)) { + if (enumValueMap[duplicateValue].length > 1) { + const err = JsiiDiagnostic.JSII_1004_DUPLICATE_ENUM_VALUE.create( + enumValueMap[duplicateValue][0].decl!, + `Value ${duplicateValue} is used for multiple enum values: ${enumValueMap[ + duplicateValue + ] + .map((e: any) => e.name) + .join(', ')}`, + ); + for (let i = 1; i < enumValueMap[duplicateValue].length; i++) { + err.addRelatedInformation( + enumValueMap[duplicateValue][i].decl!, + 'Same value assigned here', + ); + } + this._diagnostics.push(err); + } + } + } + } + /** * Return docs for a symbol */ diff --git a/packages/jsii/lib/jsii-diagnostic.ts b/packages/jsii/lib/jsii-diagnostic.ts index 272ac9bffc..54079b436f 100644 --- a/packages/jsii/lib/jsii-diagnostic.ts +++ b/packages/jsii/lib/jsii-diagnostic.ts @@ -292,6 +292,12 @@ export class JsiiDiagnostic implements ts.Diagnostic { name: 'typescript-restrictions/unsupported-type', }); + public static readonly JSII_1004_DUPLICATE_ENUM_VALUE = Code.error({ + code: 1004, + formatter: (messageText) => messageText, + name: 'typescript-restrictions/duplicate-enum-value', + }); + ////////////////////////////////////////////////////////////////////////////// // 2000 => 2999 -- RESERVED diff --git a/packages/jsii/test/enums.test.ts b/packages/jsii/test/enums.test.ts index 7cae6cb00e..c8ff29bd88 100644 --- a/packages/jsii/test/enums.test.ts +++ b/packages/jsii/test/enums.test.ts @@ -57,3 +57,16 @@ test('enums can have a mix of letters and number', async () => { { name: 'IB3M' }, ]); }); + +test('enums with the same assigned value should fail', async () => { + await expect(() => + sourceToAssemblyHelper(` + export enum Foo { + BAR = 'Bar', + BAR_DUPE = 'Bar', + BAZ = 'Baz', + BAZ_DUPE = 'Baz', + } + `), + ).rejects.toThrowError('There were compiler errors'); +});