eslint curly
labelled safe
Analysis Nemotron 3 Super
The curly rule flags missing braces around single-statement bodies of if, else, for, while, and do-while statements.
Fix: Wraps the single-statement body in curly braces, preserving indentation and formatting where possible.
8 hazards
- Rewriting 'if (x) y();' to 'if (x) { y(); }' changes nothing semantically but may affect automatic semicolon insertion if the original relied on ASI across a line break—though rare, adding braces could prevent a newline from being interpreted as a statement terminator in edge cases involving line-break-sensitive expressions.
- If the single statement is a labeled statement like 'label: break;', wrapping it in braces creates a new block scope, which does not affect the label's validity but could confuse readers expecting the label to function outside the block—though the label remains function-scoped, the block may mislead about scope boundaries.
- In a 'with' statement context (deprecated but still parsable), adding braces around the body could alter scope resolution if the original relied on the with object being implicitly available—though braces don't change with behavior, the visual nesting might mislead maintainers about scope, potentially causing bugs if they refactor based on perceived scope.
- If the statement contains a trailing comment on the same line (e.g., 'if (x) y++; // comment'), the fix may reposition the comment inside the braces, which is harmless syntactically but could break tools or conventions that expect comments to remain at specific column positions or outside control structures.
- In a macro-like preprocessing environment (non-standard), tools that scan for patterns like 'if (cond) stmt;' might fail to match the braced version, breaking build-time instrumentation or code generation that depends on the unbraceled form.
- If the single statement is a directive prologue like '"use strict";' inside a function, wrapping it in braces does not change its effect, but if mistakenly applied to a non-function block, it could create a block-scoped strict directive—which has no effect, so no behavioral change, though it may confuse linters expecting directives only at function or script top level.
- In rare cases where the statement is an empty statement (';'), rewriting 'if (x);' to 'if (x) {}' is functionally identical, but some minifiers or parsers might treat the empty block differently in terms of byte output or syntax tree structure, though execution remains unchanged.
- If the code relies on dynamic scope inspection via non-standard APIs (e.g., examining the call stack or function.caller), adding a block introduces an additional lexical environment, which could alter the depth of scopes inspected—though such APIs are obsolete and unreliable, this represents a theoretical hazard in legacy environments.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Var Hoisting & Redeclaration Inside Loops | var hoisting and re-declaration inside loops or after use |
| r1-p002 | fix ok prediction off | Directive Prologue Misapplication | If the single statement is a directive prologue like \"use strict\" inside a function, wrapping it in braces does not change its effect, but if mistakenly applied to a non-function block, it could cre |
| r1-p003 | fix ok | Empty Statement Rewriting | In rare cases where the statement is an empty statement (';'), rewriting 'if (x);' to 'if (x) {}' is functionally identical, but some minifiers or parsers might treat the empty block differently in te |
| r1-p004 | fix ok | __proto__ Keys & Prototype Access | __proto__ keys, prototype access, Object.assign vs spread |
| r1-p005 | fix ok | This Binding: Arrow vs Function | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
| r1-p006 | fix ok | Class Fields, Static Blocks, Private Names | class fields, static blocks, private names, super calls |
| r1-p007 | silent prediction off | ASI and Semicolon-less Code | ASI and semicolon-less code, leading ( or [ on the next line |
| r1-p008 | fix ok | Numeric/String Coercion | numeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt |
| r2-p001 | fix ok | Empty statement rewriting affects JSDoc parsing | In rare cases where the statement is an empty statement (';'), rewriting 'if (x)' to 'if (x) {}' changes how trailing comments or docblocks attach syntactically. |
| r2-p002 | fix ok | ASI hazard with leading parenthesis after if-body | Semicolon-less code with leading '(' on next line causes misparse when body is braced due to altered ASI boundaries. |
| r2-p003 | fix ok prediction off | Getter/setter side effect timing changed by block wrap | Wrapping a getter access in braces doesn't change value but alters microtask timing if getter has async side effects. |
| r2-p004 | fix ok | Non-standard stack inspection depth altered by new block | Adding a block introduces a new lexical environment, increasing stack depth observed by legacy .caller chains. |
| r2-p005 | fix ok | arguments object capture shifted by block scope | Bracing a one-line loop body creates a new scope, causing closures to capture different arguments objects. |
| r2-p006 | invalid | TDZ interaction with re-declaration after braced loop | Re-declaring a var after a braced loop vs unbraced affects hoisting visibility in TDZ-sensitive contexts. |
| r2-p007 | fix ok | Sparse array mutation via braced loop body | Bracing a loop body doesn't change iteration but altering comment attachment may affect tooling-dependent codegen. |
| r2-p008 | fix ok | Numeric/string coercion altered by brace-induced newline handling | Brace wrapping changes how JS engines handle newline-dependent coercion in edge-case expressions. |
Sources
r1-p001 · Var Hoisting & Redeclaration Inside Loops
1function f() {
2 for (var i = 0; i < 2; i++)
3 if (i === 0)
4 var x = 1;
5 console.log(x);
6}
7f();
--- orig.js 2026-10-22 14:12:19.494673111 -0400 +++ fixed.js 2026-10-22 14:12:21.890689273 -0400 @@ -1,7 +1,7 @@ function f() { for (var i = 0; i < 2; i++) - if (i === 0) - var x = 1; + {if (i === 0) + {var x = 1;}} console.log(x); } f();
1
r1-p002 · Directive Prologue Misapplication
1(function () {
2 if (false)
3 "use strict";
4 return typeof x;
5})();
--- orig.js 2026-10-22 14:12:19.496673124 -0400 +++ fixed.js 2026-10-22 14:12:22.642694543 -0400 @@ -1,5 +1,5 @@ (function () { if (false) - "use strict"; + {"use strict";} return typeof x; })();
r1-p003 · Empty Statement Rewriting
1let count = 0;
2for (let i = 0; i < 2; i++)
3 if (i === 0);
4 count++;
5console.log(count);
--- orig.js 2026-10-22 14:12:19.496673124 -0400 +++ fixed.js 2026-10-22 14:12:23.171698288 -0400 @@ -1,5 +1,5 @@ let count = 0; for (let i = 0; i < 2; i++) - if (i === 0); + {if (i === 0){;}} count++; console.log(count);
1
r1-p004 · __proto__ Keys & Prototype Access
1const obj = {};
2for (let k of ['a', '__proto__'])
3 if (k === '__proto__')
4 obj[k] = { toString: () => 'hijacked' };
5console.log(obj.toString());
--- orig.js 2026-10-22 14:12:19.497673130 -0400 +++ fixed.js 2026-10-22 14:12:22.695694915 -0400 @@ -1,5 +1,5 @@ const obj = {}; for (let k of ['a', '__proto__']) - if (k === '__proto__') - obj[k] = { toString: () => 'hijacked' }; + {if (k === '__proto__') + {obj[k] = { toString: () => 'hijacked' };}} console.log(obj.toString());
hijacked
r1-p005 · This Binding: Arrow vs Function
1const o = { m() { return this.x; }, x: 1 };
2let f;
3if (true)
4 f = o.m;
5console.log(f(), f.call(o));
--- orig.js 2026-10-22 14:12:19.498673137 -0400 +++ fixed.js 2026-10-22 14:12:22.604694276 -0400 @@ -1,5 +1,5 @@ const o = { m() { return this.x; }, x: 1 }; let f; if (true) - f = o.m; + {f = o.m;} console.log(f(), f.call(o));
undefined 1
r1-p006 · Class Fields, Static Blocks, Private Names
1class B { constructor() { this.x = 1; } }
2class D extends B {
3 constructor() {
4 super();
5 if (true)
6 this.y = 2;
7 }
8}
9console.log(new D().y);
--- orig.js 2026-10-22 14:12:19.499673143 -0400 +++ fixed.js 2026-10-22 14:12:22.631694465 -0400 @@ -3,7 +3,7 @@ constructor() { super(); if (true) - this.y = 2; + {this.y = 2;} } } console.log(new D().y);
2
r1-p007 · ASI and Semicolon-less Code
1let val = 1
2[
3 2, 3
4].forEach(v => val += v)
5console.log(val)
r1-p008 · Numeric/String Coercion
1const obj = { valueOf: () => 2, toString: () => '3' };
2let sum = 0;
3if (true)
4 sum += obj;
5console.log(sum, typeof sum);
--- orig.js 2026-10-22 14:12:23.861703245 -0400 +++ fixed.js 2026-10-22 14:12:27.474730404 -0400 @@ -1,5 +1,5 @@ const obj = { valueOf: () => 2, toString: () => '3' }; let sum = 0; if (true) - sum += obj; + {sum += obj;} console.log(sum, typeof sum);
2 number
r2-p001 · Empty statement rewriting affects JSDoc parsing
1// @ts-ignore
2let x = 0;
3function f() {
4 // pre-directive-like comment
5 if (x++ === 0)
6 ; // <-- triggers curly: empty stmt
7}
8f();
9f();
10console.log(x);
--- orig.js 2026-10-22 14:12:33.197777425 -0400 +++ fixed.js 2026-10-22 14:12:36.022802418 -0400 @@ -3,7 +3,7 @@ function f() { // pre-directive-like comment if (x++ === 0) - ; // <-- triggers curly: empty stmt + {;} // <-- triggers curly: empty stmt } f(); f();
2
r2-p002 · ASI hazard with leading parenthesis after if-body
1let n = 0
2function g() {
3 if (n < 2)
4 console.log('a')
5 (function(){ n++; return })()
6}
7g();
8g();
9console.log(n)
--- orig.js 2026-10-22 14:12:33.198777433 -0400 +++ fixed.js 2026-10-22 14:12:36.057802737 -0400 @@ -1,8 +1,8 @@ let n = 0 function g() { if (n < 2) - console.log('a') - (function(){ n++; return })() + {console.log('a') + (function(){ n++; return })()} } g(); g();
a
r2-p003 · Getter/setter side effect timing changed by block wrap
1const obj = { get val() { return side++; }, side: 0 }
2let captured;
3if (true)
4 captured = obj.val
5console.log(captured)
--- orig.js 2026-10-22 14:12:33.200777451 -0400 +++ fixed.js 2026-10-22 14:12:36.112803239 -0400 @@ -1,5 +1,5 @@ const obj = { get val() { return side++; }, side: 0 } let captured; if (true) - captured = obj.val + {captured = obj.val} console.log(captured)
r2-p004 · Non-standard stack inspection depth altered by new block
1function outer() {
2 if (true)
3 return inner()
4}
5function inner() {
6 try { throw new Error() } catch (e) {
7 let depth = 0
8 let f = arguments.callee.caller
9 while (f && f !== outer) { depth++; f = f.caller }
10 console.log(depth)
11 }
12}
13outer()
--- orig.js 2026-10-22 14:12:33.201777459 -0400 +++ fixed.js 2026-10-22 14:12:36.194803988 -0400 @@ -1,6 +1,6 @@ function outer() { if (true) - return inner() + {return inner()} } function inner() { try { throw new Error() } catch (e) {
0
r2-p005 · arguments object capture shifted by block scope
1function h(...args) {
2 let fns = []
3 for (let i = 0; i < args.length; i++)
4 fns.push(() => args[i])
5 return fns.map(fn => fn())
6}
7console.log(h(10, 20))
--- orig.js 2026-10-22 14:12:33.202777468 -0400 +++ fixed.js 2026-10-22 14:12:36.063802792 -0400 @@ -1,7 +1,7 @@ function h(...args) { let fns = [] for (let i = 0; i < args.length; i++) - fns.push(() => args[i]) + {fns.push(() => args[i])} return fns.map(fn => fn()) } console.log(h(10, 20))
[ 10, 20 ]
r2-p006 · TDZ interaction with re-declaration after braced loop (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-12-1)
1function tdztest() {
2 if (false)
3 ; // dummy to avoid warning
4 for (var i = 0; i < 1; i++)
5 let tmp = i
6 console.log(typeof i, typeof tmp)
7 var i = 99
8}
9tdztest()
r2-p007 · Sparse array mutation via braced loop body
1let arr = Array(3)
2arr[1] = 1
3let sum = 0
4for (let i = 0; i < arr.length; i++)
5 if (arr[i] !== undefined) sum += arr[i]
6console.log(sum)
--- orig.js 2026-10-22 14:12:33.246777850 -0400 +++ fixed.js 2026-10-22 14:12:36.623807904 -0400 @@ -2,5 +2,5 @@ arr[1] = 1 let sum = 0 for (let i = 0; i < arr.length; i++) - if (arr[i] !== undefined) sum += arr[i] + {if (arr[i] !== undefined) {sum += arr[i]}} console.log(sum)
1
r2-p008 · Numeric/string coercion altered by brace-induced newline handling
1let val = '5'
2if (+val == 5)
3 console.log('loose eq true')
4else
5 console.log('loose eq false')
6console.log(String(+val))
--- orig.js 2026-10-22 14:12:37.203813227 -0400 +++ fixed.js 2026-10-22 14:12:41.168850958 -0400 @@ -1,6 +1,6 @@ let val = '5' if (+val == 5) - console.log('loose eq true') + {console.log('loose eq true')} else - console.log('loose eq false') + {console.log('loose eq false')} console.log(String(+val))
loose eq true 5