eslint 4.10.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -22
- package/conf/default-cli-options.js +5 -3
- package/lib/ast-utils.js +81 -41
- package/lib/cli-engine.js +27 -12
- package/lib/code-path-analysis/code-path-analyzer.js +8 -4
- package/lib/code-path-analysis/code-path-segment.js +4 -2
- package/lib/code-path-analysis/code-path-state.js +4 -2
- package/lib/config/autoconfig.js +14 -12
- package/lib/config/config-file.js +8 -51
- package/lib/config/config-initializer.js +10 -6
- package/lib/config/config-ops.js +21 -21
- package/lib/config/config-rule.js +24 -24
- package/lib/config/config-validator.js +38 -36
- package/lib/config/plugins.js +8 -35
- package/lib/config.js +12 -8
- package/lib/formatters/junit.js +21 -15
- package/lib/formatters/tap.js +5 -3
- package/lib/ignored-paths.js +4 -2
- package/lib/linter.js +16 -10
- package/lib/rules/.eslintrc.yml +2 -2
- package/lib/rules/array-bracket-newline.js +20 -20
- package/lib/rules/array-bracket-spacing.js +28 -28
- package/lib/rules/array-callback-return.js +13 -9
- package/lib/rules/array-element-newline.js +8 -8
- package/lib/rules/arrow-body-style.js +12 -6
- package/lib/rules/arrow-parens.js +4 -2
- package/lib/rules/brace-style.js +14 -14
- package/lib/rules/computed-property-spacing.js +22 -22
- package/lib/rules/consistent-return.js +4 -4
- package/lib/rules/consistent-this.js +4 -2
- package/lib/rules/curly.js +13 -9
- package/lib/rules/dot-notation.js +5 -5
- package/lib/rules/func-call-spacing.js +4 -2
- package/lib/rules/getter-return.js +2 -1
- package/lib/rules/indent-legacy.js +20 -12
- package/lib/rules/indent.js +77 -73
- package/lib/rules/key-spacing.js +5 -3
- package/lib/rules/lines-around-directive.js +16 -12
- package/lib/rules/max-statements-per-line.js +5 -3
- package/lib/rules/newline-after-var.js +8 -6
- package/lib/rules/newline-before-return.js +9 -7
- package/lib/rules/no-await-in-loop.js +17 -9
- package/lib/rules/no-bitwise.js +5 -3
- package/lib/rules/no-catch-shadow.js +4 -2
- package/lib/rules/no-console.js +2 -1
- package/lib/rules/no-else-return.js +17 -11
- package/lib/rules/no-empty-character-class.js +11 -11
- package/lib/rules/no-extra-parens.js +16 -8
- package/lib/rules/no-extra-semi.js +5 -3
- package/lib/rules/no-global-assign.js +4 -2
- package/lib/rules/no-implicit-coercion.js +6 -6
- package/lib/rules/no-implied-eval.js +2 -1
- package/lib/rules/no-label-var.js +4 -2
- package/lib/rules/no-lone-blocks.js +3 -3
- package/lib/rules/no-loop-func.js +8 -4
- package/lib/rules/no-native-reassign.js +4 -2
- package/lib/rules/no-param-reassign.js +4 -2
- package/lib/rules/no-regex-spaces.js +1 -1
- package/lib/rules/no-restricted-properties.js +10 -10
- package/lib/rules/no-return-await.js +6 -6
- package/lib/rules/no-self-assign.js +4 -2
- package/lib/rules/no-sequences.js +6 -4
- package/lib/rules/no-trailing-spaces.js +13 -7
- package/lib/rules/no-unreachable.js +4 -2
- package/lib/rules/no-use-before-define.js +13 -11
- package/lib/rules/no-useless-call.js +1 -25
- package/lib/rules/no-useless-escape.js +23 -22
- package/lib/rules/no-useless-return.js +14 -8
- package/lib/rules/no-whitespace-before-property.js +4 -2
- package/lib/rules/object-curly-newline.js +9 -2
- package/lib/rules/object-curly-spacing.js +20 -20
- package/lib/rules/object-shorthand.js +41 -33
- package/lib/rules/operator-assignment.js +9 -9
- package/lib/rules/operator-linebreak.js +12 -10
- package/lib/rules/padding-line-between-statements.js +4 -2
- package/lib/rules/prefer-arrow-callback.js +12 -10
- package/lib/rules/prefer-const.js +18 -10
- package/lib/rules/prefer-destructuring.js +4 -2
- package/lib/rules/prefer-numeric-literals.js +4 -2
- package/lib/rules/prefer-promise-reject-errors.js +16 -16
- package/lib/rules/prefer-rest-params.js +4 -2
- package/lib/rules/prefer-spread.js +1 -25
- package/lib/rules/prefer-template.js +33 -29
- package/lib/rules/quote-props.js +8 -8
- package/lib/rules/semi-style.js +44 -19
- package/lib/rules/semi.js +5 -3
- package/lib/rules/sort-imports.js +5 -3
- package/lib/rules/space-unary-ops.js +61 -61
- package/lib/rules/strict.js +8 -8
- package/lib/rules/valid-typeof.js +4 -4
- package/lib/rules/wrap-iife.js +4 -4
- package/lib/rules/yoda.js +9 -7
- package/lib/testers/rule-tester.js +43 -34
- package/lib/token-store/backward-token-cursor.js +5 -3
- package/lib/token-store/forward-token-cursor.js +5 -3
- package/lib/token-store/utils.js +8 -4
- package/lib/util/glob.js +1 -1
- package/lib/util/naming.js +112 -0
- package/lib/util/node-event-generator.js +10 -10
- package/lib/util/safe-emitter.js +1 -1
- package/lib/util/source-code-fixer.js +4 -2
- package/lib/util/source-code.js +2 -1
- package/package.json +6 -5
package/lib/rules/indent.js
CHANGED
@@ -200,19 +200,19 @@ class TokenInfo {
|
|
200
200
|
}
|
201
201
|
|
202
202
|
/**
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
203
|
+
* Gets the first token on a given token's line
|
204
|
+
* @param {Token|ASTNode} token a node or token
|
205
|
+
* @returns {Token} The first token on the given line
|
206
|
+
*/
|
207
207
|
getFirstTokenOfLine(token) {
|
208
208
|
return this.firstTokensByLineNumber.get(token.loc.start.line);
|
209
209
|
}
|
210
210
|
|
211
211
|
/**
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
212
|
+
* Determines whether a token is the first token in its line
|
213
|
+
* @param {Token} token The token
|
214
|
+
* @returns {boolean} `true` if the token is the first on its line
|
215
|
+
*/
|
216
216
|
isFirstTokenOfLine(token) {
|
217
217
|
return this.getFirstTokenOfLine(token) === token;
|
218
218
|
}
|
@@ -336,31 +336,31 @@ class OffsetStorage {
|
|
336
336
|
}
|
337
337
|
|
338
338
|
/**
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
339
|
+
* Sets the desired offset of all tokens in a range
|
340
|
+
* It's common for node listeners in this file to need to apply the same offset to a large, contiguous range of tokens.
|
341
|
+
* Moreover, the offset of any given token is usually updated multiple times (roughly once for each node that contains
|
342
|
+
* it). This means that the offset of each token is updated O(AST depth) times.
|
343
|
+
* It would not be performant to store and update the offsets for each token independently, because the rule would end
|
344
|
+
* up having a time complexity of O(number of tokens * AST depth), which is quite slow for large files.
|
345
|
+
*
|
346
|
+
* Instead, the offset tree is represented as a collection of contiguous offset ranges in a file. For example, the following
|
347
|
+
* list could represent the state of the offset tree at a given point:
|
348
|
+
*
|
349
|
+
* * Tokens starting in the interval [0, 15) are aligned with the beginning of the file
|
350
|
+
* * Tokens starting in the interval [15, 30) are offset by 1 indent level from the `bar` token
|
351
|
+
* * Tokens starting in the interval [30, 43) are offset by 1 indent level from the `foo` token
|
352
|
+
* * Tokens starting in the interval [43, 820) are offset by 2 indent levels from the `bar` token
|
353
|
+
* * Tokens starting in the interval [820, ∞) are offset by 1 indent level from the `baz` token
|
354
|
+
*
|
355
|
+
* The `setDesiredOffsets` methods inserts ranges like the ones above. The third line above would be inserted by using:
|
356
|
+
* `setDesiredOffsets([30, 43], fooToken, 1);`
|
357
|
+
*
|
358
|
+
* @param {[number, number]} range A [start, end] pair. All tokens with range[0] <= token.start < range[1] will have the offset applied.
|
359
|
+
* @param {Token} fromToken The token that this is offset from
|
360
|
+
* @param {number} offset The desired indent level
|
361
|
+
* @param {boolean} force `true` if this offset should not use the normal collapsing behavior. This should almost always be false.
|
362
|
+
* @returns {void}
|
363
|
+
*/
|
364
364
|
setDesiredOffsets(range, fromToken, offset, force) {
|
365
365
|
|
366
366
|
/*
|
@@ -408,17 +408,19 @@ class OffsetStorage {
|
|
408
408
|
}
|
409
409
|
|
410
410
|
/**
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
411
|
+
* Gets the desired indent of a token
|
412
|
+
* @param {Token} token The token
|
413
|
+
* @returns {string} The desired indent of the token
|
414
|
+
*/
|
415
415
|
getDesiredIndent(token) {
|
416
416
|
if (!this._desiredIndentCache.has(token)) {
|
417
417
|
|
418
418
|
if (this._ignoredTokens.has(token)) {
|
419
419
|
|
420
|
-
|
421
|
-
|
420
|
+
/*
|
421
|
+
* If the token is ignored, use the actual indent of the token as the desired indent.
|
422
|
+
* This ensures that no errors are reported for this token.
|
423
|
+
*/
|
422
424
|
this._desiredIndentCache.set(
|
423
425
|
token,
|
424
426
|
this._tokenInfo.getTokenIndent(token)
|
@@ -453,10 +455,10 @@ class OffsetStorage {
|
|
453
455
|
}
|
454
456
|
|
455
457
|
/**
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
458
|
+
* Ignores a token, preventing it from being reported.
|
459
|
+
* @param {Token} token The token
|
460
|
+
* @returns {void}
|
461
|
+
*/
|
460
462
|
ignoreToken(token) {
|
461
463
|
if (this._tokenInfo.isFirstTokenOfLine(token)) {
|
462
464
|
this._ignoredTokens.add(token);
|
@@ -681,8 +683,10 @@ module.exports = {
|
|
681
683
|
|
682
684
|
if (actualSpaces > 0) {
|
683
685
|
|
684
|
-
|
685
|
-
|
686
|
+
/*
|
687
|
+
* Abbreviate the message if the expected indentation is also spaces.
|
688
|
+
* e.g. 'Expected 4 spaces but found 2' rather than 'Expected 4 spaces but found 2 spaces'
|
689
|
+
*/
|
686
690
|
foundStatement = indentType === "space" ? actualSpaces : `${actualSpaces} ${foundSpacesWord}`;
|
687
691
|
} else if (actualTabs > 0) {
|
688
692
|
foundStatement = indentType === "tab" ? actualTabs : `${actualTabs} ${foundTabsWord}`;
|
@@ -770,20 +774,20 @@ module.exports = {
|
|
770
774
|
}
|
771
775
|
|
772
776
|
/**
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
777
|
+
* Check indentation for lists of elements (arrays, objects, function params)
|
778
|
+
* @param {ASTNode[]} elements List of elements that should be offset
|
779
|
+
* @param {Token} startToken The start token of the list that element should be aligned against, e.g. '['
|
780
|
+
* @param {Token} endToken The end token of the list, e.g. ']'
|
781
|
+
* @param {number|string} offset The amount that the elements should be offset
|
782
|
+
* @returns {void}
|
783
|
+
*/
|
780
784
|
function addElementListIndent(elements, startToken, endToken, offset) {
|
781
785
|
|
782
786
|
/**
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
+
* Gets the first token of a given element, including surrounding parentheses.
|
788
|
+
* @param {ASTNode} element A node in the `elements` list
|
789
|
+
* @returns {Token} The first token of this element
|
790
|
+
*/
|
787
791
|
function getFirstToken(element) {
|
788
792
|
let token = sourceCode.getTokenBefore(element);
|
789
793
|
|
@@ -872,10 +876,10 @@ module.exports = {
|
|
872
876
|
}
|
873
877
|
|
874
878
|
/**
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
+
* Checks the indentation for nodes that are like function calls (`CallExpression` and `NewExpression`)
|
880
|
+
* @param {ASTNode} node A CallExpression or NewExpression node
|
881
|
+
* @returns {void}
|
882
|
+
*/
|
879
883
|
function addFunctionCallIndent(node) {
|
880
884
|
let openingParen;
|
881
885
|
|
@@ -894,10 +898,10 @@ module.exports = {
|
|
894
898
|
}
|
895
899
|
|
896
900
|
/**
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
+
* Checks the indentation of parenthesized values, given a list of tokens in a program
|
902
|
+
* @param {Token[]} tokens A list of tokens
|
903
|
+
* @returns {void}
|
904
|
+
*/
|
901
905
|
function addParensIndent(tokens) {
|
902
906
|
const parenStack = [];
|
903
907
|
const parenPairs = [];
|
@@ -932,11 +936,11 @@ module.exports = {
|
|
932
936
|
}
|
933
937
|
|
934
938
|
/**
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
939
|
+
* Ignore all tokens within an unknown node whose offset do not depend
|
940
|
+
* on another token's offset within the unknown node
|
941
|
+
* @param {ASTNode} node Unknown Node
|
942
|
+
* @returns {void}
|
943
|
+
*/
|
940
944
|
function ignoreNode(node) {
|
941
945
|
const unknownNodeTokens = new Set(sourceCode.getTokens(node, { includeComments: true }));
|
942
946
|
|
@@ -1023,10 +1027,10 @@ module.exports = {
|
|
1023
1027
|
const operator = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
|
1024
1028
|
|
1025
1029
|
/*
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
+
* For backwards compatibility, don't check BinaryExpression indents, e.g.
|
1031
|
+
* var foo = bar &&
|
1032
|
+
* baz;
|
1033
|
+
*/
|
1030
1034
|
|
1031
1035
|
const tokenAfterOperator = sourceCode.getTokenAfter(operator);
|
1032
1036
|
|
package/lib/rules/key-spacing.js
CHANGED
@@ -329,9 +329,11 @@ module.exports = {
|
|
329
329
|
return true;
|
330
330
|
}
|
331
331
|
|
332
|
-
|
333
|
-
|
334
|
-
|
332
|
+
/*
|
333
|
+
* Check that the first comment is adjacent to the end of the group, the
|
334
|
+
* last comment is adjacent to the candidate property, and that successive
|
335
|
+
* comments are adjacent to each other.
|
336
|
+
*/
|
335
337
|
const leadingComments = sourceCode.getCommentsBefore(candidate);
|
336
338
|
|
337
339
|
if (
|
@@ -67,12 +67,12 @@ module.exports = {
|
|
67
67
|
}
|
68
68
|
|
69
69
|
/**
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
70
|
+
* Gets the last token of a node that is on the same line as the rest of the node.
|
71
|
+
* This will usually be the last token of the node, but it will be the second-to-last token if the node has a trailing
|
72
|
+
* semicolon on a different line.
|
73
|
+
* @param {ASTNode} node A directive node
|
74
|
+
* @returns {Token} The last token of the node on the line
|
75
|
+
*/
|
76
76
|
function getLastTokenOnLine(node) {
|
77
77
|
const lastToken = sourceCode.getLastToken(node);
|
78
78
|
const secondToLastToken = sourceCode.getTokenBefore(lastToken);
|
@@ -136,9 +136,11 @@ module.exports = {
|
|
136
136
|
const firstDirective = directives[0];
|
137
137
|
const leadingComments = sourceCode.getCommentsBefore(firstDirective);
|
138
138
|
|
139
|
-
|
140
|
-
|
141
|
-
|
139
|
+
/*
|
140
|
+
* Only check before the first directive if it is preceded by a comment or if it is at the top of
|
141
|
+
* the file and expectLineBefore is set to "never". This is to not force a newline at the top of
|
142
|
+
* the file if there are no comments as well as for compatibility with padded-blocks.
|
143
|
+
*/
|
142
144
|
if (leadingComments.length) {
|
143
145
|
if (expectLineBefore === "always" && !hasNewlineBefore(firstDirective)) {
|
144
146
|
reportError(firstDirective, "before", true);
|
@@ -159,9 +161,11 @@ module.exports = {
|
|
159
161
|
const lastDirective = directives[directives.length - 1];
|
160
162
|
const statements = node.type === "Program" ? node.body : node.body.body;
|
161
163
|
|
162
|
-
|
163
|
-
|
164
|
-
|
164
|
+
/*
|
165
|
+
* Do not check after the last directive if the body only
|
166
|
+
* contains a directive prologue and isn't followed by a comment to ensure
|
167
|
+
* this rule behaves well with padded-blocks.
|
168
|
+
*/
|
165
169
|
if (lastDirective === statements[statements.length - 1] && !lastDirective.trailingComments) {
|
166
170
|
return;
|
167
171
|
}
|
@@ -93,9 +93,11 @@ module.exports = {
|
|
93
93
|
function enterStatement(node) {
|
94
94
|
const line = node.loc.start.line;
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
/*
|
97
|
+
* Skip to allow non-block statements if this is direct child of control statements.
|
98
|
+
* `if (a) foo();` is counted as 1.
|
99
|
+
* But `if (a) foo(); else foo();` should be counted as 2.
|
100
|
+
*/
|
99
101
|
if (SINGLE_CHILD_ALLOWED.test(node.parent.type) &&
|
100
102
|
node.parent.alternate !== node
|
101
103
|
) {
|
@@ -132,10 +132,10 @@ module.exports = {
|
|
132
132
|
}
|
133
133
|
|
134
134
|
/**
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
* Gets the last line of a group of consecutive comments
|
136
|
+
* @param {number} commentStartLine The starting line of the group
|
137
|
+
* @returns {number} The number of the last comment line of the group
|
138
|
+
*/
|
139
139
|
function getLastCommentLineOfBlock(commentStartLine) {
|
140
140
|
const currentCommentEnd = commentEndLine[commentStartLine];
|
141
141
|
|
@@ -190,8 +190,10 @@ module.exports = {
|
|
190
190
|
return;
|
191
191
|
}
|
192
192
|
|
193
|
-
|
194
|
-
|
193
|
+
/*
|
194
|
+
* Some coding styles use multiple `var` statements, so do nothing if
|
195
|
+
* the next token is a `var` statement.
|
196
|
+
*/
|
195
197
|
if (nextToken.type === "Keyword" && isVar(nextToken.value)) {
|
196
198
|
return;
|
197
199
|
}
|
@@ -166,13 +166,15 @@ module.exports = {
|
|
166
166
|
return true;
|
167
167
|
}
|
168
168
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
169
|
+
/*
|
170
|
+
* if the last leading comment ends in the same line as the previous token and
|
171
|
+
* does not share a line with the `return` node, we can consider it safe to fix.
|
172
|
+
* Example:
|
173
|
+
* function a() {
|
174
|
+
* var b; //comment
|
175
|
+
* return;
|
176
|
+
* }
|
177
|
+
*/
|
176
178
|
if (lastLeadingComment.loc.end.line === tokenBefore.loc.end.line &&
|
177
179
|
lastLeadingComment.loc.end.line !== node.loc.start.line) {
|
178
180
|
return true;
|
@@ -13,8 +13,10 @@ const loopTypes = new Set([
|
|
13
13
|
"DoWhileStatement"
|
14
14
|
]);
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
/*
|
17
|
+
* Node types at which we should stop looking for loops. For example, it is fine to declare an async
|
18
|
+
* function within a loop, and use await inside of that.
|
19
|
+
*/
|
18
20
|
const boundaryTypes = new Set([
|
19
21
|
"FunctionDeclaration",
|
20
22
|
"FunctionExpression",
|
@@ -38,9 +40,11 @@ module.exports = {
|
|
38
40
|
// Reverse so that we can traverse from the deepest node upwards.
|
39
41
|
ancestors.reverse();
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
/*
|
44
|
+
* Create a set of all the ancestors plus this node so that we can check
|
45
|
+
* if this use of await appears in the body of the loop as opposed to
|
46
|
+
* the right-hand side of a for...of, for example.
|
47
|
+
*/
|
44
48
|
const ancestorSet = new Set(ancestors).add(node);
|
45
49
|
|
46
50
|
for (let i = 0; i < ancestors.length; i++) {
|
@@ -48,14 +52,18 @@ module.exports = {
|
|
48
52
|
|
49
53
|
if (boundaryTypes.has(ancestor.type)) {
|
50
54
|
|
51
|
-
|
52
|
-
|
55
|
+
/*
|
56
|
+
* Short-circuit out if we encounter a boundary type. Loops above
|
57
|
+
* this do not matter.
|
58
|
+
*/
|
53
59
|
return;
|
54
60
|
}
|
55
61
|
if (loopTypes.has(ancestor.type)) {
|
56
62
|
|
57
|
-
|
58
|
-
|
63
|
+
/*
|
64
|
+
* Only report if we are actually in the body or another part that gets executed on
|
65
|
+
* every iteration.
|
66
|
+
*/
|
59
67
|
if (
|
60
68
|
ancestorSet.has(ancestor.body) ||
|
61
69
|
ancestorSet.has(ancestor.test) ||
|
package/lib/rules/no-bitwise.js
CHANGED
@@ -51,8 +51,10 @@ module.exports = {
|
|
51
51
|
CatchClause(node) {
|
52
52
|
let scope = context.getScope();
|
53
53
|
|
54
|
-
|
55
|
-
|
54
|
+
/*
|
55
|
+
* When ecmaVersion >= 6, CatchClause creates its own scope
|
56
|
+
* so start from one upper scope to exclude the current node
|
57
|
+
*/
|
56
58
|
if (scope.block === node) {
|
57
59
|
scope = scope.upper;
|
58
60
|
}
|
package/lib/rules/no-console.js
CHANGED
@@ -111,7 +111,8 @@ module.exports = {
|
|
111
111
|
const consoleVar = astUtils.getVariableByName(scope, "console");
|
112
112
|
const shadowed = consoleVar && consoleVar.defs.length > 0;
|
113
113
|
|
114
|
-
/*
|
114
|
+
/*
|
115
|
+
* 'scope.through' includes all references to undefined
|
115
116
|
* variables. If the variable 'console' is not defined, it uses
|
116
117
|
* 'scope.through'.
|
117
118
|
*/
|
@@ -66,10 +66,12 @@ module.exports = {
|
|
66
66
|
firstTokenOfElseBlock = startToken;
|
67
67
|
}
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
/*
|
70
|
+
* If the if block does not have curly braces and does not end in a semicolon
|
71
|
+
* and the else block starts with (, [, /, +, ` or -, then it is not
|
72
|
+
* safe to remove the else keyword, because ASI will not add a semicolon
|
73
|
+
* after the if block
|
74
|
+
*/
|
73
75
|
const ifBlockMaybeUnsafe = node.parent.consequent.type !== "BlockStatement" && lastIfToken.value !== ";";
|
74
76
|
const elseBlockUnsafe = /^[([/+`-]/.test(firstTokenOfElseBlock.value);
|
75
77
|
|
@@ -86,10 +88,12 @@ module.exports = {
|
|
86
88
|
const nextTokenUnsafe = nextToken && /^[([/+`-]/.test(nextToken.value);
|
87
89
|
const nextTokenOnSameLine = nextToken && nextToken.loc.start.line === lastTokenOfElseBlock.loc.start.line;
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
/*
|
92
|
+
* If the else block contents does not end in a semicolon,
|
93
|
+
* and the else block starts with (, [, /, +, ` or -, then it is not
|
94
|
+
* safe to remove the else block, because ASI will not add a semicolon
|
95
|
+
* after the remaining else block contents
|
96
|
+
*/
|
93
97
|
if (nextTokenUnsafe || (nextTokenOnSameLine && nextToken.value !== "}")) {
|
94
98
|
return null;
|
95
99
|
}
|
@@ -101,9 +105,11 @@ module.exports = {
|
|
101
105
|
fixedSource = source;
|
102
106
|
}
|
103
107
|
|
104
|
-
|
105
|
-
|
106
|
-
|
108
|
+
/*
|
109
|
+
* Extend the replacement range to include the entire
|
110
|
+
* function to avoid conflicting with no-useless-return.
|
111
|
+
* https://github.com/eslint/eslint/issues/8026
|
112
|
+
*/
|
107
113
|
return new FixTracker(fixer, sourceCode)
|
108
114
|
.retainEnclosingFunction(node)
|
109
115
|
.replaceTextRange([elseToken.range[0], node.range[1]], fixedSource);
|
@@ -10,17 +10,17 @@
|
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
12
|
/*
|
13
|
-
plain-English description of the following regexp:
|
14
|
-
0. `^` fix the match at the beginning of the string
|
15
|
-
1. `\/`: the `/` that begins the regexp
|
16
|
-
2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
3. `\/` the `/` that ends the regexp
|
21
|
-
4. `[gimuy]*`: optional regexp flags
|
22
|
-
5. `$`: fix the match at the end of the string
|
23
|
-
*/
|
13
|
+
* plain-English description of the following regexp:
|
14
|
+
* 0. `^` fix the match at the beginning of the string
|
15
|
+
* 1. `\/`: the `/` that begins the regexp
|
16
|
+
* 2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
|
17
|
+
* 2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
|
18
|
+
* 2.1. `\\.`: an escape sequence
|
19
|
+
* 2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
|
20
|
+
* 3. `\/` the `/` that ends the regexp
|
21
|
+
* 4. `[gimuy]*`: optional regexp flags
|
22
|
+
* 5. `$`: fix the match at the end of the string
|
23
|
+
*/
|
24
24
|
const regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+])*\/[gimuy]*$/;
|
25
25
|
|
26
26
|
//------------------------------------------------------------------------------
|
@@ -372,8 +372,10 @@ module.exports = {
|
|
372
372
|
hasDoubleExcessParens(callee) ||
|
373
373
|
!isIIFE(node) && !hasNewParensException && !(
|
374
374
|
|
375
|
-
|
376
|
-
|
375
|
+
/*
|
376
|
+
* Allow extra parens around a new expression if
|
377
|
+
* there are intervening parentheses.
|
378
|
+
*/
|
377
379
|
callee.type === "MemberExpression" &&
|
378
380
|
doesMemberExpressionContainCallExpression(callee)
|
379
381
|
)
|
@@ -425,8 +427,10 @@ module.exports = {
|
|
425
427
|
return;
|
426
428
|
}
|
427
429
|
|
428
|
-
|
429
|
-
|
430
|
+
/*
|
431
|
+
* If `node.superClass` is a LeftHandSideExpression, parentheses are extra.
|
432
|
+
* Otherwise, parentheses are needed.
|
433
|
+
*/
|
430
434
|
const hasExtraParens = precedence(node.superClass) > PRECEDENCE_OF_UPDATE_EXPR
|
431
435
|
? hasExcessParens(node.superClass)
|
432
436
|
: hasDoubleExcessParens(node.superClass);
|
@@ -560,12 +564,16 @@ module.exports = {
|
|
560
564
|
if (
|
561
565
|
firstLeftToken.value === "let" && (
|
562
566
|
|
563
|
-
|
564
|
-
|
567
|
+
/*
|
568
|
+
* If `let` is the only thing on the left side of the loop, it's the loop variable: `for ((let) of foo);`
|
569
|
+
* Removing it will cause a syntax error, because it will be parsed as the start of a VariableDeclarator.
|
570
|
+
*/
|
565
571
|
firstLeftToken.range[1] === node.left.range[1] ||
|
566
572
|
|
567
|
-
|
568
|
-
|
573
|
+
/*
|
574
|
+
* If `let` is followed by a `[` token, it's a property access on the `let` value: `for ((let[foo]) of bar);`
|
575
|
+
* Removing it will cause the property access to be parsed as a destructuring declaration of `foo` instead.
|
576
|
+
*/
|
569
577
|
astUtils.isOpeningBracketToken(
|
570
578
|
sourceCode.getTokenAfter(firstLeftToken, astUtils.isNotClosingParenToken)
|
571
579
|
)
|
@@ -42,9 +42,11 @@ module.exports = {
|
|
42
42
|
message: "Unnecessary semicolon.",
|
43
43
|
fix(fixer) {
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
/*
|
46
|
+
* Expand the replacement range to include the surrounding
|
47
|
+
* tokens to avoid conflicting with semi.
|
48
|
+
* https://github.com/eslint/eslint/issues/7928
|
49
|
+
*/
|
48
50
|
return new FixTracker(fixer, context.getSourceCode())
|
49
51
|
.retainSurroundingTokens(nodeOrToken)
|
50
52
|
.remove(nodeOrToken);
|
@@ -49,8 +49,10 @@ module.exports = {
|
|
49
49
|
if (reference.init === false &&
|
50
50
|
reference.isWrite() &&
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
/*
|
53
|
+
* Destructuring assignments can have multiple default value,
|
54
|
+
* so possibly there are multiple writeable references for the same identifier.
|
55
|
+
*/
|
54
56
|
(index === 0 || references[index - 1].identifier !== identifier)
|
55
57
|
) {
|
56
58
|
context.report({
|