eslint 6.2.2 → 6.5.1

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +9 -8
  3. package/bin/eslint.js +38 -12
  4. package/conf/config-schema.js +1 -0
  5. package/conf/default-cli-options.js +1 -1
  6. package/lib/cli-engine/cli-engine.js +2 -4
  7. package/lib/cli-engine/config-array/config-array.js +6 -0
  8. package/lib/cli-engine/config-array/extracted-config.js +6 -0
  9. package/lib/cli-engine/config-array/override-tester.js +2 -2
  10. package/lib/cli-engine/config-array-factory.js +2 -0
  11. package/lib/cli-engine/formatters/stylish.js +2 -1
  12. package/lib/cli-engine/ignored-paths.js +3 -3
  13. package/lib/cli-engine/lint-result-cache.js +0 -1
  14. package/lib/cli.js +13 -12
  15. package/lib/init/config-initializer.js +29 -0
  16. package/lib/init/config-rule.js +2 -2
  17. package/lib/init/npm-utils.js +9 -9
  18. package/lib/linter/apply-disable-directives.js +17 -9
  19. package/lib/linter/code-path-analysis/debug-helpers.js +1 -1
  20. package/lib/linter/linter.js +23 -4
  21. package/lib/options.js +7 -1
  22. package/lib/rule-tester/rule-tester.js +1 -2
  23. package/lib/rules/accessor-pairs.js +51 -11
  24. package/lib/rules/capitalized-comments.js +2 -2
  25. package/lib/rules/computed-property-spacing.js +18 -1
  26. package/lib/rules/default-param-last.js +61 -0
  27. package/lib/rules/eqeqeq.js +7 -19
  28. package/lib/rules/func-name-matching.js +1 -0
  29. package/lib/rules/function-paren-newline.js +2 -2
  30. package/lib/rules/indent-legacy.js +1 -1
  31. package/lib/rules/indent.js +44 -6
  32. package/lib/rules/index.js +3 -0
  33. package/lib/rules/new-parens.js +5 -1
  34. package/lib/rules/no-extra-bind.js +7 -1
  35. package/lib/rules/no-extra-boolean-cast.js +12 -2
  36. package/lib/rules/no-extra-label.js +9 -1
  37. package/lib/rules/no-extra-parens.js +23 -3
  38. package/lib/rules/no-import-assign.js +238 -0
  39. package/lib/rules/no-lone-blocks.js +6 -1
  40. package/lib/rules/no-obj-calls.js +29 -9
  41. package/lib/rules/no-octal-escape.js +14 -8
  42. package/lib/rules/no-regex-spaces.js +106 -45
  43. package/lib/rules/no-self-assign.js +17 -6
  44. package/lib/rules/no-sequences.js +2 -2
  45. package/lib/rules/no-undef-init.js +7 -1
  46. package/lib/rules/no-unsafe-negation.js +2 -10
  47. package/lib/rules/no-useless-rename.js +25 -13
  48. package/lib/rules/no-useless-return.js +3 -2
  49. package/lib/rules/object-curly-spacing.js +1 -1
  50. package/lib/rules/object-shorthand.js +35 -9
  51. package/lib/rules/prefer-named-capture-group.js +3 -15
  52. package/lib/rules/prefer-numeric-literals.js +4 -0
  53. package/lib/rules/prefer-regex-literals.js +125 -0
  54. package/lib/rules/quotes.js +6 -0
  55. package/lib/rules/space-before-function-paren.js +12 -1
  56. package/lib/rules/space-in-parens.js +77 -71
  57. package/lib/rules/use-isnan.js +67 -6
  58. package/lib/rules/yoda.js +11 -2
  59. package/lib/shared/logging.js +2 -0
  60. package/lib/shared/runtime-info.js +163 -0
  61. package/lib/shared/types.js +2 -0
  62. package/lib/source-code/source-code.js +3 -4
  63. package/package.json +3 -1
