Skip to content

Commit 960e21c

Browse files
hugop95azat-io
authored andcommittedNov 19, 2024
fix(sort-switch-case): fix ignoring breaks in case statements
1 parent c9a48dd commit 960e21c

File tree

2 files changed

+264
-7
lines changed

2 files changed

+264
-7
lines changed
 

Diff for: ‎rules/sort-switch-case.ts

+36-7
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ export default createEslintRule<Options, MESSAGE_ID>({
7070
defaultOptions: [defaultOptions],
7171
create: context => ({
7272
SwitchStatement: switchNode => {
73+
if (switchNode.cases.length <= 1) {
74+
return
75+
}
76+
7377
let settings = getSettings(context.settings)
7478

7579
let options = complete(context.options.at(0), settings, defaultOptions)
@@ -217,18 +221,28 @@ export default createEslintRule<Options, MESSAGE_ID>({
217221
// Ensure case blocks are in the correct order
218222
let sortingNodeGroupsForBlockSort = reduceCaseSortingNodes(
219223
sortingNodes,
220-
caseNode =>
221-
caseNode.node.consequent.some(
222-
currentConsequent =>
223-
currentConsequent.type === 'BreakStatement' ||
224-
currentConsequent.type === 'ReturnStatement' ||
225-
currentConsequent.type === 'BlockStatement',
226-
),
224+
caseNode => caseHasBreakOrReturn(caseNode.node),
225+
)
226+
// If the last case does not have a return/break, leave its group at its place
227+
let lastNodeGroup = sortingNodeGroupsForBlockSort.at(-1)
228+
let lastBlockCaseShouldStayInPlace = !caseHasBreakOrReturn(
229+
lastNodeGroup!.at(-1)!.node,
227230
)
228231
let sortedSortingNodeGroupsForBlockSort = [
229232
...sortingNodeGroupsForBlockSort,
230233
]
231234
.sort((a, b) => {
235+
if (lastBlockCaseShouldStayInPlace) {
236+
if (a === lastNodeGroup) {
237+
return 1
238+
}
239+
/* c8 ignore start - last element might never be b */
240+
if (b === lastNodeGroup) {
241+
return -1
242+
/* c8 ignore end */
243+
}
244+
}
245+
232246
if (a.some(node => node.isDefaultClause)) {
233247
return 1
234248
}
@@ -298,3 +312,18 @@ const reduceCaseSortingNodes = (
298312
},
299313
[[]],
300314
)
315+
316+
const caseHasBreakOrReturn = (caseNode: TSESTree.SwitchCase) => {
317+
if (caseNode.consequent.length === 0) {
318+
return false
319+
}
320+
if (caseNode.consequent[0]?.type === 'BlockStatement') {
321+
return caseNode.consequent[0].body.some(statementIsBreakOrReturn)
322+
}
323+
return caseNode.consequent.some(currentConsequent =>
324+
statementIsBreakOrReturn(currentConsequent),
325+
)
326+
}
327+
328+
const statementIsBreakOrReturn = (statement: TSESTree.Statement) =>
329+
statement.type === 'BreakStatement' || statement.type === 'ReturnStatement'

Diff for: ‎test/sort-switch-case.test.ts

+228
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ describe(ruleName, () => {
3030
rule,
3131
{
3232
valid: [
33+
{
34+
code: dedent`
35+
switch(x) {
36+
}
37+
`,
38+
options: [{}],
39+
},
40+
{
41+
code: dedent`
42+
switch(x) {
43+
case "a":
44+
break;
45+
}
46+
`,
47+
options: [{}],
48+
},
3349
{
3450
code: dedent`
3551
function func(name) {
@@ -252,15 +268,19 @@ describe(ruleName, () => {
252268
switch(name) {
253269
case 'aaa': {
254270
height = 1
271+
break
255272
}
256273
case 'c': {
257274
height = 3
275+
break
258276
}
259277
case 'bb': {
260278
height = 2
279+
break
261280
}
262281
default:
263282
height = NaN
283+
break
264284
}
265285
return size
266286
}
@@ -271,15 +291,19 @@ describe(ruleName, () => {
271291
switch(name) {
272292
case 'aaa': {
273293
height = 1
294+
break
274295
}
275296
case 'bb': {
276297
height = 2
298+
break
277299
}
278300
case 'c': {
279301
height = 3
302+
break
280303
}
281304
default:
282305
height = NaN
306+
break
283307
}
284308
return size
285309
}
@@ -299,6 +323,109 @@ describe(ruleName, () => {
299323
},
300324
)
301325

326+
ruleTester.run(
327+
`${ruleName}(${type}): sorts switch cases without ending statements`,
328+
rule,
329+
{
330+
valid: [
331+
{
332+
code: dedent`
333+
function func(name) {
334+
switch(name) {
335+
case 'b':
336+
let b
337+
break
338+
case 'a':
339+
}
340+
}
341+
`,
342+
options: [options],
343+
},
344+
{
345+
code: dedent`
346+
function func(name) {
347+
switch(name) {
348+
case 'b':
349+
let b
350+
break
351+
case 'a':
352+
let a
353+
}
354+
}
355+
`,
356+
options: [options],
357+
},
358+
{
359+
code: dedent`
360+
function func(name) {
361+
switch(name) {
362+
case 'b':
363+
let b
364+
break
365+
default:
366+
}
367+
}
368+
`,
369+
options: [options],
370+
},
371+
{
372+
code: dedent`
373+
function func(name) {
374+
switch(name) {
375+
case 'b':
376+
let b
377+
break
378+
default:
379+
let x
380+
}
381+
}
382+
`,
383+
options: [options],
384+
},
385+
],
386+
invalid: [
387+
{
388+
code: dedent`
389+
function func(name) {
390+
switch(name) {
391+
case 'c':
392+
let c
393+
break
394+
case 'b':
395+
let b
396+
break
397+
case 'a':
398+
}
399+
}
400+
`,
401+
output: dedent`
402+
function func(name) {
403+
switch(name) {
404+
case 'b':
405+
let b
406+
break
407+
case 'c':
408+
let c
409+
break
410+
case 'a':
411+
}
412+
}
413+
`,
414+
options: [options],
415+
errors: [
416+
{
417+
messageId: 'unexpectedSwitchCaseOrder',
418+
data: {
419+
left: 'c',
420+
right: 'b',
421+
},
422+
},
423+
],
424+
},
425+
],
426+
},
427+
)
428+
302429
ruleTester.run(`${ruleName}(${type}): works with grouped cases`, rule, {
303430
valid: [
304431
{
@@ -945,6 +1072,91 @@ describe(ruleName, () => {
9451072
},
9461073
],
9471074
})
1075+
1076+
ruleTester.run(`${ruleName}: handles last case without break`, rule, {
1077+
valid: [
1078+
{
1079+
code: dedent`
1080+
switch(x) {
1081+
case "b": {
1082+
break
1083+
}
1084+
case "a": {
1085+
let a
1086+
}
1087+
}
1088+
`,
1089+
options: [{}],
1090+
},
1091+
{
1092+
code: dedent`
1093+
switch(x) {
1094+
default: {
1095+
break
1096+
}
1097+
case "a": {
1098+
let a
1099+
}
1100+
}
1101+
`,
1102+
options: [{}],
1103+
},
1104+
{
1105+
code: dedent`
1106+
switch(x) {
1107+
case "b":
1108+
break
1109+
case "a":
1110+
let a
1111+
}
1112+
`,
1113+
options: [{}],
1114+
},
1115+
{
1116+
code: dedent`
1117+
switch(x) {
1118+
default:
1119+
break;
1120+
case "a":
1121+
let a
1122+
}
1123+
`,
1124+
options: [{}],
1125+
},
1126+
],
1127+
invalid: [
1128+
{
1129+
code: dedent`
1130+
switch(x) {
1131+
default:
1132+
break;
1133+
case "a":
1134+
break;
1135+
case "a":
1136+
}
1137+
`,
1138+
output: dedent`
1139+
switch(x) {
1140+
case "a":
1141+
break;
1142+
default:
1143+
break;
1144+
case "a":
1145+
}
1146+
`,
1147+
options: [{}],
1148+
errors: [
1149+
{
1150+
messageId: 'unexpectedSwitchCaseOrder',
1151+
data: {
1152+
left: 'default',
1153+
right: 'a',
1154+
},
1155+
},
1156+
],
1157+
},
1158+
],
1159+
})
9481160
})
9491161

9501162
describe(`${ruleName}: sorting by natural order`, () => {
@@ -1143,15 +1355,19 @@ describe(ruleName, () => {
11431355
switch(name) {
11441356
case 'aaa': {
11451357
height = 1
1358+
break
11461359
}
11471360
case 'c': {
11481361
height = 3
1362+
break
11491363
}
11501364
case 'bb': {
11511365
height = 2
1366+
break
11521367
}
11531368
default:
11541369
height = NaN
1370+
break
11551371
}
11561372
return size
11571373
}
@@ -1162,15 +1378,19 @@ describe(ruleName, () => {
11621378
switch(name) {
11631379
case 'aaa': {
11641380
height = 1
1381+
break
11651382
}
11661383
case 'bb': {
11671384
height = 2
1385+
break
11681386
}
11691387
case 'c': {
11701388
height = 3
1389+
break
11711390
}
11721391
default:
11731392
height = NaN
1393+
break
11741394
}
11751395
return size
11761396
}
@@ -1760,15 +1980,19 @@ describe(ruleName, () => {
17601980
switch(name) {
17611981
case 'aaa': {
17621982
height = 1
1983+
break
17631984
}
17641985
case 'c': {
17651986
height = 3
1987+
break
17661988
}
17671989
case 'bb': {
17681990
height = 2
1991+
break
17691992
}
17701993
default:
17711994
height = NaN
1995+
break
17721996
}
17731997
return size
17741998
}
@@ -1779,15 +2003,19 @@ describe(ruleName, () => {
17792003
switch(name) {
17802004
case 'aaa': {
17812005
height = 1
2006+
break
17822007
}
17832008
case 'bb': {
17842009
height = 2
2010+
break
17852011
}
17862012
case 'c': {
17872013
height = 3
2014+
break
17882015
}
17892016
default:
17902017
height = NaN
2018+
break
17912019
}
17922020
return size
17932021
}

0 commit comments

Comments
 (0)
Please sign in to comment.