update socials section
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