Skip to content

Commit

Permalink
Finish up onUnreachable* work
Browse files Browse the repository at this point in the history
  • Loading branch information
nzakas committed Aug 30, 2023
1 parent ecc2dcc commit a402c0f
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 69 deletions.
98 changes: 72 additions & 26 deletions docs/src/extend/code-path-analysis.md
Expand Up @@ -69,7 +69,7 @@ module.exports = function(context) {
* @param {ASTNode} node - The current node.
* @returns {void}
*/
"onCodePathStart": function(codePath, node) {
onCodePathStart(codePath, node) {
// do something with codePath
},

Expand All @@ -81,12 +81,12 @@ module.exports = function(context) {
* @param {ASTNode} node - The current node.
* @returns {void}
*/
"onCodePathEnd": function(codePath, node) {
onCodePathEnd(codePath, node) {
// do something with codePath
},

/**
* This is called when a code path segment was created.
* This is called when a reachable code path segment was created.
* It meant the code path is forked or merged.
* In this time, the segment has the previous segments and has been
* judged reachable or not.
Expand All @@ -95,19 +95,45 @@ module.exports = function(context) {
* @param {ASTNode} node - The current node.
* @returns {void}
*/
"onCodePathSegmentStart": function(segment, node) {
onCodePathSegmentStart(segment, node) {
// do something with segment
},

/**
* This is called when a code path segment was left.
* This is called when a reachable code path segment was left.
* In this time, the segment does not have the next segments yet.
*
* @param {CodePathSegment} segment - The left code path segment.
* @param {ASTNode} node - The current node.
* @returns {void}
*/
"onCodePathSegmentEnd": function(segment, node) {
onCodePathSegmentEnd(segment, node) {
// do something with segment
},

/**
* This is called when an unreachable code path segment was created.
* It meant the code path is forked or merged.
* In this time, the segment has the previous segments and has been
* judged reachable or not.
*
* @param {CodePathSegment} segment - The new code path segment.
* @param {ASTNode} node - The current node.
* @returns {void}
*/
onUnreachableCodePathSegmentStart(segment, node) {
// do something with segment
},

/**
* This is called when an unreachable code path segment was left.
* In this time, the segment does not have the next segments yet.
*
* @param {CodePathSegment} segment - The left code path segment.
* @param {ASTNode} node - The current node.
* @returns {void}
*/
onUnreachableCodePathSegmentEnd(segment, node) {
// do something with segment
},

Expand All @@ -122,7 +148,7 @@ module.exports = function(context) {
* @param {ASTNode} node - The current node.
* @returns {void}
*/
"onCodePathSegmentLoop": function(fromSegment, toSegment, node) {
"onCodePathSegmentLoop(fromSegment, toSegment, node) {
// do something with segment
}
};
Expand Down Expand Up @@ -272,23 +298,28 @@ function isCbCalled(info) {
}
module.exports = function(context) {
var funcInfoStack = [];
var segmentInfoMap = Object.create(null);
let funcInfo;
const funcInfoStack = [];
const segmentInfoMap = Object.create(null);
return {
// Checks `cb`.
"onCodePathStart": function(codePath, node) {
funcInfoStack.push({
onCodePathStart(codePath, node) {
funcInfo = {
codePath: codePath,
hasCb: hasCb(node, context)
});
hasCb: hasCb(node, context),
currentSegments: []
};
funcInfoStack.push(funcInfo);
},
"onCodePathEnd": function(codePath, node) {
funcInfoStack.pop();
onCodePathEnd(codePath, node) {
funcInfo = funcInfoStack.pop();
// Checks `cb` was called in every paths.
var cbCalled = codePath.finalSegments.every(function(segment) {
var info = segmentInfoMap[segment.id];
const cbCalled = codePath.finalSegments.every(function(segment) {
const info = segmentInfoMap[segment.id];
return info.cbCalled;
});
Expand All @@ -300,17 +331,18 @@ module.exports = function(context) {
}
},
// Manages state of code paths.
"onCodePathSegmentStart": function(segment) {
var funcInfo = funcInfoStack[funcInfoStack.length - 1];
// Manages state of code paths and tracks traversed segments
onCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
// Ignores if `cb` doesn't exist.
if (!funcInfo.hasCb) {
return;
}
// Initialize state of this path.
var info = segmentInfoMap[segment.id] = {
const info = segmentInfoMap[segment.id] = {
cbCalled: false
};
Expand All @@ -321,20 +353,34 @@ module.exports = function(context) {
}
},
// Tracks unreachable segment traversal
onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.push(segment);
}
// Tracks reachable segment traversal
onCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
}
// Tracks unreachable segment traversal
onUnreachableCodePathSegmentEnd() {
funcInfo.currentSegments.pop();
}
// Checks reachable or not.
"CallExpression": function(node) {
var funcInfo = funcInfoStack[funcInfoStack.length - 1];
CallExpression(node) {
// Ignores if `cb` doesn't exist.
if (!funcInfo.hasCb) {
return;
}
// Sets marks that `cb` was called.
var callee = node.callee;
const callee = node.callee;
if (callee.type === "Identifier" && callee.name === "cb") {
funcInfo.codePath.currentSegments.forEach(function(segment) {
var info = segmentInfoMap[segment.id];
funcInfo.currentSegments.forEach(segment => {
const info = segmentInfoMap[segment.id];
info.cbCalled = true;
});
}
Expand Down
22 changes: 20 additions & 2 deletions lib/rules/array-callback-return.js
Expand Up @@ -205,7 +205,7 @@ module.exports = {
messageId = "expectedNoReturnValue";
}
} else {
if (node.body.type === "BlockStatement" && funcInfo.codePath.currentSegments.some(isReachable)) {
if (node.body.type === "BlockStatement" && funcInfo.currentSegments.some(isReachable)) {
messageId = funcInfo.hasReturn ? "expectedAtEnd" : "expectedInside";
}
}
Expand Down Expand Up @@ -242,7 +242,8 @@ module.exports = {
methodName &&
!node.async &&
!node.generator,
node
node,
currentSegments: []
};
},

Expand All @@ -251,6 +252,23 @@ module.exports = {
funcInfo = funcInfo.upper;
},

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

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

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

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


// Checks the return statement is valid.
ReturnStatement(node) {

Expand Down
22 changes: 20 additions & 2 deletions lib/rules/consistent-return.js
Expand Up @@ -88,7 +88,7 @@ module.exports = {
* When unreachable, all paths are returned or thrown.
*/
if (!funcInfo.hasReturnValue ||
funcInfo.codePath.currentSegments.every(isUnreachable) ||
funcInfo.currentSegments.every(isUnreachable) ||
astUtils.isES5Constructor(node) ||
isClassConstructor(node)
) {
Expand Down Expand Up @@ -141,13 +141,31 @@ module.exports = {
hasReturn: false,
hasReturnValue: false,
messageId: "",
node
node,
currentSegments: []
};
},
onCodePathEnd() {
funcInfo = funcInfo.upper;
},

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

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

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

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


// Reports a given return statement if it's inconsistent.
ReturnStatement(node) {
const argument = node.argument;
Expand Down
27 changes: 22 additions & 5 deletions lib/rules/constructor-super.js
Expand Up @@ -210,15 +210,17 @@ module.exports = {
isConstructor: true,
hasExtends: Boolean(superClass),
superIsConstructor: isPossibleConstructor(superClass),
codePath
codePath,
currentSegments: []
};
} else {
funcInfo = {
upper: funcInfo,
isConstructor: false,
hasExtends: false,
superIsConstructor: false,
codePath
codePath,
currentSegments: []
};
}
},
Expand Down Expand Up @@ -261,6 +263,9 @@ module.exports = {
* @returns {void}
*/
onCodePathSegmentStart(segment) {

funcInfo.currentSegments.push(segment);

if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
return;
}
Expand All @@ -281,6 +286,18 @@ module.exports = {
}
},

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

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

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

/**
* Update information of the code path segment when a code path was
* looped.
Expand Down Expand Up @@ -344,7 +361,7 @@ module.exports = {

// Reports if needed.
if (funcInfo.hasExtends) {
const segments = funcInfo.codePath.currentSegments;
const segments = funcInfo.currentSegments;
let duplicate = false;
let info = null;

Expand Down Expand Up @@ -374,7 +391,7 @@ module.exports = {
info.validNodes.push(node);
}
}
} else if (funcInfo.codePath.currentSegments.some(isReachable)) {
} else if (funcInfo.currentSegments.some(isReachable)) {
context.report({
messageId: "unexpected",
node
Expand All @@ -398,7 +415,7 @@ module.exports = {
}

// Returning argument is a substitute of 'super()'.
const segments = funcInfo.codePath.currentSegments;
const segments = funcInfo.currentSegments;

for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
Expand Down
24 changes: 21 additions & 3 deletions lib/rules/getter-return.js
Expand Up @@ -71,7 +71,8 @@ module.exports = {
codePath: null,
hasReturn: false,
shouldCheck: false,
node: null
node: null,
currentSegments: []
};

/**
Expand All @@ -85,7 +86,7 @@ module.exports = {
*/
function checkLastSegment(node) {
if (funcInfo.shouldCheck &&
funcInfo.codePath.currentSegments.some(isReachable)
funcInfo.currentSegments.some(isReachable)
) {
context.report({
node,
Expand Down Expand Up @@ -144,7 +145,8 @@ module.exports = {
codePath,
hasReturn: false,
shouldCheck: isGetter(node),
node
node,
currentSegments: []
};
},

Expand All @@ -153,6 +155,22 @@ module.exports = {
funcInfo = funcInfo.upper;
},

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

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

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

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

// Checks the return statement is valid.
ReturnStatement(node) {
if (funcInfo.shouldCheck) {
Expand Down

0 comments on commit a402c0f

Please sign in to comment.