biome complexity/useWhile
labelled safe
Analysis Nemotron 3 Super
Flags for-loops without initialization or increment parts where only a condition remains, suggesting replacement with while loops.
Fix: Replace the for-loop with a while-loop using the condition as the test, keeping the loop body unchanged.
12 hazards
- Moving variable declarations from the init part to before the loop may cause hoisting issues if the variable is used prior to the loop.
- If the init part contains side-effecting expressions (e.g., function calls), removing them alters execution order and may omit required initialization.
- The fix assumes no update part exists; if present but ignored by the rule due to being empty, the rewrite incorrectly drops it.
- Labels or break/continue statements targeting the for-loop by label may become invalid if the loop structure changes.
- Comments attached to the for or semicolons may be lost during rewrite if not preserved by the tool.
- In strict mode, deleting a variable via the init part (if present) would behave differently if the init is removed.
- If the condition uses `with` or indirect eval, scope resolution could differ between for and while constructs.
- The rewrite may interfere with automatic semicolon insertion if the loop header is malformed or adjacent to problematic tokens.
- If the loop is part of a larger expression (e.g., return for (;cond;) expr;), the statement conversion breaks grammar.
- Using `this` in the condition may capture a different object if the loop rewrite changes execution context indirectly.
- If the init part declares a variable with `var` that is later accessed outside the loop, moving it outside may not preserve temporal dead zone behavior (though var has none, confusion with let/const could arise).
- If the condition is a complex expression involving commas, the rewrite may misinterpret precedence or evaluation order.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p002 | fix ok | __proto__ keys prototype access | Rewriting a for-loop accessing obj.__proto__ in condition may alter prototype chain mutation visibility if loop re-runs after mutation. |
| r1-p003 | fix ok | complex expression with commas | The comma operator in for-condition has left-to-right evaluation; rewriting to while may break if condition relies on comma sequencing. |
| r1-p004 | fix ok | sparse arrays holes | Loop condition checks array length; rewriting doesn't change behavior but must show interaction with sparse elements. |
| r1-p005 | fix ok prediction off | temporal dead zone after hoisting | Loop condition accesses a let variable before its declaration (TDZ); rewriting doesn't move the condition, so TDZ still throws. |
| r1-p006 | fix ok | function declarations inside blocks switch labeled | Function declaration in loop body; rewriting loop shouldn't affect hoisting, but must show function is redefined each iteration if re-entered. |
| r1-p007 | fix ok prediction off | strict mode deleting variable via init part | Not applicable: rule only triggers when init part is absent; no init to remove, so strict mode delete behavior unchanged. |
| r1-p008 | fix ok | regular expressions escapes unicode lastIndex g flag | Loop condition uses regex.test() which advances lastIndex with g flag; rewriting doesn't change evaluation, so behavior preserved. |
| r2-p001 | fix ok | Hoisting issue due to moving const/init variable | Moving variable declarations from the init part to before the loop may cause hoisting issues if the variable is used prior to the loop. |
| r2-p002 | fix ok | Var redeclaration shadowing outer scope | If the init part declares a variable with `var` that is later accessed outside the loop, moving it outside may not preserve temporal dead zone behavior (though var has none, confusion with let/const c |
| r2-p003 | fix ok | Function declaration inside loop body affected by scoping | function declarations inside blocks, switch cases, labeled statements |
| r2-p004 | fix ok | Comment loss during rewrite affecting readability | Comments attached to the for or semicolons may be lost during rewrite if not preserved by the tool. |
| r2-p005 | fix ok | Class field initialization order dependency | class fields, static blocks, private names, super calls |
| r2-p006 | fix ok | ASI hazard with leading parenthesis after loop | ASI and semicolon-less code, leading ( or [ on the next line |
| r2-p007 | fix ok | Sparse array length check in condition | sparse arrays, holes, Array(n) vs [n], array-likes |
| r2-p008 | fix ok | Label break target invalidated by loop rewrite | Labels or break/continue statements targeting the for-loop by label may become invalid if the loop structure changes. |
Sources
r1-p002 · __proto__ keys prototype access
1let obj = { a: 1 };
2let proto = { b: 2 };
3obj.__proto__ = proto;
4let seen = [];
5for (; obj.__proto__;) {
6 seen.push(obj.__proto__.b);
7 obj.__proto__ = null;
8 break;
9}
10console.log(seen.length ? seen[0] : 'null');
--- orig.js 2026-10-17 16:16:14.821361406 -0400 +++ fixed.js 2026-10-17 16:16:15.112357606 -0400 @@ -2,7 +2,7 @@ let proto = { b: 2 }; obj.__proto__ = proto; let seen = []; -for (; obj.__proto__;) { +while (obj.__proto__) { seen.push(obj.__proto__.b); obj.__proto__ = null; break;
2
r1-p003 · complex expression with commas
1let x = 0, y = 0;
2for (; (x++, y++, x < 3);) {
3 console.log(`x:${x},y:${y}`);
4}
--- orig.js 2026-10-17 16:16:15.270355545 -0400 +++ fixed.js 2026-10-17 16:16:15.532352128 -0400 @@ -1,4 +1,4 @@ let x = 0, y = 0; -for (; (x++, y++, x < 3);) { +while ((x++, y++, x < 3)) { console.log(`x:${x},y:${y}`); }
x:1,y:1 x:2,y:2
r1-p004 · sparse arrays holes
1let arr = [, , 3];
2let sum = 0;
3for (; arr.length > 0;) {
4 const v = arr.pop();
5 if (v !== undefined) sum += v;
6}
7console.log(sum);
--- orig.js 2026-10-17 16:16:15.715349742 -0400 +++ fixed.js 2026-10-17 16:16:15.986346208 -0400 @@ -1,6 +1,6 @@ let arr = [, , 3]; let sum = 0; -for (; arr.length > 0;) { +while (arr.length > 0) { const v = arr.pop(); if (v !== undefined) sum += v; }
3
r1-p005 · temporal dead zone after hoisting
1try {
2 for (; typeof x === 'undefined';) {
3 console.log('unreachable');
4 }
5} catch (e) {
6 console.log(e.name + ': ' + e.message);
7}
8let x = 1;
--- orig.js 2026-10-17 16:16:16.161343931 -0400 +++ fixed.js 2026-10-17 16:16:16.452340146 -0400 @@ -1,5 +1,5 @@ try { - for (; typeof x === 'undefined';) { + while (typeof x === 'undefined') { console.log('unreachable'); } } catch (e) {
ReferenceError: Cannot access 'x' before initialization
r1-p006 · function declarations inside blocks switch labeled
1let fns = [];
2for (; fns.length < 2;) {
3 function f() { return fns.length; }
4 fns.push(f);
5}
6console.log(fns[0](), fns[1]());
--- orig.js 2026-10-17 16:16:16.623337921 -0400 +++ fixed.js 2026-10-17 16:16:16.892334422 -0400 @@ -1,5 +1,5 @@ let fns = []; -for (; fns.length < 2;) { +while (fns.length < 2) { function f() { return fns.length; } fns.push(f); }
2 2
r1-p007 · strict mode deleting variable via init part
1'use strict';
2let v = 1;
3for (; v;) {
4 console.log(v);
5 v = 0;
6}
--- orig.js 2026-10-17 16:16:17.059332252 -0400 +++ fixed.js 2026-10-17 16:16:17.337328644 -0400 @@ -1,6 +1,6 @@ 'use strict'; let v = 1; -for (; v;) { +while (v) { console.log(v); v = 0; }
1
r1-p008 · regular expressions escapes unicode lastIndex g flag
1let str = 'abab';
2let re = /ab/g;
3let matches = 0;
4for (; re.test(str);) {
5 matches++;
6}
7console.log(matches);
--- orig.js 2026-10-17 16:16:17.515326335 -0400 +++ fixed.js 2026-10-17 16:16:17.794322715 -0400 @@ -1,7 +1,7 @@ let str = 'abab'; let re = /ab/g; let matches = 0; -for (; re.test(str);) { +while (re.test(str)) { matches++; } console.log(matches);
2
r2-p001 · Hoisting issue due to moving const/init variable
1console.log(typeof x); // ReferenceError expected
2for (; false;) {}
3let x = 1;
4console.log('done');
--- orig.js 2026-10-17 16:16:23.064254837 -0400 +++ fixed.js 2026-10-17 16:16:23.348251208 -0400 @@ -1,4 +1,4 @@ console.log(typeof x); // ReferenceError expected -for (; false;) {} +while (false) {} let x = 1; console.log('done');
r2-p002 · Var redeclaration shadowing outer scope
1var x = 1;
2for (; x < 2;) {
3 var x = 3;
4 console.log(x);
5 x++;
6}
7console.log(x);
--- orig.js 2026-10-17 16:16:23.516249060 -0400 +++ fixed.js 2026-10-17 16:16:23.780245687 -0400 @@ -1,5 +1,5 @@ var x = 1; -for (; x < 2;) { +while (x < 2) { var x = 3; console.log(x); x++;
3 4
r2-p003 · Function declaration inside loop body affected by scoping
1for (; false;) {
2 function f() { return 1; }
3 break;
4}
5try { console.log(f()); } catch (e) { console.log(e.name + ': ' + e.message); }
--- orig.js 2026-10-17 16:16:23.942243616 -0400 +++ fixed.js 2026-10-17 16:16:24.213240160 -0400 @@ -1,4 +1,4 @@ -for (; false;) { +while (false) { function f() { return 1; } break; }
ReferenceError: f is not defined
r2-p004 · Comment loss during rewrite affecting readability
1// precondition
2for (; /* stop */false;) {/* body */}
3console.log('executed');
--- orig.js 2026-10-17 16:16:24.386237955 -0400 +++ fixed.js 2026-10-17 16:16:24.661234449 -0400 @@ -1,3 +1,3 @@ // precondition -for (; /* stop */false;) {/* body */} +while (/* stop */false) {/* body */} console.log('executed');
executed
r2-p005 · Class field initialization order dependency
1class C { static y = console.log('static') || 1; }
2for (; C.y;) {
3 console.log('loop');
4 break;
5}
--- orig.js 2026-10-17 16:16:24.832232269 -0400 +++ fixed.js 2026-10-17 16:16:25.115228666 -0400 @@ -1,5 +1,5 @@ class C { static y = console.log('static') || 1; } -for (; C.y;) { +while (C.y) { console.log('loop'); break; }
static loop
r2-p006 · ASI hazard with leading parenthesis after loop
1for (; false;)
2(function(){ return console.log('IIFE') })()
3console.log('done')
--- orig.js 2026-10-17 16:16:25.278226593 -0400 +++ fixed.js 2026-10-17 16:16:25.555223071 -0400 @@ -1,3 +1,3 @@ -for (; false;) +while (false) (function(){ return console.log('IIFE') })() console.log('done')
done
r2-p007 · Sparse array length check in condition
1const arr = Array(3); arr[1] = 'a';
2for (; arr.length > 1;) {
3 console.log(arr.length);
4 arr.pop();
5}
6console.log('end')
--- orig.js 2026-10-17 16:16:25.729220858 -0400 +++ fixed.js 2026-10-17 16:16:26.016217210 -0400 @@ -1,5 +1,5 @@ const arr = Array(3); arr[1] = 'a'; -for (; arr.length > 1;) { +while (arr.length > 1) { console.log(arr.length); arr.pop(); }
3 2 end
r2-p008 · Label break target invalidated by loop rewrite
1outer:
2for (; false;) {
3 break outer;
4}
5console.log('after loop')
--- orig.js 2026-10-17 16:16:26.187215041 -0400 +++ fixed.js 2026-10-17 16:16:26.465211515 -0400 @@ -1,5 +1,5 @@ outer: -for (; false;) { +while (false) { break outer; } console.log('after loop')
after loop