UNPKG

3.52 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 },
18
19 schema: []
20 },
21
22 create(context) {
23 let stack = [];
24
25 /**
26 * Makes a block scope.
27 * @param {ASTNode} node - A node of a scope.
28 * @returns {void}
29 */
30 function enterScope(node) {
31 stack.push(node.range);
32 }
33
34 /**
35 * Pops the last block scope.
36 * @returns {void}
37 */
38 function exitScope() {
39 stack.pop();
40 }
41
42 /**
43 * Reports a given reference.
44 * @param {escope.Reference} reference - A reference to report.
45 * @returns {void}
46 */
47 function report(reference) {
48 const identifier = reference.identifier;
49
50 context.report(
51 identifier,
52 "'{{name}}' used outside of binding context.",
53 {name: identifier.name});
54 }
55
56 /**
57 * Finds and reports references which are outside of valid scopes.
58 * @param {ASTNode} node - A node to get variables.
59 * @returns {void}
60 */
61 function checkForVariables(node) {
62 if (node.kind !== "var") {
63 return;
64 }
65
66 // Defines a predicate to check whether or not a given reference is outside of valid scope.
67 const scopeRange = stack[stack.length - 1];
68
69 /**
70 * Check if a reference is out of scope
71 * @param {ASTNode} reference node to examine
72 * @returns {boolean} True is its outside the scope
73 * @private
74 */
75 function isOutsideOfScope(reference) {
76 const idRange = reference.identifier.range;
77
78 return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
79 }
80
81 // Gets declared variables, and checks its references.
82 const variables = context.getDeclaredVariables(node);
83
84 for (let i = 0; i < variables.length; ++i) {
85
86 // Reports.
87 variables[i]
88 .references
89 .filter(isOutsideOfScope)
90 .forEach(report);
91 }
92 }
93
94 return {
95 Program(node) {
96 stack = [node.range];
97 },
98
99 // Manages scopes.
100 BlockStatement: enterScope,
101 "BlockStatement:exit": exitScope,
102 ForStatement: enterScope,
103 "ForStatement:exit": exitScope,
104 ForInStatement: enterScope,
105 "ForInStatement:exit": exitScope,
106 ForOfStatement: enterScope,
107 "ForOfStatement:exit": exitScope,
108 SwitchStatement: enterScope,
109 "SwitchStatement:exit": exitScope,
110 CatchClause: enterScope,
111 "CatchClause:exit": exitScope,
112
113 // Finds and reports references which are outside of valid scope.
114 VariableDeclaration: checkForVariables
115 };
116
117 }
118};