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