fix(linter): no-lone-blocks erroring on block statements containing comments (#8720)

This PR fixes #8697 by checking if semantic detects a comment in the
span of an otherwise empty block statement.

I also formatted some of the existing test cases to improve readability.
This commit is contained in:
Tyler Earls 2025-01-25 19:20:05 -06:00 committed by GitHub
parent 3e509e1c9b
commit 1de6f854cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 276 additions and 257 deletions

View file

@ -62,7 +62,12 @@ impl Rule for NoLoneBlocks {
}; };
if stmt.body.is_empty() { if stmt.body.is_empty() {
if !matches!(parent_node.kind(), AstKind::TryStatement(_) | AstKind::CatchClause(_)) { let is_comment_in_stmt =
ctx.semantic().comments_range(stmt.span.start..stmt.span.end).last().is_some();
if !is_comment_in_stmt
&& !matches!(parent_node.kind(), AstKind::TryStatement(_) | AstKind::CatchClause(_))
{
report(ctx, node, parent_node); report(ctx, node, parent_node);
} }
return; return;
@ -155,30 +160,30 @@ fn test() {
"{ class Bar {} }", // { "ecmaVersion": 6 }, "{ class Bar {} }", // { "ecmaVersion": 6 },
"{ {let y = 1;} let x = 1; }", // { "ecmaVersion": 6 }, "{ {let y = 1;} let x = 1; }", // { "ecmaVersion": 6 },
" "
switch (foo) { switch (foo) {
case bar: { case bar: {
baz; baz;
} }
} }
", ",
" "
switch (foo) { switch (foo) {
case bar: { case bar: {
baz; baz;
} }
case qux: { case qux: {
boop; boop;
} }
} }
", ",
" "
switch (foo) { switch (foo) {
case bar: case bar:
{ {
baz; baz;
} }
} }
", ",
"function foo() { { const x = 4 } const x = 3 }", // { "ecmaVersion": 6 }, "function foo() { { const x = 4 } const x = 3 }", // { "ecmaVersion": 6 },
"class C { static {} }", // { "ecmaVersion": 2022 }, "class C { static {} }", // { "ecmaVersion": 2022 },
"class C { static { foo; } }", // { "ecmaVersion": 2022 }, "class C { static { foo; } }", // { "ecmaVersion": 2022 },
@ -189,15 +194,29 @@ fn test() {
"class C { static { { function block(){} } something; } }", // { "ecmaVersion": 2022 }, "class C { static { { function block(){} } something; } }", // { "ecmaVersion": 2022 },
"class C { static { something; { class block {} } } }", // { "ecmaVersion": 2022 }, "class C { static { something; { class block {} } } }", // { "ecmaVersion": 2022 },
" "
{ {
using x = makeDisposable(); using x = makeDisposable();
}", // { "parser": require(parser("typescript-parsers/no-lone-blocks/using")), "ecmaVersion": 2022 }, }
", // { "parser": require(parser("typescript-parsers/no-lone-blocks/using")), "ecmaVersion": 2022 },
" "
{ {
await using x = makeDisposable(); await using x = makeDisposable();
}", // { "parser": require(parser("typescript-parsers/no-lone-blocks/await-using")), "ecmaVersion": 2022 } }
", // { "parser": require(parser("typescript-parsers/no-lone-blocks/await-using")), "ecmaVersion": 2022 }
// Issue: <https://github.com/oxc-project/oxc/issues/8515> // Issue: <https://github.com/oxc-project/oxc/issues/8515>
"try {} catch {}", "try {} catch {}",
// Issue: https://github.com/oxc-project/oxc/issues/8697
"
if (foo) {
// do nothing
}
else if (bar) {
// do nothing again
}
else {
// do nothing
}
",
]; ];
let fail = vec![ let fail = vec![
@ -215,154 +234,154 @@ fn test() {
"{var x = 1;}", // { "ecmaVersion": 6 }, "{var x = 1;}", // { "ecmaVersion": 6 },
"{ "{
{var x = 1;} {var x = 1;}
let y = 2; } {let z = 1;}", // { "ecmaVersion": 6 }, let y = 2; } {let z = 1;}", // { "ecmaVersion": 6 },
"{ "{
{let x = 1;} {let x = 1;}
var y = 2; } {let z = 1;}", // { "ecmaVersion": 6 }, var y = 2; } {let z = 1;}", // { "ecmaVersion": 6 },
"{ "{
{var x = 1;} {var x = 1;}
var y = 2; } var y = 2; }
{var z = 1;}", // { "ecmaVersion": 6 }, {var z = 1;}", // { "ecmaVersion": 6 },
" "
switch (foo) { switch (foo) {
case 1: case 1:
foo(); foo();
{ {
bar; bar;
} }
} }
", ",
" "
switch (foo) { switch (foo) {
case 1: case 1:
{ {
bar; bar;
} }
foo(); foo();
} }
", ",
" "
function foo () { function foo () {
{ {
const x = 4; const x = 4;
} }
} }
", // { "ecmaVersion": 6 }, ", // { "ecmaVersion": 6 },
" "
function foo () { function foo () {
{ {
var x = 4; var x = 4;
} }
} }
", ",
" "
class C { class C {
static { static {
if (foo) { if (foo) {
{ {
let block; let block;
} }
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
if (foo) { if (foo) {
{ {
block; block;
} }
something; something;
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
{ {
block; block;
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
{ {
let block; let block;
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
{ {
const block = 1; const block = 1;
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
{ {
function block() {} function block() {}
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
{ {
class block {} class block {}
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
{ {
var block; var block;
} }
something; something;
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
something; something;
{ {
var block; var block;
} }
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
{ {
block; block;
} }
something; something;
} }
} }
", // { "ecmaVersion": 2022 }, ", // { "ecmaVersion": 2022 },
" "
class C { class C {
static { static {
something; something;
{ {
block; block;
} }
} }
} }
", // { "ecmaVersion": 2022 } ", // { "ecmaVersion": 2022 }
]; ];
Tester::new(NoLoneBlocks::NAME, NoLoneBlocks::PLUGIN, pass, fail).test_and_snapshot(); Tester::new(NoLoneBlocks::NAME, NoLoneBlocks::PLUGIN, pass, fail).test_and_snapshot();

View file

@ -67,22 +67,22 @@ source: crates/oxc_linter/src/tester.rs
1 │ { 1 │ {
2 │ {var x = 1;} 2 │ {var x = 1;}
· ──────────── · ────────────
3 │ let y = 2; } {let z = 1;} 3 │ let y = 2; } {let z = 1;}
╰──── ╰────
⚠ eslint(no-lone-blocks): Block is unnecessary. ⚠ eslint(no-lone-blocks): Block is unnecessary.
╭─[no_lone_blocks.tsx:1:1] ╭─[no_lone_blocks.tsx:1:1]
1 │ ╭─▶ { 1 │ ╭─▶ {
2 │ │ {let x = 1;} 2 │ │ {let x = 1;}
3 │ ╰─▶ var y = 2; } {let z = 1;} 3 │ ╰─▶ var y = 2; } {let z = 1;}
╰──── ╰────
⚠ eslint(no-lone-blocks): Block is unnecessary. ⚠ eslint(no-lone-blocks): Block is unnecessary.
╭─[no_lone_blocks.tsx:1:1] ╭─[no_lone_blocks.tsx:1:1]
1 │ ╭─▶ { 1 │ ╭─▶ {
2 │ │ {var x = 1;} 2 │ │ {var x = 1;}
3 │ ╰─▶ var y = 2; } 3 │ ╰─▶ var y = 2; }
4 │ {var z = 1;} 4 │ {var z = 1;}
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
@ -90,147 +90,147 @@ source: crates/oxc_linter/src/tester.rs
1 │ { 1 │ {
2 │ {var x = 1;} 2 │ {var x = 1;}
· ──────────── · ────────────
3 │ var y = 2; } 3 │ var y = 2; }
╰──── ╰────
⚠ eslint(no-lone-blocks): Block is unnecessary. ⚠ eslint(no-lone-blocks): Block is unnecessary.
╭─[no_lone_blocks.tsx:4:5] ╭─[no_lone_blocks.tsx:4:4]
3 │ var y = 2; } 3 │ var y = 2; }
4 │ {var z = 1;} 4 │ {var z = 1;}
· ──────────── · ────────────
╰──── ╰────
⚠ eslint(no-lone-blocks): Block is unnecessary. ⚠ eslint(no-lone-blocks): Block is unnecessary.
╭─[no_lone_blocks.tsx:5:24] ╭─[no_lone_blocks.tsx:5:17]
4 │ foo(); 4 │ foo();
5 │ ╭─▶ { 5 │ ╭─▶ {
6 │ │ bar; 6 │ │ bar;
7 │ ╰─▶ } 7 │ ╰─▶ }
8 │ } 8 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Block is unnecessary. ⚠ eslint(no-lone-blocks): Block is unnecessary.
╭─[no_lone_blocks.tsx:4:20] ╭─[no_lone_blocks.tsx:4:21]
3 │ case 1: 3 │ case 1:
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ bar; 5 │ │ bar;
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ foo(); 7 │ foo();
╰──── ╰────
⚠ eslint(no-lone-blocks): Block is unnecessary. ⚠ eslint(no-lone-blocks): Block is unnecessary.
╭─[no_lone_blocks.tsx:3:20] ╭─[no_lone_blocks.tsx:3:17]
2 │ function foo () { 2 │ function foo () {
3 │ ╭─▶ { 3 │ ╭─▶ {
4 │ │ const x = 4; 4 │ │ const x = 4;
5 │ ╰─▶ } 5 │ ╰─▶ }
6 │ } 6 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Block is unnecessary. ⚠ eslint(no-lone-blocks): Block is unnecessary.
╭─[no_lone_blocks.tsx:3:20] ╭─[no_lone_blocks.tsx:3:17]
2 │ function foo () { 2 │ function foo () {
3 │ ╭─▶ { 3 │ ╭─▶ {
4 │ │ var x = 4; 4 │ │ var x = 4;
5 │ ╰─▶ } 5 │ ╰─▶ }
6 │ } 6 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:5:24] ╭─[no_lone_blocks.tsx:5:25]
4 │ if (foo) { 4 │ if (foo) {
5 │ ╭─▶ { 5 │ ╭─▶ {
6 │ │ let block; 6 │ │ let block;
7 │ ╰─▶ } 7 │ ╰─▶ }
8 │ } 8 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:5:24] ╭─[no_lone_blocks.tsx:5:25]
4 │ if (foo) { 4 │ if (foo) {
5 │ ╭─▶ { 5 │ ╭─▶ {
6 │ │ block; 6 │ │ block;
7 │ ╰─▶ } 7 │ ╰─▶ }
8 │ something; 8 │ something;
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:4:22] ╭─[no_lone_blocks.tsx:4:21]
3 │ static { 3 │ static {
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ block; 5 │ │ block;
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ } 7 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:4:22] ╭─[no_lone_blocks.tsx:4:21]
3 │ static { 3 │ static {
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ let block; 5 │ │ let block;
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ } 7 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:4:22] ╭─[no_lone_blocks.tsx:4:21]
3 │ static { 3 │ static {
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ const block = 1; 5 │ │ const block = 1;
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ } 7 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:4:22] ╭─[no_lone_blocks.tsx:4:21]
3 │ static { 3 │ static {
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ function block() {} 5 │ │ function block() {}
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ } 7 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:4:22] ╭─[no_lone_blocks.tsx:4:21]
3 │ static { 3 │ static {
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ class block {} 5 │ │ class block {}
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ } 7 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:4:22] ╭─[no_lone_blocks.tsx:4:21]
3 │ static { 3 │ static {
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ var block; 5 │ │ var block;
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ something; 7 │ something;
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:5:22] ╭─[no_lone_blocks.tsx:5:21]
4 │ something; 4 │ something;
5 │ ╭─▶ { 5 │ ╭─▶ {
6 │ │ var block; 6 │ │ var block;
7 │ ╰─▶ } 7 │ ╰─▶ }
8 │ } 8 │ }
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:4:22] ╭─[no_lone_blocks.tsx:4:21]
3 │ static { 3 │ static {
4 │ ╭─▶ { 4 │ ╭─▶ {
5 │ │ block; 5 │ │ block;
6 │ ╰─▶ } 6 │ ╰─▶ }
7 │ something; 7 │ something;
╰──── ╰────
⚠ eslint(no-lone-blocks): Nested block is redundant. ⚠ eslint(no-lone-blocks): Nested block is redundant.
╭─[no_lone_blocks.tsx:5:22] ╭─[no_lone_blocks.tsx:5:21]
4 │ something; 4 │ something;
5 │ ╭─▶ { 5 │ ╭─▶ {
6 │ │ block; 6 │ │ block;
7 │ ╰─▶ } 7 │ ╰─▶ }
8 │ } 8 │ }
╰──── ╰────