Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / linter / code-path-analysis / code-path.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/linter/code-path-analysis/code-path.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/linter/code-path-analysis/code-path.js
new file mode 100644 (file)
index 0000000..49b37c6
--- /dev/null
@@ -0,0 +1,238 @@
+/**
+ * @fileoverview A class of the code path.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const CodePathState = require("./code-path-state");
+const IdGenerator = require("./id-generator");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * A code path.
+ */
+class CodePath {
+
+    // eslint-disable-next-line jsdoc/require-description
+    /**
+     * @param {string} id An identifier.
+     * @param {CodePath|null} upper The code path of the upper function scope.
+     * @param {Function} onLooped A callback function to notify looping.
+     */
+    constructor(id, upper, onLooped) {
+
+        /**
+         * The identifier of this code path.
+         * Rules use it to store additional information of each rule.
+         * @type {string}
+         */
+        this.id = id;
+
+        /**
+         * The code path of the upper function scope.
+         * @type {CodePath|null}
+         */
+        this.upper = upper;
+
+        /**
+         * The code paths of nested function scopes.
+         * @type {CodePath[]}
+         */
+        this.childCodePaths = [];
+
+        // Initializes internal state.
+        Object.defineProperty(
+            this,
+            "internal",
+            { value: new CodePathState(new IdGenerator(`${id}_`), onLooped) }
+        );
+
+        // Adds this into `childCodePaths` of `upper`.
+        if (upper) {
+            upper.childCodePaths.push(this);
+        }
+    }
+
+    /**
+     * Gets the state of a given code path.
+     * @param {CodePath} codePath A code path to get.
+     * @returns {CodePathState} The state of the code path.
+     */
+    static getState(codePath) {
+        return codePath.internal;
+    }
+
+    /**
+     * The initial code path segment.
+     * @type {CodePathSegment}
+     */
+    get initialSegment() {
+        return this.internal.initialSegment;
+    }
+
+    /**
+     * Final code path segments.
+     * This array is a mix of `returnedSegments` and `thrownSegments`.
+     * @type {CodePathSegment[]}
+     */
+    get finalSegments() {
+        return this.internal.finalSegments;
+    }
+
+    /**
+     * Final code path segments which is with `return` statements.
+     * This array contains the last path segment if it's reachable.
+     * Since the reachable last path returns `undefined`.
+     * @type {CodePathSegment[]}
+     */
+    get returnedSegments() {
+        return this.internal.returnedForkContext;
+    }
+
+    /**
+     * Final code path segments which is with `throw` statements.
+     * @type {CodePathSegment[]}
+     */
+    get thrownSegments() {
+        return this.internal.thrownForkContext;
+    }
+
+    /**
+     * Current code path segments.
+     * @type {CodePathSegment[]}
+     */
+    get currentSegments() {
+        return this.internal.currentSegments;
+    }
+
+    /**
+     * Traverses all segments in this code path.
+     *
+     *     codePath.traverseSegments(function(segment, controller) {
+     *         // do something.
+     *     });
+     *
+     * This method enumerates segments in order from the head.
+     *
+     * The `controller` object has two methods.
+     *
+     * - `controller.skip()` - Skip the following segments in this branch.
+     * - `controller.break()` - Skip all following segments.
+     * @param {Object} [options] Omittable.
+     * @param {CodePathSegment} [options.first] The first segment to traverse.
+     * @param {CodePathSegment} [options.last] The last segment to traverse.
+     * @param {Function} callback A callback function.
+     * @returns {void}
+     */
+    traverseSegments(options, callback) {
+        let resolvedOptions;
+        let resolvedCallback;
+
+        if (typeof options === "function") {
+            resolvedCallback = options;
+            resolvedOptions = {};
+        } else {
+            resolvedOptions = options || {};
+            resolvedCallback = callback;
+        }
+
+        const startSegment = resolvedOptions.first || this.internal.initialSegment;
+        const lastSegment = resolvedOptions.last;
+
+        let item = null;
+        let index = 0;
+        let end = 0;
+        let segment = null;
+        const visited = Object.create(null);
+        const stack = [[startSegment, 0]];
+        let skippedSegment = null;
+        let broken = false;
+        const controller = {
+            skip() {
+                if (stack.length <= 1) {
+                    broken = true;
+                } else {
+                    skippedSegment = stack[stack.length - 2][0];
+                }
+            },
+            break() {
+                broken = true;
+            }
+        };
+
+        /**
+         * Checks a given previous segment has been visited.
+         * @param {CodePathSegment} prevSegment A previous segment to check.
+         * @returns {boolean} `true` if the segment has been visited.
+         */
+        function isVisited(prevSegment) {
+            return (
+                visited[prevSegment.id] ||
+                segment.isLoopedPrevSegment(prevSegment)
+            );
+        }
+
+        while (stack.length > 0) {
+            item = stack[stack.length - 1];
+            segment = item[0];
+            index = item[1];
+
+            if (index === 0) {
+
+                // Skip if this segment has been visited already.
+                if (visited[segment.id]) {
+                    stack.pop();
+                    continue;
+                }
+
+                // Skip if all previous segments have not been visited.
+                if (segment !== startSegment &&
+                    segment.prevSegments.length > 0 &&
+                    !segment.prevSegments.every(isVisited)
+                ) {
+                    stack.pop();
+                    continue;
+                }
+
+                // Reset the flag of skipping if all branches have been skipped.
+                if (skippedSegment && segment.prevSegments.indexOf(skippedSegment) !== -1) {
+                    skippedSegment = null;
+                }
+                visited[segment.id] = true;
+
+                // Call the callback when the first time.
+                if (!skippedSegment) {
+                    resolvedCallback.call(this, segment, controller);
+                    if (segment === lastSegment) {
+                        controller.skip();
+                    }
+                    if (broken) {
+                        break;
+                    }
+                }
+            }
+
+            // Update the stack.
+            end = segment.nextSegments.length - 1;
+            if (index < end) {
+                item[1] += 1;
+                stack.push([segment.nextSegments[index], 0]);
+            } else if (index === end) {
+                item[0] = segment.nextSegments[index];
+                item[1] = 0;
+            } else {
+                stack.pop();
+            }
+        }
+    }
+}
+
+module.exports = CodePath;