eslint 7.4.0 → 7.8.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +29 -17
  3. package/conf/config-schema.js +12 -0
  4. package/lib/cli-engine/cascading-config-array-factory.js +12 -0
  5. package/lib/cli-engine/cli-engine.js +2 -2
  6. package/lib/cli-engine/config-array/config-array.js +12 -0
  7. package/lib/cli-engine/config-array/config-dependency.js +12 -0
  8. package/lib/cli-engine/config-array/extracted-config.js +12 -0
  9. package/lib/cli-engine/config-array/ignore-pattern.js +12 -0
  10. package/lib/cli-engine/config-array/index.js +12 -0
  11. package/lib/cli-engine/config-array/override-tester.js +12 -0
  12. package/lib/cli-engine/config-array-factory.js +13 -1
  13. package/lib/cli-engine/formatters/checkstyle.js +2 -2
  14. package/lib/eslint/eslint.js +7 -1
  15. package/lib/init/autoconfig.js +1 -1
  16. package/lib/init/config-initializer.js +3 -3
  17. package/lib/linter/code-path-analysis/code-path-analyzer.js +76 -0
  18. package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
  19. package/lib/linter/code-path-analysis/code-path-state.js +61 -2
  20. package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
  21. package/lib/linter/config-comment-parser.js +1 -1
  22. package/lib/linter/linter.js +6 -3
  23. package/lib/rule-tester/rule-tester.js +10 -0
  24. package/lib/rules/accessor-pairs.js +1 -14
  25. package/lib/rules/array-callback-return.js +5 -7
  26. package/lib/rules/arrow-body-style.js +41 -6
  27. package/lib/rules/comma-dangle.js +1 -2
  28. package/lib/rules/consistent-return.js +1 -12
  29. package/lib/rules/constructor-super.js +18 -1
  30. package/lib/rules/dot-location.js +20 -14
  31. package/lib/rules/dot-notation.js +36 -33
  32. package/lib/rules/func-call-spacing.js +42 -6
  33. package/lib/rules/func-name-matching.js +1 -4
  34. package/lib/rules/global-require.js +2 -1
  35. package/lib/rules/id-blacklist.js +233 -0
  36. package/lib/rules/id-length.js +19 -1
  37. package/lib/rules/indent.js +23 -3
  38. package/lib/rules/index.js +1 -3
  39. package/lib/rules/keyword-spacing.js +2 -2
  40. package/lib/rules/max-len.js +13 -2
  41. package/lib/rules/new-cap.js +10 -14
  42. package/lib/rules/newline-per-chained-call.js +15 -5
  43. package/lib/rules/no-alert.js +10 -3
  44. package/lib/rules/no-duplicate-case.js +23 -4
  45. package/lib/rules/no-eval.js +8 -38
  46. package/lib/rules/no-extend-native.js +37 -40
  47. package/lib/rules/no-extra-bind.js +57 -17
  48. package/lib/rules/no-extra-boolean-cast.js +7 -0
  49. package/lib/rules/no-extra-parens.js +27 -7
  50. package/lib/rules/no-implicit-coercion.js +11 -6
  51. package/lib/rules/no-implied-eval.js +7 -28
  52. package/lib/rules/no-import-assign.js +33 -32
  53. package/lib/rules/no-irregular-whitespace.js +22 -12
  54. package/lib/rules/no-loss-of-precision.js +10 -2
  55. package/lib/rules/no-magic-numbers.js +24 -11
  56. package/lib/rules/no-obj-calls.js +7 -4
  57. package/lib/rules/no-prototype-builtins.js +13 -3
  58. package/lib/rules/no-self-assign.js +3 -53
  59. package/lib/rules/no-setter-return.js +5 -8
  60. package/lib/rules/no-underscore-dangle.js +66 -21
  61. package/lib/rules/no-unexpected-multiline.js +2 -2
  62. package/lib/rules/no-unneeded-ternary.js +0 -2
  63. package/lib/rules/no-unused-expressions.js +55 -23
  64. package/lib/rules/no-useless-call.js +10 -7
  65. package/lib/rules/no-warning-comments.js +40 -7
  66. package/lib/rules/no-whitespace-before-property.js +16 -4
  67. package/lib/rules/object-curly-newline.js +4 -4
  68. package/lib/rules/operator-assignment.js +4 -43
  69. package/lib/rules/padding-line-between-statements.js +2 -2
  70. package/lib/rules/prefer-arrow-callback.js +90 -25
  71. package/lib/rules/prefer-exponentiation-operator.js +1 -1
  72. package/lib/rules/prefer-numeric-literals.js +14 -13
  73. package/lib/rules/prefer-promise-reject-errors.js +1 -3
  74. package/lib/rules/prefer-regex-literals.js +2 -5
  75. package/lib/rules/prefer-spread.js +2 -6
  76. package/lib/rules/radix.js +5 -2
  77. package/lib/rules/sort-imports.js +28 -0
  78. package/lib/rules/use-isnan.js +1 -1
  79. package/lib/rules/utils/ast-utils.js +363 -165
  80. package/lib/rules/wrap-iife.js +9 -2
  81. package/lib/rules/yoda.js +2 -55
  82. package/lib/shared/config-validator.js +14 -2
  83. package/lib/shared/relative-module-resolver.js +12 -0
  84. package/lib/shared/types.js +1 -1
  85. package/messages/extend-config-missing.txt +1 -1
  86. package/messages/no-config-found.txt +1 -1
  87. package/messages/plugin-conflict.txt +1 -1
  88. package/messages/plugin-missing.txt +1 -1
  89. package/messages/whitespace-found.txt +1 -1
  90. package/package.json +7 -7
  91. package/conf/environments.js +0 -168
  92. package/lib/shared/config-ops.js +0 -130
  93. package/lib/shared/naming.js +0 -97
