biome style/useConst
labelled safe
Analysis Nemotron 3 Super
Flags `let` declarations where the variable is never reassigned after initialization.
Fix: Replaces `let` with `const` for each variable declarator that has an initializer or is assigned exactly once before any reassignment, preserving declarators, commas, and block structure while removing the `let` keyword and inserting `const`.
12 hazards
- Rewriting `let` in a loop header like `for (let i of arr)` to `const` causes a TypeError because `const` bindings cannot be reassigned per iteration.
- Changing `let` to `const` in a block-scoped redeclaration such as `{ let a = 1; let a = 2; }` results in a SyntaxError due to duplicate `const` declarations in the same block.
- If the initializer contains a `delete` expression targeting an unqualified identifier (e.g., `let x = delete y;`), converting to `const` may throw a SyntaxError in strict mode because `const` bindings cannot be deleted.
- When the variable is conditionally declared (e.g., `if (x) let y = 1;`), replacing `let` with `const` can cause a ReferenceError if the block is not entered, as `const` is not hoisted and remains inaccessible outside its block.
- In non-strict mode, if the initializer assigns to an undeclared variable (e.g., `let a = (b = 5);`), switching to `const` may silently create a global variable instead of throwing, altering implicit globals behavior.
- If the declarator lacks an initializer and is assigned later (e.g., `let a; a = 1;`), the fix incorrectly applies `const` to the uninitialized declarator, causing a SyntaxError because `const` must be initialized at declaration.
- Moving a `let` declaration to `const` across a function boundary (e.g., from outer to inner scope via hoisting misunderstandings) breaks encapsulation and may expose unintended variables.
- Comments attached to the `let` keyword or declarators (e.g., `let /* ignored */ x = 1;`) may be stripped or misplaced during the rewrite, altering source formatting or tooling expectations.
- In a `try` block (e.g., `try { let e = 5; }`), rewriting to `const` is safe only if no reassignment occurs, but if the variable is referenced in `finally` after potential reassignment in `catch`, the timing of binding initialization may differ.
- When used with `export let` (non-standard but possible in some environments), replacing with `export const` may break bindings expected to be mutable by importers.
- If the variable name is `let` or `const` itself (e.g., `let let = 5;`), the rewrite produces invalid syntax (`const const = 5;`).
- In eval contexts or dynamic scope manipulations, changing `let` to `const` alters the mutability of the binding, which may be relied upon for runtime redefinition via indirect eval.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | ASI and semicolon-less code with leading parenthesis | When `let` is converted to `const` in ASI-prone code without semicolons, a following line starting with `(` or `[` can be misinterpreted as a function call or array indexing on the prior expression, c |
| r1-p002 | silent prediction off | Moving let to const across function boundary breaks encapsulation | If a `let` declared in an outer scope is reassigned inside an inner function, converting it to `const` prevents reassignment and alters behavior. This tests whether the linter incorrectly hoists or mi |
| r1-p003 | fix ok | Non-strict mode: initializer assigns to undeclared variable | In sloppy mode, `let a = (b = 5);` creates a global `b`. If `let` becomes `const`, the assignment still happens but `b` remains global—however, if the linter mistakenly treats the inner assignment as |
| r1-p004 | fix ok | Strict vs sloppy mode semantics | In strict mode, assigning to an undeclared variable throws; in sloppy mode it creates a global. Converting `let` to `const` doesn't change this directly, but if the initializer has such an assignment, |
| r1-p005 | fix ok | Sparse arrays, holes, Array(n) vs [n] | When initializing a `let` variable with an array literal containing holes (e.g., `[, , 3]`), converting to `const` should preserve the array structure. However, if the linter mishandles the initialize |
| r1-p006 | fix ok | Regular expressions: escapes, unicode flag, lastIndex with g flag | If a `let` variable holds a regex with `/g` flag, its `lastIndex` mutates on each `exec`. Converting `let` to `const` should not affect this mutability. But if the linter incorrectly assumes immutabil |
| r1-p007 | invalid | Block-scoped redeclaration with const causes SyntaxError | Two `let` declarations in the same block are allowed; converting both to `const` causes a `SyntaxError` due to duplicate `const` declarations. This tests whether the linter attempts to rewrite both wi |
| r1-p008 | silent prediction off | Order of evaluation when expressions are merged or duplicated | If a `let` variable's initializer has side effects (e.g., function calls), and the linter duplicates or merges expressions during rewrite, those effects might run twice or not at all. We use a comma-s |
| r2-p001 | fired, no fix | Unassigned let converted to const | If the declarator lacks an initializer and is assigned later (e.g., `let a; a = 1;`), the fix incorrectly applies `const` to the uninitialized declarator, causing a SyntaxError because `const` must be |
| r2-p002 | invalid | Delete unqualified identifier in initializer | If the initializer contains a `delete` expression targeting an unqualified identifier (e.g., `let x = delete y;`), converting to `const` may throw a SyntaxError in strict mode because `const` bindings |
| r2-p003 | fix ok prediction off | Conditional let declaration | When the variable is conditionally declared (e.g., `if (x) let y = 1;`), replacing `let` with `const` can cause a ReferenceError if the block is not entered, as `const` is not hoisted and remains inac |
| r2-p004 | fix ok | Numeric/string coercion with toString/valueOf | numeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt |
| r2-p005 | silent prediction off | Export let (non-standard) | When used with `export let` (non-standard but possible in some environments), replacing with `export const` may break bindings expected to be mutable by importers. |
| r2-p006 | fix ok | __proto__ and prototype access | __proto__ keys, prototype access, Object.assign vs spread |
| r2-p007 | silent prediction off | Strict vs sloppy mode semantics | strict vs sloppy mode semantics |
| r2-p008 | fix ok | Eval context mutability | In eval contexts or dynamic scope manipulations, changing `let` to `const` alters the mutability of the binding, which may be relied upon for runtime redefinition via indirect eval. |
Sources
r1-p001 · ASI and semicolon-less code with leading parenthesis
1let x = 5
2[1,2].forEach(v=>console.log(x+v))
--- orig.js 2026-10-17 17:01:20.097451115 -0400 +++ fixed.js 2026-10-17 17:01:20.379454550 -0400 @@ -1,2 +1,2 @@ -let x = 5 +const x = 5 [1,2].forEach(v=>console.log(x+v))
r1-p002 · Moving let to const across function boundary breaks encapsulation
1let v = 0;
2function update() { v = 42; }
3update();
4console.log(v);
42
r1-p003 · Non-strict mode: initializer assigns to undeclared variable
1(function(){ 'use sloppy'; let a = (b = 5); console.log(typeof b, b); })();
--- orig.js 2026-10-17 17:01:20.778459409 -0400 +++ fixed.js 2026-10-17 17:01:21.052462746 -0400 @@ -1 +1 @@ -(function(){ 'use sloppy'; let a = (b = 5); console.log(typeof b, b); })(); +(function(){ 'use sloppy'; const a = (b = 5); console.log(typeof b, b); })();
r1-p004 · Strict vs sloppy mode semantics
1'use strict';
2try {
3 let c = (d = 1);
4} catch(e) {
5 console.log(e.name, e.message);
6}
--- orig.js 2026-10-17 17:01:21.225464852 -0400 +++ fixed.js 2026-10-17 17:01:21.502468224 -0400 @@ -1,6 +1,6 @@ 'use strict'; try { - let c = (d = 1); + const c = (d = 1); } catch(e) { console.log(e.name, e.message); }
ReferenceError d is not defined
r1-p005 · Sparse arrays, holes, Array(n) vs [n]
1let arr = [, , 3];
2console.log(arr.length, arr[0], arr[1], arr[2]);
--- orig.js 2026-10-17 17:01:21.677470354 -0400 +++ fixed.js 2026-10-17 17:01:21.965473860 -0400 @@ -1,2 +1,2 @@ -let arr = [, , 3]; +const arr = [, , 3]; console.log(arr.length, arr[0], arr[1], arr[2]);
3 undefined undefined 3
r1-p006 · Regular expressions: escapes, unicode flag, lastIndex with g flag
1let re = /a/g;
2console.log(re.exec('aba'), re.lastIndex);
3console.log(re.exec('aba'), re.lastIndex);
--- orig.js 2026-10-17 17:01:22.140475990 -0400 +++ fixed.js 2026-10-17 17:01:22.413479312 -0400 @@ -1,3 +1,3 @@ -let re = /a/g; +const re = /a/g; console.log(re.exec('aba'), re.lastIndex); console.log(re.exec('aba'), re.lastIndex);
[ 'a', index: 0, input: 'aba', groups: undefined ] 1 [ 'a', index: 2, input: 'aba', groups: undefined ] 3
r1-p007 · Block-scoped redeclaration with const causes SyntaxError (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-01-0)
1{ let a = 1; let a = 2; console.log(a); }
r1-p008 · Order of evaluation when expressions are merged or duplicated
1let n = 0, x = (n++, 10);
2console.log(n, x);
1 10
r2-p001 · Unassigned let converted to const
1let a;
2a = 1;
3console.log(a);
1
r2-p002 · Delete unqualified identifier in initializer (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-01-0)
1"use strict";
2let x = delete y;
3console.log(x);
r2-p003 · Conditional let declaration
1if (false) { let y = 1; }
2try { console.log(y); } catch (e) { console.log(e.name + ': ' + e.message); }
--- orig.js 2026-10-17 17:01:27.815544970 -0400 +++ fixed.js 2026-10-17 17:01:28.092548333 -0400 @@ -1,2 +1,2 @@ -if (false) { let y = 1; } +if (false) { const y = 1; } try { console.log(y); } catch (e) { console.log(e.name + ': ' + e.message); }
ReferenceError: y is not defined
r2-p004 · Numeric/string coercion with toString/valueOf
1let o = { valueOf: () => 2, toString: () => '3' };
2let result = o + '';
3console.log(result);
--- orig.js 2026-10-17 17:01:28.269550481 -0400 +++ fixed.js 2026-10-17 17:01:28.535553709 -0400 @@ -1,3 +1,3 @@ -let o = { valueOf: () => 2, toString: () => '3' }; -let result = o + ''; +const o = { valueOf: () => 2, toString: () => '3' }; +const result = o + ''; console.log(result);
2
r2-p005 · Export let (non-standard)
1export let counter = 0;
2counter++;
3console.log(counter);
1
r2-p006 · __proto__ and prototype access
1let obj = { __proto__: { x: 1 } };
2console.log(obj.x);
--- orig.js 2026-10-17 17:01:28.940558624 -0400 +++ fixed.js 2026-10-17 17:01:29.224562070 -0400 @@ -1,2 +1,2 @@ -let obj = { __proto__: { x: 1 } }; +const obj = { __proto__: { x: 1 } }; console.log(obj.x);
1
r2-p007 · Strict vs sloppy mode semantics
1"use strict";
2try { let v = 1; v = 2; } catch (e) { console.log(e.name); }
3(function() { "use sloppy"; let w = 1; w = 2; console.log(w); })();
2
r2-p008 · Eval context mutability
1let global = (0, eval)('this');
2let x = 1;
3(0, eval)('x = 2');
4console.log(x);
--- orig.js 2026-10-17 17:01:29.623566910 -0400 +++ fixed.js 2026-10-17 17:01:29.893570185 -0400 @@ -1,4 +1,4 @@ -let global = (0, eval)('this'); -let x = 1; +const global = (0, eval)('this'); +const x = 1; (0, eval)('x = 2'); console.log(x);
1