]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-loop-func.js
c5460616dc13cc9e1090d496528d3e943730d58e
2 * @fileoverview Rule to flag creation of function inside a loop
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
13 * Gets the containing loop node of a specified node.
15 * We don't need to check nested functions, so this ignores those.
16 * `Scope.through` contains references of nested functions.
17 * @param {ASTNode} node An AST node to get.
18 * @returns {ASTNode|null} The containing loop node of the specified node, or
21 function getContainingLoopNode(node
) {
22 for (let currentNode
= node
; currentNode
.parent
; currentNode
= currentNode
.parent
) {
23 const parent
= currentNode
.parent
;
25 switch (parent
.type
) {
26 case "WhileStatement":
27 case "DoWhileStatement":
32 // `init` is outside of the loop.
33 if (parent
.init
!== currentNode
) {
38 case "ForInStatement":
39 case "ForOfStatement":
41 // `right` is outside of the loop.
42 if (parent
.right
!== currentNode
) {
47 case "ArrowFunctionExpression":
48 case "FunctionExpression":
49 case "FunctionDeclaration":
51 // We don't need to check nested functions.
63 * Gets the containing loop node of a given node.
64 * If the loop was nested, this returns the most outer loop.
65 * @param {ASTNode} node A node to get. This is a loop node.
66 * @param {ASTNode|null} excludedNode A node that the result node should not
68 * @returns {ASTNode} The most outer loop node.
70 function getTopLoopNode(node
, excludedNode
) {
71 const border
= excludedNode
? excludedNode
.range
[1] : 0;
73 let containingLoopNode
= node
;
75 while (containingLoopNode
&& containingLoopNode
.range
[0] >= border
) {
76 retv
= containingLoopNode
;
77 containingLoopNode
= getContainingLoopNode(containingLoopNode
);
84 * Checks whether a given reference which refers to an upper scope's variable is
86 * @param {ASTNode} loopNode A containing loop node.
87 * @param {eslint-scope.Reference} reference A reference to check.
88 * @returns {boolean} `true` if the reference is safe or not.
90 function isSafe(loopNode
, reference
) {
91 const variable
= reference
.resolved
;
92 const definition
= variable
&& variable
.defs
[0];
93 const declaration
= definition
&& definition
.parent
;
94 const kind
= (declaration
&& declaration
.type
=== "VariableDeclaration")
98 // Variables which are declared by `const` is safe.
99 if (kind
=== "const") {
104 * Variables which are declared by `let` in the loop is safe.
105 * It's a different instance from the next loop step's.
107 if (kind
=== "let" &&
108 declaration
.range
[0] > loopNode
.range
[0] &&
109 declaration
.range
[1] < loopNode
.range
[1]
115 * WriteReferences which exist after this border are unsafe because those
116 * can modify the variable.
118 const border
= getTopLoopNode(
120 (kind
=== "let") ? declaration
: null
124 * Checks whether a given reference is safe or not.
125 * The reference is every reference of the upper scope's variable we are
128 * It's safeafe if the reference matches one of the following condition.
130 * - doesn't exist inside a local function and after the border.
131 * @param {eslint-scope.Reference} upperRef A reference to check.
132 * @returns {boolean} `true` if the reference is safe.
134 function isSafeReference(upperRef
) {
135 const id
= upperRef
.identifier
;
138 !upperRef
.isWrite() ||
139 variable
.scope
.variableScope
=== upperRef
.from.variableScope
&&
144 return Boolean(variable
) && variable
.references
.every(isSafeReference
);
147 //------------------------------------------------------------------------------
149 //------------------------------------------------------------------------------
151 /** @type {import('../shared/types').Rule} */
157 description
: "disallow function declarations that contain unsafe references inside loop statements",
159 url
: "https://eslint.org/docs/rules/no-loop-func"
165 unsafeRefs
: "Function declared in a loop contains unsafe references to variable(s) {{ varNames }}."
172 * Reports functions which match the following condition:
174 * - has a loop node in ancestors.
175 * - has any references which refers to an unsafe variable.
176 * @param {ASTNode} node The AST node to check.
179 function checkForLoops(node
) {
180 const loopNode
= getContainingLoopNode(node
);
186 const references
= context
.getScope().through
;
187 const unsafeRefs
= references
.filter(r
=> !isSafe(loopNode
, r
)).map(r
=> r
.identifier
.name
);
189 if (unsafeRefs
.length
> 0) {
192 messageId
: "unsafeRefs",
193 data
: { varNames
: `'${unsafeRefs.join("', '")}'` }
199 ArrowFunctionExpression
: checkForLoops
,
200 FunctionExpression
: checkForLoops
,
201 FunctionDeclaration
: checkForLoops