@@ -37,9 +37,11 @@ const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
37
37
  // A set of node types that can contain a list of statements
38
38
  const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
39
39
 
40
- const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
40
+ const DECIMAL_INTEGER_PATTERN = /^(0|[1-9](?:_?\d)*)$/u;
41
41
  const OCTAL_ESCAPE_PATTERN = /^(?:[^\\]|\\[^0-7]|\\0(?![0-9]))*\\(?:[1-7]|0[0-9])/u;
42
42
 
43
+ const LOGICAL_ASSIGNMENT_OPERATORS = new Set(["&&=", "||=", "??="]);
44
+
43
45
  /**
44
46
  * Checks reference if is non initializer and writable.
45
47
  * @param {Reference} reference A reference to check.
@@ -143,6 +145,23 @@ function isInLoop(node) {
143
145
  return false;
144
146
  }
145
147
 
148
+ /**
149
+ * Determines whether the given node is a `null` literal.
150
+ * @param {ASTNode} node The node to check
151
+ * @returns {boolean} `true` if the node is a `null` literal
152
+ */
153
+ function isNullLiteral(node) {
154
+
155
+ /*
156
+ * Checking `node.value === null` does not guarantee that a literal is a null literal.
157
+ * When parsing values that cannot be represented in the current environment (e.g. unicode
158
+ * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
159
+ * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
160
+ * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
161
+ */
162
+ return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
163
+ }
164
+
146
165
  /**
147
166
  * Checks whether or not a node is `null` or `undefined`.
148
167
  * @param {ASTNode} node A node to check.
@@ -151,7 +170,7 @@ function isInLoop(node) {
151
170
  */
