]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-unreachable-loop.js
f100263308fad66c4d61e9276cce5070f109dab3
2 * @fileoverview Rule to disallow loops with a body that allows only one iteration
3 * @author Milos Djermanovic
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const allLoopTypes
= ["WhileStatement", "DoWhileStatement", "ForStatement", "ForInStatement", "ForOfStatement"];
15 * Determines whether the given node is the first node in the code path to which a loop statement
16 * 'loops' for the next iteration.
17 * @param {ASTNode} node The node to check.
18 * @returns {boolean} `true` if the node is a looping target.
20 function isLoopingTarget(node
) {
21 const parent
= node
.parent
;
24 switch (parent
.type
) {
25 case "WhileStatement":
26 return node
=== parent
.test
;
27 case "DoWhileStatement":
28 return node
=== parent
.body
;
30 return node
=== (parent
.update
|| parent
.test
|| parent
.body
);
31 case "ForInStatement":
32 case "ForOfStatement":
33 return node
=== parent
.left
;
43 * Creates an array with elements from the first given array that are not included in the second given array.
44 * @param {Array} arrA The array to compare from.
45 * @param {Array} arrB The array to compare against.
46 * @returns {Array} a new array that represents `arrA \ arrB`.
48 function getDifference(arrA
, arrB
) {
49 return arrA
.filter(a
=> !arrB
.includes(a
));
52 //------------------------------------------------------------------------------
54 //------------------------------------------------------------------------------
56 /** @type {import('../shared/types').Rule} */
62 description
: "disallow loops with a body that allows only one iteration",
64 url
: "https://eslint.org/docs/rules/no-unreachable-loop"
78 additionalProperties
: false
82 invalid
: "Invalid loop. Its body allows only one iteration."
87 const ignoredLoopTypes
= context
.options
[0] && context
.options
[0].ignore
|| [],
88 loopTypesToCheck
= getDifference(allLoopTypes
, ignoredLoopTypes
),
89 loopSelector
= loopTypesToCheck
.join(","),
90 loopsByTargetSegments
= new Map(),
91 loopsToReport
= new Set();
93 let currentCodePath
= null;
96 onCodePathStart(codePath
) {
97 currentCodePath
= codePath
;
101 currentCodePath
= currentCodePath
.upper
;
104 [loopSelector
](node
) {
107 * Ignore unreachable loop statements to avoid unnecessary complexity in the implementation, or false positives otherwise.
108 * For unreachable segments, the code path analysis does not raise events required for this implementation.
110 if (currentCodePath
.currentSegments
.some(segment
=> segment
.reachable
)) {
111 loopsToReport
.add(node
);
115 onCodePathSegmentStart(segment
, node
) {
116 if (isLoopingTarget(node
)) {
117 const loop
= node
.parent
;
119 loopsByTargetSegments
.set(segment
, loop
);
123 onCodePathSegmentLoop(_
, toSegment
, node
) {
124 const loop
= loopsByTargetSegments
.get(toSegment
);
127 * The second iteration is reachable, meaning that the loop is valid by the logic of this rule,
128 * only if there is at least one loop event with the appropriate target (which has been already
129 * determined in the `loopsByTargetSegments` map), raised from either:
131 * - the end of the loop's body (in which case `node === loop`)
132 * - a `continue` statement
134 * This condition skips loop events raised from `ForInStatement > .right` and `ForOfStatement > .right` nodes.
136 if (node
=== loop
|| node
.type
=== "ContinueStatement") {
138 // Removes loop if it exists in the set. Otherwise, `Set#delete` has no effect and doesn't throw.
139 loopsToReport
.delete(loop
);
144 loopsToReport
.forEach(
145 node
=> context
.report({ node
, messageId
: "invalid" })