eslint-plugin-mocha
Version:
Eslint rules for mocha.
136 lines • 6.21 kB
JavaScript
import { asRuleNode } from "./ast/rule-node.js";
import { applyBindingSourceValue, applyContainerPropertyAssignment as applyTrackedContainerPropertyAssignment, clonePendingPathState, createHandledPendingPathState, createInitialPendingPathState, getTrackedContainerPropertiesFromExpression, haveSamePendingPathStates, isTrackedCallbackExpression, mergeIncomingPendingPathStates } from "./tracked-callback-reference-state.js";
function isHandledHandoffExpression(sourceCode, node, state) {
if (isTrackedCallbackExpression(sourceCode, node, state)) {
return true;
}
return (getTrackedContainerPropertiesFromExpression(sourceCode, node, state)?.size ?? 0) > 0;
}
function callFinishesPendingPaths(sourceCode, node, state) {
if (isTrackedCallbackExpression(sourceCode, asRuleNode(node.callee), state)) {
return true;
}
return node.arguments.some(function (argument) {
const candidate = argument.type === 'SpreadElement' ? argument.argument : argument;
return isHandledHandoffExpression(sourceCode, asRuleNode(candidate), state);
});
}
function applyBindingAssignment(sourceCode, state, operation) {
const nextState = {
...state,
aliasBindings: new Set(Array.from(state.aliasBindings).filter(function (binding) {
return binding !== operation.target;
})),
containerPropertiesByBinding: new Map(Array.from(state.containerPropertiesByBinding).filter(function ([binding]) {
return binding !== operation.target;
}))
};
return applyBindingSourceValue(sourceCode, nextState, operation.source, operation.target);
}
function applyOperation(sourceCode, state, operation) {
if (operation.type === 'callbackHandoff') {
return createHandledPendingPathState();
}
if (operation.type === 'bindingAssignment') {
return applyBindingAssignment(sourceCode, state, operation);
}
if (operation.type === 'call') {
return callFinishesPendingPaths(sourceCode, operation.node, state)
? createHandledPendingPathState()
: clonePendingPathState(state);
}
return applyTrackedContainerPropertyAssignment(sourceCode, state, operation);
}
function runOperations(sourceCode, entryState, operations) {
let nextState = clonePendingPathState(entryState);
for (const operation of operations) {
nextState = applyOperation(sourceCode, nextState, operation);
}
return nextState;
}
function computeEntryState(callbackBinding, initialSegmentId, segment, exitStatesBySegmentId) {
if (segment.id === initialSegmentId) {
return createInitialPendingPathState(callbackBinding);
}
return mergeIncomingPendingPathStates(segment.prevSegments.map(function (previousSegment) {
return exitStatesBySegmentId.get(previousSegment.id) ?? createHandledPendingPathState();
}));
}
export function enqueueNextSegments(nextSegments, pendingSegments, queuedSegmentIds) {
let nextPendingSegments = pendingSegments;
let nextQueuedSegmentIds = queuedSegmentIds;
nextSegments.forEach(function enqueueNextSegment(nextSegment) {
if (!nextQueuedSegmentIds.has(nextSegment.id)) {
nextPendingSegments = [...nextPendingSegments, nextSegment];
nextQueuedSegmentIds = new Set([...nextQueuedSegmentIds, nextSegment.id]);
}
});
return [nextPendingSegments, nextQueuedSegmentIds];
}
function segmentHasUnhandledReturnPath(exitStatesBySegmentId, segment) {
return (exitStatesBySegmentId.get(segment.id) ?? createHandledPendingPathState()).hasUnhandledPath;
}
function processPendingSegment(context, segment, exitStatesBySegmentId) {
const entryState = computeEntryState(context.callbackBinding, context.codePath.initialSegment.id, segment, exitStatesBySegmentId);
return runOperations(context.sourceCode, entryState, context.operationsBySegmentId.get(segment.id) ?? []);
}
function hasUnprocessedSegments(pendingSegments) {
return pendingSegments.length > 0;
}
function createPendingSegmentQueue(context) {
return {
exitStatesBySegmentId: new Map(),
pendingSegments: [context.codePath.initialSegment],
queuedSegmentIds: new Set([context.codePath.initialSegment.id])
};
}
function removeQueuedSegmentId(queuedSegmentIds, segmentId) {
return new Set(Array.from(queuedSegmentIds).filter(function (queuedSegmentId) {
return queuedSegmentId !== segmentId;
}));
}
function dropPendingSegment(queue) {
const [segment, ...pendingSegments] = queue.pendingSegments;
return [
segment,
{
...queue,
pendingSegments,
queuedSegmentIds: removeQueuedSegmentId(queue.queuedSegmentIds, segment?.id)
}
];
}
function processKnownPendingSegment(context, queue, segment) {
const nextState = processPendingSegment(context, segment, queue.exitStatesBySegmentId);
const previousState = queue.exitStatesBySegmentId.get(segment.id);
if (previousState !== undefined && haveSamePendingPathStates(previousState, nextState)) {
return queue;
}
const [pendingSegments, queuedSegmentIds] = enqueueNextSegments(segment.nextSegments, queue.pendingSegments, queue.queuedSegmentIds);
return {
exitStatesBySegmentId: new Map([
...queue.exitStatesBySegmentId,
[segment.id, nextState]
]),
pendingSegments,
queuedSegmentIds
};
}
function processAllPendingSegments(context, initialQueue) {
let queue = initialQueue;
while (hasUnprocessedSegments(queue.pendingSegments)) {
const [segment, queueWithoutSegment] = dropPendingSegment(queue);
queue = queueWithoutSegment;
if (segment !== undefined) {
queue = processKnownPendingSegment(context, queue, segment);
}
}
return queue;
}
export function hasUnhandledReturnPath(context) {
const queue = processAllPendingSegments(context, createPendingSegmentQueue(context));
return context.codePath.returnedSegments.some(function (segment) {
return segmentHasUnhandledReturnPath(queue.exitStatesBySegmentId, segment);
});
}
//# sourceMappingURL=done-callback-paths.js.map