• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)$/u;
13const SENTINEL_NODE_TYPE_BREAK = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement|SwitchStatement)$/u;
14const SENTINEL_NODE_TYPE_CONTINUE = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement)$/u;
15
16
17//------------------------------------------------------------------------------
18// Rule Definition
19//------------------------------------------------------------------------------
20
21module.exports = {
22    meta: {
23        type: "problem",
24
25        docs: {
26            description: "disallow control flow statements in `finally` blocks",
27            category: "Possible Errors",
28            recommended: true,
29            url: "https://eslint.org/docs/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};
112