152
171
  function isNullOrUndefined(node) {
153
172
  return (
154
- module.exports.isNullLiteral(node) ||
173
+ isNullLiteral(node) ||
155
174
  (node.type === "Identifier" && node.name === "undefined") ||
156
175
  (node.type === "UnaryExpression" && node.operator === "void")
157
176
  );
@@ -166,20 +185,270 @@ function isCallee(node) {
166
185
  return node.parent.type === "CallExpression" && node.parent.callee === node;
167
186
  }
168
187
 
188
+ /**
189
+ * Returns the result of the string conversion applied to the evaluated value of the given expression node,
190
+ * if it can be determined statically.
191
+ *
192
+ * This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
193
+ * In all other cases, this function returns `null`.
194
+ * @param {ASTNode} node Expression node.
195
+ * @returns {string|null} String value if it can be determined. Otherwise, `null`.
196
+ */
197
+ function getStaticStringValue(node) {
198
+ switch (node.type) {
199
+ case "Literal":
200
+ if (node.value === null) {
201
+ if (isNullLiteral(node)) {
202
+ return String(node.value); // "null"
203
+ }
204
+ if (node.regex) {
205
+ return `/${node.regex.pattern}/${node.regex.flags}`;
206
+ }
207
+ if (node.bigint) {
208
+ return node.bigint;
209
+ }
210
+
211
+ // Otherwise, this is an unknown literal. The function will return null.
212
+
213
+ } else {
214
+ return String(node.value);
215
+ }
216
+ break;
217
+ case "TemplateLiteral":
218
+ if (node.expressions.length === 0 && node.quasis.length === 1) {
219
+ return node.quasis[0].value.cooked;
220
+ }
221
+ break;
222
+
223
+ // no default
224
+ }
225
+
226
+ return null;
227
+ }
228
+
229
+ /**
230
+ * Gets the property name of a given node.
231
+ * The node can be a MemberExpression, a Property, or a MethodDefinition.
232
+ *
233
+ * If the name is dynamic, this returns `null`.
234
+ *
235
+ * For examples:
236
+ *
237
+ * a.b // => "b"
238
+ * a["b"] // => "b"
239
+ * a['b'] // => "b"
240
+ * a[`b`] // => "b"
241
+ * a[100] // => "100"
242
+ * a[b] // => null
243
+ * a["a" + "b"] // => null
244
+ * a[tag`b`] // => null
245
+ * a[`${b}`] // => null
246
+ *
247
+ * let a = {b: 1} // => "b"
248
+ * let a = {["b"]: 1} // => "b"
249
+ * let a = {['b']: 1} // => "b"
250
+ * let a = {[`b`]: 1} // => "b"
251
+ * let a = {[100]: 1} // => "100"
252
+ * let a = {[b]: 1} // => null
253
+ * let a = {["a" + "b"]: 1} // => null
254
+ * let a = {[tag`b`]: 1} // => null
255
+ * let a = {[`${b}`]: 1} // => null
256
+ * @param {ASTNode} node The node to get.
257
+ * @returns {string|null} The property name if static. Otherwise, null.
258
+ */
259
+ function getStaticPropertyName(node) {
260
+ let prop;
261
+
262
+ switch (node && node.type) {
263
+ case "ChainExpression":
264
+ return getStaticPropertyName(node.expression);
265
+
266
+ case "Property":
267
+ case "MethodDefinition":
268
+ prop = node.key;
269
+ break;
270
+
271
+ case "MemberExpression":
272
+ prop = node.property;
273
+ break;
274
+
275
+ // no default
276
+ }
277
+
278
+ if (prop) {
279
+ if (prop.type === "Identifier" && !node.computed) {
280
+ return prop.name;
281
+ }
282
+
283
+ return getStaticStringValue(prop);
284
+ }
285
+
286
+ return null;
287
+ }
288
+
289
+ /**
290
+ * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
291
+ * @param {ASTNode} node The node to address.
292
+ * @returns {ASTNode} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node.
293
+ */
294
+ function skipChainExpression(node) {
295
+ return node && node.type === "ChainExpression" ? node.expression : node;
296
+ }
297
+
298
+ /**
299
+ * Check if the `actual` is an expected value.
300
+ * @param {string} actual The string value to check.
301
+ * @param {string | RegExp} expected The expected string value or pattern.
302
+ * @returns {boolean} `true` if the `actual` is an expected value.
303
+ */
304
+ function checkText(actual, expected) {
305
+ return typeof expected === "string"
306
+ ? actual === expected
307
+ : expected.test(actual);
308
+ }
309
+
310
+ /**
311
+ * Check if a given node is an Identifier node with a given name.
312
+ * @param {ASTNode} node The node to check.
313
+ * @param {string | RegExp} name The expected name or the expected pattern of the object name.
314
+ * @returns {boolean} `true` if the node is an Identifier node with the name.
315
+ */
316
+ function isSpecificId(node, name) {
317
+ return node.type === "Identifier" && checkText(node.name, name);
318
+ }
319
+
320
+ /**
321
+ * Check if a given node is member access with a given object name and property name pair.
322
+ * This is regardless of optional or not.
323
+ * @param {ASTNode} node The node to check.
324
+ * @param {string | RegExp | null} objectName The expected name or the expected pattern of the object name. If this is nullish, this method doesn't check object.
325
+ * @param {string | RegExp | null} propertyName The expected name or the expected pattern of the property name. If this is nullish, this method doesn't check property.
326
+ * @returns {boolean} `true` if the node is member access with the object name and property name pair.
327
+ * The node is a `MemberExpression` or `ChainExpression`.
328
+ */
329
+ function isSpecificMemberAccess(node, objectName, propertyName) {
330
+ const checkNode = skipChainExpression(node);
331
+
332
+ if (checkNode.type !== "MemberExpression") {
333
+ return false;
334
+ }
335
+
336
+ if (objectName && !isSpecificId(checkNode.object, objectName)) {
337
+ return false;
338
+ }
339
+
340
+ if (propertyName) {
341
+ const actualPropertyName = getStaticPropertyName(checkNode);
342
+
343
+ if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
344
+ return false;
345
+ }
346
+ }
347
+
348
+ return true;
349
+ }
350
+
351
+ /**
352
+ * Check if two literal nodes are the same value.
353
+ * @param {ASTNode} left The Literal node to compare.
354
+ * @param {ASTNode} right The other Literal node to compare.
355
+ * @returns {boolean} `true` if the two literal nodes are the same value.
356
+ */
357
+ function equalLiteralValue(left, right) {
358
+
359
+ // RegExp literal.
360
+ if (left.regex || right.regex) {
361
+ return Boolean(
362
+ left.regex &&
363
+ right.regex &&
364
+ left.regex.pattern === right.regex.pattern &&
365
+ left.regex.flags === right.regex.flags
366
+ );
367
+ }
368
+
369
+ // BigInt literal.
370
+ if (left.bigint || right.bigint) {
371
+ return left.bigint === right.bigint;
372
+ }
373
+
374
+ return left.value === right.value;
375
+ }
376
+
377
+ /**
378
+ * Check if two expressions reference the same value. For example:
379
+ * a = a
380
+ * a.b = a.b
381
+ * a[0] = a[0]
382
+ * a['b'] = a['b']
383
+ * @param {ASTNode} left The left side of the comparison.
384
+ * @param {ASTNode} right The right side of the comparison.
385
+ * @param {boolean} [disableStaticComputedKey] Don't address `a.b` and `a["b"]` are the same if `true`. For backward compatibility.
386
+ * @returns {boolean} `true` if both sides match and reference the same value.
387
+ */
388
+ function isSameReference(left, right, disableStaticComputedKey = false) {
389
+ if (left.type !== right.type) {
390
+
391
+ // Handle `a.b` and `a?.b` are samely.
392
+ if (left.type === "ChainExpression") {
393
+ return isSameReference(left.expression, right, disableStaticComputedKey);
394
+ }
395
+ if (right.type === "ChainExpression") {
396
+ return isSameReference(left, right.expression, disableStaticComputedKey);
397
+ }
398
+
399
+ return false;
400
+ }
401
+
402
+ switch (left.type) {
403
+ case "Super":
404
+ case "ThisExpression":
405
+ return true;
406
+
407
+ case "Identifier":
408
+ return left.name === right.name;
409
+ case "Literal":
410
+ return equalLiteralValue(left, right);
411
+
412
+ case "ChainExpression":
413
+ return isSameReference(left.expression, right.expression, disableStaticComputedKey);
414
+
415
+ case "MemberExpression": {
416
+ if (!disableStaticComputedKey) {
417
+ const nameA = getStaticPropertyName(left);
418
+
419
+ // x.y = x["y"]
420
+ if (nameA !== null) {
421
+ return (
422
+ isSameReference(left.object, right.object, disableStaticComputedKey) &&
423
+ nameA === getStaticPropertyName(right)
424
+ );
425
+ }
426
+ }
427
+
428
+ /*
429
+ * x[0] = x[0]
430
+ * x[y] = x[y]
431
+ * x.y = x.y
432
+ */
433
+ return (
434
+ left.computed === right.computed &&
435
+ isSameReference(left.object, right.object, disableStaticComputedKey) &&
436
+ isSameReference(left.property, right.property, disableStaticComputedKey)
437
+ );
438
+ }
439
+
440
+ default:
441
+ return false;
442
+ }
443
+ }
444
+
169
445
  /**
170
446
  * Checks whether or not a node is `Reflect.apply`.
171
447
  * @param {ASTNode} node A node to check.
172
448
  * @returns {boolean} Whether or not the node is a `Reflect.apply`.
173
449
  */
