5
5
6
6
"use strict" ;
7
7
8
+ //------------------------------------------------------------------------------
9
+ // Helpers
10
+ //------------------------------------------------------------------------------
11
+
12
+ var SCOPE_NODE_TYPE = / ^ (?: P r o g r a m | B l o c k S t a t e m e n t | S w i t c h S t a t e m e n t | F o r S t a t e m e n t | F o r I n S t a t e m e n t | F o r O f S t a t e m e n t ) $ / ;
13
+
14
+ /**
15
+ * Gets the scope node which directly contains a given node.
16
+ *
17
+ * @param {ASTNode } node - A node to get. This is a `VariableDeclaration` or
18
+ * an `Identifier`.
19
+ * @returns {ASTNode } A scope node. This is one of `Program`, `BlockStatement`,
20
+ * `SwitchStatement`, `ForStatement`, `ForInStatement`, and
21
+ * `ForOfStatement`.
22
+ */
23
+ function getScopeNode ( node ) {
24
+ while ( node ) {
25
+ if ( SCOPE_NODE_TYPE . test ( node . type ) ) {
26
+ return node ;
27
+ }
28
+
29
+ node = node . parent ;
30
+ }
31
+
32
+ /* istanbul ignore next : unreachable */
33
+ return null ;
34
+ }
35
+
36
+ /**
37
+ * Checks whether a given variable is redeclared or not.
38
+ *
39
+ * @param {escope.Variable } variable - A variable to check.
40
+ * @returns {boolean } `true` if the variable is redeclared.
41
+ */
42
+ function isRedeclared ( variable ) {
43
+ return variable . defs . length >= 2 ;
44
+ }
45
+
46
+ /**
47
+ * Checks whether a given variable is used from outside of the specified scope.
48
+ *
49
+ * @param {ASTNode } scopeNode - A scope node to check.
50
+ * @returns {Function } The predicate function which checks whether a given
51
+ * variable is used from outside of the specified scope.
52
+ */
53
+ function isUsedFromOutsideOf ( scopeNode ) {
54
+
55
+ /**
56
+ * Checks whether a given reference is inside of the specified scope or not.
57
+ *
58
+ * @param {escope.Reference } reference - A reference to check.
59
+ * @returns {boolean } `true` if the reference is inside of the specified
60
+ * scope.
61
+ */
62
+ function isOutsideOfScope ( reference ) {
63
+ var scope = scopeNode . range ;
64
+ var id = reference . identifier . range ;
65
+
66
+ return id [ 0 ] < scope [ 0 ] || id [ 1 ] > scope [ 1 ] ;
67
+ }
68
+
69
+ return function ( variable ) {
70
+ return variable . references . some ( isOutsideOfScope ) ;
71
+ } ;
72
+ }
73
+
8
74
//------------------------------------------------------------------------------
9
75
// Rule Definition
10
76
//------------------------------------------------------------------------------
@@ -17,19 +83,80 @@ module.exports = {
17
83
recommended : false
18
84
} ,
19
85
20
- schema : [ ]
86
+ schema : [ ] ,
87
+ fixable : "code"
21
88
} ,
22
89
23
90
create : function ( context ) {
91
+ var sourceCode = context . getSourceCode ( ) ;
92
+
93
+ /**
94
+ * Checks whether it can fix a given variable declaration or not.
95
+ * It cannot fix if the following cases:
96
+ *
97
+ * - A variable is declared on a SwitchCase node.
98
+ * - A variable is redeclared.
99
+ * - A variable is used from outside the scope.
100
+ *
101
+ * ## A variable is declared on a SwitchCase node.
102
+ *
103
+ * If this rule modifies 'var' declarations on a SwitchCase node, it
104
+ * would generate the warnings of 'no-case-declarations' rule. And the
105
+ * 'eslint:recommended' preset includes 'no-case-declarations' rule, so
106
+ * this rule doesn't modify those declarations.
107
+ *
108
+ * ## A variable is redeclared.
109
+ *
110
+ * The language spec disallows redeclarations of `let` declarations.
111
+ * Those variables would cause syntax errors.
112
+ *
113
+ * ## A variable is used from outside the scope.
114
+ *
115
+ * The language spec disallows accesses from outside of the scope for
116
+ * `let` declarations. Those variables would cause reference errors.
117
+ *
118
+ * @param {ASTNode } node - A variable declaration node to check.
119
+ * @returns {boolean } `true` if it can fix the node.
120
+ */
121
+ function canFix ( node ) {
122
+ var variables = context . getDeclaredVariables ( node ) ;
123
+ var scopeNode = getScopeNode ( node ) ;
124
+
125
+ return ! (
126
+ node . parent . type === "SwitchCase" ||
127
+ variables . some ( isRedeclared ) ||
128
+ variables . some ( isUsedFromOutsideOf ( scopeNode ) )
129
+ ) ;
130
+ }
131
+
132
+ /**
133
+ * Reports a given variable declaration node.
134
+ *
135
+ * @param {ASTNode } node - A variable declaration node to report.
136
+ * @returns {void }
137
+ */
138
+ function report ( node ) {
139
+ var varToken = sourceCode . getFirstToken ( node ) ;
140
+
141
+ context . report ( {
142
+ node : node ,
143
+ message : "Unexpected var, use let or const instead." ,
144
+
145
+ fix : function ( fixer ) {
146
+ if ( canFix ( node ) ) {
147
+ return fixer . replaceText ( varToken , "let" ) ;
148
+ }
149
+ return null ;
150
+ }
151
+ } ) ;
152
+ }
24
153
25
154
return {
26
155
VariableDeclaration : function ( node ) {
27
156
if ( node . kind === "var" ) {
28
- context . report ( node , "Unexpected var, use let or const instead." ) ;
157
+ report ( node ) ;
29
158
}
30
159
}
31
-
32
160
} ;
33
-
34
161
}
35
162
} ;
0 commit comments