eslint prefer-const
labelled safe
Analysis Nemotron 3 Super
Flags let-declared variables that are never reassigned after initialization.
Fix: Replaces 'let' with 'const' for variables that are never reassigned after their initial assignment.
10 hazards
- Rewriting 'let' to 'const' in a block-scoped loop initializer like 'for (let i = 0; i < n; ++i)' breaks reassignment and causes a TypeError.
- Changing 'let' to 'const' for a variable declared in a conditional block but used outside (e.g., 'if (true) let x = 1;') moves the declaration into the block scope, making it inaccessible outside and causing a ReferenceError.
- If the variable is read before its assignment (e.g., 'let x; console.log(x); x = 1;'), replacing 'let' with 'const' creates a temporal dead zone, causing a ReferenceError on the read.
- In destructuring like 'let {a, b} = obj;', rewriting only 'b' to 'const' when 'a' is reassigned later leaves 'b' as const but may mislead if 'b' is actually used in a way that assumes mutability (though rare).
- If the variable is declared with 'let' in the init part of a for-in or for-of loop (e.g., 'for (let k in obj)'), changing to 'const' is correct per spec (new binding per iteration), but if the loop body relies on reassignment of the variable across iterations (which it shouldn't), it could break—though the rule only fires when there is no reassignment, so this is safe in practice.
- If the variable is declared in a class static block (e.g., 'static { let x = 1; }'), rewriting to 'const' is safe because the block has its own scope and no reassignment occurs.
- If the variable is declared in a scope where it is shadowed later (e.g., 'let x = 1; { let x = 2; }'), changing the outer 'let' to 'const' does not affect the inner shadowing and is safe.
- If the variable is declared but never used (e.g., 'let unused = 1;'), changing to 'const' is safe and may even help tools detect unused variables.
- If the variable is initialized with a function call that has side effects (e.g., 'let x = foo();'), moving the declaration to const does not change the timing or number of side effects.
- If the variable is declared in a comma-separated list (e.g., 'let a = 1, b = 2;'), rewriting only 'b' to 'const' when 'a' is reassigned later results in mixed const/let in the same statement, which is syntactically valid but may confuse readers or tools expecting uniformity.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Evaluation Order - Side Effect Duplication | order of evaluation when expressions are merged, duplicated or deleted (side effects run twice or never) |
| r1-p002 | fix ok | Static Block Scope Safety | If the variable is declared in a class static block (e.g., 'static { let x = 1; }'), rewriting to 'const' is safe because the block has its own scope and no reassignment occurs. |
| r1-p003 | fix ok | Partial Destructuring Misleading Const | In destructuring like 'let {a, b} = obj;', rewriting only 'b' to 'const' when 'a' is reassigned later leaves 'b' as const but may mislead if 'b' is actually used in a way that assumes mutability (thou |
| r1-p004 | fix ok | RegExp lastIndex Shared Mutation | regular expressions: escapes, unicode flag, lastIndex with the g flag |
| r1-p005 | fix ok | this Binding in Extracted Method | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
| r1-p006 | invalid | Strict vs Sloppy Mode Semantics | strict vs sloppy mode semantics |
| r1-p007 | fired, no fix | Mixed const/let in Comma Declaration | If the variable is declared in a comma-separated list (e.g., 'let a = 1, b = 2;'), rewriting only 'b' to 'const' when 'a' is reassigned later results in mixed const/let in the same statement, which is |
| r1-p008 | fix ok | Class Fields and Private Names Interaction | class fields, static blocks, private names, super calls |
| r2-p001 | fix ok | arguments-object-default-rest | arguments object, function.length, default parameters, rest parameters |
| r2-p002 | fix ok prediction off | class-static-block-safety | variable declared in a class static block – rewriting to const is safe due to isolated scope |
| r2-p003 | fired, no fix | temporal-dead-zone-read-before-assign | variable read before assignment – let → const causes TDZ ReferenceError |
| r2-p004 | silent prediction off | comments-attached-to-removed-code | comments attached to the removed or moved code |
| r2-p005 | fix ok | name-collision-shadow-after-move | name collisions across scopes after a move (shadowing) |
| r2-p006 | fix ok | asi-semicolonless-leading-paren-bracket | ASI and semicolon-less code, leading ( or [ on the next line |
| r2-p007 | fix ok | class-fields-static-blocks-private-names-super | class fields, static blocks, private names, super calls |
| r2-p008 | fix ok | var-hoisting-redeclaration-inside-loops-after-use | var hoisting and re-declaration inside loops or after use |
Sources
r1-p001 · Evaluation Order - Side Effect Duplication
1function get() { return ++counter; }
2let counter = 0;
3console.log(get(), get());
4counter = 0;
5{ let temp = get(); console.log(temp, temp); }
--- orig.js 2026-10-22 14:25:40.987105514 -0400 +++ fixed.js 2026-10-22 14:25:43.661224897 -0400 @@ -2,4 +2,4 @@ let counter = 0; console.log(get(), get()); counter = 0; -{ let temp = get(); console.log(temp, temp); } +{ const temp = get(); console.log(temp, temp); }
1 2 1 1
r1-p002 · Static Block Scope Safety
1class C {
2 static { let x = 1; console.log(x); }
3}
4new C(); // triggers rule on x → becomes const, still logs 1
--- orig.js 2026-10-22 14:25:40.988105559 -0400 +++ fixed.js 2026-10-22 14:25:44.790275316 -0400 @@ -1,4 +1,4 @@ class C { - static { let x = 1; console.log(x); } + static { const x = 1; console.log(x); } } new C(); // triggers rule on x → becomes const, still logs 1
1
r1-p003 · Partial Destructuring Misleading Const
1let obj = {a: 1, b: 2};
2let {a, b} = obj;
3a = 99; // reassigned
4console.log(a, b);
5obj.b = 88; // b not mutated via obj, but user might expect it could be
6console.log(obj.b); // shows 88, but b is const → confusion risk
--- orig.js 2026-10-22 14:25:40.989105603 -0400 +++ fixed.js 2026-10-22 14:25:43.649224361 -0400 @@ -1,4 +1,4 @@ -let obj = {a: 1, b: 2}; +const obj = {a: 1, b: 2}; let {a, b} = obj; a = 99; // reassigned console.log(a, b);
99 2 88
r1-p004 · RegExp lastIndex Shared Mutation
1let regex = /a/g;
2let str = 'aa';
3let match;
4while ((match = regex.exec(str)) !== null) {
5 console.log(match.index);
6}
7// After fix: let → const, but regex still mutable via lastIndex
8console.log('done');
--- orig.js 2026-10-22 14:25:40.990105648 -0400 +++ fixed.js 2026-10-22 14:25:43.675225522 -0400 @@ -1,5 +1,5 @@ -let regex = /a/g; -let str = 'aa'; +const regex = /a/g; +const str = 'aa'; let match; while ((match = regex.exec(str)) !== null) { console.log(match.index);
0 1 done
r1-p005 · this Binding in Extracted Method
1let obj = {
2 value: 42,
3 getValue: function() { return this.value; }
4};
5let fn = obj.getValue;
6console.log(fn()); // undefined (sloppy)
7'use strict';
8let obj2 = {
9 value: 99,
10 getValue: function() { return this.value; }
11};
12let fn2 = obj2.getValue;
13console.log(fn2()); // undefined (strict)
--- orig.js 2026-10-22 14:25:40.990105648 -0400 +++ fixed.js 2026-10-22 14:25:43.646224227 -0400 @@ -1,13 +1,13 @@ -let obj = { +const obj = { value: 42, getValue: function() { return this.value; } }; -let fn = obj.getValue; +const fn = obj.getValue; console.log(fn()); // undefined (sloppy) 'use strict'; -let obj2 = { +const obj2 = { value: 99, getValue: function() { return this.value; } }; -let fn2 = obj2.getValue; +const fn2 = obj2.getValue; console.log(fn2()); // undefined (strict)
undefined undefined
r1-p006 · Strict vs Sloppy Mode Semantics (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-25-3)
1'use strict';
2let x = 1;
3try { delete x; } catch(e) { console.log(e.name); }
4
5// Sloppy mode equivalent
6(function() {
7 let y = 1;
8 try { delete y; } catch(e) { console.log(e.name); }
9})();
r1-p007 · Mixed const/let in Comma Declaration
1let a = 1, b = 2;
2a = 3;
3console.log(a, b);
4// After fix: let a = 1, const b = 2; → a reassigned, b const → valid but mixed
3 2
r1-p008 · Class Fields and Private Names Interaction
1class Base {
2 static { let baseInit = 1; console.log('base:', baseInit); }
3}
4class Derived extends Base {
5 static { let derivedInit = 2; console.log('derived:', derivedInit); }
6 #x = baseInit + derivedInit; // Error: baseInit not accessible
7}
8try { new Derived(); } catch(e) { console.log(e.name); }
--- orig.js 2026-10-22 14:25:44.818276567 -0400 +++ fixed.js 2026-10-22 14:25:47.431393293 -0400 @@ -1,8 +1,8 @@ class Base { - static { let baseInit = 1; console.log('base:', baseInit); } + static { const baseInit = 1; console.log('base:', baseInit); } } class Derived extends Base { - static { let derivedInit = 2; console.log('derived:', derivedInit); } + static { const derivedInit = 2; console.log('derived:', derivedInit); } #x = baseInit + derivedInit; // Error: baseInit not accessible } try { new Derived(); } catch(e) { console.log(e.name); }
base: 1 derived: 2 ReferenceError
r2-p001 · arguments-object-default-rest
1function f(a = g(), ...rest) {
2 let len = arguments.length;
3 console.log('len:', len, 'a:', a, 'rest:', rest.join(',')||'empty');
4}
5function g() { return 1; }
6f();
--- orig.js 2026-10-22 14:25:52.579623390 -0400 +++ fixed.js 2026-10-22 14:25:56.905816880 -0400 @@ -1,5 +1,5 @@ function f(a = g(), ...rest) { - let len = arguments.length; + const len = arguments.length; console.log('len:', len, 'a:', a, 'rest:', rest.join(',')||'empty'); } function g() { return 1; }
len: 0 a: 1 rest: empty
r2-p002 · class-static-block-safety
1class C {
2 static {
3 let x = 1;
4 console.log('static init:', x);
5 }
6 constructor() { console.log('instance:', C.x ?? 'undefined'); }
7}
8new C();
--- orig.js 2026-10-22 14:25:52.580623435 -0400 +++ fixed.js 2026-10-22 14:25:56.982820325 -0400 @@ -1,6 +1,6 @@ class C { static { - let x = 1; + const x = 1; console.log('static init:', x); } constructor() { console.log('instance:', C.x ?? 'undefined'); }
static init: 1 instance: undefined
r2-p003 · temporal-dead-zone-read-before-assign
1let x;
2console.log(typeof x);
3x = 42;
4console.log('x:', x);
undefined x: 42
r2-p004 · comments-attached-to-removed-code
1let y = 1; // initialized
2console.log('y:', y);
3y = 2;
y: 1
r2-p005 · name-collision-shadow-after-move
1{ let tmp = 'outer'; }
2{ let tmp = 'inner'; console.log(tmp); }
--- orig.js 2026-10-22 14:25:52.582623525 -0400 +++ fixed.js 2026-10-22 14:25:55.738764672 -0400 @@ -1,2 +1,2 @@ -{ let tmp = 'outer'; } -{ let tmp = 'inner'; console.log(tmp); } +{ const tmp = 'outer'; } +{ const tmp = 'inner'; console.log(tmp); }
inner
r2-p006 · asi-semicolonless-leading-paren-bracket
1let x = 1
2(function(){ console.log('IIFE:', x); })()
--- orig.js 2026-10-22 14:25:52.583623569 -0400 +++ fixed.js 2026-10-22 14:25:56.910817104 -0400 @@ -1,2 +1,2 @@ -let x = 1 +const x = 1 (function(){ console.log('IIFE:', x); })()
r2-p007 · class-fields-static-blocks-private-names-super
1class Base {
2 static { let s = 1; console.log('Base static:', s); }
3}
4class Der extends Base {
5 static { let s = 2; console.log('Der static:', s); }
6 constructor() { super(); console.log('Der instance:'); }
7}
8new Der();
--- orig.js 2026-10-22 14:25:55.189740114 -0400 +++ fixed.js 2026-10-22 14:25:57.810857373 -0400 @@ -1,8 +1,8 @@ class Base { - static { let s = 1; console.log('Base static:', s); } + static { const s = 1; console.log('Base static:', s); } } class Der extends Base { - static { let s = 2; console.log('Der static:', s); } + static { const s = 2; console.log('Der static:', s); } constructor() { super(); console.log('Der instance:'); } } new Der();
Base static: 1 Der static: 2 Der instance:
r2-p008 · var-hoisting-redeclaration-inside-loops-after-use
1for (var i = 0; i < 2; i++) {
2 let j = i;
3 console.log('i:', i, 'j:', j);
4}
5console.log('final i:', i);
--- orig.js 2026-10-22 14:25:56.865815090 -0400 +++ fixed.js 2026-10-22 14:26:00.636866486 -0400 @@ -1,5 +1,5 @@ for (var i = 0; i < 2; i++) { - let j = i; + const j = i; console.log('i:', i, 'j:', j); } console.log('final i:', i);
i: 0 j: 0 i: 1 j: 1 final i: 2