UNPKG

3.81 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 while (node && !sentinelNodeType.test(node.type)) {
64 if (node.parent.label && label && (node.parent.label.name === label.name)) {
65 labelInside = true;
66 }
67 if (isFinallyBlock(node)) {
68 if (label && labelInside) {
69 return false;
70 }
71 return true;
72 }
73 node = node.parent;
74 }
75 return false;
76 }
77
78 /**
79 * Checks whether the possibly-unsafe statement is inside a finally block.
80 *
81 * @param {ASTNode} node - node to check.
82 * @returns {void}
83 */
84 function check(node) {
85 if (isInFinallyBlock(node, node.label)) {
86 context.report({
87 message: "Unsafe usage of {{nodeType}}.",
88 data: {
89 nodeType: node.type
90 },
91 node,
92 line: node.loc.line,
93 column: node.loc.column
94 });
95 }
96 }
97
98 return {
99 ReturnStatement: check,
100 ThrowStatement: check,
101 BreakStatement: check,
102 ContinueStatement: check
103 };
104 }
105};