fixhunt

biome complexity/noUselessLoneBlockStatements: fix changes program behavior

fix-changes-behaviorlabelled safereal · highrelated
Findings / 2026-10-17T20-06-50-863Z-e9k5-biome-complexity_noUselessLoneBlockStatements / r2-p008biome 2.5.1513 → 6 lines2026-10-17 20:06 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

ASI and semicolon-less code, leading ( or [ on the next line

Program

before
1try {
2 let x = 1
3{}
4[1,2,3].forEach(v => console.log(v))
5} catch(e) {
6}
after
1try {
2 let x = 1
3[1,2,3].forEach(v => console.log(v))
4} catch(e) {
5}

Output

stdout beforeexit 0
1
2
3
stdout afterexit 0
(no stdout)
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory real · high

The fix removes a standalone block `{}` inside a try block that contains a `let x = 1` declaration. According to the documentation, a block is not redundant when a block-level binding like `let` is present in the enclosing scope, because the block creates a new scope for that binding. Removing the block changes the scope structure, which can alter runtime behavior (here, the `forEach` callback no longer executes, as shown by the missing stdout output). The rule incorrectly treats this block as useless and its removal as safe, but it is not semantically equivalent.

Explanation Nemotron 3 Super unverified

The removed empty block `{}` was part of the `try` statement’s body; deleting it changes which statements belong to the `try`. After removal, `[1,2,3].forEach(...)` executes outside any `try`, so if its callback threw (it does not here), it would not be caught. More importantly, JavaScript’s automatic semicolon insertion treats the line break before `[1,2,3]` as ending the `let x = 1` statement, making the array literal a standalone expression statement whose value is discarded—so nothing is printed. Originally, the empty block prevented ASI from interfering, allowing the `forEach` call to remain inside the `try` and execute normally. Thus the fix alters control flow and output.

Reproduce

file
prog.js (the before program)
minimized program
r2-p008.js
evidence sheet
r2-p008.md
regression fixture
r2-p008.js the minimized program with a header stating the observed change, for the tool's test suite
node prog.js
bunx @biomejs/biome lint --only=complexity/noUselessLoneBlockStatements --write prog.js
node prog.js

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

noUselessLoneBlockStatements fix · complexity noUselessLoneBlockStatement empty block · biome lint rule remove useless lone blocks · fix behavior removes standalone braces in try

  • related biomejs/biome/issues/11935 same rule but different construct (block-scoped functions in strict scripts/class bodies vs lone block in try body)
  • related biomejs/biome/issues/834 same rule (noUselessLoneBlockStatements) but different construct (switch statement interaction with useSingleCaseStatement)
  • related biomejs/biome/issues/8685 same rule (noUselessLoneBlockStatements) but different construct (empty block with comments)
  • related biomejs/biome/issues/39 same rule family (noUselessLoneBlockStatements/noEmptyBlockStatements) but different construct (empty block statements generally)

Details

program
ASI Semicolon-Less Hazard
hazard
ASI and semicolon-less code, leading ( or [ on the next line
oracle
stdout before: 1 2 3 stdout after:
run
2026-10-17T20-06-50-863Z-e9k5-biome-complexity_noUselessLoneBlockStatements
generated program (13 lines) and the fix's diff
1let x = 1
2{}
3[1,2,3].forEach(v => console.log(v))
4// Before: {} is parsed as empty block, then [1...] is array literal -> prints 1,2,3
5// After fix: remove {} -> now: let x = 1
6// [1,2,3].forEach(...) -> ASI: tries to parse as 1[1,2,3] -> TypeError
7try {
8 let x = 1
9{}
10[1,2,3].forEach(v => console.log(v))
11} catch(e) {
12 console.log('Error:', e.name + ': ' + e.message)
13}
--- orig.js	2026-10-17 16:07:12.347577815 -0400
+++ fixed.js	2026-10-17 16:07:12.655564001 -0400
@@ -6,7 +6,6 @@
 // [1,2,3].forEach(...) -> ASI: tries to parse as 1[1,2,3] -> TypeError
 try {
   let x = 1
-{}
 [1,2,3].forEach(v => console.log(v))
 } catch(e) {
   console.log('Error:', e.name + ': ' + e.message)
rule documentation given to the models
Summary

- Name: noUselessLoneBlockStatements
- Fix: safe
- Default severity: info
- Available from version: 1.3.3
- Diagnostic category: lint/complexity/noUselessLoneBlockStatements
- This rule is recommended

Description

 Disallow unnecessary nested block statements.

 > In JavaScript, prior to ES6, standalone code blocks delimited by curly braces do not create a new scope and have no use.
 > In ES6, code blocks may create a new scope if a block-level binding (let and const), a class declaration or a function declaration (in strict mode) are present. A block is not considered redundant in these cases.

Examples

Invalid

 ```js,expect_diagnostic
 {}
 ```

 ```js,expect_diagnostic
 if (foo) {
   bar();
   {
     baz();
   }
 }
 ```

Valid

 ```js
 while (foo) {
   bar();
 }
 ```