Skip to content
Permalink
9bfb9ba527
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
111 lines (96 sloc) 3.91 KB
/**
* @fileoverview Rule to flag unsafe statements in finally block
* @author Onur Temizkan
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const SENTINEL_NODE_TYPE_RETURN_THROW = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression)$/u;
const SENTINEL_NODE_TYPE_BREAK = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement|SwitchStatement)$/u;
const SENTINEL_NODE_TYPE_CONTINUE = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement)$/u;
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
type: "problem",
docs: {
description: "Disallow control flow statements in `finally` blocks",
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-unsafe-finally"
},
schema: [],
messages: {
unsafeUsage: "Unsafe usage of {{nodeType}}."
}
},
create(context) {
/**
* Checks if the node is the finalizer of a TryStatement
* @param {ASTNode} node node to check.
* @returns {boolean} - true if the node is the finalizer of a TryStatement
*/
function isFinallyBlock(node) {
return node.parent.type === "TryStatement" && node.parent.finalizer === node;
}
/**
* Climbs up the tree if the node is not a sentinel node
* @param {ASTNode} node node to check.
* @param {string} label label of the break or continue statement
* @returns {boolean} - return whether the node is a finally block or a sentinel node
*/
function isInFinallyBlock(node, label) {
let labelInside = false;
let sentinelNodeType;
if (node.type === "BreakStatement" && !node.label) {
sentinelNodeType = SENTINEL_NODE_TYPE_BREAK;
} else if (node.type === "ContinueStatement") {
sentinelNodeType = SENTINEL_NODE_TYPE_CONTINUE;
} else {
sentinelNodeType = SENTINEL_NODE_TYPE_RETURN_THROW;
}
for (
let currentNode = node;
currentNode && !sentinelNodeType.test(currentNode.type);
currentNode = currentNode.parent
) {
if (currentNode.parent.label && label && (currentNode.parent.label.name === label.name)) {
labelInside = true;
}
if (isFinallyBlock(currentNode)) {
if (label && labelInside) {
return false;
}
return true;
}
}
return false;
}
/**
* Checks whether the possibly-unsafe statement is inside a finally block.
* @param {ASTNode} node node to check.
* @returns {void}
*/
function check(node) {
if (isInFinallyBlock(node, node.label)) {
context.report({
messageId: "unsafeUsage",
data: {
nodeType: node.type
},
node,
line: node.loc.line,
column: node.loc.column
});
}
}
return {
ReturnStatement: check,
ThrowStatement: check,
BreakStatement: check,
ContinueStatement: check
};
}
};