@@ -116,7 +116,15 @@ module.exports = {
116
116
  node: labelNode,
117
117
  messageId: "unexpected",
118
118
  data: labelNode,
119
- fix: fixer => fixer.removeRange([sourceCode.getFirstToken(node).range[1], labelNode.range[1]])
119
+ fix(fixer) {
120
+ const breakOrContinueToken = sourceCode.getFirstToken(node);
121
+
122
+ if (sourceCode.commentsExistBetween(breakOrContinueToken, labelNode)) {
123
+ return null;
124
+ }
125
+
126
+ return fixer.removeRange([breakOrContinueToken.range[1], labelNode.range[1]]);
127
+ }
120
128
  });
121
129
  }
122
130
  return;
@@ -49,7 +49,8 @@ module.exports = {
49
49
  nestedBinaryExpressions: { type: "boolean" },
50
50
  returnAssign: { type: "boolean" },
51
51
  ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] },
52
- enforceForArrowConditionals: { type: "boolean" }
52
+ enforceForArrowConditionals: { type: "boolean" },
53
+ enforceForSequenceExpressions: { type: "boolean" }
53
54
  },
54
55
  additionalProperties: false
55
56
  }
@@ -77,6 +78,8 @@ module.exports = {
77
78
  const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
78
79
  const IGNORE_ARROW_CONDITIONALS = ALL_NODES && context.options[1] &&
79
80
  context.options[1].enforceForArrowConditionals === false;
81
+ const IGNORE_SEQUENCE_EXPRESSIONS = ALL_NODES && context.options[1] &&
82
+ context.options[1].enforceForSequenceExpressions === false;
80
83
 
81
84
  const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
82
85
  const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
@@ -115,6 +118,10 @@ module.exports = {
115
118
  }
116
119
  }
117
120
 
121
+ if (node.type === "SequenceExpression" && IGNORE_SEQUENCE_EXPRESSIONS) {
122
+ return false;
123
+ }
124
+
118
125
  return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
119
126
  }
120
127
 
@@ -198,7 +205,14 @@ module.exports = {
198
205
  const lastToken = sourceCode.getLastToken(newExpression);
199
206
  const penultimateToken = sourceCode.getTokenBefore(lastToken);
200
207
 
201
- return newExpression.arguments.length > 0 || astUtils.isOpeningParenToken(penultimateToken) && astUtils.isClosingParenToken(lastToken);
208
+ return newExpression.arguments.length > 0 ||
209
+ (
210
+
211
+ // The expression should end with its own parens, e.g., new new foo() is not a new expression with parens
212
+ astUtils.isOpeningParenToken(penultimateToken) &&
213
+ astUtils.isClosingParenToken(lastToken) &&
214
+ newExpression.callee.range[1] < newExpression.range[1]
215
+ );
202
216
  }
203
217
 