174
450
  function isReflectApply(node) {
175
- return (
176
- node.type === "MemberExpression" &&
177
- node.object.type === "Identifier" &&
178
- node.object.name === "Reflect" &&
179
- node.property.type === "Identifier" &&
180
- node.property.name === "apply" &&
181
- node.computed === false
182
- );
451
+ return isSpecificMemberAccess(node, "Reflect", "apply");
183
452
  }
184
453
 
185
454
  /**
@@ -188,14 +457,7 @@ function isReflectApply(node) {
188
457
  * @returns {boolean} Whether or not the node is a `Array.from`.
189
458
  */
190
459
  function isArrayFromMethod(node) {
191
- return (
192
- node.type === "MemberExpression" &&
193
- node.object.type === "Identifier" &&
194
- arrayOrTypedArrayPattern.test(node.object.name) &&
195
- node.property.type === "Identifier" &&
196
- node.property.name === "from" &&
197
- node.computed === false
198
- );
460
+ return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
199
461
  }
200
462
 
201
463
  /**
@@ -204,17 +466,7 @@ function isArrayFromMethod(node) {
204
466
  * @returns {boolean} Whether or not the node is a method which has `thisArg`.
205
467
  */
206
468
  function isMethodWhichHasThisArg(node) {
207
- for (
208
- let currentNode = node;
209
- currentNode.type === "MemberExpression" && !currentNode.computed;
210
- currentNode = currentNode.property
211
- ) {
212
- if (currentNode.property.type === "Identifier") {
213
- return arrayMethodPattern.test(currentNode.property.name);
214
- }
215
- }
216
-
217
- return false;
469
+ return isSpecificMemberAccess(node, null, arrayMethodPattern);
218
470
  }
