biome complexity/noUselessContinue
labelled safe
Analysis Nemotron 3 Super
Flags redundant 'continue' statements at the end of loops where they have no effect.
Fix: Removes the redundant 'continue;' statement entirely from the loop body.
8 hazards
- Removing a labeled 'continue' may alter control flow if outer loops depend on skipping iterations via the label.
- If the 'continue' is the only statement in a loop body, removing it creates an empty loop body which may be invalid or change semantics in edge cases involving ASI.
- In a 'do-while' loop, removing the 'continue' may cause the condition to be evaluated on the first iteration when it previously was not, altering side-effect timing.
- If the loop contains no other statements and the 'continue' is removed, some minifiers or tools might treat the empty block differently, risking parsing issues in strict contexts.
- Removing a 'continue' that follows a side-effecting expression (e.g., after a function call) could change evaluation order if the autofix incorrectly assumes the continue is last when it isn't due to formatting.
- In loops with multiple labels, removing a labeled 'continue' might break intended jumps to outer loops if inner loops rely on the label for control flow.
- If the 'continue' is followed by a comment that relies on its presence for JSDoc or tooling interpretation, removing it may detach or misalign the comment.
- In a loop with a trailing semicolon after the block (rare), removing the 'continue' could expose the semicolon as an empty statement, potentially confusing parsers or linters.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | invalid | Do-while continue removal alters condition evaluation timing | In a 'do-while' loop, removing the 'continue' may cause the condition to be evaluated on the first iteration when it previously was not, altering side-effect timing. |
| r1-p002 | silent prediction off | Arguments object and function length affected by continue placement | arguments object, function.length, default parameters, rest parameters |
| r1-p003 | silent prediction off | Sparse array iteration affected by continue removal | sparse arrays, holes, Array(n) vs [n], array-likes |
| r1-p004 | silent prediction off | Side-effect after continue changes evaluation order when fix misidentifies position | Removing a 'continue' that follows a side-effecting expression (e.g., after a function call) could change evaluation order if the autofix incorrectly assumes the continue is last when it isn't due to |
| r1-p005 | silent prediction off | Function declaration in block scope affected by loop continuation | function declarations inside blocks, switch cases, labeled statements |
| r1-p006 | silent prediction off | Var hoisting and redeclaration inside loop altered by continue removal | var hoisting and re-declaration inside loops or after use |
| r1-p007 | silent prediction off | Comment detachment after continue removal affects JSDoc/tooling | If the 'continue' is followed by a comment that relies on its presence for JSDoc or tooling interpretation, removing it may detach or misalign the comment. |
| r1-p008 | silent prediction off | Async/await ordering changed by continue removal in generator-like flow | async/await ordering, generators and yield position |
| r2-p001 | fix ok | Comment attached to removed continue | comments attached to the removed or moved code |
| r2-p002 | fix ok | Async await ordering changed | async/await ordering changed by continue removal in generator-like flow |
| r2-p003 | silent | Function decl in block scope | function declarations inside blocks, switch cases, labeled statements |
| r2-p004 | fix ok | Sparse array iteration | sparse array iteration affected by continue removal |
| r2-p005 | fix ok | Trailing semicolon exposes empty statement | In a loop with a trailing semicolon after the block (rare), removing the 'continue' could expose the semicolon as an empty statement |
| r2-p006 | fix ok | Getter setter side effects | getters, setters and Object.defineProperty side effects |
| r2-p007 | fix ok prediction off | __proto__ and prototype access | __proto__ keys, prototype access, Object.assign vs spread |
| r2-p008 | fix ok | ASI and leading ( or [ | ASI and semicolon-less code, leading ( or [ on the next line |
Sources
r1-p001 · Do-while continue removal alters condition evaluation timing (generated program is not deterministic)
1let x = 0;
2do {
3 if (x === 0) { x++; continue; }
4 console.log('loop:', x);
5} while (x < 2);
r1-p002 · Arguments object and function length affected by continue placement
1function f(a, b = 1, ...rest) {
2 for (let i = 0; i < 2; i++) {
3 if (i === 0) continue;
4 console.log('args:', arguments.length, 'rest:', rest.length);
5 }
6}
7f(10);
args: 1 rest: 0
r1-p003 · Sparse array iteration affected by continue removal
1const arr = Array(3);
2arr[1] = 'present';
3for (let i = 0; i < arr.length; i++) {
4 if (!(i in arr)) { continue; }
5 console.log('index:', i, 'value:', arr[i]);
6}
index: 1 value: present
r1-p004 · Side-effect after continue changes evaluation order when fix misidentifies position
1let log = [];
2for (let i = 0; i < 2; i++) {
3 log.push('before');
4 if (i === 0) { continue; log.push('after'); }
5}
6console.log(log.join('|'));
before|before
r1-p005 · Function declaration in block scope affected by loop continuation
1for (let i = 0; i < 2; i++) {
2 if (i === 0) { continue; }
3 function inner() { return i; }
4 console.log('inner:', inner());
5}
inner: 1
r1-p006 · Var hoisting and redeclaration inside loop altered by continue removal
1for (let i = 0; i < 2; i++) {
2 if (i === 0) { continue; }
3 var x = i;
4 console.log('x:', x);
5}
6console.log('final x:', x);
x: 1 final x: 1
r1-p007 · Comment detachment after continue removal affects JSDoc/tooling
1for (let i = 0; i < 2; i++) {
2 if (i === 0) { continue; /** Skips first iteration */ }
3 console.log('value:', i);
4}
value: 1
r1-p008 · Async/await ordering changed by continue removal in generator-like flow
1async function main() {
2 const results = [];
3 for (let i = 0; i < 2; i++) {
4 if (i === 0) { continue; await Promise.resolve().then(() => results.push(i)); }
5 results.push(i * 2);
6 }
7 console.log('results:', results);
8}
9main().catch(e => console.error(e));
results: [ 2 ]
r2-p001 · Comment attached to removed continue
1// This comment should stay
2for (let i = 0; i < 2; i++) {
3 // Continue here
4 continue;
5 /* Never reached */
6}
7console.log('done');
--- orig.js 2026-10-17 16:05:57.082238069 -0400 +++ fixed.js 2026-10-17 16:05:57.342224410 -0400 @@ -1,7 +1,5 @@ // This comment should stay for (let i = 0; i < 2; i++) { - // Continue here - continue; /* Never reached */ } console.log('done');
done
r2-p002 · Async await ordering changed
1async function f() {
2 let x = 0;
3 for (let i = 0; i < 2; i++) {
4 await Promise.resolve().then(() => ++x);
5 continue; // <-- removes this, moves await earlier logically? Actually same order.
6 }
7 return x;
8}
9f().then(console.log).catch(e=>console.error(e));
--- orig.js 2026-10-17 16:05:57.510215584 -0400 +++ fixed.js 2026-10-17 16:05:57.782201295 -0400 @@ -2,7 +2,6 @@ let x = 0; for (let i = 0; i < 2; i++) { await Promise.resolve().then(() => ++x); - continue; // <-- removes this, moves await earlier logically? Actually same order. } return x; }
2
r2-p003 · Function decl in block scope
1loop: for (let i = 0; i < 1; i++) {
2 function inner() { return 42; }
3 continue loop;
4 inner(); // unreachable
5}
6console.log(typeof inner); // Should error if hoisted wrongly
undefined
r2-p004 · Sparse array iteration
1const arr = [1,,3]; // hole at index 1
2let sum = 0;
3for (let i = 0; i < arr.length; i++) {
4 if (!(i in arr)) continue; // skip holes
5 sum += arr[i];
6}
7console.log(sum); // Before fix: skips hole -> 4. After fix? Same logic.
8// But if we remove a useless continue elsewhere...
9for (let j = 0; j < 1; j++) {
10 continue; // <-- fired, removed
11}
12console.log('sparse done', sum);
--- orig.js 2026-10-17 16:05:58.165181193 -0400 +++ fixed.js 2026-10-17 16:05:58.425167563 -0400 @@ -7,6 +7,5 @@ console.log(sum); // Before fix: skips hole -> 4. After fix? Same logic. // But if we remove a useless continue elsewhere... for (let j = 0; j < 1; j++) { - continue; // <-- fired, removed } console.log('sparse done', sum);
4 sparse done 4
r2-p005 · Trailing semicolon exposes empty statement
1for (let i = 0; i < 1; i++) {
2 console.log(i);
3 continue;
4}; // <-- semicolon after block
5console.log('after'); // Should still run
6// If fix removes continue, we have: } ; <-- empty stmt, but harmless
7// To make it matter: put something sensitive after
8let x = 1;
9for (let i = 0; i < 1; i++) { x++; continue; }; // semicolon here
10console.log(x); // Should be 2 regardless
--- orig.js 2026-10-17 16:05:58.590158913 -0400 +++ fixed.js 2026-10-17 16:05:58.827146489 -0400 @@ -1,10 +1,9 @@ for (let i = 0; i < 1; i++) { console.log(i); - continue; }; // <-- semicolon after block console.log('after'); // Should still run // If fix removes continue, we have: } ; <-- empty stmt, but harmless // To make it matter: put something sensitive after let x = 1; -for (let i = 0; i < 1; i++) { x++; continue; }; // semicolon here +for (let i = 0; i < 1; i++) { x++; }; // semicolon here console.log(x); // Should be 2 regardless
0 after 2
r2-p006 · Getter setter side effects
1const obj = { get val() { console.log('get'); return 1; } };
2for (let i = 0; i < 2; i++) {
3 const v = obj.val; // side effect: log
4 continue; // <-- removed
5}
6console.log('end');
--- orig.js 2026-10-17 16:05:58.985138206 -0400 +++ fixed.js 2026-10-17 16:05:59.242124759 -0400 @@ -1,6 +1,5 @@ const obj = { get val() { console.log('get'); return 1; } }; for (let i = 0; i < 2; i++) { const v = obj.val; // side effect: log - continue; // <-- removed } console.log('end');
get get end
r2-p007 · __proto__ and prototype access
1class Base { method() { return 'base'; } }
2class Der extends Base {}
3const d = new Der;
4for (let i = 0; i < 1; i++) {
5 console.log(d.method());
6 continue; // <-- if removed, still calls method
7}
8console.log('proto check done');
--- orig.js 2026-10-17 16:05:59.399116546 -0400 +++ fixed.js 2026-10-17 16:05:59.644103729 -0400 @@ -3,6 +3,5 @@ const d = new Der; for (let i = 0; i < 1; i++) { console.log(d.method()); - continue; // <-- if removed, still calls method } console.log('proto check done');
base proto check done
r2-p008 · ASI and leading ( or [
1let x = 1
2for (let i = 0; i < 1; i++) {
3 x++
4 continue
5}
6(x => { console.log('called', x); })(5)
7// Without ASI fix: x++ then continue then (x=>...) call is part of expr?
8// Actually: after fix, we have x++; continue; then (x=>..)(5) -> ASI sees ; before (
9// So it's fine. But if we had:
10let y = 2
11for (let j = 0; j < 1; j++) { y++; continue }
12[1,2].forEach(v=>{console.log('arr',v)})
13// Here: y++; continue; [1,2]... -> without semicolon, [ is interpreted as indexing?
14// y++ ; continue [1,2]... -> SyntaxError
15// So we need to show that after fix, ASI breaks.
16try {
17 let z = 0
18 for (let k = 0; k < 1; k++) { z++; continue }
19 [z].forEach(v=>console.log('ASI safe:', v))
20} catch(e) {
21 console.error(e.name, e.message)
22}
--- orig.js 2026-10-17 16:05:59.808095150 -0400 +++ fixed.js 2026-10-17 16:06:00.076081138 -0400 @@ -1,21 +1,20 @@ let x = 1 for (let i = 0; i < 1; i++) { x++ - continue } (x => { console.log('called', x); })(5) // Without ASI fix: x++ then continue then (x=>...) call is part of expr? // Actually: after fix, we have x++; continue; then (x=>..)(5) -> ASI sees ; before ( // So it's fine. But if we had: let y = 2 -for (let j = 0; j < 1; j++) { y++; continue } +for (let j = 0; j < 1; j++) { y++; } [1,2].forEach(v=>{console.log('arr',v)}) // Here: y++; continue; [1,2]... -> without semicolon, [ is interpreted as indexing? // y++ ; continue [1,2]... -> SyntaxError // So we need to show that after fix, ASI breaks. try { let z = 0 - for (let k = 0; k < 1; k++) { z++; continue } + for (let k = 0; k < 1; k++) { z++; } [z].forEach(v=>console.log('ASI safe:', v)) } catch(e) { console.error(e.name, e.message)
called 5 arr 1 arr 2 ASI safe: 1