source: imaps-frontend/node_modules/eslint/lib/rules/no-unreachable-loop.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 5.9 KB
Line 
1/**
2 * @fileoverview Rule to disallow loops with a body that allows only one iteration
3 * @author Milos Djermanovic
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Helpers
10//------------------------------------------------------------------------------
11
12const allLoopTypes = ["WhileStatement", "DoWhileStatement", "ForStatement", "ForInStatement", "ForOfStatement"];
13
14/**
15 * Checks all segments in a set and returns true if any are reachable.
16 * @param {Set<CodePathSegment>} segments The segments to check.
17 * @returns {boolean} True if any segment is reachable; false otherwise.
18 */
19function isAnySegmentReachable(segments) {
20
21 for (const segment of segments) {
22 if (segment.reachable) {
23 return true;
24 }
25 }
26
27 return false;
28}
29
30/**
31 * Determines whether the given node is the first node in the code path to which a loop statement
32 * 'loops' for the next iteration.
33 * @param {ASTNode} node The node to check.
34 * @returns {boolean} `true` if the node is a looping target.
35 */
36function isLoopingTarget(node) {
37 const parent = node.parent;
38
39 if (parent) {
40 switch (parent.type) {
41 case "WhileStatement":
42 return node === parent.test;
43 case "DoWhileStatement":
44 return node === parent.body;
45 case "ForStatement":
46 return node === (parent.update || parent.test || parent.body);
47 case "ForInStatement":
48 case "ForOfStatement":
49 return node === parent.left;
50
51 // no default
52 }
53 }
54
55 return false;
56}
57
58/**
59 * Creates an array with elements from the first given array that are not included in the second given array.
60 * @param {Array} arrA The array to compare from.
61 * @param {Array} arrB The array to compare against.
62 * @returns {Array} a new array that represents `arrA \ arrB`.
63 */
64function getDifference(arrA, arrB) {
65 return arrA.filter(a => !arrB.includes(a));
66}
67
68//------------------------------------------------------------------------------
69// Rule Definition
70//------------------------------------------------------------------------------
71
72/** @type {import('../shared/types').Rule} */
73module.exports = {
74 meta: {
75 type: "problem",
76
77 docs: {
78 description: "Disallow loops with a body that allows only one iteration",
79 recommended: false,
80 url: "https://eslint.org/docs/latest/rules/no-unreachable-loop"
81 },
82
83 schema: [{
84 type: "object",
85 properties: {
86 ignore: {
87 type: "array",
88 items: {
89 enum: allLoopTypes
90 },
91 uniqueItems: true
92 }
93 },
94 additionalProperties: false
95 }],
96
97 messages: {
98 invalid: "Invalid loop. Its body allows only one iteration."
99 }
100 },
101
102 create(context) {
103 const ignoredLoopTypes = context.options[0] && context.options[0].ignore || [],
104 loopTypesToCheck = getDifference(allLoopTypes, ignoredLoopTypes),
105 loopSelector = loopTypesToCheck.join(","),
106 loopsByTargetSegments = new Map(),
107 loopsToReport = new Set();
108
109 const codePathSegments = [];
110 let currentCodePathSegments = new Set();
111
112 return {
113
114 onCodePathStart() {
115 codePathSegments.push(currentCodePathSegments);
116 currentCodePathSegments = new Set();
117 },
118
119 onCodePathEnd() {
120 currentCodePathSegments = codePathSegments.pop();
121 },
122
123 onUnreachableCodePathSegmentStart(segment) {
124 currentCodePathSegments.add(segment);
125 },
126
127 onUnreachableCodePathSegmentEnd(segment) {
128 currentCodePathSegments.delete(segment);
129 },
130
131 onCodePathSegmentEnd(segment) {
132 currentCodePathSegments.delete(segment);
133 },
134
135 onCodePathSegmentStart(segment, node) {
136
137 currentCodePathSegments.add(segment);
138
139 if (isLoopingTarget(node)) {
140 const loop = node.parent;
141
142 loopsByTargetSegments.set(segment, loop);
143 }
144 },
145
146 onCodePathSegmentLoop(_, toSegment, node) {
147 const loop = loopsByTargetSegments.get(toSegment);
148
149 /**
150 * The second iteration is reachable, meaning that the loop is valid by the logic of this rule,
151 * only if there is at least one loop event with the appropriate target (which has been already
152 * determined in the `loopsByTargetSegments` map), raised from either:
153 *
154 * - the end of the loop's body (in which case `node === loop`)
155 * - a `continue` statement
156 *
157 * This condition skips loop events raised from `ForInStatement > .right` and `ForOfStatement > .right` nodes.
158 */
159 if (node === loop || node.type === "ContinueStatement") {
160
161 // Removes loop if it exists in the set. Otherwise, `Set#delete` has no effect and doesn't throw.
162 loopsToReport.delete(loop);
163 }
164 },
165
166 [loopSelector](node) {
167
168 /**
169 * Ignore unreachable loop statements to avoid unnecessary complexity in the implementation, or false positives otherwise.
170 * For unreachable segments, the code path analysis does not raise events required for this implementation.
171 */
172 if (isAnySegmentReachable(currentCodePathSegments)) {
173 loopsToReport.add(node);
174 }
175 },
176
177
178 "Program:exit"() {
179 loopsToReport.forEach(
180 node => context.report({ node, messageId: "invalid" })
181 );
182 }
183 };
184 }
185};
Note: See TracBrowser for help on using the repository browser.