219
471
 
220
472
  /**
@@ -289,6 +541,15 @@ function isDotToken(token) {
289
541
  return token.value === "." && token.type === "Punctuator";
290
542
  }
291
543
 
544
+ /**
545
+ * Checks if the given token is a `?.` token or not.
546
+ * @param {Token} token The token to check.
547
+ * @returns {boolean} `true` if the token is a `?.` token.
548
+ */
549
+ function isQuestionDotToken(token) {
550
+ return token.value === "?." && token.type === "Punctuator";
551
+ }
552
+
292
553
  /**
293
554
  * Checks if the given token is a semicolon token or not.
294
555
  * @param {Token} token The token to check.
@@ -463,6 +724,15 @@ function isMixedLogicalAndCoalesceExpressions(left, right) {
463
724
  );
464
725
  }
465
726
 
727
+ /**
728
+ * Checks if the given operator is a logical assignment operator.
729
+ * @param {string} operator The operator to check.
730
+ * @returns {boolean} `true` if the operator is a logical assignment operator.
731
+ */
732
+ function isLogicalAssignmentOperator(operator) {
733
+ return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
734
+ }
735
+
466
736
  //------------------------------------------------------------------------------
467
737
  // Public Interface
468
738
  //------------------------------------------------------------------------------
@@ -505,6 +775,7 @@ module.exports = {
505
775
  isCommaToken,
506
776
  isCommentToken,
507
777
  isDotToken,
778
+ isQuestionDotToken,
508
779
  isKeywordToken,
509
780
  isNotClosingBraceToken: negate(isClosingBraceToken),
510
781
  isNotClosingBracketToken: negate(isClosingBracketToken),
@@ -512,6 +783,7 @@ module.exports = {
512
783
  isNotColonToken: negate(isColonToken),
513
784
  isNotCommaToken: negate(isCommaToken),
514
785
  isNotDotToken: negate(isDotToken),
786
+ isNotQuestionDotToken: negate(isQuestionDotToken),
515
787
  isNotOpeningBraceToken: negate(isOpeningBraceToken),
516
788
  isNotOpeningBracketToken: negate(isOpeningBracketToken),
517
789
  isNotOpeningParenToken: negate(isOpeningParenToken),
@@ -669,6 +941,7 @@ module.exports = {
669
941
  */
670
942
  case "LogicalExpression":
671
943
  case "ConditionalExpression":
944
+ case "ChainExpression":
672
945
  currentNode = parent;
673
946
  break;
674
947
 
@@ -755,14 +1028,21 @@ module.exports = {
755
1028
  * (function foo() { ... }).apply(obj, []);
756
1029
  */
757
1030
  case "MemberExpression":
758
- return (
759
- parent.object !== currentNode ||
760
- parent.property.type !== "Identifier" ||
761
- !bindOrCallOrApplyPattern.test(parent.property.name) ||
762
- !isCallee(parent) ||
763
- parent.parent.arguments.length === 0 ||
764
- isNullOrUndefined(parent.parent.arguments[0])
765
- );
1031
+ if (
1032
+ parent.object === currentNode &&
1033
+ isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
1034
+ ) {
1035
+ const maybeCalleeNode = parent.parent.type === "ChainExpression"
1036
+ ? parent.parent
1037
+ : parent;
1038
+
1039
+ return !(
1040
+ isCallee(maybeCalleeNode) &&
1041
+ maybeCalleeNode.parent.arguments.length >= 1 &&
1042
+ !isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
1043
+ );
1044
+ }
1045
+ return true;
766
1046
 
767
1047
  /*
768
1048
  * e.g.
@@ -884,6 +1164,7 @@ module.exports = {
884
1164
  return 17;
885
1165
 
886
1166
  case "CallExpression":
1167
+ case "ChainExpression":
887
1168
  case "ImportExpression":
888
1169
  return 18;
889
1170
 
@@ -913,104 +1194,6 @@ module.exports = {
913
1194
  return isFunction(node) && module.exports.isEmptyBlock(node.body);
914
1195
  },
915
1196
 
916
- /**
917
- * Returns the result of the string conversion applied to the evaluated value of the given expression node,
918
- * if it can be determined statically.
919
- *
920
- * This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
921
- * In all other cases, this function returns `null`.
922
- * @param {ASTNode} node Expression node.
923
- * @returns {string|null} String value if it can be determined. Otherwise, `null`.
924
- */
925
- getStaticStringValue(node) {
926
- switch (node.type) {
927
- case "Literal":
928
- if (node.value === null) {
929
- if (module.exports.isNullLiteral(node)) {
930
- return String(node.value); // "null"
931
- }
932
- if (node.regex) {
933
- return `/${node.regex.pattern}/${node.regex.flags}`;
934
- }
935
- if (node.bigint) {
936
- return node.bigint;
937
- }
938
-
939
- // Otherwise, this is an unknown literal. The function will return null.
940
-
941
- } else {
942
- return String(node.value);
943
- }
944
- break;
945
- case "TemplateLiteral":
946
- if (node.expressions.length === 0 && node.quasis.length === 1) {
947
- return node.quasis[0].value.cooked;
948
- }
949
- break;
950
-
951
- // no default
952
- }
953
-
954
- return null;
955
- },
956
-
957
- /**
958
- * Gets the property name of a given node.
959
- * The node can be a MemberExpression, a Property, or a MethodDefinition.
960
- *
961
- * If the name is dynamic, this returns `null`.
962
- *
963
- * For examples:
964
- *
965
- * a.b // => "b"
966
- * a["b"] // => "b"
967
- * a['b'] // => "b"
968
- * a[`b`] // => "b"
969
- * a[100] // => "100"
970
- * a[b] // => null
971
- * a["a" + "b"] // => null
972
- * a[tag`b`] // => null
973
- * a[`${b}`] // => null
974
- *
975
- * let a = {b: 1} // => "b"
976
- * let a = {["b"]: 1} // => "b"
977
- * let a = {['b']: 1} // => "b"
978
- * let a = {[`b`]: 1} // => "b"
979
- * let a = {[100]: 1} // => "100"
980
- * let a = {[b]: 1} // => null
981
- * let a = {["a" + "b"]: 1} // => null
982
- * let a = {[tag`b`]: 1} // => null
983
- * let a = {[`${b}`]: 1} // => null
984
- * @param {ASTNode} node The node to get.
985
- * @returns {string|null} The property name if static. Otherwise, null.
986
- */
987
- getStaticPropertyName(node) {
988
- let prop;
989
-
990
- switch (node && node.type) {
991
- case "Property":
992
- case "MethodDefinition":
993
- prop = node.key;
994
- break;
995
-
996
- case "MemberExpression":
997
- prop = node.property;
998
- break;
999
-
1000
- // no default
1001
- }
1002
-
1003
- if (prop) {
1004
- if (prop.type === "Identifier" && !node.computed) {
1005
- return prop.name;
1006
- }
1007
-
1008
- return module.exports.getStaticStringValue(prop);
1009
- }
1010
-
1011
- return null;
1012
- },
1013
-
1014
1197
  /**
1015
1198
  * Get directives from directive prologue of a Program or Function node.
1016
1199
  * @param {ASTNode} node The node to check.
@@ -1056,16 +1239,25 @@ module.exports = {
1056
1239
  * @returns {boolean} `true` if this node is a decimal integer.
1057
1240
  * @example
1058
1241
  *
1059
- * 5 // true
1060
- * 5. // false
1061
- * 5.0 // false
1062
- * 05 // false
1063
- * 0x5 // false
1064
- * 0b101 // false
1065
- * 0o5 // false
1066
- * 5e0 // false
1067
- * '5' // false
1068
- * 5n // false
1242
+ * 0 // true
1243
+ * 5 // true
1244
+ * 50 // true
1245
+ * 5_000 // true
1246
+ * 1_234_56 // true
1247
+ * 5. // false
1248
+ * .5 // false
1249
+ * 5.0 // false
1250
+ * 5.00_00 // false
1251
+ * 05 // false
1252
+ * 0x5 // false
1253
+ * 0b101 // false
1254
+ * 0b11_01 // false
1255
+ * 0o5 // false
1256
+ * 5e0 // false
1257
+ * 5e1_000 // false
1258
+ * 5n // false
1259
+ * 1_000n // false
1260
+ * '5' // false
1069
1261
  */
1070
1262
  isDecimalInteger(node) {
1071
1263
  return node.type === "Literal" && typeof node.value === "number" &&
@@ -1164,7 +1356,7 @@ module.exports = {
1164
1356
  if (node.id) {
1165
1357
  tokens.push(`'${node.id.name}'`);
1166
1358
  } else {
1167
- const name = module.exports.getStaticPropertyName(parent);
1359
+ const name = getStaticPropertyName(parent);
1168
1360
 
1169
1361
  if (name !== null) {
1170
1362
  tokens.push(`'${name}'`);
@@ -1391,10 +1583,24 @@ module.exports = {
1391
1583
  case "TaggedTemplateExpression":
1392
1584
  case "YieldExpression":
1393
1585
  case "AwaitExpression":
1586
+ case "ChainExpression":
1394
1587
  return true; // possibly an error object.
1395
1588
 
1396
1589
  case "AssignmentExpression":
1397
- return module.exports.couldBeError(node.right);
1590
+ if (["=", "&&="].includes(node.operator)) {
1591
+ return module.exports.couldBeError(node.right);
1592
+ }
1593
+
1594
+ if (["||=", "??="].includes(node.operator)) {
1595
+ return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1596
+ }
1597
+
1598
+ /**
1599
+ * All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
1600
+ * An assignment expression with a mathematical operator can either evaluate to a primitive value,
1601
+ * or throw, depending on the operands. Thus, it cannot evaluate to an `Error` object.
1602
+ */
1603
+ return false;
1398
1604
 
1399
1605
  case "SequenceExpression": {
1400
1606
  const exprs = node.expressions;
@@ -1413,23 +1619,6 @@ module.exports = {
1413
1619
  }
1414
1620
  },
1415
1621
 
1416
- /**
1417
- * Determines whether the given node is a `null` literal.
1418
- * @param {ASTNode} node The node to check
1419
- * @returns {boolean} `true` if the node is a `null` literal
1420
- */
1421
- isNullLiteral(node) {
1422
-
1423
- /*
1424
- * Checking `node.value === null` does not guarantee that a literal is a null literal.
1425
- * When parsing values that cannot be represented in the current environment (e.g. unicode
1426
- * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
1427
- * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
1428
- * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
1429
- */
1430
- return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
1431
- },
1432
-
1433
1622
  /**
1434
1623
  * Check if a given node is a numeric literal or not.
1435
1624
  * @param {ASTNode} node The node to check.
@@ -1590,5 +1779,14 @@ module.exports = {
1590
1779
 
1591
1780
  isLogicalExpression,
1592
1781
  isCoalesceExpression,
1593
- isMixedLogicalAndCoalesceExpressions
1782
+ isMixedLogicalAndCoalesceExpressions,
1783
+ isNullLiteral,
1784
+ getStaticStringValue,
1785
+ getStaticPropertyName,
1786
+ skipChainExpression,
1787
+ isSpecificId,
1788
+ isSpecificMemberAccess,
1789
+ equalLiteralValue,
1790
+ isSameReference,
1791
+ isLogicalAssignmentOperator
1594
1792
  };
@@ -23,7 +23,14 @@ const eslintUtils = require("eslint-utils");
23
23
  * @private
24
24
  */
25
25
  function isCalleeOfNewExpression(node) {
26
- return node.parent.type === "NewExpression" && node.parent.callee === node;
26
+ const maybeCallee = node.parent.type === "ChainExpression"
27
+ ? node.parent
28
+ : node;
29
+
30
+ return (
31
+ maybeCallee.parent.type === "NewExpression" &&
32
+ maybeCallee.parent.callee === maybeCallee
33
+ );
27
34
  }
28
35
 
29
36
  //------------------------------------------------------------------------------
@@ -98,7 +105,7 @@ module.exports = {
98
105
  * @returns {ASTNode} node that is the function expression of the given IIFE, or null if none exist
99
106
  */
100
107
  function getFunctionNodeFromIIFE(node) {
101
- const callee = node.callee;
108
+ const callee = astUtils.skipChainExpression(node.callee);
102
109
 
103
110
  if (callee.type === "FunctionExpression") {
104
111
  return callee;