UNPKG

3.66 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 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};