UNPKG

3.64 kBJavaScriptView Raw
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 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};