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