feat: docker compose maybe
This commit is contained in:
		
							
								
								
									
										852
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										852
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,852 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @fileoverview A class of the code path analyzer.
 | 
			
		||||
 * @author Toru Nagashima
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Requirements
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const assert = require("assert"),
 | 
			
		||||
    { breakableTypePattern } = require("../../shared/ast-utils"),
 | 
			
		||||
    CodePath = require("./code-path"),
 | 
			
		||||
    CodePathSegment = require("./code-path-segment"),
 | 
			
		||||
    IdGenerator = require("./id-generator"),
 | 
			
		||||
    debug = require("./debug-helpers");
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Helpers
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks whether or not a given node is a `case` node (not `default` node).
 | 
			
		||||
 * @param {ASTNode} node A `SwitchCase` node to check.
 | 
			
		||||
 * @returns {boolean} `true` if the node is a `case` node (not `default` node).
 | 
			
		||||
 */
 | 
			
		||||
function isCaseNode(node) {
 | 
			
		||||
    return Boolean(node.test);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks if a given node appears as the value of a PropertyDefinition node.
 | 
			
		||||
 * @param {ASTNode} node THe node to check.
 | 
			
		||||
 * @returns {boolean} `true` if the node is a PropertyDefinition value,
 | 
			
		||||
 *      false if not.
 | 
			
		||||
 */
 | 
			
		||||
function isPropertyDefinitionValue(node) {
 | 
			
		||||
    const parent = node.parent;
 | 
			
		||||
 | 
			
		||||
    return parent && parent.type === "PropertyDefinition" && parent.value === node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks whether the given logical operator is taken into account for the code
 | 
			
		||||
 * path analysis.
 | 
			
		||||
 * @param {string} operator The operator found in the LogicalExpression node
 | 
			
		||||
 * @returns {boolean} `true` if the operator is "&&" or "||" or "??"
 | 
			
		||||
 */
 | 
			
		||||
function isHandledLogicalOperator(operator) {
 | 
			
		||||
    return operator === "&&" || operator === "||" || operator === "??";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks whether the given assignment operator is a logical assignment operator.
 | 
			
		||||
 * Logical assignments are taken into account for the code path analysis
 | 
			
		||||
 * because of their short-circuiting semantics.
 | 
			
		||||
 * @param {string} operator The operator found in the AssignmentExpression node
 | 
			
		||||
 * @returns {boolean} `true` if the operator is "&&=" or "||=" or "??="
 | 
			
		||||
 */
 | 
			
		||||
function isLogicalAssignmentOperator(operator) {
 | 
			
		||||
    return operator === "&&=" || operator === "||=" || operator === "??=";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the label if the parent node of a given node is a LabeledStatement.
 | 
			
		||||
 * @param {ASTNode} node A node to get.
 | 
			
		||||
 * @returns {string|null} The label or `null`.
 | 
			
		||||
 */
 | 
			
		||||
function getLabel(node) {
 | 
			
		||||
    if (node.parent.type === "LabeledStatement") {
 | 
			
		||||
        return node.parent.label.name;
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks whether or not a given logical expression node goes different path
 | 
			
		||||
 * between the `true` case and the `false` case.
 | 
			
		||||
 * @param {ASTNode} node A node to check.
 | 
			
		||||
 * @returns {boolean} `true` if the node is a test of a choice statement.
 | 
			
		||||
 */
 | 
			
		||||
function isForkingByTrueOrFalse(node) {
 | 
			
		||||
    const parent = node.parent;
 | 
			
		||||
 | 
			
		||||
    switch (parent.type) {
 | 
			
		||||
        case "ConditionalExpression":
 | 
			
		||||
        case "IfStatement":
 | 
			
		||||
        case "WhileStatement":
 | 
			
		||||
        case "DoWhileStatement":
 | 
			
		||||
        case "ForStatement":
 | 
			
		||||
            return parent.test === node;
 | 
			
		||||
 | 
			
		||||
        case "LogicalExpression":
 | 
			
		||||
            return isHandledLogicalOperator(parent.operator);
 | 
			
		||||
 | 
			
		||||
        case "AssignmentExpression":
 | 
			
		||||
            return isLogicalAssignmentOperator(parent.operator);
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the boolean value of a given literal node.
 | 
			
		||||
 *
 | 
			
		||||
 * This is used to detect infinity loops (e.g. `while (true) {}`).
 | 
			
		||||
 * Statements preceded by an infinity loop are unreachable if the loop didn't
 | 
			
		||||
 * have any `break` statement.
 | 
			
		||||
 * @param {ASTNode} node A node to get.
 | 
			
		||||
 * @returns {boolean|undefined} a boolean value if the node is a Literal node,
 | 
			
		||||
 *   otherwise `undefined`.
 | 
			
		||||
 */
 | 
			
		||||
function getBooleanValueIfSimpleConstant(node) {
 | 
			
		||||
    if (node.type === "Literal") {
 | 
			
		||||
        return Boolean(node.value);
 | 
			
		||||
    }
 | 
			
		||||
    return void 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks that a given identifier node is a reference or not.
 | 
			
		||||
 *
 | 
			
		||||
 * This is used to detect the first throwable node in a `try` block.
 | 
			
		||||
 * @param {ASTNode} node An Identifier node to check.
 | 
			
		||||
 * @returns {boolean} `true` if the node is a reference.
 | 
			
		||||
 */
 | 
			
		||||
function isIdentifierReference(node) {
 | 
			
		||||
    const parent = node.parent;
 | 
			
		||||
 | 
			
		||||
    switch (parent.type) {
 | 
			
		||||
        case "LabeledStatement":
 | 
			
		||||
        case "BreakStatement":
 | 
			
		||||
        case "ContinueStatement":
 | 
			
		||||
        case "ArrayPattern":
 | 
			
		||||
        case "RestElement":
 | 
			
		||||
        case "ImportSpecifier":
 | 
			
		||||
        case "ImportDefaultSpecifier":
 | 
			
		||||
        case "ImportNamespaceSpecifier":
 | 
			
		||||
        case "CatchClause":
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        case "FunctionDeclaration":
 | 
			
		||||
        case "FunctionExpression":
 | 
			
		||||
        case "ArrowFunctionExpression":
 | 
			
		||||
        case "ClassDeclaration":
 | 
			
		||||
        case "ClassExpression":
 | 
			
		||||
        case "VariableDeclarator":
 | 
			
		||||
            return parent.id !== node;
 | 
			
		||||
 | 
			
		||||
        case "Property":
 | 
			
		||||
        case "PropertyDefinition":
 | 
			
		||||
        case "MethodDefinition":
 | 
			
		||||
            return (
 | 
			
		||||
                parent.key !== node ||
 | 
			
		||||
                parent.computed ||
 | 
			
		||||
                parent.shorthand
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        case "AssignmentPattern":
 | 
			
		||||
            return parent.key !== node;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the current segment with the head segment.
 | 
			
		||||
 * This is similar to local branches and tracking branches of git.
 | 
			
		||||
 *
 | 
			
		||||
 * To separate the current and the head is in order to not make useless segments.
 | 
			
		||||
 *
 | 
			
		||||
 * In this process, both "onCodePathSegmentStart" and "onCodePathSegmentEnd"
 | 
			
		||||
 * events are fired.
 | 
			
		||||
 * @param {CodePathAnalyzer} analyzer The instance.
 | 
			
		||||
 * @param {ASTNode} node The current AST node.
 | 
			
		||||
 * @returns {void}
 | 
			
		||||
 */
 | 
			
		||||
function forwardCurrentToHead(analyzer, node) {
 | 
			
		||||
    const codePath = analyzer.codePath;
 | 
			
		||||
    const state = CodePath.getState(codePath);
 | 
			
		||||
    const currentSegments = state.currentSegments;
 | 
			
		||||
    const headSegments = state.headSegments;
 | 
			
		||||
    const end = Math.max(currentSegments.length, headSegments.length);
 | 
			
		||||
    let i, currentSegment, headSegment;
 | 
			
		||||
 | 
			
		||||
    // Fires leaving events.
 | 
			
		||||
    for (i = 0; i < end; ++i) {
 | 
			
		||||
        currentSegment = currentSegments[i];
 | 
			
		||||
        headSegment = headSegments[i];
 | 
			
		||||
 | 
			
		||||
        if (currentSegment !== headSegment && currentSegment) {
 | 
			
		||||
 | 
			
		||||
            const eventName = currentSegment.reachable
 | 
			
		||||
                ? "onCodePathSegmentEnd"
 | 
			
		||||
                : "onUnreachableCodePathSegmentEnd";
 | 
			
		||||
 | 
			
		||||
            debug.dump(`${eventName} ${currentSegment.id}`);
 | 
			
		||||
 | 
			
		||||
            analyzer.emitter.emit(
 | 
			
		||||
                eventName,
 | 
			
		||||
                currentSegment,
 | 
			
		||||
                node
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update state.
 | 
			
		||||
    state.currentSegments = headSegments;
 | 
			
		||||
 | 
			
		||||
    // Fires entering events.
 | 
			
		||||
    for (i = 0; i < end; ++i) {
 | 
			
		||||
        currentSegment = currentSegments[i];
 | 
			
		||||
        headSegment = headSegments[i];
 | 
			
		||||
 | 
			
		||||
        if (currentSegment !== headSegment && headSegment) {
 | 
			
		||||
 | 
			
		||||
            const eventName = headSegment.reachable
 | 
			
		||||
                ? "onCodePathSegmentStart"
 | 
			
		||||
                : "onUnreachableCodePathSegmentStart";
 | 
			
		||||
 | 
			
		||||
            debug.dump(`${eventName} ${headSegment.id}`);
 | 
			
		||||
 | 
			
		||||
            CodePathSegment.markUsed(headSegment);
 | 
			
		||||
            analyzer.emitter.emit(
 | 
			
		||||
                eventName,
 | 
			
		||||
                headSegment,
 | 
			
		||||
                node
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the current segment with empty.
 | 
			
		||||
 * This is called at the last of functions or the program.
 | 
			
		||||
 * @param {CodePathAnalyzer} analyzer The instance.
 | 
			
		||||
 * @param {ASTNode} node The current AST node.
 | 
			
		||||
 * @returns {void}
 | 
			
		||||
 */
 | 
			
		||||
function leaveFromCurrentSegment(analyzer, node) {
 | 
			
		||||
    const state = CodePath.getState(analyzer.codePath);
 | 
			
		||||
    const currentSegments = state.currentSegments;
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < currentSegments.length; ++i) {
 | 
			
		||||
        const currentSegment = currentSegments[i];
 | 
			
		||||
        const eventName = currentSegment.reachable
 | 
			
		||||
            ? "onCodePathSegmentEnd"
 | 
			
		||||
            : "onUnreachableCodePathSegmentEnd";
 | 
			
		||||
 | 
			
		||||
        debug.dump(`${eventName} ${currentSegment.id}`);
 | 
			
		||||
 | 
			
		||||
        analyzer.emitter.emit(
 | 
			
		||||
            eventName,
 | 
			
		||||
            currentSegment,
 | 
			
		||||
            node
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.currentSegments = [];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the code path due to the position of a given node in the parent node
 | 
			
		||||
 * thereof.
 | 
			
		||||
 *
 | 
			
		||||
 * For example, if the node is `parent.consequent`, this creates a fork from the
 | 
			
		||||
 * current path.
 | 
			
		||||
 * @param {CodePathAnalyzer} analyzer The instance.
 | 
			
		||||
 * @param {ASTNode} node The current AST node.
 | 
			
		||||
 * @returns {void}
 | 
			
		||||
 */
 | 
			
		||||
function preprocess(analyzer, node) {
 | 
			
		||||
    const codePath = analyzer.codePath;
 | 
			
		||||
    const state = CodePath.getState(codePath);
 | 
			
		||||
    const parent = node.parent;
 | 
			
		||||
 | 
			
		||||
    switch (parent.type) {
 | 
			
		||||
 | 
			
		||||
        // The `arguments.length == 0` case is in `postprocess` function.
 | 
			
		||||
        case "CallExpression":
 | 
			
		||||
            if (parent.optional === true && parent.arguments.length >= 1 && parent.arguments[0] === node) {
 | 
			
		||||
                state.makeOptionalRight();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case "MemberExpression":
 | 
			
		||||
            if (parent.optional === true && parent.property === node) {
 | 
			
		||||
                state.makeOptionalRight();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "LogicalExpression":
 | 
			
		||||
            if (
 | 
			
		||||
                parent.right === node &&
 | 
			
		||||
                isHandledLogicalOperator(parent.operator)
 | 
			
		||||
            ) {
 | 
			
		||||
                state.makeLogicalRight();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "AssignmentExpression":
 | 
			
		||||
            if (
 | 
			
		||||
                parent.right === node &&
 | 
			
		||||
                isLogicalAssignmentOperator(parent.operator)
 | 
			
		||||
            ) {
 | 
			
		||||
                state.makeLogicalRight();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ConditionalExpression":
 | 
			
		||||
        case "IfStatement":
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * Fork if this node is at `consequent`/`alternate`.
 | 
			
		||||
             * `popForkContext()` exists at `IfStatement:exit` and
 | 
			
		||||
             * `ConditionalExpression:exit`.
 | 
			
		||||
             */
 | 
			
		||||
            if (parent.consequent === node) {
 | 
			
		||||
                state.makeIfConsequent();
 | 
			
		||||
            } else if (parent.alternate === node) {
 | 
			
		||||
                state.makeIfAlternate();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "SwitchCase":
 | 
			
		||||
            if (parent.consequent[0] === node) {
 | 
			
		||||
                state.makeSwitchCaseBody(false, !parent.test);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "TryStatement":
 | 
			
		||||
            if (parent.handler === node) {
 | 
			
		||||
                state.makeCatchBlock();
 | 
			
		||||
            } else if (parent.finalizer === node) {
 | 
			
		||||
                state.makeFinallyBlock();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "WhileStatement":
 | 
			
		||||
            if (parent.test === node) {
 | 
			
		||||
                state.makeWhileTest(getBooleanValueIfSimpleConstant(node));
 | 
			
		||||
            } else {
 | 
			
		||||
                assert(parent.body === node);
 | 
			
		||||
                state.makeWhileBody();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "DoWhileStatement":
 | 
			
		||||
            if (parent.body === node) {
 | 
			
		||||
                state.makeDoWhileBody();
 | 
			
		||||
            } else {
 | 
			
		||||
                assert(parent.test === node);
 | 
			
		||||
                state.makeDoWhileTest(getBooleanValueIfSimpleConstant(node));
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ForStatement":
 | 
			
		||||
            if (parent.test === node) {
 | 
			
		||||
                state.makeForTest(getBooleanValueIfSimpleConstant(node));
 | 
			
		||||
            } else if (parent.update === node) {
 | 
			
		||||
                state.makeForUpdate();
 | 
			
		||||
            } else if (parent.body === node) {
 | 
			
		||||
                state.makeForBody();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ForInStatement":
 | 
			
		||||
        case "ForOfStatement":
 | 
			
		||||
            if (parent.left === node) {
 | 
			
		||||
                state.makeForInOfLeft();
 | 
			
		||||
            } else if (parent.right === node) {
 | 
			
		||||
                state.makeForInOfRight();
 | 
			
		||||
            } else {
 | 
			
		||||
                assert(parent.body === node);
 | 
			
		||||
                state.makeForInOfBody();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "AssignmentPattern":
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * Fork if this node is at `right`.
 | 
			
		||||
             * `left` is executed always, so it uses the current path.
 | 
			
		||||
             * `popForkContext()` exists at `AssignmentPattern:exit`.
 | 
			
		||||
             */
 | 
			
		||||
            if (parent.right === node) {
 | 
			
		||||
                state.pushForkContext();
 | 
			
		||||
                state.forkBypassPath();
 | 
			
		||||
                state.forkPath();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the code path due to the type of a given node in entering.
 | 
			
		||||
 * @param {CodePathAnalyzer} analyzer The instance.
 | 
			
		||||
 * @param {ASTNode} node The current AST node.
 | 
			
		||||
 * @returns {void}
 | 
			
		||||
 */
 | 
			
		||||
function processCodePathToEnter(analyzer, node) {
 | 
			
		||||
    let codePath = analyzer.codePath;
 | 
			
		||||
    let state = codePath && CodePath.getState(codePath);
 | 
			
		||||
    const parent = node.parent;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new code path and trigger the onCodePathStart event
 | 
			
		||||
     * based on the currently selected node.
 | 
			
		||||
     * @param {string} origin The reason the code path was started.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    function startCodePath(origin) {
 | 
			
		||||
        if (codePath) {
 | 
			
		||||
 | 
			
		||||
            // Emits onCodePathSegmentStart events if updated.
 | 
			
		||||
            forwardCurrentToHead(analyzer, node);
 | 
			
		||||
            debug.dumpState(node, state, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Create the code path of this scope.
 | 
			
		||||
        codePath = analyzer.codePath = new CodePath({
 | 
			
		||||
            id: analyzer.idGenerator.next(),
 | 
			
		||||
            origin,
 | 
			
		||||
            upper: codePath,
 | 
			
		||||
            onLooped: analyzer.onLooped
 | 
			
		||||
        });
 | 
			
		||||
        state = CodePath.getState(codePath);
 | 
			
		||||
 | 
			
		||||
        // Emits onCodePathStart events.
 | 
			
		||||
        debug.dump(`onCodePathStart ${codePath.id}`);
 | 
			
		||||
        analyzer.emitter.emit("onCodePathStart", codePath, node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Special case: The right side of class field initializer is considered
 | 
			
		||||
     * to be its own function, so we need to start a new code path in this
 | 
			
		||||
     * case.
 | 
			
		||||
     */
 | 
			
		||||
    if (isPropertyDefinitionValue(node)) {
 | 
			
		||||
        startCodePath("class-field-initializer");
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Intentional fall through because `node` needs to also be
 | 
			
		||||
         * processed by the code below. For example, if we have:
 | 
			
		||||
         *
 | 
			
		||||
         * class Foo {
 | 
			
		||||
         *     a = () => {}
 | 
			
		||||
         * }
 | 
			
		||||
         *
 | 
			
		||||
         * In this case, we also need start a second code path.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (node.type) {
 | 
			
		||||
        case "Program":
 | 
			
		||||
            startCodePath("program");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "FunctionDeclaration":
 | 
			
		||||
        case "FunctionExpression":
 | 
			
		||||
        case "ArrowFunctionExpression":
 | 
			
		||||
            startCodePath("function");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "StaticBlock":
 | 
			
		||||
            startCodePath("class-static-block");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ChainExpression":
 | 
			
		||||
            state.pushChainContext();
 | 
			
		||||
            break;
 | 
			
		||||
        case "CallExpression":
 | 
			
		||||
            if (node.optional === true) {
 | 
			
		||||
                state.makeOptionalNode();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case "MemberExpression":
 | 
			
		||||
            if (node.optional === true) {
 | 
			
		||||
                state.makeOptionalNode();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "LogicalExpression":
 | 
			
		||||
            if (isHandledLogicalOperator(node.operator)) {
 | 
			
		||||
                state.pushChoiceContext(
 | 
			
		||||
                    node.operator,
 | 
			
		||||
                    isForkingByTrueOrFalse(node)
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "AssignmentExpression":
 | 
			
		||||
            if (isLogicalAssignmentOperator(node.operator)) {
 | 
			
		||||
                state.pushChoiceContext(
 | 
			
		||||
                    node.operator.slice(0, -1), // removes `=` from the end
 | 
			
		||||
                    isForkingByTrueOrFalse(node)
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ConditionalExpression":
 | 
			
		||||
        case "IfStatement":
 | 
			
		||||
            state.pushChoiceContext("test", false);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "SwitchStatement":
 | 
			
		||||
            state.pushSwitchContext(
 | 
			
		||||
                node.cases.some(isCaseNode),
 | 
			
		||||
                getLabel(node)
 | 
			
		||||
            );
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "TryStatement":
 | 
			
		||||
            state.pushTryContext(Boolean(node.finalizer));
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "SwitchCase":
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * Fork if this node is after the 2st node in `cases`.
 | 
			
		||||
             * It's similar to `else` blocks.
 | 
			
		||||
             * The next `test` node is processed in this path.
 | 
			
		||||
             */
 | 
			
		||||
            if (parent.discriminant !== node && parent.cases[0] !== node) {
 | 
			
		||||
                state.forkPath();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "WhileStatement":
 | 
			
		||||
        case "DoWhileStatement":
 | 
			
		||||
        case "ForStatement":
 | 
			
		||||
        case "ForInStatement":
 | 
			
		||||
        case "ForOfStatement":
 | 
			
		||||
            state.pushLoopContext(node.type, getLabel(node));
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "LabeledStatement":
 | 
			
		||||
            if (!breakableTypePattern.test(node.body.type)) {
 | 
			
		||||
                state.pushBreakContext(false, node.label.name);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Emits onCodePathSegmentStart events if updated.
 | 
			
		||||
    forwardCurrentToHead(analyzer, node);
 | 
			
		||||
    debug.dumpState(node, state, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the code path due to the type of a given node in leaving.
 | 
			
		||||
 * @param {CodePathAnalyzer} analyzer The instance.
 | 
			
		||||
 * @param {ASTNode} node The current AST node.
 | 
			
		||||
 * @returns {void}
 | 
			
		||||
 */
 | 
			
		||||
function processCodePathToExit(analyzer, node) {
 | 
			
		||||
 | 
			
		||||
    const codePath = analyzer.codePath;
 | 
			
		||||
    const state = CodePath.getState(codePath);
 | 
			
		||||
    let dontForward = false;
 | 
			
		||||
 | 
			
		||||
    switch (node.type) {
 | 
			
		||||
        case "ChainExpression":
 | 
			
		||||
            state.popChainContext();
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "IfStatement":
 | 
			
		||||
        case "ConditionalExpression":
 | 
			
		||||
            state.popChoiceContext();
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "LogicalExpression":
 | 
			
		||||
            if (isHandledLogicalOperator(node.operator)) {
 | 
			
		||||
                state.popChoiceContext();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "AssignmentExpression":
 | 
			
		||||
            if (isLogicalAssignmentOperator(node.operator)) {
 | 
			
		||||
                state.popChoiceContext();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "SwitchStatement":
 | 
			
		||||
            state.popSwitchContext();
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "SwitchCase":
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * This is the same as the process at the 1st `consequent` node in
 | 
			
		||||
             * `preprocess` function.
 | 
			
		||||
             * Must do if this `consequent` is empty.
 | 
			
		||||
             */
 | 
			
		||||
            if (node.consequent.length === 0) {
 | 
			
		||||
                state.makeSwitchCaseBody(true, !node.test);
 | 
			
		||||
            }
 | 
			
		||||
            if (state.forkContext.reachable) {
 | 
			
		||||
                dontForward = true;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "TryStatement":
 | 
			
		||||
            state.popTryContext();
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "BreakStatement":
 | 
			
		||||
            forwardCurrentToHead(analyzer, node);
 | 
			
		||||
            state.makeBreak(node.label && node.label.name);
 | 
			
		||||
            dontForward = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ContinueStatement":
 | 
			
		||||
            forwardCurrentToHead(analyzer, node);
 | 
			
		||||
            state.makeContinue(node.label && node.label.name);
 | 
			
		||||
            dontForward = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ReturnStatement":
 | 
			
		||||
            forwardCurrentToHead(analyzer, node);
 | 
			
		||||
            state.makeReturn();
 | 
			
		||||
            dontForward = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "ThrowStatement":
 | 
			
		||||
            forwardCurrentToHead(analyzer, node);
 | 
			
		||||
            state.makeThrow();
 | 
			
		||||
            dontForward = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "Identifier":
 | 
			
		||||
            if (isIdentifierReference(node)) {
 | 
			
		||||
                state.makeFirstThrowablePathInTryBlock();
 | 
			
		||||
                dontForward = true;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "CallExpression":
 | 
			
		||||
        case "ImportExpression":
 | 
			
		||||
        case "MemberExpression":
 | 
			
		||||
        case "NewExpression":
 | 
			
		||||
        case "YieldExpression":
 | 
			
		||||
            state.makeFirstThrowablePathInTryBlock();
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "WhileStatement":
 | 
			
		||||
        case "DoWhileStatement":
 | 
			
		||||
        case "ForStatement":
 | 
			
		||||
        case "ForInStatement":
 | 
			
		||||
        case "ForOfStatement":
 | 
			
		||||
            state.popLoopContext();
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "AssignmentPattern":
 | 
			
		||||
            state.popForkContext();
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "LabeledStatement":
 | 
			
		||||
            if (!breakableTypePattern.test(node.body.type)) {
 | 
			
		||||
                state.popBreakContext();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Emits onCodePathSegmentStart events if updated.
 | 
			
		||||
    if (!dontForward) {
 | 
			
		||||
        forwardCurrentToHead(analyzer, node);
 | 
			
		||||
    }
 | 
			
		||||
    debug.dumpState(node, state, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the code path to finalize the current code path.
 | 
			
		||||
 * @param {CodePathAnalyzer} analyzer The instance.
 | 
			
		||||
 * @param {ASTNode} node The current AST node.
 | 
			
		||||
 * @returns {void}
 | 
			
		||||
 */
 | 
			
		||||
function postprocess(analyzer, node) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Ends the code path for the current node.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    function endCodePath() {
 | 
			
		||||
        let codePath = analyzer.codePath;
 | 
			
		||||
 | 
			
		||||
        // Mark the current path as the final node.
 | 
			
		||||
        CodePath.getState(codePath).makeFinal();
 | 
			
		||||
 | 
			
		||||
        // Emits onCodePathSegmentEnd event of the current segments.
 | 
			
		||||
        leaveFromCurrentSegment(analyzer, node);
 | 
			
		||||
 | 
			
		||||
        // Emits onCodePathEnd event of this code path.
 | 
			
		||||
        debug.dump(`onCodePathEnd ${codePath.id}`);
 | 
			
		||||
        analyzer.emitter.emit("onCodePathEnd", codePath, node);
 | 
			
		||||
        debug.dumpDot(codePath);
 | 
			
		||||
 | 
			
		||||
        codePath = analyzer.codePath = analyzer.codePath.upper;
 | 
			
		||||
        if (codePath) {
 | 
			
		||||
            debug.dumpState(node, CodePath.getState(codePath), true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (node.type) {
 | 
			
		||||
        case "Program":
 | 
			
		||||
        case "FunctionDeclaration":
 | 
			
		||||
        case "FunctionExpression":
 | 
			
		||||
        case "ArrowFunctionExpression":
 | 
			
		||||
        case "StaticBlock": {
 | 
			
		||||
            endCodePath();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The `arguments.length >= 1` case is in `preprocess` function.
 | 
			
		||||
        case "CallExpression":
 | 
			
		||||
            if (node.optional === true && node.arguments.length === 0) {
 | 
			
		||||
                CodePath.getState(analyzer.codePath).makeOptionalRight();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Special case: The right side of class field initializer is considered
 | 
			
		||||
     * to be its own function, so we need to end a code path in this
 | 
			
		||||
     * case.
 | 
			
		||||
     *
 | 
			
		||||
     * We need to check after the other checks in order to close the
 | 
			
		||||
     * code paths in the correct order for code like this:
 | 
			
		||||
     *
 | 
			
		||||
     *
 | 
			
		||||
     * class Foo {
 | 
			
		||||
     *     a = () => {}
 | 
			
		||||
     * }
 | 
			
		||||
     *
 | 
			
		||||
     * In this case, The ArrowFunctionExpression code path is closed first
 | 
			
		||||
     * and then we need to close the code path for the PropertyDefinition
 | 
			
		||||
     * value.
 | 
			
		||||
     */
 | 
			
		||||
    if (isPropertyDefinitionValue(node)) {
 | 
			
		||||
        endCodePath();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Public Interface
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The class to analyze code paths.
 | 
			
		||||
 * This class implements the EventGenerator interface.
 | 
			
		||||
 */
 | 
			
		||||
class CodePathAnalyzer {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {EventGenerator} eventGenerator An event generator to wrap.
 | 
			
		||||
     */
 | 
			
		||||
    constructor(eventGenerator) {
 | 
			
		||||
        this.original = eventGenerator;
 | 
			
		||||
        this.emitter = eventGenerator.emitter;
 | 
			
		||||
        this.codePath = null;
 | 
			
		||||
        this.idGenerator = new IdGenerator("s");
 | 
			
		||||
        this.currentNode = null;
 | 
			
		||||
        this.onLooped = this.onLooped.bind(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Does the process to enter a given AST node.
 | 
			
		||||
     * This updates state of analysis and calls `enterNode` of the wrapped.
 | 
			
		||||
     * @param {ASTNode} node A node which is entering.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    enterNode(node) {
 | 
			
		||||
        this.currentNode = node;
 | 
			
		||||
 | 
			
		||||
        // Updates the code path due to node's position in its parent node.
 | 
			
		||||
        if (node.parent) {
 | 
			
		||||
            preprocess(this, node);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Updates the code path.
 | 
			
		||||
         * And emits onCodePathStart/onCodePathSegmentStart events.
 | 
			
		||||
         */
 | 
			
		||||
        processCodePathToEnter(this, node);
 | 
			
		||||
 | 
			
		||||
        // Emits node events.
 | 
			
		||||
        this.original.enterNode(node);
 | 
			
		||||
 | 
			
		||||
        this.currentNode = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Does the process to leave a given AST node.
 | 
			
		||||
     * This updates state of analysis and calls `leaveNode` of the wrapped.
 | 
			
		||||
     * @param {ASTNode} node A node which is leaving.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    leaveNode(node) {
 | 
			
		||||
        this.currentNode = node;
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Updates the code path.
 | 
			
		||||
         * And emits onCodePathStart/onCodePathSegmentStart events.
 | 
			
		||||
         */
 | 
			
		||||
        processCodePathToExit(this, node);
 | 
			
		||||
 | 
			
		||||
        // Emits node events.
 | 
			
		||||
        this.original.leaveNode(node);
 | 
			
		||||
 | 
			
		||||
        // Emits the last onCodePathStart/onCodePathSegmentStart events.
 | 
			
		||||
        postprocess(this, node);
 | 
			
		||||
 | 
			
		||||
        this.currentNode = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This is called on a code path looped.
 | 
			
		||||
     * Then this raises a looped event.
 | 
			
		||||
     * @param {CodePathSegment} fromSegment A segment of prev.
 | 
			
		||||
     * @param {CodePathSegment} toSegment A segment of next.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    onLooped(fromSegment, toSegment) {
 | 
			
		||||
        if (fromSegment.reachable && toSegment.reachable) {
 | 
			
		||||
            debug.dump(`onCodePathSegmentLoop ${fromSegment.id} -> ${toSegment.id}`);
 | 
			
		||||
            this.emitter.emit(
 | 
			
		||||
                "onCodePathSegmentLoop",
 | 
			
		||||
                fromSegment,
 | 
			
		||||
                toSegment,
 | 
			
		||||
                this.currentNode
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = CodePathAnalyzer;
 | 
			
		||||
							
								
								
									
										263
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path-segment.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path-segment.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,263 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @fileoverview The CodePathSegment class.
 | 
			
		||||
 * @author Toru Nagashima
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Requirements
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const debug = require("./debug-helpers");
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Helpers
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks whether or not a given segment is reachable.
 | 
			
		||||
 * @param {CodePathSegment} segment A segment to check.
 | 
			
		||||
 * @returns {boolean} `true` if the segment is reachable.
 | 
			
		||||
 */
 | 
			
		||||
function isReachable(segment) {
 | 
			
		||||
    return segment.reachable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Public Interface
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A code path segment.
 | 
			
		||||
 *
 | 
			
		||||
 * Each segment is arranged in a series of linked lists (implemented by arrays)
 | 
			
		||||
 * that keep track of the previous and next segments in a code path. In this way,
 | 
			
		||||
 * you can navigate between all segments in any code path so long as you have a
 | 
			
		||||
 * reference to any segment in that code path.
 | 
			
		||||
 *
 | 
			
		||||
 * When first created, the segment is in a detached state, meaning that it knows the
 | 
			
		||||
 * segments that came before it but those segments don't know that this new segment
 | 
			
		||||
 * follows it. Only when `CodePathSegment#markUsed()` is called on a segment does it
 | 
			
		||||
 * officially become part of the code path by updating the previous segments to know
 | 
			
		||||
 * that this new segment follows.
 | 
			
		||||
 */
 | 
			
		||||
class CodePathSegment {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new instance.
 | 
			
		||||
     * @param {string} id An identifier.
 | 
			
		||||
     * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
 | 
			
		||||
     *   This array includes unreachable segments.
 | 
			
		||||
     * @param {boolean} reachable A flag which shows this is reachable.
 | 
			
		||||
     */
 | 
			
		||||
    constructor(id, allPrevSegments, reachable) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The identifier of this code path.
 | 
			
		||||
         * Rules use it to store additional information of each rule.
 | 
			
		||||
         * @type {string}
 | 
			
		||||
         */
 | 
			
		||||
        this.id = id;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * An array of the next reachable segments.
 | 
			
		||||
         * @type {CodePathSegment[]}
 | 
			
		||||
         */
 | 
			
		||||
        this.nextSegments = [];
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * An array of the previous reachable segments.
 | 
			
		||||
         * @type {CodePathSegment[]}
 | 
			
		||||
         */
 | 
			
		||||
        this.prevSegments = allPrevSegments.filter(isReachable);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * An array of all next segments including reachable and unreachable.
 | 
			
		||||
         * @type {CodePathSegment[]}
 | 
			
		||||
         */
 | 
			
		||||
        this.allNextSegments = [];
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * An array of all previous segments including reachable and unreachable.
 | 
			
		||||
         * @type {CodePathSegment[]}
 | 
			
		||||
         */
 | 
			
		||||
        this.allPrevSegments = allPrevSegments;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A flag which shows this is reachable.
 | 
			
		||||
         * @type {boolean}
 | 
			
		||||
         */
 | 
			
		||||
        this.reachable = reachable;
 | 
			
		||||
 | 
			
		||||
        // Internal data.
 | 
			
		||||
        Object.defineProperty(this, "internal", {
 | 
			
		||||
            value: {
 | 
			
		||||
 | 
			
		||||
                // determines if the segment has been attached to the code path
 | 
			
		||||
                used: false,
 | 
			
		||||
 | 
			
		||||
                // array of previous segments coming from the end of a loop
 | 
			
		||||
                loopedPrevSegments: []
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        /* c8 ignore start */
 | 
			
		||||
        if (debug.enabled) {
 | 
			
		||||
            this.internal.nodes = [];
 | 
			
		||||
        }/* c8 ignore stop */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks a given previous segment is coming from the end of a loop.
 | 
			
		||||
     * @param {CodePathSegment} segment A previous segment to check.
 | 
			
		||||
     * @returns {boolean} `true` if the segment is coming from the end of a loop.
 | 
			
		||||
     */
 | 
			
		||||
    isLoopedPrevSegment(segment) {
 | 
			
		||||
        return this.internal.loopedPrevSegments.includes(segment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates the root segment.
 | 
			
		||||
     * @param {string} id An identifier.
 | 
			
		||||
     * @returns {CodePathSegment} The created segment.
 | 
			
		||||
     */
 | 
			
		||||
    static newRoot(id) {
 | 
			
		||||
        return new CodePathSegment(id, [], true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new segment and appends it after the given segments.
 | 
			
		||||
     * @param {string} id An identifier.
 | 
			
		||||
     * @param {CodePathSegment[]} allPrevSegments An array of the previous segments
 | 
			
		||||
     *      to append to.
 | 
			
		||||
     * @returns {CodePathSegment} The created segment.
 | 
			
		||||
     */
 | 
			
		||||
    static newNext(id, allPrevSegments) {
 | 
			
		||||
        return new CodePathSegment(
 | 
			
		||||
            id,
 | 
			
		||||
            CodePathSegment.flattenUnusedSegments(allPrevSegments),
 | 
			
		||||
            allPrevSegments.some(isReachable)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates an unreachable segment and appends it after the given segments.
 | 
			
		||||
     * @param {string} id An identifier.
 | 
			
		||||
     * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
 | 
			
		||||
     * @returns {CodePathSegment} The created segment.
 | 
			
		||||
     */
 | 
			
		||||
    static newUnreachable(id, allPrevSegments) {
 | 
			
		||||
        const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * In `if (a) return a; foo();` case, the unreachable segment preceded by
 | 
			
		||||
         * the return statement is not used but must not be removed.
 | 
			
		||||
         */
 | 
			
		||||
        CodePathSegment.markUsed(segment);
 | 
			
		||||
 | 
			
		||||
        return segment;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a segment that follows given segments.
 | 
			
		||||
     * This factory method does not connect with `allPrevSegments`.
 | 
			
		||||
     * But this inherits `reachable` flag.
 | 
			
		||||
     * @param {string} id An identifier.
 | 
			
		||||
     * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
 | 
			
		||||
     * @returns {CodePathSegment} The created segment.
 | 
			
		||||
     */
 | 
			
		||||
    static newDisconnected(id, allPrevSegments) {
 | 
			
		||||
        return new CodePathSegment(id, [], allPrevSegments.some(isReachable));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Marks a given segment as used.
 | 
			
		||||
     *
 | 
			
		||||
     * And this function registers the segment into the previous segments as a next.
 | 
			
		||||
     * @param {CodePathSegment} segment A segment to mark.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    static markUsed(segment) {
 | 
			
		||||
        if (segment.internal.used) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        segment.internal.used = true;
 | 
			
		||||
 | 
			
		||||
        let i;
 | 
			
		||||
 | 
			
		||||
        if (segment.reachable) {
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * If the segment is reachable, then it's officially part of the
 | 
			
		||||
             * code path. This loops through all previous segments to update
 | 
			
		||||
             * their list of next segments. Because the segment is reachable,
 | 
			
		||||
             * it's added to both `nextSegments` and `allNextSegments`.
 | 
			
		||||
             */
 | 
			
		||||
            for (i = 0; i < segment.allPrevSegments.length; ++i) {
 | 
			
		||||
                const prevSegment = segment.allPrevSegments[i];
 | 
			
		||||
 | 
			
		||||
                prevSegment.allNextSegments.push(segment);
 | 
			
		||||
                prevSegment.nextSegments.push(segment);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * If the segment is not reachable, then it's not officially part of the
 | 
			
		||||
             * code path. This loops through all previous segments to update
 | 
			
		||||
             * their list of next segments. Because the segment is not reachable,
 | 
			
		||||
             * it's added only to `allNextSegments`.
 | 
			
		||||
             */
 | 
			
		||||
            for (i = 0; i < segment.allPrevSegments.length; ++i) {
 | 
			
		||||
                segment.allPrevSegments[i].allNextSegments.push(segment);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Marks a previous segment as looped.
 | 
			
		||||
     * @param {CodePathSegment} segment A segment.
 | 
			
		||||
     * @param {CodePathSegment} prevSegment A previous segment to mark.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    static markPrevSegmentAsLooped(segment, prevSegment) {
 | 
			
		||||
        segment.internal.loopedPrevSegments.push(prevSegment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new array based on an array of segments. If any segment in the
 | 
			
		||||
     * array is unused, then it is replaced by all of its previous segments.
 | 
			
		||||
     * All used segments are returned as-is without replacement.
 | 
			
		||||
     * @param {CodePathSegment[]} segments The array of segments to flatten.
 | 
			
		||||
     * @returns {CodePathSegment[]} The flattened array.
 | 
			
		||||
     */
 | 
			
		||||
    static flattenUnusedSegments(segments) {
 | 
			
		||||
        const done = new Set();
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < segments.length; ++i) {
 | 
			
		||||
            const segment = segments[i];
 | 
			
		||||
 | 
			
		||||
            // Ignores duplicated.
 | 
			
		||||
            if (done.has(segment)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Use previous segments if unused.
 | 
			
		||||
            if (!segment.internal.used) {
 | 
			
		||||
                for (let j = 0; j < segment.allPrevSegments.length; ++j) {
 | 
			
		||||
                    const prevSegment = segment.allPrevSegments[j];
 | 
			
		||||
 | 
			
		||||
                    if (!done.has(prevSegment)) {
 | 
			
		||||
                        done.add(prevSegment);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                done.add(segment);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [...done];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = CodePathSegment;
 | 
			
		||||
							
								
								
									
										2348
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path-state.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2348
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path-state.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										342
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								node_modules/eslint/lib/linter/code-path-analysis/code-path.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,342 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @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 {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new instance.
 | 
			
		||||
     * @param {Object} options Options for the function (see below).
 | 
			
		||||
     * @param {string} options.id An identifier.
 | 
			
		||||
     * @param {string} options.origin The type of code path origin.
 | 
			
		||||
     * @param {CodePath|null} options.upper The code path of the upper function scope.
 | 
			
		||||
     * @param {Function} options.onLooped A callback function to notify looping.
 | 
			
		||||
     */
 | 
			
		||||
    constructor({ id, origin, upper, onLooped }) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The identifier of this code path.
 | 
			
		||||
         * Rules use it to store additional information of each rule.
 | 
			
		||||
         * @type {string}
 | 
			
		||||
         */
 | 
			
		||||
        this.id = id;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The reason that this code path was started. May be "program",
 | 
			
		||||
         * "function", "class-field-initializer", or "class-static-block".
 | 
			
		||||
         * @type {string}
 | 
			
		||||
         */
 | 
			
		||||
        this.origin = origin;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 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. This is the segment that is at the head
 | 
			
		||||
     * of the code path.
 | 
			
		||||
     * This is a passthrough to the underlying `CodePathState`.
 | 
			
		||||
     * @type {CodePathSegment}
 | 
			
		||||
     */
 | 
			
		||||
    get initialSegment() {
 | 
			
		||||
        return this.internal.initialSegment;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Final code path segments. These are the terminal (tail) segments in the
 | 
			
		||||
     * code path, which is the combination of `returnedSegments` and `thrownSegments`.
 | 
			
		||||
     * All segments in this array are reachable.
 | 
			
		||||
     * This is a passthrough to the underlying `CodePathState`.
 | 
			
		||||
     * @type {CodePathSegment[]}
 | 
			
		||||
     */
 | 
			
		||||
    get finalSegments() {
 | 
			
		||||
        return this.internal.finalSegments;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Final code path segments that represent normal completion of the code path.
 | 
			
		||||
     * For functions, this means both explicit `return` statements and implicit returns,
 | 
			
		||||
     * such as the last reachable segment in a function that does not have an
 | 
			
		||||
     * explicit `return` as this implicitly returns `undefined`. For scripts,
 | 
			
		||||
     * modules, class field initializers, and class static blocks, this means
 | 
			
		||||
     * all lines of code have been executed.
 | 
			
		||||
     * These segments are also present in `finalSegments`.
 | 
			
		||||
     * This is a passthrough to the underlying `CodePathState`.
 | 
			
		||||
     * @type {CodePathSegment[]}
 | 
			
		||||
     */
 | 
			
		||||
    get returnedSegments() {
 | 
			
		||||
        return this.internal.returnedForkContext;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Final code path segments that represent `throw` statements.
 | 
			
		||||
     * This is a passthrough to the underlying `CodePathState`.
 | 
			
		||||
     * These segments are also present in `finalSegments`.
 | 
			
		||||
     * @type {CodePathSegment[]}
 | 
			
		||||
     */
 | 
			
		||||
    get thrownSegments() {
 | 
			
		||||
        return this.internal.thrownForkContext;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tracks the traversal of the code path through each segment. This array
 | 
			
		||||
     * starts empty and segments are added or removed as the code path is
 | 
			
		||||
     * traversed. This array always ends up empty at the end of a code path
 | 
			
		||||
     * traversal. The `CodePathState` uses this to track its progress through
 | 
			
		||||
     * the code path.
 | 
			
		||||
     * This is a passthrough to the underlying `CodePathState`.
 | 
			
		||||
     * @type {CodePathSegment[]}
 | 
			
		||||
     * @deprecated
 | 
			
		||||
     */
 | 
			
		||||
    get currentSegments() {
 | 
			
		||||
        return this.internal.currentSegments;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Traverses all segments in this code path.
 | 
			
		||||
     *
 | 
			
		||||
     *     codePath.traverseSegments((segment, controller) => {
 | 
			
		||||
     *         // do something.
 | 
			
		||||
     *     });
 | 
			
		||||
     *
 | 
			
		||||
     * This method enumerates segments in order from the head.
 | 
			
		||||
     *
 | 
			
		||||
     * The `controller` argument has two methods:
 | 
			
		||||
     *
 | 
			
		||||
     * - `skip()` - skips the following segments in this branch
 | 
			
		||||
     * - `break()` - skips all following segments in the traversal
 | 
			
		||||
     *
 | 
			
		||||
     * A note on the parameters: the `options` argument is optional. This means
 | 
			
		||||
     * the first argument might be an options object or the callback function.
 | 
			
		||||
     * @param {Object} [optionsOrCallback] Optional first and last segments to traverse.
 | 
			
		||||
     * @param {CodePathSegment} [optionsOrCallback.first] The first segment to traverse.
 | 
			
		||||
     * @param {CodePathSegment} [optionsOrCallback.last] The last segment to traverse.
 | 
			
		||||
     * @param {Function} callback A callback function.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    traverseSegments(optionsOrCallback, callback) {
 | 
			
		||||
 | 
			
		||||
        // normalize the arguments into a callback and options
 | 
			
		||||
        let resolvedOptions;
 | 
			
		||||
        let resolvedCallback;
 | 
			
		||||
 | 
			
		||||
        if (typeof optionsOrCallback === "function") {
 | 
			
		||||
            resolvedCallback = optionsOrCallback;
 | 
			
		||||
            resolvedOptions = {};
 | 
			
		||||
        } else {
 | 
			
		||||
            resolvedOptions = optionsOrCallback || {};
 | 
			
		||||
            resolvedCallback = callback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // determine where to start traversing from based on the options
 | 
			
		||||
        const startSegment = resolvedOptions.first || this.internal.initialSegment;
 | 
			
		||||
        const lastSegment = resolvedOptions.last;
 | 
			
		||||
 | 
			
		||||
        // set up initial location information
 | 
			
		||||
        let record = null;
 | 
			
		||||
        let index = 0;
 | 
			
		||||
        let end = 0;
 | 
			
		||||
        let segment = null;
 | 
			
		||||
 | 
			
		||||
        // segments that have already been visited during traversal
 | 
			
		||||
        const visited = new Set();
 | 
			
		||||
 | 
			
		||||
        // tracks the traversal steps
 | 
			
		||||
        const stack = [[startSegment, 0]];
 | 
			
		||||
 | 
			
		||||
        // tracks the last skipped segment during traversal
 | 
			
		||||
        let skippedSegment = null;
 | 
			
		||||
 | 
			
		||||
        // indicates if we exited early from the traversal
 | 
			
		||||
        let broken = false;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Maintains traversal state.
 | 
			
		||||
         */
 | 
			
		||||
        const controller = {
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Skip the following segments in this branch.
 | 
			
		||||
             * @returns {void}
 | 
			
		||||
             */
 | 
			
		||||
            skip() {
 | 
			
		||||
                if (stack.length <= 1) {
 | 
			
		||||
                    broken = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    skippedSegment = stack[stack.length - 2][0];
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Stop traversal completely - do not traverse to any
 | 
			
		||||
             * other segments.
 | 
			
		||||
             * @returns {void}
 | 
			
		||||
             */
 | 
			
		||||
            break() {
 | 
			
		||||
                broken = true;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Checks if 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.has(prevSegment) ||
 | 
			
		||||
                segment.isLoopedPrevSegment(prevSegment)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // the traversal
 | 
			
		||||
        while (stack.length > 0) {
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * This isn't a pure stack. We use the top record all the time
 | 
			
		||||
             * but don't always pop it off. The record is popped only if
 | 
			
		||||
             * one of the following is true:
 | 
			
		||||
             *
 | 
			
		||||
             * 1) We have already visited the segment.
 | 
			
		||||
             * 2) We have not visited *all* of the previous segments.
 | 
			
		||||
             * 3) We have traversed past the available next segments.
 | 
			
		||||
             *
 | 
			
		||||
             * Otherwise, we just read the value and sometimes modify the
 | 
			
		||||
             * record as we traverse.
 | 
			
		||||
             */
 | 
			
		||||
            record = stack[stack.length - 1];
 | 
			
		||||
            segment = record[0];
 | 
			
		||||
            index = record[1];
 | 
			
		||||
 | 
			
		||||
            if (index === 0) {
 | 
			
		||||
 | 
			
		||||
                // Skip if this segment has been visited already.
 | 
			
		||||
                if (visited.has(segment)) {
 | 
			
		||||
                    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 skipping flag if all branches have been skipped.
 | 
			
		||||
                if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
 | 
			
		||||
                    skippedSegment = null;
 | 
			
		||||
                }
 | 
			
		||||
                visited.add(segment);
 | 
			
		||||
 | 
			
		||||
                /*
 | 
			
		||||
                 * If the most recent segment hasn't been skipped, then we call
 | 
			
		||||
                 * the callback, passing in the segment and the controller.
 | 
			
		||||
                 */
 | 
			
		||||
                if (!skippedSegment) {
 | 
			
		||||
                    resolvedCallback.call(this, segment, controller);
 | 
			
		||||
 | 
			
		||||
                    // exit if we're at the last segment
 | 
			
		||||
                    if (segment === lastSegment) {
 | 
			
		||||
                        controller.skip();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    /*
 | 
			
		||||
                     * If the previous statement was executed, or if the callback
 | 
			
		||||
                     * called a method on the controller, we might need to exit the
 | 
			
		||||
                     * loop, so check for that and break accordingly.
 | 
			
		||||
                     */
 | 
			
		||||
                    if (broken) {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update the stack.
 | 
			
		||||
            end = segment.nextSegments.length - 1;
 | 
			
		||||
            if (index < end) {
 | 
			
		||||
 | 
			
		||||
                /*
 | 
			
		||||
                 * If we haven't yet visited all of the next segments, update
 | 
			
		||||
                 * the current top record on the stack to the next index to visit
 | 
			
		||||
                 * and then push a record for the current segment on top.
 | 
			
		||||
                 *
 | 
			
		||||
                 * Setting the current top record's index lets us know how many
 | 
			
		||||
                 * times we've been here and ensures that the segment won't be
 | 
			
		||||
                 * reprocessed (because we only process segments with an index
 | 
			
		||||
                 * of 0).
 | 
			
		||||
                 */
 | 
			
		||||
                record[1] += 1;
 | 
			
		||||
                stack.push([segment.nextSegments[index], 0]);
 | 
			
		||||
            } else if (index === end) {
 | 
			
		||||
 | 
			
		||||
                /*
 | 
			
		||||
                 * If we are at the last next segment, then reset the top record
 | 
			
		||||
                 * in the stack to next segment and set its index to 0 so it will
 | 
			
		||||
                 * be processed next.
 | 
			
		||||
                 */
 | 
			
		||||
                record[0] = segment.nextSegments[index];
 | 
			
		||||
                record[1] = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
                /*
 | 
			
		||||
                 * If index > end, that means we have no more segments that need
 | 
			
		||||
                 * processing. So, we pop that record off of the stack in order to
 | 
			
		||||
                 * continue traversing at the next level up.
 | 
			
		||||
                 */
 | 
			
		||||
                stack.pop();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = CodePath;
 | 
			
		||||
							
								
								
									
										203
									
								
								node_modules/eslint/lib/linter/code-path-analysis/debug-helpers.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								node_modules/eslint/lib/linter/code-path-analysis/debug-helpers.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @fileoverview Helpers to debug for code path analysis.
 | 
			
		||||
 * @author Toru Nagashima
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Requirements
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const debug = require("debug")("eslint:code-path");
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Helpers
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets id of a given segment.
 | 
			
		||||
 * @param {CodePathSegment} segment A segment to get.
 | 
			
		||||
 * @returns {string} Id of the segment.
 | 
			
		||||
 */
 | 
			
		||||
/* c8 ignore next */
 | 
			
		||||
function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc -- Ignoring
 | 
			
		||||
    return segment.id + (segment.reachable ? "" : "!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get string for the given node and operation.
 | 
			
		||||
 * @param {ASTNode} node The node to convert.
 | 
			
		||||
 * @param {"enter" | "exit" | undefined} label The operation label.
 | 
			
		||||
 * @returns {string} The string representation.
 | 
			
		||||
 */
 | 
			
		||||
function nodeToString(node, label) {
 | 
			
		||||
    const suffix = label ? `:${label}` : "";
 | 
			
		||||
 | 
			
		||||
    switch (node.type) {
 | 
			
		||||
        case "Identifier": return `${node.type}${suffix} (${node.name})`;
 | 
			
		||||
        case "Literal": return `${node.type}${suffix} (${node.value})`;
 | 
			
		||||
        default: return `${node.type}${suffix}`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Public Interface
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A flag that debug dumping is enabled or not.
 | 
			
		||||
     * @type {boolean}
 | 
			
		||||
     */
 | 
			
		||||
    enabled: debug.enabled,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Dumps given objects.
 | 
			
		||||
     * @param {...any} args objects to dump.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    dump: debug,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Dumps the current analyzing state.
 | 
			
		||||
     * @param {ASTNode} node A node to dump.
 | 
			
		||||
     * @param {CodePathState} state A state to dump.
 | 
			
		||||
     * @param {boolean} leaving A flag whether or not it's leaving
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    dumpState: !debug.enabled ? debug : /* c8 ignore next */ function(node, state, leaving) {
 | 
			
		||||
        for (let i = 0; i < state.currentSegments.length; ++i) {
 | 
			
		||||
            const segInternal = state.currentSegments[i].internal;
 | 
			
		||||
 | 
			
		||||
            if (leaving) {
 | 
			
		||||
                const last = segInternal.nodes.length - 1;
 | 
			
		||||
 | 
			
		||||
                if (last >= 0 && segInternal.nodes[last] === nodeToString(node, "enter")) {
 | 
			
		||||
                    segInternal.nodes[last] = nodeToString(node, void 0);
 | 
			
		||||
                } else {
 | 
			
		||||
                    segInternal.nodes.push(nodeToString(node, "exit"));
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                segInternal.nodes.push(nodeToString(node, "enter"));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        debug([
 | 
			
		||||
            `${state.currentSegments.map(getId).join(",")})`,
 | 
			
		||||
            `${node.type}${leaving ? ":exit" : ""}`
 | 
			
		||||
        ].join(" "));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Dumps a DOT code of a given code path.
 | 
			
		||||
     * The DOT code can be visualized with Graphvis.
 | 
			
		||||
     * @param {CodePath} codePath A code path to dump.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     * @see http://www.graphviz.org
 | 
			
		||||
     * @see http://www.webgraphviz.com
 | 
			
		||||
     */
 | 
			
		||||
    dumpDot: !debug.enabled ? debug : /* c8 ignore next */ function(codePath) {
 | 
			
		||||
        let text =
 | 
			
		||||
            "\n" +
 | 
			
		||||
            "digraph {\n" +
 | 
			
		||||
            "node[shape=box,style=\"rounded,filled\",fillcolor=white];\n" +
 | 
			
		||||
            "initial[label=\"\",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
 | 
			
		||||
 | 
			
		||||
        if (codePath.returnedSegments.length > 0) {
 | 
			
		||||
            text += "final[label=\"\",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
 | 
			
		||||
        }
 | 
			
		||||
        if (codePath.thrownSegments.length > 0) {
 | 
			
		||||
            text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize=true];\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const traceMap = Object.create(null);
 | 
			
		||||
        const arrows = this.makeDotArrows(codePath, traceMap);
 | 
			
		||||
 | 
			
		||||
        for (const id in traceMap) { // eslint-disable-line guard-for-in -- Want ability to traverse prototype
 | 
			
		||||
            const segment = traceMap[id];
 | 
			
		||||
 | 
			
		||||
            text += `${id}[`;
 | 
			
		||||
 | 
			
		||||
            if (segment.reachable) {
 | 
			
		||||
                text += "label=\"";
 | 
			
		||||
            } else {
 | 
			
		||||
                text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (segment.internal.nodes.length > 0) {
 | 
			
		||||
                text += segment.internal.nodes.join("\\n");
 | 
			
		||||
            } else {
 | 
			
		||||
                text += "????";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            text += "\"];\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        text += `${arrows}\n`;
 | 
			
		||||
        text += "}";
 | 
			
		||||
        debug("DOT", text);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Makes a DOT code of a given code path.
 | 
			
		||||
     * The DOT code can be visualized with Graphvis.
 | 
			
		||||
     * @param {CodePath} codePath A code path to make DOT.
 | 
			
		||||
     * @param {Object} traceMap Optional. A map to check whether or not segments had been done.
 | 
			
		||||
     * @returns {string} A DOT code of the code path.
 | 
			
		||||
     */
 | 
			
		||||
    makeDotArrows(codePath, traceMap) {
 | 
			
		||||
        const stack = [[codePath.initialSegment, 0]];
 | 
			
		||||
        const done = traceMap || Object.create(null);
 | 
			
		||||
        let lastId = codePath.initialSegment.id;
 | 
			
		||||
        let text = `initial->${codePath.initialSegment.id}`;
 | 
			
		||||
 | 
			
		||||
        while (stack.length > 0) {
 | 
			
		||||
            const item = stack.pop();
 | 
			
		||||
            const segment = item[0];
 | 
			
		||||
            const index = item[1];
 | 
			
		||||
 | 
			
		||||
            if (done[segment.id] && index === 0) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            done[segment.id] = segment;
 | 
			
		||||
 | 
			
		||||
            const nextSegment = segment.allNextSegments[index];
 | 
			
		||||
 | 
			
		||||
            if (!nextSegment) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (lastId === segment.id) {
 | 
			
		||||
                text += `->${nextSegment.id}`;
 | 
			
		||||
            } else {
 | 
			
		||||
                text += `;\n${segment.id}->${nextSegment.id}`;
 | 
			
		||||
            }
 | 
			
		||||
            lastId = nextSegment.id;
 | 
			
		||||
 | 
			
		||||
            stack.unshift([segment, 1 + index]);
 | 
			
		||||
            stack.push([nextSegment, 0]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        codePath.returnedSegments.forEach(finalSegment => {
 | 
			
		||||
            if (lastId === finalSegment.id) {
 | 
			
		||||
                text += "->final";
 | 
			
		||||
            } else {
 | 
			
		||||
                text += `;\n${finalSegment.id}->final`;
 | 
			
		||||
            }
 | 
			
		||||
            lastId = null;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        codePath.thrownSegments.forEach(finalSegment => {
 | 
			
		||||
            if (lastId === finalSegment.id) {
 | 
			
		||||
                text += "->thrown";
 | 
			
		||||
            } else {
 | 
			
		||||
                text += `;\n${finalSegment.id}->thrown`;
 | 
			
		||||
            }
 | 
			
		||||
            lastId = null;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return `${text};`;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										349
									
								
								node_modules/eslint/lib/linter/code-path-analysis/fork-context.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										349
									
								
								node_modules/eslint/lib/linter/code-path-analysis/fork-context.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,349 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @fileoverview A class to operate forking.
 | 
			
		||||
 *
 | 
			
		||||
 * This is state of forking.
 | 
			
		||||
 * This has a fork list and manages it.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Toru Nagashima
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Requirements
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const assert = require("assert"),
 | 
			
		||||
    CodePathSegment = require("./code-path-segment");
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Helpers
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines whether or not a given segment is reachable.
 | 
			
		||||
 * @param {CodePathSegment} segment The segment to check.
 | 
			
		||||
 * @returns {boolean} `true` if the segment is reachable.
 | 
			
		||||
 */
 | 
			
		||||
function isReachable(segment) {
 | 
			
		||||
    return segment.reachable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates a new segment for each fork in the given context and appends it
 | 
			
		||||
 * to the end of the specified range of segments. Ultimately, this ends up calling
 | 
			
		||||
 * `new CodePathSegment()` for each of the forks using the `create` argument
 | 
			
		||||
 * as a wrapper around special behavior.
 | 
			
		||||
 *
 | 
			
		||||
 * The `startIndex` and `endIndex` arguments specify a range of segments in
 | 
			
		||||
 * `context` that should become `allPrevSegments` for the newly created
 | 
			
		||||
 * `CodePathSegment` objects.
 | 
			
		||||
 *
 | 
			
		||||
 * When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
 | 
			
		||||
 * `end` is `-1`, this creates two new segments, `[g, h]`. This `g` is appended to
 | 
			
		||||
 * the end of the path from `a`, `c`, and `e`. This `h` is appended to the end of
 | 
			
		||||
 * `b`, `d`, and `f`.
 | 
			
		||||
 * @param {ForkContext} context An instance from which the previous segments
 | 
			
		||||
 *      will be obtained.
 | 
			
		||||
 * @param {number} startIndex The index of the first segment in the context
 | 
			
		||||
 *      that should be specified as previous segments for the newly created segments.
 | 
			
		||||
 * @param {number} endIndex The index of the last segment in the context
 | 
			
		||||
 *      that should be specified as previous segments for the newly created segments.
 | 
			
		||||
 * @param {Function} create A function that creates new `CodePathSegment`
 | 
			
		||||
 *      instances in a particular way. See the `CodePathSegment.new*` methods.
 | 
			
		||||
 * @returns {Array<CodePathSegment>} An array of the newly-created segments.
 | 
			
		||||
 */
 | 
			
		||||
function createSegments(context, startIndex, endIndex, create) {
 | 
			
		||||
 | 
			
		||||
    /** @type {Array<Array<CodePathSegment>>} */
 | 
			
		||||
    const list = context.segmentsList;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Both `startIndex` and `endIndex` work the same way: if the number is zero
 | 
			
		||||
     * or more, then the number is used as-is. If the number is negative,
 | 
			
		||||
     * then that number is added to the length of the segments list to
 | 
			
		||||
     * determine the index to use. That means -1 for either argument
 | 
			
		||||
     * is the last element, -2 is the second to last, and so on.
 | 
			
		||||
     *
 | 
			
		||||
     * So if `startIndex` is 0, `endIndex` is -1, and `list.length` is 3, the
 | 
			
		||||
     * effective `startIndex` is 0 and the effective `endIndex` is 2, so this function
 | 
			
		||||
     * will include items at indices 0, 1, and 2.
 | 
			
		||||
     *
 | 
			
		||||
     * Therefore, if `startIndex` is -1 and `endIndex` is -1, that means we'll only
 | 
			
		||||
     * be using the last segment in `list`.
 | 
			
		||||
     */
 | 
			
		||||
    const normalizedBegin = startIndex >= 0 ? startIndex : list.length + startIndex;
 | 
			
		||||
    const normalizedEnd = endIndex >= 0 ? endIndex : list.length + endIndex;
 | 
			
		||||
 | 
			
		||||
    /** @type {Array<CodePathSegment>} */
 | 
			
		||||
    const segments = [];
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < context.count; ++i) {
 | 
			
		||||
 | 
			
		||||
        // this is passed into `new CodePathSegment` to add to code path.
 | 
			
		||||
        const allPrevSegments = [];
 | 
			
		||||
 | 
			
		||||
        for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
 | 
			
		||||
            allPrevSegments.push(list[j][i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // note: `create` is just a wrapper that augments `new CodePathSegment`.
 | 
			
		||||
        segments.push(create(context.idGenerator.next(), allPrevSegments));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return segments;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Inside of a `finally` block we end up with two parallel paths. If the code path
 | 
			
		||||
 * exits by a control statement (such as `break` or `continue`) from the `finally`
 | 
			
		||||
 * block, then we need to merge the remaining parallel paths back into one.
 | 
			
		||||
 * @param {ForkContext} context The fork context to work on.
 | 
			
		||||
 * @param {Array<CodePathSegment>} segments Segments to merge.
 | 
			
		||||
 * @returns {Array<CodePathSegment>} The merged segments.
 | 
			
		||||
 */
 | 
			
		||||
function mergeExtraSegments(context, segments) {
 | 
			
		||||
    let currentSegments = segments;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * We need to ensure that the array returned from this function contains no more
 | 
			
		||||
     * than the number of segments that the context allows. `context.count` indicates
 | 
			
		||||
     * how many items should be in the returned array to ensure that the new segment
 | 
			
		||||
     * entries will line up with the already existing segment entries.
 | 
			
		||||
     */
 | 
			
		||||
    while (currentSegments.length > context.count) {
 | 
			
		||||
        const merged = [];
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Because `context.count` is a factor of 2 inside of a `finally` block,
 | 
			
		||||
         * we can divide the segment count by 2 to merge the paths together.
 | 
			
		||||
         * This loops through each segment in the list and creates a new `CodePathSegment`
 | 
			
		||||
         * that has the segment and the segment two slots away as previous segments.
 | 
			
		||||
         *
 | 
			
		||||
         * If `currentSegments` is [a,b,c,d], this will create new segments e and f, such
 | 
			
		||||
         * that:
 | 
			
		||||
         *
 | 
			
		||||
         * When `i` is 0:
 | 
			
		||||
         * a->e
 | 
			
		||||
         * c->e
 | 
			
		||||
         *
 | 
			
		||||
         * When `i` is 1:
 | 
			
		||||
         * b->f
 | 
			
		||||
         * d->f
 | 
			
		||||
         */
 | 
			
		||||
        for (let i = 0, length = Math.floor(currentSegments.length / 2); i < length; ++i) {
 | 
			
		||||
            merged.push(CodePathSegment.newNext(
 | 
			
		||||
                context.idGenerator.next(),
 | 
			
		||||
                [currentSegments[i], currentSegments[i + length]]
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Go through the loop condition one more time to see if we have the
 | 
			
		||||
         * number of segments for the context. If not, we'll keep merging paths
 | 
			
		||||
         * of the merged segments until we get there.
 | 
			
		||||
         */
 | 
			
		||||
        currentSegments = merged;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return currentSegments;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Public Interface
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Manages the forking of code paths.
 | 
			
		||||
 */
 | 
			
		||||
class ForkContext {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new instance.
 | 
			
		||||
     * @param {IdGenerator} idGenerator An identifier generator for segments.
 | 
			
		||||
     * @param {ForkContext|null} upper The preceding fork context.
 | 
			
		||||
     * @param {number} count The number of parallel segments in each element
 | 
			
		||||
     *      of `segmentsList`.
 | 
			
		||||
     */
 | 
			
		||||
    constructor(idGenerator, upper, count) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The ID generator that will generate segment IDs for any new
 | 
			
		||||
         * segments that are created.
 | 
			
		||||
         * @type {IdGenerator}
 | 
			
		||||
         */
 | 
			
		||||
        this.idGenerator = idGenerator;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The preceding fork context.
 | 
			
		||||
         * @type {ForkContext|null}
 | 
			
		||||
         */
 | 
			
		||||
        this.upper = upper;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The number of elements in each element of `segmentsList`. In most
 | 
			
		||||
         * cases, this is 1 but can be 2 when there is a `finally` present,
 | 
			
		||||
         * which forks the code path outside of normal flow. In the case of nested
 | 
			
		||||
         * `finally` blocks, this can be a multiple of 2.
 | 
			
		||||
         * @type {number}
 | 
			
		||||
         */
 | 
			
		||||
        this.count = count;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The segments within this context. Each element in this array has
 | 
			
		||||
         * `count` elements that represent one step in each fork. For example,
 | 
			
		||||
         * when `segmentsList` is `[[a, b], [c, d], [e, f]]`, there is one path
 | 
			
		||||
         * a->c->e and one path b->d->f, and `count` is 2 because each element
 | 
			
		||||
         * is an array with two elements.
 | 
			
		||||
         * @type {Array<Array<CodePathSegment>>}
 | 
			
		||||
         */
 | 
			
		||||
        this.segmentsList = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The segments that begin this fork context.
 | 
			
		||||
     * @type {Array<CodePathSegment>}
 | 
			
		||||
     */
 | 
			
		||||
    get head() {
 | 
			
		||||
        const list = this.segmentsList;
 | 
			
		||||
 | 
			
		||||
        return list.length === 0 ? [] : list[list.length - 1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Indicates if the context contains no segments.
 | 
			
		||||
     * @type {boolean}
 | 
			
		||||
     */
 | 
			
		||||
    get empty() {
 | 
			
		||||
        return this.segmentsList.length === 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Indicates if there are any segments that are reachable.
 | 
			
		||||
     * @type {boolean}
 | 
			
		||||
     */
 | 
			
		||||
    get reachable() {
 | 
			
		||||
        const segments = this.head;
 | 
			
		||||
 | 
			
		||||
        return segments.length > 0 && segments.some(isReachable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates new segments in this context and appends them to the end of the
 | 
			
		||||
     * already existing `CodePathSegment`s specified by `startIndex` and
 | 
			
		||||
     * `endIndex`.
 | 
			
		||||
     * @param {number} startIndex The index of the first segment in the context
 | 
			
		||||
     *      that should be specified as previous segments for the newly created segments.
 | 
			
		||||
     * @param {number} endIndex The index of the last segment in the context
 | 
			
		||||
     *      that should be specified as previous segments for the newly created segments.
 | 
			
		||||
     * @returns {Array<CodePathSegment>} An array of the newly created segments.
 | 
			
		||||
     */
 | 
			
		||||
    makeNext(startIndex, endIndex) {
 | 
			
		||||
        return createSegments(this, startIndex, endIndex, CodePathSegment.newNext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates new unreachable segments in this context and appends them to the end of the
 | 
			
		||||
     * already existing `CodePathSegment`s specified by `startIndex` and
 | 
			
		||||
     * `endIndex`.
 | 
			
		||||
     * @param {number} startIndex The index of the first segment in the context
 | 
			
		||||
     *      that should be specified as previous segments for the newly created segments.
 | 
			
		||||
     * @param {number} endIndex The index of the last segment in the context
 | 
			
		||||
     *      that should be specified as previous segments for the newly created segments.
 | 
			
		||||
     * @returns {Array<CodePathSegment>} An array of the newly created segments.
 | 
			
		||||
     */
 | 
			
		||||
    makeUnreachable(startIndex, endIndex) {
 | 
			
		||||
        return createSegments(this, startIndex, endIndex, CodePathSegment.newUnreachable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates new segments in this context and does not append them to the end
 | 
			
		||||
     *  of the already existing `CodePathSegment`s specified by `startIndex` and
 | 
			
		||||
     * `endIndex`. The `startIndex` and `endIndex` are only used to determine if
 | 
			
		||||
     * the new segments should be reachable. If any of the segments in this range
 | 
			
		||||
     * are reachable then the new segments are also reachable; otherwise, the new
 | 
			
		||||
     * segments are unreachable.
 | 
			
		||||
     * @param {number} startIndex The index of the first segment in the context
 | 
			
		||||
     *      that should be considered for reachability.
 | 
			
		||||
     * @param {number} endIndex The index of the last segment in the context
 | 
			
		||||
     *      that should be considered for reachability.
 | 
			
		||||
     * @returns {Array<CodePathSegment>} An array of the newly created segments.
 | 
			
		||||
     */
 | 
			
		||||
    makeDisconnected(startIndex, endIndex) {
 | 
			
		||||
        return createSegments(this, startIndex, endIndex, CodePathSegment.newDisconnected);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds segments to the head of this context.
 | 
			
		||||
     * @param {Array<CodePathSegment>} segments The segments to add.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    add(segments) {
 | 
			
		||||
        assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
 | 
			
		||||
        this.segmentsList.push(mergeExtraSegments(this, segments));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Replaces the head segments with the given segments.
 | 
			
		||||
     * The current head segments are removed.
 | 
			
		||||
     * @param {Array<CodePathSegment>} replacementHeadSegments The new head segments.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    replaceHead(replacementHeadSegments) {
 | 
			
		||||
        assert(
 | 
			
		||||
            replacementHeadSegments.length >= this.count,
 | 
			
		||||
            `${replacementHeadSegments.length} >= ${this.count}`
 | 
			
		||||
        );
 | 
			
		||||
        this.segmentsList.splice(-1, 1, mergeExtraSegments(this, replacementHeadSegments));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds all segments of a given fork context into this context.
 | 
			
		||||
     * @param {ForkContext} otherForkContext The fork context to add from.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    addAll(otherForkContext) {
 | 
			
		||||
        assert(otherForkContext.count === this.count);
 | 
			
		||||
        this.segmentsList.push(...otherForkContext.segmentsList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clears all segments in this context.
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    clear() {
 | 
			
		||||
        this.segmentsList = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new root context, meaning that there are no parent
 | 
			
		||||
     * fork contexts.
 | 
			
		||||
     * @param {IdGenerator} idGenerator An identifier generator for segments.
 | 
			
		||||
     * @returns {ForkContext} New fork context.
 | 
			
		||||
     */
 | 
			
		||||
    static newRoot(idGenerator) {
 | 
			
		||||
        const context = new ForkContext(idGenerator, null, 1);
 | 
			
		||||
 | 
			
		||||
        context.add([CodePathSegment.newRoot(idGenerator.next())]);
 | 
			
		||||
 | 
			
		||||
        return context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates an empty fork context preceded by a given context.
 | 
			
		||||
     * @param {ForkContext} parentContext The parent fork context.
 | 
			
		||||
     * @param {boolean} shouldForkLeavingPath Indicates that we are inside of
 | 
			
		||||
     *      a `finally` block and should therefore fork the path that leaves
 | 
			
		||||
     *      `finally`.
 | 
			
		||||
     * @returns {ForkContext} New fork context.
 | 
			
		||||
     */
 | 
			
		||||
    static newEmpty(parentContext, shouldForkLeavingPath) {
 | 
			
		||||
        return new ForkContext(
 | 
			
		||||
            parentContext.idGenerator,
 | 
			
		||||
            parentContext,
 | 
			
		||||
            (shouldForkLeavingPath ? 2 : 1) * parentContext.count
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = ForkContext;
 | 
			
		||||
							
								
								
									
										45
									
								
								node_modules/eslint/lib/linter/code-path-analysis/id-generator.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								node_modules/eslint/lib/linter/code-path-analysis/id-generator.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @fileoverview A class of identifiers generator for code path segments.
 | 
			
		||||
 *
 | 
			
		||||
 * Each rule uses the identifier of code path segments to store additional
 | 
			
		||||
 * information of the code path.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Toru Nagashima
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Public Interface
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A generator for unique ids.
 | 
			
		||||
 */
 | 
			
		||||
class IdGenerator {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} prefix Optional. A prefix of generated ids.
 | 
			
		||||
     */
 | 
			
		||||
    constructor(prefix) {
 | 
			
		||||
        this.prefix = String(prefix);
 | 
			
		||||
        this.n = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates id.
 | 
			
		||||
     * @returns {string} A generated id.
 | 
			
		||||
     */
 | 
			
		||||
    next() {
 | 
			
		||||
        this.n = 1 + this.n | 0;
 | 
			
		||||
 | 
			
		||||
        /* c8 ignore start */
 | 
			
		||||
        if (this.n < 0) {
 | 
			
		||||
            this.n = 1;
 | 
			
		||||
        }/* c8 ignore stop */
 | 
			
		||||
 | 
			
		||||
        return this.prefix + this.n;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = IdGenerator;
 | 
			
		||||
		Reference in New Issue
	
	Block a user