Skip to content

Commit

Permalink
Refactor to account for out-of-order events
Browse files Browse the repository at this point in the history
  • Loading branch information
nzakas committed Sep 1, 2023
1 parent a402c0f commit 40a1475
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 136 deletions.
1 change: 1 addition & 0 deletions lib/linter/linter.js
Expand Up @@ -898,6 +898,7 @@ const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
getTokensBetween: "getTokensBetween"
};


const BASE_TRAVERSAL_CONTEXT = Object.freeze(
Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).reduce(
(contextInfo, methodName) =>
Expand Down
41 changes: 24 additions & 17 deletions lib/rules/array-callback-return.js
Expand Up @@ -18,15 +18,6 @@ const astUtils = require("./utils/ast-utils");
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort|toSorted)$/u;

/**
* Checks a given code path segment is reachable.
* @param {CodePathSegment} segment A segment to check.
* @returns {boolean} `true` if the segment is reachable.
*/
function isReachable(segment) {
return segment.reachable;
}

/**
* Checks a given node is a member access which has the specified name's
* property.
Expand All @@ -38,6 +29,22 @@ function isTargetMethod(node) {
return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
}

/**
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isAnySegmentReachable(segments) {

for (const segment of segments) {
if (segment.reachable) {
return true;
}
}

return false;
}

/**
* Returns a human-legible description of an array method
* @param {string} arrayMethodName A method name to fully qualify
Expand Down Expand Up @@ -205,7 +212,7 @@ module.exports = {
messageId = "expectedNoReturnValue";
}
} else {
if (node.body.type === "BlockStatement" && funcInfo.currentSegments.some(isReachable)) {
if (node.body.type === "BlockStatement" && isAnySegmentReachable(funcInfo.currentSegments)) {
messageId = funcInfo.hasReturn ? "expectedAtEnd" : "expectedInside";
}
}
Expand Down Expand Up @@ -243,7 +250,7 @@ module.exports = {
!node.async &&
!node.generator,
node,
currentSegments: []
currentSegments: new Set()
};
},

Expand All @@ -253,19 +260,19 @@ module.exports = {
},

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);
},

onUnreachableCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},

onCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);
},

onCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},


Expand Down
37 changes: 24 additions & 13 deletions lib/rules/consistent-return.js
Expand Up @@ -16,12 +16,23 @@ const { upperCaseFirst } = require("../shared/string-utils");
//------------------------------------------------------------------------------

/**
* Checks whether or not a given code path segment is unreachable.
* @param {CodePathSegment} segment A CodePathSegment to check.
* @returns {boolean} `true` if the segment is unreachable.
* Checks all segments in a set and returns true if all are unreachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if all segments are unreachable; false otherwise.
*/
function isUnreachable(segment) {
return !segment.reachable;
function areAllSegmentsUnreachable(segments) {

if (segments.size === 0) {
return false;
}

for (const segment of segments) {
if (segment.reachable) {
return false;
}
}

return true;
}

/**
Expand Down Expand Up @@ -88,7 +99,7 @@ module.exports = {
* When unreachable, all paths are returned or thrown.
*/
if (!funcInfo.hasReturnValue ||
funcInfo.currentSegments.every(isUnreachable) ||
areAllSegmentsUnreachable(funcInfo.currentSegments) ||
astUtils.isES5Constructor(node) ||
isClassConstructor(node)
) {
Expand Down Expand Up @@ -142,27 +153,27 @@ module.exports = {
hasReturnValue: false,
messageId: "",
node,
currentSegments: []
currentSegments: new Set()
};
},
onCodePathEnd() {
funcInfo = funcInfo.upper;
},

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);
},

onUnreachableCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},

onCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);
},

onCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},


Expand Down
42 changes: 24 additions & 18 deletions lib/rules/constructor-super.js
Expand Up @@ -10,12 +10,19 @@
//------------------------------------------------------------------------------

/**
* Checks whether a given code path segment is reachable or not.
* @param {CodePathSegment} segment A code path segment to check.
* @returns {boolean} `true` if the segment is reachable.
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isReachable(segment) {
return segment.reachable;
function isAnySegmentReachable(segments) {

for (const segment of segments) {
if (segment.reachable) {
return true;
}
}

return false;
}

/**
Expand Down Expand Up @@ -211,7 +218,7 @@ module.exports = {
hasExtends: Boolean(superClass),
superIsConstructor: isPossibleConstructor(superClass),
codePath,
currentSegments: []
currentSegments: new Set()
};
} else {
funcInfo = {
Expand All @@ -220,7 +227,7 @@ module.exports = {
hasExtends: false,
superIsConstructor: false,
codePath,
currentSegments: []
currentSegments: new Set()
};
}
},
Expand Down Expand Up @@ -264,7 +271,7 @@ module.exports = {
*/
onCodePathSegmentStart(segment) {

funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);

if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
return;
Expand All @@ -287,17 +294,18 @@ module.exports = {
},

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);
},

onUnreachableCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},

onCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},


/**
* Update information of the code path segment when a code path was
* looped.
Expand Down Expand Up @@ -365,8 +373,7 @@ module.exports = {
let duplicate = false;
let info = null;

for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
for (const segment of segments) {

if (segment.reachable) {
info = segInfoMap[segment.id];
Expand All @@ -391,7 +398,7 @@ module.exports = {
info.validNodes.push(node);
}
}
} else if (funcInfo.currentSegments.some(isReachable)) {
} else if (isAnySegmentReachable(funcInfo.currentSegments)) {
context.report({
messageId: "unexpected",
node
Expand All @@ -417,8 +424,7 @@ module.exports = {
// Returning argument is a substitute of 'super()'.
const segments = funcInfo.currentSegments;

for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
for (const segment of segments) {

if (segment.reachable) {
const info = segInfoMap[segment.id];
Expand Down
35 changes: 21 additions & 14 deletions lib/rules/getter-return.js
Expand Up @@ -14,15 +14,23 @@ const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;

/**
* Checks a given code path segment is reachable.
* @param {CodePathSegment} segment A segment to check.
* @returns {boolean} `true` if the segment is reachable.
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isReachable(segment) {
return segment.reachable;
function isAnySegmentReachable(segments) {

for (const segment of segments) {
if (segment.reachable) {
return true;
}
}

return false;
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -86,7 +94,7 @@ module.exports = {
*/
function checkLastSegment(node) {
if (funcInfo.shouldCheck &&
funcInfo.currentSegments.some(isReachable)
isAnySegmentReachable(funcInfo.currentSegments)
) {
context.report({
node,
Expand Down Expand Up @@ -146,29 +154,28 @@ module.exports = {
hasReturn: false,
shouldCheck: isGetter(node),
node,
currentSegments: []
currentSegments: new Set()
};
},

// Pops this function's information.
onCodePathEnd() {
funcInfo = funcInfo.upper;
},

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);
},

onUnreachableCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},

onCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
funcInfo.currentSegments.add(segment);
},

onCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},

// Checks the return statement is valid.
Expand Down

0 comments on commit 40a1475

Please sign in to comment.