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