UNPKG

3.93 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag unsafe statements in finally block
3 * @author Onur Temizkan
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Helpers
10//------------------------------------------------------------------------------
11
12const SENTINEL_NODE_TYPE_RETURN_THROW = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression)$/;
13const SENTINEL_NODE_TYPE_BREAK = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement|SwitchStatement)$/;
14const SENTINEL_NODE_TYPE_CONTINUE = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement)$/;
15
16
17//------------------------------------------------------------------------------
18// Rule Definition
19//------------------------------------------------------------------------------
20
21module.exports = {
22 meta: {
23 docs: {
24 description: "disallow control flow statements in `finally` blocks",
25 category: "Possible Errors",
26 recommended: true,
27 url: "https://eslint.org/docs/rules/no-unsafe-finally"
28 },
29
30 schema: []
31 },
32 create(context) {
33
34 /**
35 * Checks if the node is the finalizer of a TryStatement
36 *
37 * @param {ASTNode} node - node to check.
38 * @returns {boolean} - true if the node is the finalizer of a TryStatement
39 */
40 function isFinallyBlock(node) {
41 return node.parent.type === "TryStatement" && node.parent.finalizer === node;
42 }
43
44 /**
45 * Climbs up the tree if the node is not a sentinel node
46 *
47 * @param {ASTNode} node - node to check.
48 * @param {string} label - label of the break or continue statement
49 * @returns {boolean} - return whether the node is a finally block or a sentinel node
50 */
51 function isInFinallyBlock(node, label) {
52 let labelInside = false;
53 let sentinelNodeType;
54
55 if (node.type === "BreakStatement" && !node.label) {
56 sentinelNodeType = SENTINEL_NODE_TYPE_BREAK;
57 } else if (node.type === "ContinueStatement") {
58 sentinelNodeType = SENTINEL_NODE_TYPE_CONTINUE;
59 } else {
60 sentinelNodeType = SENTINEL_NODE_TYPE_RETURN_THROW;
61 }
62
63 for (
64 let currentNode = node;
65 currentNode && !sentinelNodeType.test(currentNode.type);
66 currentNode = currentNode.parent
67 ) {
68 if (currentNode.parent.label && label && (currentNode.parent.label.name === label.name)) {
69 labelInside = true;
70 }
71 if (isFinallyBlock(currentNode)) {
72 if (label && labelInside) {
73 return false;
74 }
75 return true;
76 }
77 }
78 return false;
79 }
80
81 /**
82 * Checks whether the possibly-unsafe statement is inside a finally block.
83 *
84 * @param {ASTNode} node - node to check.
85 * @returns {void}
86 */
87 function check(node) {
88 if (isInFinallyBlock(node, node.label)) {
89 context.report({
90 message: "Unsafe usage of {{nodeType}}.",
91 data: {
92 nodeType: node.type
93 },
94 node,
95 line: node.loc.line,
96 column: node.loc.column
97 });
98 }
99 }
100
101 return {
102 ReturnStatement: check,
103 ThrowStatement: check,
104 BreakStatement: check,
105 ContinueStatement: check
106 };
107 }
108};