.gitignore added
[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         }
96     }
97
98     /**
99      * Checks a given previous segment is coming from the end of a loop.
100      * @param {CodePathSegment} segment A previous segment to check.
101      * @returns {boolean} `true` if the segment is coming from the end of a loop.
102      */
103     isLoopedPrevSegment(segment) {
104         return this.internal.loopedPrevSegments.indexOf(segment) !== -1;
105     }
106
107     /**
108      * Creates the root segment.
109      * @param {string} id An identifier.
110      * @returns {CodePathSegment} The created segment.
111      */
112     static newRoot(id) {
113         return new CodePathSegment(id, [], true);
114     }
115
116     /**
117      * Creates a segment that follows given segments.
118      * @param {string} id An identifier.
119      * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
120      * @returns {CodePathSegment} The created segment.
121      */
122     static newNext(id, allPrevSegments) {
123         return new CodePathSegment(
124             id,
125             CodePathSegment.flattenUnusedSegments(allPrevSegments),
126             allPrevSegments.some(isReachable)
127         );
128     }
129
130     /**
131      * Creates an unreachable segment that follows given segments.
132      * @param {string} id An identifier.
133      * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
134      * @returns {CodePathSegment} The created segment.
135      */
136     static newUnreachable(id, allPrevSegments) {
137         const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
138
139         /*
140          * In `if (a) return a; foo();` case, the unreachable segment preceded by
141          * the return statement is not used but must not be remove.
142          */
143         CodePathSegment.markUsed(segment);
144
145         return segment;
146     }
147
148     /**
149      * Creates a segment that follows given segments.
150      * This factory method does not connect with `allPrevSegments`.
151      * But this inherits `reachable` flag.
152      * @param {string} id An identifier.
153      * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
154      * @returns {CodePathSegment} The created segment.
155      */
156     static newDisconnected(id, allPrevSegments) {
157         return new CodePathSegment(id, [], allPrevSegments.some(isReachable));
158     }
159
160     /**
161      * Makes a given segment being used.
162      *
163      * And this function registers the segment into the previous segments as a next.
164      * @param {CodePathSegment} segment A segment to mark.
165      * @returns {void}
166      */
167     static markUsed(segment) {
168         if (segment.internal.used) {
169             return;
170         }
171         segment.internal.used = true;
172
173         let i;
174
175         if (segment.reachable) {
176             for (i = 0; i < segment.allPrevSegments.length; ++i) {
177                 const prevSegment = segment.allPrevSegments[i];
178
179                 prevSegment.allNextSegments.push(segment);
180                 prevSegment.nextSegments.push(segment);
181             }
182         } else {
183             for (i = 0; i < segment.allPrevSegments.length; ++i) {
184                 segment.allPrevSegments[i].allNextSegments.push(segment);
185             }
186         }
187     }
188
189     /**
190      * Marks a previous segment as looped.
191      * @param {CodePathSegment} segment A segment.
192      * @param {CodePathSegment} prevSegment A previous segment to mark.
193      * @returns {void}
194      */
195     static markPrevSegmentAsLooped(segment, prevSegment) {
196         segment.internal.loopedPrevSegments.push(prevSegment);
197     }
198
199     /**
200      * Replaces unused segments with the previous segments of each unused segment.
201      * @param {CodePathSegment[]} segments An array of segments to replace.
202      * @returns {CodePathSegment[]} The replaced array.
203      */
204     static flattenUnusedSegments(segments) {
205         const done = Object.create(null);
206         const retv = [];
207
208         for (let i = 0; i < segments.length; ++i) {
209             const segment = segments[i];
210
211             // Ignores duplicated.
212             if (done[segment.id]) {
213                 continue;
214             }
215
216             // Use previous segments if unused.
217             if (!segment.internal.used) {
218                 for (let j = 0; j < segment.allPrevSegments.length; ++j) {
219                     const prevSegment = segment.allPrevSegments[j];
220
221                     if (!done[prevSegment.id]) {
222                         done[prevSegment.id] = true;
223                         retv.push(prevSegment);
224                     }
225                 }
226             } else {
227                 done[segment.id] = true;
228                 retv.push(segment);
229             }
230         }
231
232         return retv;
233     }
234 }
235
236 module.exports = CodePathSegment;