Skip to content

Commit 464910c

Browse files
authored
fix(formatter): switch case comments (#9303)
1 parent 1003229 commit 464910c

4 files changed

Lines changed: 70 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed [#2786](https://github.com/biomejs/biome/issues/2786): The formatter no longer produces different output on subsequent runs when a `case` clause has a trailing line comment followed by a single block statement.

crates/biome_js_formatter/src/js/auxiliary/case_clause.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,22 @@ impl FormatNodeRule<JsCaseClause> for FormatJsCaseClause {
7070
// default:
7171
// break;
7272
// }
73+
// If the case clause has a trailing line comment after the colon
74+
// (e.g. `case 1337: // ELITE`), we must not hug the block statement on
75+
// the same line. Doing so produces `case 1337: { // ELITE`, and on the
76+
// next format pass the comment is reparsed as a leading comment inside
77+
// the block, causing it to move to its own line — an idempotence bug.
78+
// The comment is stored as a trailing comment of the `test` node.
79+
let has_trailing_comment = test
80+
.as_ref()
81+
.is_ok_and(|test| f.comments().has_trailing_comments(test.syntax()));
82+
7383
if consequent.is_empty() {
7484
// Print nothing to ensure that trailing comments on the same line
7585
// are printed on the same line. The parent list formatter takes
7686
// care of inserting a hard line break between cases.
7787
Ok(())
78-
} else if is_single_block_statement {
88+
} else if is_single_block_statement && !has_trailing_comment {
7989
write![f, [space(), consequent.format()]]
8090
} else {
8191
// no line break needed after because it is added by the indent in the switch statement

crates/biome_js_formatter/tests/specs/js/module/statement/switch_comment.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,21 @@ switch(x) {
1616
a(); // ab
1717
break;
1818
}
19+
20+
// Trailing comment on a case clause followed by a single block statement.
21+
// The comment must stay on the `case` line and not migrate into the block
22+
// on subsequent format passes (idempotence, issue #2786).
23+
function cool(x) {
24+
switch (x) {
25+
case 4: // guaranteed to be random
26+
case 42: // classic
27+
case 1337: // ELITE
28+
{
29+
console.log("x is cool");
30+
break;
31+
}
32+
default: {
33+
console.error("x is not cool");
34+
}
35+
}
36+
}

crates/biome_js_formatter/tests/specs/js/module/statement/switch_comment.js.snap

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ switch(x) {
2525
break;
2626
}
2727
28+
// Trailing comment on a case clause followed by a single block statement.
29+
// The comment must stay on the `case` line and not migrate into the block
30+
// on subsequent format passes (idempotence, issue #2786).
31+
function cool(x) {
32+
switch (x) {
33+
case 4: // guaranteed to be random
34+
case 42: // classic
35+
case 1337: // ELITE
36+
{
37+
console.log("x is cool");
38+
break;
39+
}
40+
default: {
41+
console.error("x is not cool");
42+
}
43+
}
44+
}
45+
2846
```
2947

3048

@@ -75,4 +93,22 @@ switch (x) {
7593
break;
7694
}
7795
96+
// Trailing comment on a case clause followed by a single block statement.
97+
// The comment must stay on the `case` line and not migrate into the block
98+
// on subsequent format passes (idempotence, issue #2786).
99+
function cool(x) {
100+
switch (x) {
101+
case 4: // guaranteed to be random
102+
case 42: // classic
103+
case 1337: // ELITE
104+
{
105+
console.log("x is cool");
106+
break;
107+
}
108+
default: {
109+
console.error("x is not cool");
110+
}
111+
}
112+
}
113+
78114
```

0 commit comments

Comments
 (0)