204
218
  /**
@@ -331,7 +345,7 @@ module.exports = {
331
345
  function finishReport() {
332
346
  context.report({
333
347
  node,
334
- loc: leftParenToken.loc.start,
348
+ loc: leftParenToken.loc,
335
349
  messageId: "unexpected",
336
350
  fix(fixer) {
337
351
  const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
@@ -880,6 +894,12 @@ module.exports = {
880
894
  report(node.object);
881
895
  }
882
896
 
897
+ if (nodeObjHasExcessParens &&
898
+ node.object.type === "NewExpression" &&
899
+ isNewExpressionWithParens(node.object)) {
900
+ report(node.object);
901
+ }
902
+
883
903
  if (node.computed && hasExcessParens(node.property)) {
884
904
  report(node.property);
885
905
  }
@@ -0,0 +1,238 @@
1
+ /**
2
+ * @fileoverview Rule to flag updates of imported bindings.
3
+ * @author Toru Nagashima <https://github.com/mysticatea>
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Helpers
10
+ //------------------------------------------------------------------------------
11
+
12
+ const { findVariable, getPropertyName } = require("eslint-utils");
13
+
14
+ const MutationMethods = {
15
+ Object: new Set([
16
+ "assign", "defineProperties", "defineProperty", "freeze",
17
+ "setPrototypeOf"
18
+ ]),
19
+ Reflect: new Set([
20
+ "defineProperty", "deleteProperty", "set", "setPrototypeOf"
21
+ ])
22
+ };
23
+
24
+ /**
25
+ * Check if a given node is LHS of an assignment node.
26
+ * @param {ASTNode} node The node to check.
27
+ * @returns {boolean} `true` if the node is LHS.
28
+ */
29
+ function isAssignmentLeft(node) {
30
+ const { parent } = node;
31
+
32
+ return (
33
+ (
34
+ parent.type === "AssignmentExpression" &&
35
+ parent.left === node
36
+ ) ||
37
+
38
+ // Destructuring assignments
39
+ parent.type === "ArrayPattern" ||
40
+ (
41
+ parent.type === "Property" &&
42
+ parent.value === node &&
43
+ parent.parent.type === "ObjectPattern"
44
+ ) ||
45
+ parent.type === "RestElement" ||
46
+ (
47
+ parent.type === "AssignmentPattern" &&
48
+ parent.left === node
49
+ )
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Check if a given node is the operand of mutation unary operator.
55
+ * @param {ASTNode} node The node to check.
56
+ * @returns {boolean} `true` if the node is the operand of mutation unary operator.
57
+ */
58
+ function isOperandOfMutationUnaryOperator(node) {
59
+ const { parent } = node;
60
+
61
+ return (
62
+ (
63
+ parent.type === "UpdateExpression" &&
64
+ parent.argument === node
65
+ ) ||
66
+ (
67
+ parent.type === "UnaryExpression" &&
68
+ parent.operator === "delete" &&
69
+ parent.argument === node
70
+ )
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Check if a given node is the iteration variable of `for-in`/`for-of` syntax.
76
+ * @param {ASTNode} node The node to check.
77
+ * @returns {boolean} `true` if the node is the iteration variable.
78
+ */
79
+ function isIterationVariable(node) {
80
+ const { parent } = node;
81
+
82
+ return (
83
+ (
84
+ parent.type === "ForInStatement" &&
85
+ parent.left === node
86
+ ) ||
87
+ (
88
+ parent.type === "ForOfStatement" &&
89
+ parent.left === node
90
+ )
91
+ );
92
+ }
93
+
94
+ /**
95
+ * Check if a given node is the iteration variable of `for-in`/`for-of` syntax.
96
+ * @param {ASTNode} node The node to check.
97
+ * @param {Scope} scope A `escope.Scope` object to find variable (whichever).
98
+ * @returns {boolean} `true` if the node is the iteration variable.
99
+ */
100
+ function isArgumentOfWellKnownMutationFunction(node, scope) {
101
+ const { parent } = node;
102
+
103
+ if (
104
+ parent.type === "CallExpression" &&
105
+ parent.arguments[0] === node &&
106
+ parent.callee.type === "MemberExpression" &&
107
+ parent.callee.object.type === "Identifier"
108
+ ) {
109
+ const { callee } = parent;
110
+ const { object } = callee;
111
+
112
+ if (Object.keys(MutationMethods).includes(object.name)) {
113
+ const variable = findVariable(scope, object);
114
+
115
+ return (
116
+ variable !== null &&
117
+ variable.scope.type === "global" &&
118
+ MutationMethods[object.name].has(getPropertyName(callee, scope))
119
+ );
120
+ }
121
+ }
122
+
123
+ return false;
124
+ }
125
+
126
+ /**
127
+ * Check if the identifier node is placed at to update members.
128
+ * @param {ASTNode} id The Identifier node to check.
129
+ * @param {Scope} scope A `escope.Scope` object to find variable (whichever).
130
+ * @returns {boolean} `true` if the member of `id` was updated.
131
+ */
132
+ function isMemberWrite(id, scope) {
133
+ const { parent } = id;
134
+
135
+ return (
136
+ (
137
+ parent.type === "MemberExpression" &&
138
+ parent.object === id &&
139
+ (
140
+ isAssignmentLeft(parent) ||
141
+ isOperandOfMutationUnaryOperator(parent) ||
142
+ isIterationVariable(parent)
143
+ )
144
+ ) ||
145
+ isArgumentOfWellKnownMutationFunction(id, scope)
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Get the mutation node.
151
+ * @param {ASTNode} id The Identifier node to get.
152
+ * @returns {ASTNode} The mutation node.
153
+ */
154
+ function getWriteNode(id) {
155
+ let node = id.parent;
156
+
157
+ while (
158
+ node &&
159
+ node.type !== "AssignmentExpression" &&
160
+ node.type !== "UpdateExpression" &&
161
+ node.type !== "UnaryExpression" &&
162
+ node.type !== "CallExpression" &&
163
+ node.type !== "ForInStatement" &&
164
+ node.type !== "ForOfStatement"
165
+ ) {
166
+ node = node.parent;
167
+ }
168
+
169
+ return node || id;
170
+ }
171
+
172
+ //------------------------------------------------------------------------------
173
+ // Rule Definition
174
+ //------------------------------------------------------------------------------
175
+
176
+ module.exports = {
177
+ meta: {
178
+ type: "problem",
179
+
180
+ docs: {
181
+ description: "disallow assigning to imported bindings",
182
+ category: "Possible Errors",
183
+ recommended: false,
184
+ url: "https://eslint.org/docs/rules/no-import-assign"
185
+ },
186
+
187
+ schema: [],
188
+
189
+ messages: {
190
+ readonly: "'{{name}}' is read-only.",
191
+ readonlyMember: "The members of '{{name}}' are read-only."
192
+ }
193
+ },
194
+
195
+ create(context) {
196
+ return {
197
+ ImportDeclaration(node) {
198
+ const scope = context.getScope();
199
+
200
+ for (const variable of context.getDeclaredVariables(node)) {
201
+ const shouldCheckMembers = variable.defs.some(
202
+ d => d.node.type === "ImportNamespaceSpecifier"
203
+ );
204
+ let prevIdNode = null;
205
+
206
+ for (const reference of variable.references) {
207
+ const idNode = reference.identifier;
208
+
209
+ /*
210
+ * AssignmentPattern (e.g. `[a = 0] = b`) makes two write
211
+ * references for the same identifier. This should skip
212
+ * the one of the two in order to prevent redundant reports.
213
+ */
214
+ if (idNode === prevIdNode) {
215
+ continue;
216
+ }
217
+ prevIdNode = idNode;
218
+
219
+ if (reference.isWrite()) {
220
+ context.report({
221
+ node: getWriteNode(idNode),
222
+ messageId: "readonly",
223
+ data: { name: idNode.name }
224
+ });
225
+ } else if (shouldCheckMembers && isMemberWrite(idNode, scope)) {
226
+ context.report({
227
+ node: getWriteNode(idNode),
228
+ messageId: "readonlyMember",
229
+ data: { name: idNode.name }
230
+ });
231
+ }
232
+ }
233
+ }
234
+ }
235
+ };
236
+
237
+ }
238
+ };
@@ -79,7 +79,7 @@ module.exports = {
79
79
  }
80
80
  };
81
81
 
82
- // ES6: report blocks without block-level bindings
82
+ // ES6: report blocks without block-level bindings, or that's only child of another block
83
83
  if (context.parserOptions.ecmaVersion >= 6) {
84
84
  ruleDef = {
85
85
  BlockStatement(node) {
@@ -91,6 +91,11 @@ module.exports = {
91
91
  if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) {
92
92
  loneBlocks.pop();
93
93
  report(node);
94
+ } else if (
95
+ node.parent.type === "BlockStatement" &&
96
+ node.parent.body.length === 1
97
+ ) {
98
+ report(node);
94
99
  }
95
100
  }
96
101
  };
@@ -5,6 +5,18 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const { CALL, ReferenceTracker } = require("eslint-utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect"];
19
+
8
20
  //------------------------------------------------------------------------------
9
21
  // Rule Definition
10
22
  //------------------------------------------------------------------------------
@@ -20,23 +32,31 @@ module.exports = {
20
32
  url: "https://eslint.org/docs/rules/no-obj-calls"
21
33
  },
22
34
 
23
- schema: []
35
+ schema: [],
36
+
37
+ messages: {
38
+ unexpectedCall: "'{{name}}' is not a function."
39
+ }
24
40
  },
25
41
 
26
42
  create(context) {
27
43
 
28
44
  return {
29
- CallExpression(node) {
30
-
31
- if (node.callee.type === "Identifier") {
32
- const name = node.callee.name;
45
+ Program() {
46
+ const scope = context.getScope();
47
+ const tracker = new ReferenceTracker(scope);
48
+ const traceMap = {};
49
+
50
+ for (const global of nonCallableGlobals) {
51
+ traceMap[global] = {
52
+ [CALL]: true
53
+ };
54
+ }
33
55
 
34
- if (name === "Math" || name === "JSON" || name === "Reflect") {
35
- context.report({ node, message: "'{{name}}' is not a function.", data: { name } });
36
- }
56
+ for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
57
+ context.report({ node, messageId: "unexpectedCall", data: { name: node.callee.name } });
37
58
  }
38
59
  }
39
60
  };
40
-
41
61
  }
42
62
  };
@@ -20,7 +20,11 @@ module.exports = {
20
20
  url: "https://eslint.org/docs/rules/no-octal-escape"
21
21
  },
22
22
 
23
- schema: []
23
+ schema: [],
24
+
25
+ messages: {
26
+ octalEscapeSequence: "Don't use octal: '\\{{sequence}}'. Use '\\u....' instead."
27
+ }
24
28
  },
25
29
 
26
30
  create(context) {
@@ -32,15 +36,17 @@ module.exports = {
32
36
  return;
33
37
  }
34
38
 
35
- const match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-3][0-7]{1,2}|[4-7][0-7]|[0-7])/u);
39
+ // \0 represents a valid NULL character if it isn't followed by a digit.
40
+ const match = node.raw.match(
41
+ /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|[1-7])/u
42
+ );
36
43
 
37
44
  if (match) {
38
- const octalDigit = match[2];
39
-
40
- // \0 is actually not considered an octal
41
- if (match[2] !== "0" || typeof match[3] !== "undefined") {
42
- context.report({ node, message: "Don't use octal: '\\{{octalDigit}}'. Use '\\u....' instead.", data: { octalDigit } });
43
- }
45
+ context.report({
46
+ node,
47
+ messageId: "octalEscapeSequence",
48
+ data: { sequence: match[1] }
49
+ });
44
50
  }
45
51
  }
