{"record":{"id":"6800ed80b8e14100","repo":"oxc-project/oxc","slug":"function-declared-in-a-loop-contains-unsafe-refere","errorCode":null,"errorMessage":"Function declared in a loop contains unsafe references to variable(s)","messagePattern":"Function declared in a loop contains unsafe references to variable\\(s\\)","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/eslint/no_loop_func.rs","lineNumber":20,"sourceCode":"\nuse oxc_ast::{\n    AstKind,\n    ast::{\n        ArrowFunctionExpression, DoWhileStatement, ForInStatement, ForOfStatement, ForStatement,\n        Function, IdentifierReference, Statement, WhileStatement,\n    },\n};\nuse oxc_ast_visit::{VisitJs, walk_js};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_semantic::{AstNode, NodeId, ScopeId, SymbolId};\nuse oxc_span::{GetSpan, Span};\nuse oxc_syntax::{scope::ScopeFlags, symbol::SymbolFlags};\n\nuse crate::{ast_util::outermost_paren_parent, context::LintContext, rule::Rule};\n\nfn no_loop_func_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Function declared in a loop contains unsafe references to variable(s)\")\n        .with_help(\"Variables declared with 'var' are function-scoped, not block-scoped. Consider using 'let' or 'const' for block-scoped variables, or move the function outside the loop.\")\n        .with_label(span)\n}\n\n#[derive(Debug, Default, Clone)]\npub struct NoLoopFunc;\n\ndeclare_oxc_lint!(\n    /// ### What it does\n    ///\n    /// Disallows function declarations and expressions inside loop statements\n    /// when they reference variables declared in the outer scope that may change\n    /// across iterations.\n    ///\n    /// ### Why is this bad?\n    ///\n    /// Writing functions within loops tends to result in errors due to the way\n    /// closures work in JavaScript. Functions capture variables by reference,","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/eslint/no_loop_func.rs#L2-L38","documentation":"Diagnostic from oxlint's eslint/no-loop-func rule (crates/oxc_linter/src/rules/eslint/no_loop_func.rs:20). It reports a function, arrow function, or class method declared inside a loop that references variables from an unsafe outer scope — typically 'var' declarations in the same loop or an enclosing one. Because var is function-scoped, every closure created in the loop shares one binding, producing the classic 'all callbacks see the last value' bug.","triggerScenarios":"for (var i = 0; i < 3; i++) { setTimeout(function () { console.log(i); }); }; forEach-style loops pushing callbacks that read a var declared inside the loop body; functions inside loops referencing an outer var that the loop reassigns.","commonSituations":"Building arrays of handlers in a loop; pre-ES2015 codebases where let was unavailable; converting such code to oxlint from ESLint and hitting the same rule; interview-style closure bugs surfacing in production.","solutions":["Change the loop variable (and any loop-body vars the function reads) from var to let so each iteration gets its own binding","Move the function outside the loop and pass the changing value as a parameter","For legacy targets, wrap in an IIFE: (function (i) { ... })(i), or use Array.prototype.map/forEach which bind per element"],"exampleFix":"// before\nvar handlers = [];\nfor (var i = 0; i < 3; i++) {\n  handlers.push(function () { return i; }); // all return 3\n}\n\n// after\nconst handlers = [];\nfor (let i = 0; i < 3; i++) {\n  handlers.push(function () { return i; }); // 0, 1, 2\n}","handlingStrategy":"validation","validationCode":"// Codegen-time guard: refuse to emit closures over loop-scoped var\nfunction assertLoopSafe(declKind, captured) {\n  if (declKind === 'var' && captured) {\n    throw new Error('closure captures var declared in a loop; use let');\n  }\n}","typeGuard":"function isSafeLoopCapture(decl) {\n  return decl.kind === 'let' || decl.kind === 'const';\n}","tryCatchPattern":null,"preventionTips":["Ban var in new code (prefer-const / no-var rules) so loop closures are always safe","Pass loop values as function parameters instead of closing over them","Write a quick test that asserts each generated callback returns its own iteration value"],"tags":["lint","javascript","closures","loops","var-scope","correctness"],"backgroundTag":"closure-in-loop","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}