• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Rule to check for "block scoped" variables by binding context
3 * @author Matt DuVall <http://www.mattduvall.com>
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Rule Definition
9//------------------------------------------------------------------------------
10
11module.exports = {
12    meta: {
13        type: "suggestion",
14
15        docs: {
16            description: "enforce the use of variables within the scope they are defined",
17            category: "Best Practices",
18            recommended: false,
19            url: "https://eslint.org/docs/rules/block-scoped-var"
20        },
21
22        schema: [],
23
24        messages: {
25            outOfScope: "'{{name}}' used outside of binding context."
26        }
27    },
28
29    create(context) {
30        let stack = [];
31
32        /**
33         * Makes a block scope.
34         * @param {ASTNode} node A node of a scope.
35         * @returns {void}
36         */
37        function enterScope(node) {
38            stack.push(node.range);
39        }
40
41        /**
42         * Pops the last block scope.
43         * @returns {void}
44         */
45        function exitScope() {
46            stack.pop();
47        }
48
49        /**
50         * Reports a given reference.
51         * @param {eslint-scope.Reference} reference A reference to report.
52         * @returns {void}
53         */
54        function report(reference) {
55            const identifier = reference.identifier;
56
57            context.report({ node: identifier, messageId: "outOfScope", data: { name: identifier.name } });
58        }
59
60        /**
61         * Finds and reports references which are outside of valid scopes.
62         * @param {ASTNode} node A node to get variables.
63         * @returns {void}
64         */
65        function checkForVariables(node) {
66            if (node.kind !== "var") {
67                return;
68            }
69
70            // Defines a predicate to check whether or not a given reference is outside of valid scope.
71            const scopeRange = stack[stack.length - 1];
72
73            /**
74             * Check if a reference is out of scope
75             * @param {ASTNode} reference node to examine
76             * @returns {boolean} True is its outside the scope
77             * @private
78             */
79            function isOutsideOfScope(reference) {
80                const idRange = reference.identifier.range;
81
82                return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
83            }
84
85            // Gets declared variables, and checks its references.
86            const variables = context.getDeclaredVariables(node);
87
88            for (let i = 0; i < variables.length; ++i) {
89
90                // Reports.
91                variables[i]
92                    .references
93                    .filter(isOutsideOfScope)
94                    .forEach(report);
95            }
96        }
97
98        return {
99            Program(node) {
100                stack = [node.range];
101            },
102
103            // Manages scopes.
104            BlockStatement: enterScope,
105            "BlockStatement:exit": exitScope,
106            ForStatement: enterScope,
107            "ForStatement:exit": exitScope,
108            ForInStatement: enterScope,
109            "ForInStatement:exit": exitScope,
110            ForOfStatement: enterScope,
111            "ForOfStatement:exit": exitScope,
112            SwitchStatement: enterScope,
113            "SwitchStatement:exit": exitScope,
114            CatchClause: enterScope,
115            "CatchClause:exit": exitScope,
116
117            // Finds and reports references which are outside of valid scope.
118            VariableDeclaration: checkForVariables
119        };
120
121    }
122};
123