46
52
 
@@ -5,7 +5,29 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
8
12
  const astUtils = require("./utils/ast-utils");
13
+ const regexpp = require("regexpp");
14
+
15
+ //------------------------------------------------------------------------------
16
+ // Helpers
17
+ //------------------------------------------------------------------------------
18
+
19
+ const regExpParser = new regexpp.RegExpParser();
20
+ const DOUBLE_SPACE = / {2}/u;
21
+
22
+ /**
23
+ * Check if node is a string
24
+ * @param {ASTNode} node node to evaluate
25
+ * @returns {boolean} True if its a string
26
+ * @private
27
+ */
28
+ function isString(node) {
29
+ return node && node.type === "Literal" && typeof node.value === "string";
30
+ }
9
31
 
10
32
  //------------------------------------------------------------------------------
11
33
  // Rule Definition
@@ -27,40 +49,70 @@ module.exports = {
27
49
  },
28
50
 
29
51
  create(context) {
30
- const sourceCode = context.getSourceCode();
31
52
 
32
53
  /**
33
- * Validate regular expressions
34
- * @param {ASTNode} node node to validate
35
- * @param {string} value regular expression to validate
36
- * @param {number} valueStart The start location of the regex/string literal. It will always be the case that
37
- * `sourceCode.getText().slice(valueStart, valueStart + value.length) === value`
54
+ * Validate regular expression
55
+ *
56
+ * @param {ASTNode} nodeToReport Node to report.
57
+ * @param {string} pattern Regular expression pattern to validate.
58
+ * @param {string} rawPattern Raw representation of the pattern in the source code.
59
+ * @param {number} rawPatternStartRange Start range of the pattern in the source code.
60
+ * @param {string} flags Regular expression flags.
38
61
  * @returns {void}
39
62
  * @private
40
63
  */
41
- function checkRegex(node, value, valueStart) {
42
- const multipleSpacesRegex = /( {2,})( [+*{?]|[^+*{?]|$)/u,
43
- regexResults = multipleSpacesRegex.exec(value);
64
+ function checkRegex(nodeToReport, pattern, rawPattern, rawPatternStartRange, flags) {
44
65
 
45
- if (regexResults !== null) {
46
- const count = regexResults[1].length;
66
+ // Skip if there are no consecutive spaces in the source code, to avoid reporting e.g., RegExp(' \ ').
67
+ if (!DOUBLE_SPACE.test(rawPattern)) {
68
+ return;
69
+ }
47
70
 
48
- context.report({
49
- node,
50
- message: "Spaces are hard to count. Use {{{count}}}.",
51
- data: { count },
52
- fix(fixer) {
53
- return fixer.replaceTextRange(
54
- [valueStart + regexResults.index, valueStart + regexResults.index + count],
55
- ` {${count}}`
56
- );
57
- }
58
- });
59
-
60
- /*
61
- * TODO: (platinumazure) Fix message to use rule message
62
- * substitution when api.report is fixed in lib/eslint.js.
63
- */
71
+ const characterClassNodes = [];
72
+ let regExpAST;
73
+
74
+ try {
75
+ regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
76
+ } catch (e) {
77
+
78
+ // Ignore regular expressions with syntax errors
79
+ return;
80
+ }
81
+
82
+ regexpp.visitRegExpAST(regExpAST, {
83
+ onCharacterClassEnter(ccNode) {
84
+ characterClassNodes.push(ccNode);
85
+ }
86
+ });
87
+
88
+ const spacesPattern = /( {2,})(?: [+*{?]|[^+*{?]|$)/gu;
89
+ let match;
90
+
91
+ while ((match = spacesPattern.exec(pattern))) {
92
+ const { 1: { length }, index } = match;
93
+
94
+ // Report only consecutive spaces that are not in character classes.
95
+ if (
96
+ characterClassNodes.every(({ start, end }) => index < start || end <= index)
97
+ ) {
98
+ context.report({
99
+ node: nodeToReport,
100
+ message: "Spaces are hard to count. Use {{{length}}}.",
101
+ data: { length },
102
+ fix(fixer) {
103
+ if (pattern !== rawPattern) {
104
+ return null;
105
+ }
106
+ return fixer.replaceTextRange(
107
+ [rawPatternStartRange + index, rawPatternStartRange + index + length],
108
+ ` {${length}}`
109
+ );
110
+ }
111
+ });
112
+
113
+ // Report only the first occurence of consecutive spaces
114
+ return;
115
+ }
64
116
  }
65
117
  }
66
118
 
@@ -71,25 +123,22 @@ module.exports = {
71
123
  * @private
72
124
  */
73
125
  function checkLiteral(node) {
74
- const token = sourceCode.getFirstToken(node),
75
- nodeType = token.type,
76
- nodeValue = token.value;
126
+ if (node.regex) {
127
+ const pattern = node.regex.pattern;
128
+ const rawPattern = node.raw.slice(1, node.raw.lastIndexOf("/"));
129
+ const rawPatternStartRange = node.range[0] + 1;
130
+ const flags = node.regex.flags;
77
131
 
78
- if (nodeType === "RegularExpression") {
79
- checkRegex(node, nodeValue, token.range[0]);
132
+ checkRegex(
133
+ node,
134
+ pattern,
135
+ rawPattern,
136
+ rawPatternStartRange,
137
+ flags
138
+ );
80
139
  }
81
140
  }
82
141
 
83
- /**
84
- * Check if node is a string
85
- * @param {ASTNode} node node to evaluate
86
- * @returns {boolean} True if its a string
87
- * @private
88
- */
89
- function isString(node) {
90
- return node && node.type === "Literal" && typeof node.value === "string";
91
- }
92
-
93
142
  /**
94
143
  * Validate strings passed to the RegExp constructor
95
144
  * @param {ASTNode} node node to validate
@@ -100,9 +149,22 @@ module.exports = {
100
149
  const scope = context.getScope();
101
150
  const regExpVar = astUtils.getVariableByName(scope, "RegExp");
102
151
  const shadowed = regExpVar && regExpVar.defs.length > 0;
152
+ const patternNode = node.arguments[0];
153
+ const flagsNode = node.arguments[1];
103
154
 
104
- if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0]) && !shadowed) {
105
- checkRegex(node, node.arguments[0].value, node.arguments[0].range[0] + 1);
155
+ if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {
156
+ const pattern = patternNode.value;
157
+ const rawPattern = patternNode.raw.slice(1, -1);
158
+ const rawPatternStartRange = patternNode.range[0] + 1;
159
+ const flags = isString(flagsNode) ? flagsNode.value : "";
160
+
161
+ checkRegex(
162
+ node,
163
+ pattern,
164
+ rawPattern,
165
+ rawPatternStartRange,
166
+ flags
167
+ );
106
168
  }
107
169
  }
108
170
 
@@ -111,6 +173,5 @@ module.exports = {
111
173
  CallExpression: checkFunction,
112
174
  NewExpression: checkFunction
113
175
  };
114
-
115
176
  }
116
177
  };
@@ -94,9 +94,19 @@ function eachSelfAssignment(left, right, props, report) {
94
94
  const end = Math.min(left.elements.length, right.elements.length);
95
95
 
96
96
  for (let i = 0; i < end; ++i) {
97
+ const leftElement = left.elements[i];
97
98
  const rightElement = right.elements[i];
98
99
 
99
- eachSelfAssignment(left.elements[i], rightElement, props, report);
100
+ // Avoid cases such as [...a] = [...a, 1]
101
+ if (
102
+ leftElement &&
103
+ leftElement.type === "RestElement" &&
104
+ i < right.elements.length - 1
105
+ ) {
106
+ break;
107
+ }
108
+
109
+ eachSelfAssignment(leftElement, rightElement, props, report);
100
110
 
101
111
  // After a spread element, those indices are unknown.
102
112
  if (rightElement && rightElement.type === "SpreadElement") {
@@ -142,13 +152,14 @@ function eachSelfAssignment(left, right, props, report) {
142
152
  } else if (
143
153
  left.type === "Property" &&
144
154
  right.type === "Property" &&
145
- !left.computed &&
146
- !right.computed &&
147
155
  right.kind === "init" &&
148
- !right.method &&
149
- left.key.name === right.key.name
156
+ !right.method
150
157
  ) {
151
- eachSelfAssignment(left.value, right.value, props, report);
158
+ const leftName = astUtils.getStaticPropertyName(left);
159
+
160
+ if (leftName !== null && leftName === astUtils.getStaticPropertyName(right)) {
161
+ eachSelfAssignment(left.value, right.value, props, report);
162
+ }
152
163
  } else if (
153
164
  props &&
154
165
  left.type === "MemberExpression" &&