]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/linter/code-path-analysis/code-path-segment.js
import 8.23.1 source
[pve-eslint.git] / eslint / lib / linter / code-path-analysis / code-path-segment.js
1 /**
2 * @fileoverview A class of the code path segment.
3 * @author Toru Nagashima
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const debug = require("./debug-helpers");
13
14 //------------------------------------------------------------------------------
15 // Helpers
16 //------------------------------------------------------------------------------
17
18 /**
19 * Checks whether or not a given segment is reachable.
20 * @param {CodePathSegment} segment A segment to check.
21 * @returns {boolean} `true` if the segment is reachable.
22 */
23 function isReachable(segment) {
24 return segment.reachable;
25 }
26
27 //------------------------------------------------------------------------------
28 // Public Interface
29 //------------------------------------------------------------------------------
30
31 /**
32 * A code path segment.
33 */
34 class CodePathSegment {
35
36 /**
37 * @param {string} id An identifier.
38 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
39 * This array includes unreachable segments.
40 * @param {boolean} reachable A flag which shows this is reachable.
41 */
42 constructor(id, allPrevSegments, reachable) {
43
44 /**
45 * The identifier of this code path.
46 * Rules use it to store additional information of each rule.
47 * @type {string}
48 */
49 this.id = id;
50
51 /**
52 * An array of the next segments.
53 * @type {CodePathSegment[]}
54 */
55 this.nextSegments = [];
56
57 /**
58 * An array of the previous segments.
59 * @type {CodePathSegment[]}
60 */
61 this.prevSegments = allPrevSegments.filter(isReachable);
62
63 /**
64 * An array of the next segments.
65 * This array includes unreachable segments.
66 * @type {CodePathSegment[]}
67 */
68 this.allNextSegments = [];
69
70 /**
71 * An array of the previous segments.
72 * This array includes unreachable segments.
73 * @type {CodePathSegment[]}
74 */
75 this.allPrevSegments = allPrevSegments;
76
77 /**
78 * A flag which shows this is reachable.
79 * @type {boolean}
80 */
81 this.reachable = reachable;
82
83 // Internal data.
84 Object.defineProperty(this, "internal", {
85 value: {
86 used: false,
87 loopedPrevSegments: []
88 }
89 });
90
91 /* c8 ignore start */
92 if (debug.enabled) {
93 this.internal.nodes = [];
94 }/* c8 ignore stop */
95 }
96
97 /**
98 * Checks a given previous segment is coming from the end of a loop.
99 * @param {CodePathSegment} segment A previous segment to check.
100 * @returns {boolean} `true` if the segment is coming from the end of a loop.
101 */
102 isLoopedPrevSegment(segment) {
103 return this.internal.loopedPrevSegments.includes(segment);
104 }
105
106 /**
107 * Creates the root segment.
108 * @param {string} id An identifier.
109 * @returns {CodePathSegment} The created segment.
110 */
111 static newRoot(id) {
112 return new CodePathSegment(id, [], true);
113 }
114
115 /**
116 * Creates a segment that follows given segments.
117 * @param {string} id An identifier.
118 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
119 * @returns {CodePathSegment} The created segment.
120 */
121 static newNext(id, allPrevSegments) {
122 return new CodePathSegment(
123 id,
124 CodePathSegment.flattenUnusedSegments(allPrevSegments),
125 allPrevSegments.some(isReachable)
126 );
127 }
128
129 /**
130 * Creates an unreachable segment that follows given segments.
131 * @param {string} id An identifier.
132 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
133 * @returns {CodePathSegment} The created segment.
134 */
135 static newUnreachable(id, allPrevSegments) {
136 const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
137
138 /*
139 * In `if (a) return a; foo();` case, the unreachable segment preceded by
140 * the return statement is not used but must not be remove.
141 */
142 CodePathSegment.markUsed(segment);
143
144 return segment;
145 }
146
147 /**
148 * Creates a segment that follows given segments.
149 * This factory method does not connect with `allPrevSegments`.
150 * But this inherits `reachable` flag.
151 * @param {string} id An identifier.
152 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
153 * @returns {CodePathSegment} The created segment.
154 */
155 static newDisconnected(id, allPrevSegments) {
156 return new CodePathSegment(id, [], allPrevSegments.some(isReachable));
157 }
158
159 /**
160 * Makes a given segment being used.
161 *
162 * And this function registers the segment into the previous segments as a next.
163 * @param {CodePathSegment} segment A segment to mark.
164 * @returns {void}
165 */
166 static markUsed(segment) {
167 if (segment.internal.used) {
168 return;
169 }
170 segment.internal.used = true;
171
172 let i;
173
174 if (segment.reachable) {
175 for (i = 0; i < segment.allPrevSegments.length; ++i) {
176 const prevSegment = segment.allPrevSegments[i];
177
178 prevSegment.allNextSegments.push(segment);
179 prevSegment.nextSegments.push(segment);
180 }
181 } else {
182 for (i = 0; i < segment.allPrevSegments.length; ++i) {
183 segment.allPrevSegments[i].allNextSegments.push(segment);
184 }
185 }
186 }
187
188 /**
189 * Marks a previous segment as looped.
190 * @param {CodePathSegment} segment A segment.
191 * @param {CodePathSegment} prevSegment A previous segment to mark.
192 * @returns {void}
193 */
194 static markPrevSegmentAsLooped(segment, prevSegment) {
195 segment.internal.loopedPrevSegments.push(prevSegment);
196 }
197
198 /**
199 * Replaces unused segments with the previous segments of each unused segment.
200 * @param {CodePathSegment[]} segments An array of segments to replace.
201 * @returns {CodePathSegment[]} The replaced array.
202 */
203 static flattenUnusedSegments(segments) {
204 const done = Object.create(null);
205 const retv = [];
206
207 for (let i = 0; i < segments.length; ++i) {
208 const segment = segments[i];
209
210 // Ignores duplicated.
211 if (done[segment.id]) {
212 continue;
213 }
214
215 // Use previous segments if unused.
216 if (!segment.internal.used) {
217 for (let j = 0; j < segment.allPrevSegments.length; ++j) {
218 const prevSegment = segment.allPrevSegments[j];
219
220 if (!done[prevSegment.id]) {
221 done[prevSegment.id] = true;
222 retv.push(prevSegment);
223 }
224 }
225 } else {
226 done[segment.id] = true;
227 retv.push(segment);
228 }
229 }
230
231 return retv;
232 }
233 }
234
235 module.exports = CodePathSegment;