summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/rules/block-scoped-var.js
blob: 481057ba6c7c6bdc7adc1f2be5e2ddf90f2720b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
 * @fileoverview Rule to check for "block scoped" variables by binding context
 * @author Matt DuVall <http://www.mattduvall.com>
 */
"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "enforce the use of variables within the scope they are defined",
            category: "Best Practices",
            recommended: false,
            url: "https://eslint.org/docs/rules/block-scoped-var"
        },

        schema: [],

        messages: {
            outOfScope: "'{{name}}' used outside of binding context."
        }
    },

    create(context) {
        let stack = [];

        /**
         * Makes a block scope.
         * @param {ASTNode} node A node of a scope.
         * @returns {void}
         */
        function enterScope(node) {
            stack.push(node.range);
        }

        /**
         * Pops the last block scope.
         * @returns {void}
         */
        function exitScope() {
            stack.pop();
        }

        /**
         * Reports a given reference.
         * @param {eslint-scope.Reference} reference A reference to report.
         * @returns {void}
         */
        function report(reference) {
            const identifier = reference.identifier;

            context.report({ node: identifier, messageId: "outOfScope", data: { name: identifier.name } });
        }

        /**
         * Finds and reports references which are outside of valid scopes.
         * @param {ASTNode} node A node to get variables.
         * @returns {void}
         */
        function checkForVariables(node) {
            if (node.kind !== "var") {
                return;
            }

            // Defines a predicate to check whether or not a given reference is outside of valid scope.
            const scopeRange = stack[stack.length - 1];

            /**
             * Check if a reference is out of scope
             * @param {ASTNode} reference node to examine
             * @returns {boolean} True is its outside the scope
             * @private
             */
            function isOutsideOfScope(reference) {
                const idRange = reference.identifier.range;

                return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
            }

            // Gets declared variables, and checks its references.
            const variables = context.getDeclaredVariables(node);

            for (let i = 0; i < variables.length; ++i) {

                // Reports.
                variables[i]
                    .references
                    .filter(isOutsideOfScope)
                    .forEach(report);
            }
        }

        return {
            Program(node) {
                stack = [node.range];
            },

            // Manages scopes.
            BlockStatement: enterScope,
            "BlockStatement:exit": exitScope,
            ForStatement: enterScope,
            "ForStatement:exit": exitScope,
            ForInStatement: enterScope,
            "ForInStatement:exit": exitScope,
            ForOfStatement: enterScope,
            "ForOfStatement:exit": exitScope,
            SwitchStatement: enterScope,
            "SwitchStatement:exit": exitScope,
            CatchClause: enterScope,
            "CatchClause:exit": exitScope,

            // Finds and reports references which are outside of valid scope.
            VariableDeclaration: checkForVariables
        };

    }
};