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