Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(eslint-plugin): [switch-exhaustiveness-check] handle special char…
…acters in enum keys (#2207)
  • Loading branch information
karishnu committed Jul 10, 2020
1 parent 742b679 commit 98ab010
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 2 deletions.
40 changes: 38 additions & 2 deletions packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts
Expand Up @@ -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);
Expand All @@ -42,6 +61,7 @@ export default createRule({
fixer: TSESLint.RuleFixer,
node: TSESTree.SwitchStatement,
missingBranchTypes: Array<ts.Type>,
symbolName?: string,
): TSESLint.RuleFix | null {
const lastCase =
node.cases.length > 0 ? node.cases[node.cases.length - 1] : null;
Expand All @@ -67,7 +87,17 @@ export default createRule({
continue;
}

const caseTest = checker.typeToString(missingBranchType);
const missingBranchName = missingBranchType.getSymbol()?.escapedName;
let caseTest = checker.typeToString(missingBranchType);

if (
symbolName &&
(missingBranchName || missingBranchName === '') &&
requiresQuoting(missingBranchName.toString())
) {
caseTest = `${symbolName}['${missingBranchName}']`;
}

const errorMessage = `Not implemented yet: ${caseTest} case`;

missingCases.push(
Expand Down Expand Up @@ -101,6 +131,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);
Expand Down Expand Up @@ -139,7 +170,12 @@ export default createRule({
{
messageId: 'addMissingCases',
fix(fixer): TSESLint.RuleFix | null {
return fixSwitch(fixer, node, missingBranchTypes);
return fixSwitch(
fixer,
node,
missingBranchTypes,
symbolName?.toString(),
);
},
},
],
Expand Down
111 changes: 111 additions & 0 deletions packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts
Expand Up @@ -483,6 +483,117 @@ 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(),
},
],
},
],
},
{
// 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(),
},
Expand Down

0 comments on commit 98ab010

Please sign in to comment.