From 70766734641463b8e0f99d567a99b84eb44ab8e9 Mon Sep 17 00:00:00 2001 From: Karishnu Poddar Date: Fri, 12 Jun 2020 03:30:40 +0530 Subject: [PATCH 1/4] fix(eslint-plugin): [switch-exhaustiveness-check] add quotes and box notation when required by rule switch-exhaustiveness-check --- .../src/rules/switch-exhaustiveness-check.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index d9e14bcf331..bf282f74d86 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -42,7 +42,9 @@ export default createRule({ fixer: TSESLint.RuleFixer, node: TSESTree.SwitchStatement, missingBranchTypes: Array, + symbolName?: string, ): TSESLint.RuleFix | null { + const identifierRegex = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/; const lastCase = node.cases.length > 0 ? node.cases[node.cases.length - 1] : null; const caseIndent = lastCase @@ -67,7 +69,17 @@ export default createRule({ continue; } - const caseTest = checker.typeToString(missingBranchType); + const missingBranchName = missingBranchType.getSymbol()?.escapedName; + let caseTest = checker.typeToString(missingBranchType); + + if ( + symbolName && + missingBranchName && + !identifierRegex.test(missingBranchName.toString()) + ) { + caseTest = `${symbolName}['${missingBranchName}']`; + } + const errorMessage = `Not implemented yet: ${caseTest} case`; missingCases.push( @@ -101,6 +113,7 @@ export default createRule({ function checkSwitchExhaustive(node: TSESTree.SwitchStatement): void { const discriminantType = getNodeType(node.discriminant); + const symbolName = discriminantType.getSymbol()?.escapedName; if (discriminantType.isUnion()) { const unionTypes = unionTypeParts(discriminantType); @@ -139,7 +152,12 @@ export default createRule({ { messageId: 'addMissingCases', fix(fixer): TSESLint.RuleFix | null { - return fixSwitch(fixer, node, missingBranchTypes); + return fixSwitch( + fixer, + node, + missingBranchTypes, + symbolName?.toString(), + ); }, }, ], From 9c1f8bd807caff9299b7aee330d9752940a260d2 Mon Sep 17 00:00:00 2001 From: Karishnu Poddar Date: Fri, 12 Jun 2020 03:42:00 +0530 Subject: [PATCH 2/4] fix(eslint-plugin): [switch-exhaustiveness-check] add test for case where enum key has special characters for rule switch-exhaustiveness-check --- .../rules/switch-exhaustiveness-check.test.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts index 3eb47fb6eb9..e3faea342ba 100644 --- a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts +++ b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts @@ -483,6 +483,43 @@ function test(value: T): number { case 1: { throw new Error('Not implemented yet: 1 case') } case 2: { throw new Error('Not implemented yet: 2 case') } } +} + `.trimRight(), + }, + ], + }, + ], + }, + { + // keys include special characters + code: ` +export enum Enum { + 'test-test' = 'test-test', + 'test' = 'test', +} + +function test(arg: Enum): string { + switch (arg) { + } +} + `.trimRight(), + errors: [ + { + messageId: 'switchIsNotExhaustive', + suggestions: [ + { + messageId: 'addMissingCases', + output: noFormat` +export enum Enum { + 'test-test' = 'test-test', + 'test' = 'test', +} + +function test(arg: Enum): string { + switch (arg) { + case Enum['test-test']: { throw new Error('Not implemented yet: Enum['test-test'] case') } + case Enum.test: { throw new Error('Not implemented yet: Enum.test case') } + } } `.trimRight(), }, From 27fd2d256bc46b4acef1c631c9c9bb6d4f979025 Mon Sep 17 00:00:00 2001 From: Karishnu Poddar Date: Wed, 8 Jul 2020 14:41:20 +0530 Subject: [PATCH 3/4] fix(eslint-plugin): [switch-exhaustiveness-check] Support non English characters --- .../src/rules/switch-exhaustiveness-check.ts | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index bf282f74d86..d8a7750efba 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -32,6 +32,25 @@ export default createRule({ const sourceCode = context.getSourceCode(); const service = getParserServices(context); const checker = service.program.getTypeChecker(); + const compilerOptions = service.program.getCompilerOptions(); + + function requiresQuoting(name: string): boolean { + if (name.length === 0) { + return true; + } + + if (!ts.isIdentifierStart(name.charCodeAt(0), compilerOptions.target)) { + return true; + } + + for (let i = 1; i < name.length; i += 1) { + if (!ts.isIdentifierPart(name.charCodeAt(i), compilerOptions.target)) { + return true; + } + } + + return false; + } function getNodeType(node: TSESTree.Node): ts.Type { const tsNode = service.esTreeNodeToTSNodeMap.get(node); @@ -44,7 +63,6 @@ export default createRule({ missingBranchTypes: Array, symbolName?: string, ): TSESLint.RuleFix | null { - const identifierRegex = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/; const lastCase = node.cases.length > 0 ? node.cases[node.cases.length - 1] : null; const caseIndent = lastCase @@ -74,8 +92,8 @@ export default createRule({ if ( symbolName && - missingBranchName && - !identifierRegex.test(missingBranchName.toString()) + (missingBranchName || missingBranchName === '') && + requiresQuoting(missingBranchName.toString()) ) { caseTest = `${symbolName}['${missingBranchName}']`; } From 5825e4a1f4031f435ce7240cf78f2d48f2db357e Mon Sep 17 00:00:00 2001 From: Karishnu Poddar Date: Wed, 8 Jul 2020 17:46:55 +0530 Subject: [PATCH 4/4] fix(eslint-plugin): [switch-exhaustiveness-check] Add tests for new valid identity check --- .../rules/switch-exhaustiveness-check.test.ts | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts index e3faea342ba..415afc20fc1 100644 --- a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts +++ b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts @@ -520,6 +520,80 @@ function test(arg: Enum): string { case Enum['test-test']: { throw new Error('Not implemented yet: Enum['test-test'] case') } case Enum.test: { throw new Error('Not implemented yet: Enum.test case') } } +} + `.trimRight(), + }, + ], + }, + ], + }, + { + // keys include empty string + code: ` +export enum Enum { + '' = 'test-test', + 'test' = 'test', +} + +function test(arg: Enum): string { + switch (arg) { + } +} + `.trimRight(), + errors: [ + { + messageId: 'switchIsNotExhaustive', + suggestions: [ + { + messageId: 'addMissingCases', + output: noFormat` +export enum Enum { + '' = 'test-test', + 'test' = 'test', +} + +function test(arg: Enum): string { + switch (arg) { + case Enum['']: { throw new Error('Not implemented yet: Enum[''] case') } + case Enum.test: { throw new Error('Not implemented yet: Enum.test case') } + } +} + `.trimRight(), + }, + ], + }, + ], + }, + { + // keys include number as first character + code: ` +export enum Enum { + '9test' = 'test-test', + 'test' = 'test', +} + +function test(arg: Enum): string { + switch (arg) { + } +} + `.trimRight(), + errors: [ + { + messageId: 'switchIsNotExhaustive', + suggestions: [ + { + messageId: 'addMissingCases', + output: noFormat` +export enum Enum { + '9test' = 'test-test', + 'test' = 'test', +} + +function test(arg: Enum): string { + switch (arg) { + case Enum['9test']: { throw new Error('Not implemented yet: Enum['9test'] case') } + case Enum.test: { throw new Error('Not implemented yet: Enum.test case') } + } } `.trimRight(), },