eslint 7.4.0 → 7.5.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 (59) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +3 -1
  3. package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
  4. package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
  5. package/lib/linter/code-path-analysis/code-path-state.js +59 -0
  6. package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
  7. package/lib/rules/accessor-pairs.js +1 -14
  8. package/lib/rules/array-callback-return.js +5 -7
  9. package/lib/rules/arrow-body-style.js +41 -6
  10. package/lib/rules/consistent-return.js +1 -12
  11. package/lib/rules/constructor-super.js +1 -0
  12. package/lib/rules/dot-location.js +20 -14
  13. package/lib/rules/dot-notation.js +36 -33
  14. package/lib/rules/func-call-spacing.js +42 -6
  15. package/lib/rules/func-name-matching.js +1 -4
  16. package/lib/rules/global-require.js +2 -1
  17. package/lib/rules/id-blacklist.js +233 -0
  18. package/lib/rules/indent.js +19 -0
  19. package/lib/rules/index.js +1 -3
  20. package/lib/rules/keyword-spacing.js +2 -2
  21. package/lib/rules/max-len.js +13 -2
  22. package/lib/rules/new-cap.js +10 -14
  23. package/lib/rules/newline-per-chained-call.js +15 -5
  24. package/lib/rules/no-alert.js +10 -3
  25. package/lib/rules/no-eval.js +8 -38
  26. package/lib/rules/no-extend-native.js +37 -40
  27. package/lib/rules/no-extra-bind.js +57 -17
  28. package/lib/rules/no-extra-boolean-cast.js +7 -0
  29. package/lib/rules/no-extra-parens.js +27 -7
  30. package/lib/rules/no-implicit-coercion.js +11 -6
  31. package/lib/rules/no-implied-eval.js +7 -28
  32. package/lib/rules/no-import-assign.js +33 -32
  33. package/lib/rules/no-irregular-whitespace.js +22 -12
  34. package/lib/rules/no-magic-numbers.js +4 -8
  35. package/lib/rules/no-obj-calls.js +7 -4
  36. package/lib/rules/no-prototype-builtins.js +13 -3
  37. package/lib/rules/no-self-assign.js +3 -53
  38. package/lib/rules/no-setter-return.js +5 -8
  39. package/lib/rules/no-unexpected-multiline.js +2 -2
  40. package/lib/rules/no-unneeded-ternary.js +0 -2
  41. package/lib/rules/no-unused-expressions.js +55 -23
  42. package/lib/rules/no-useless-call.js +10 -7
  43. package/lib/rules/no-whitespace-before-property.js +16 -4
  44. package/lib/rules/object-curly-newline.js +4 -4
  45. package/lib/rules/operator-assignment.js +3 -42
  46. package/lib/rules/padding-line-between-statements.js +2 -2
  47. package/lib/rules/prefer-arrow-callback.js +90 -25
  48. package/lib/rules/prefer-exponentiation-operator.js +1 -1
  49. package/lib/rules/prefer-numeric-literals.js +4 -13
  50. package/lib/rules/prefer-promise-reject-errors.js +1 -3
  51. package/lib/rules/prefer-regex-literals.js +2 -5
  52. package/lib/rules/prefer-spread.js +2 -6
  53. package/lib/rules/radix.js +5 -2
  54. package/lib/rules/sort-imports.js +28 -0
  55. package/lib/rules/use-isnan.js +1 -1
  56. package/lib/rules/utils/ast-utils.js +317 -153
  57. package/lib/rules/wrap-iife.js +9 -2
  58. package/lib/rules/yoda.js +2 -55
  59. package/package.json +6 -6
@@ -12,12 +12,6 @@
12
12
  const astUtils = require("./utils/ast-utils");
13
13
  const globals = require("globals");
14
14
 
15
- //------------------------------------------------------------------------------
16
- // Helpers
17
- //------------------------------------------------------------------------------
18
-
19
- const propertyDefinitionMethods = new Set(["defineProperty", "defineProperties"]);
20
-
21
15
  //------------------------------------------------------------------------------
22
16
  // Rule Definition
23
17
  //------------------------------------------------------------------------------
@@ -100,40 +94,30 @@ module.exports = {
100
94
  }
101
95
 
102
96
  /**
103
- * Checks that an identifier is an object of a prototype whose member
104
- * is being assigned in an AssignmentExpression.
105
- * Example: Object.prototype.foo = "bar"
106
- * @param {ASTNode} identifierNode The identifier to check.
107
- * @returns {boolean} True if the identifier's prototype is modified.
97
+ * Check if it's an assignment to the property of the given node.
98
+ * Example: `*.prop = 0` // the `*` is the given node.
99
+ * @param {ASTNode} node The node to check.
100
+ * @returns {boolean} True if an assignment to the property of the node.
108
101
  */
109
- function isInPrototypePropertyAssignment(identifierNode) {
110
- return Boolean(
111
- isPrototypePropertyAccessed(identifierNode) &&
112
- identifierNode.parent.parent.type === "MemberExpression" &&
113
- identifierNode.parent.parent.parent.type === "AssignmentExpression" &&
114
- identifierNode.parent.parent.parent.left === identifierNode.parent.parent
102
+ function isAssigningToPropertyOf(node) {
103
+ return (
104
+ node.parent.type === "MemberExpression" &&
105
+ node.parent.object === node &&
106
+ node.parent.parent.type === "AssignmentExpression" &&
107
+ node.parent.parent.left === node.parent
115
108
  );
116
109
  }
117
110
 
118
111
  /**
119
- * Checks that an identifier is an object of a prototype whose member
120
- * is being extended via the Object.defineProperty() or
121
- * Object.defineProperties() methods.
122
- * Example: Object.defineProperty(Array.prototype, "foo", ...)
123
- * Example: Object.defineProperties(Array.prototype, ...)
124
- * @param {ASTNode} identifierNode The identifier to check.
125
- * @returns {boolean} True if the identifier's prototype is modified.
112
+ * Checks if the given node is at the first argument of the method call of `Object.defineProperty()` or `Object.defineProperties()`.
113
+ * @param {ASTNode} node The node to check.
114
+ * @returns {boolean} True if the node is at the first argument of the method call of `Object.defineProperty()` or `Object.defineProperties()`.
126
115
  */
127
- function isInDefinePropertyCall(identifierNode) {
128
- return Boolean(
129
- isPrototypePropertyAccessed(identifierNode) &&
130
- identifierNode.parent.parent.type === "CallExpression" &&
131
- identifierNode.parent.parent.arguments[0] === identifierNode.parent &&
132
- identifierNode.parent.parent.callee.type === "MemberExpression" &&
133
- identifierNode.parent.parent.callee.object.type === "Identifier" &&
134
- identifierNode.parent.parent.callee.object.name === "Object" &&
135
- identifierNode.parent.parent.callee.property.type === "Identifier" &&
136
- propertyDefinitionMethods.has(identifierNode.parent.parent.callee.property.name)
116
+ function isInDefinePropertyCall(node) {
117
+ return (
118
+ node.parent.type === "CallExpression" &&
119
+ node.parent.arguments[0] === node &&
120
+ astUtils.isSpecificMemberAccess(node.parent.callee, "Object", /^definePropert(?:y|ies)$/u)
137
121
  );
138
122
  }
139
123
 
@@ -149,14 +133,27 @@ module.exports = {
149
133
  * @returns {void}
150
134
  */
151
135
  function checkAndReportPrototypeExtension(identifierNode) {
152
- if (isInPrototypePropertyAssignment(identifierNode)) {
136
+ if (!isPrototypePropertyAccessed(identifierNode)) {
137
+ return; // This is not `*.prototype` access.
138
+ }
139
+
140
+ /*
141
+ * `identifierNode.parent` is a MamberExpression `*.prototype`.
142
+ * If it's an optional member access, it may be wrapped by a `ChainExpression` node.
143
+ */
144
+ const prototypeNode =
145
+ identifierNode.parent.parent.type === "ChainExpression"
146
+ ? identifierNode.parent.parent
147
+ : identifierNode.parent;
148
+
149
+ if (isAssigningToPropertyOf(prototypeNode)) {
153
150
 
154
- // Identifier --> MemberExpression --> MemberExpression --> AssignmentExpression
155
- reportNode(identifierNode.parent.parent.parent, identifierNode.name);
156
- } else if (isInDefinePropertyCall(identifierNode)) {
151
+ // `*.prototype` -> MemberExpression -> AssignmentExpression
152
+ reportNode(prototypeNode.parent.parent, identifierNode.name);
153
+ } else if (isInDefinePropertyCall(prototypeNode)) {
157
154
 
158
- // Identifier --> MemberExpression --> CallExpression
159
- reportNode(identifierNode.parent.parent, identifierNode.name);
155
+ // `*.prototype` -> CallExpression
156
+ reportNode(prototypeNode.parent, identifierNode.name);
160
157
  }
161
158
  }
162
159
 
@@ -61,24 +61,62 @@ module.exports = {
61
61
  * @returns {void}
62
62
  */
63
63
  function report(node) {
64
+ const memberNode = node.parent;
65
+ const callNode = memberNode.parent.type === "ChainExpression"
66
+ ? memberNode.parent.parent
67
+ : memberNode.parent;
68
+
64
69
  context.report({
65
- node: node.parent.parent,
70
+ node: callNode,
66
71
  messageId: "unexpected",
67
- loc: node.parent.property.loc,
72
+ loc: memberNode.property.loc,
73
+
68
74
  fix(fixer) {
69
- if (node.parent.parent.arguments.length && !isSideEffectFree(node.parent.parent.arguments[0])) {
75
+ if (!isSideEffectFree(callNode.arguments[0])) {
70
76
  return null;
71
77
  }
72
78
 
73
- const firstTokenToRemove = sourceCode
74
- .getFirstTokenBetween(node.parent.object, node.parent.property, astUtils.isNotClosingParenToken);
75
- const lastTokenToRemove = sourceCode.getLastToken(node.parent.parent);
79
+ /*
80
+ * The list of the first/last token pair of a removal range.
81
+ * This is two parts because closing parentheses may exist between the method name and arguments.
82
+ * E.g. `(function(){}.bind ) (obj)`
83
+ * ^^^^^ ^^^^^ < removal ranges
84
+ * E.g. `(function(){}?.['bind'] ) ?.(obj)`
85
+ * ^^^^^^^^^^ ^^^^^^^ < removal ranges
86
+ */
87
+ const tokenPairs = [
88
+ [
89
+
90
+ // `.`, `?.`, or `[` token.
91
+ sourceCode.getTokenAfter(
92
+ memberNode.object,
93
+ astUtils.isNotClosingParenToken
94
+ ),
95
+
96
+ // property name or `]` token.
97
+ sourceCode.getLastToken(memberNode)
98
+ ],
99
+ [
100
+
101
+ // `?.` or `(` token of arguments.
102
+ sourceCode.getTokenAfter(
103
+ memberNode,
104
+ astUtils.isNotClosingParenToken
105
+ ),
106
+
107
+ // `)` token of arguments.
108
+ sourceCode.getLastToken(callNode)
109
+ ]
110
+ ];
111
+ const firstTokenToRemove = tokenPairs[0][0];
112
+ const lastTokenToRemove = tokenPairs[1][1];
76
113
 
77
114
  if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
78
115
  return null;
79
116
  }
80
117
 
81
- return fixer.removeRange([firstTokenToRemove.range[0], node.parent.parent.range[1]]);
118
+ return tokenPairs.map(([start, end]) =>
119
+ fixer.removeRange([start.range[0], end.range[1]]));
82
120
  }
83
121
  });
84
122
  }
@@ -93,18 +131,20 @@ module.exports = {
93
131
  * @returns {boolean} `true` if the node is the callee of `.bind()` method.
94
132
  */
95
133
  function isCalleeOfBindMethod(node) {
96
- const parent = node.parent;
97
- const grandparent = parent.parent;
134
+ if (!astUtils.isSpecificMemberAccess(node.parent, null, "bind")) {
135
+ return false;
136
+ }
137
+
138
+ // The node of `*.bind` member access.
139
+ const bindNode = node.parent.parent.type === "ChainExpression"
140
+ ? node.parent.parent
141
+ : node.parent;
98
142
 
99
143
  return (
100
- grandparent &&
101
- grandparent.type === "CallExpression" &&
102
- grandparent.callee === parent &&
103
- grandparent.arguments.length === 1 &&
104
- grandparent.arguments[0].type !== "SpreadElement" &&
105
- parent.type === "MemberExpression" &&
106
- parent.object === node &&
107
- astUtils.getStaticPropertyName(parent) === "bind"
144
+ bindNode.parent.type === "CallExpression" &&
145
+ bindNode.parent.callee === bindNode &&
146
+ bindNode.parent.arguments.length === 1 &&
147
+ bindNode.parent.arguments[0].type !== "SpreadElement"
108
148
  );
109
149
  }
110
150
 
@@ -111,6 +111,10 @@ module.exports = {
111
111
  * @returns {boolean} If the node is in one of the flagged contexts
112
112
  */
113
113
  function isInFlaggedContext(node) {
114
+ if (node.parent.type === "ChainExpression") {
115
+ return isInFlaggedContext(node.parent);
116
+ }
117
+
114
118
  return isInBooleanContext(node) ||
115
119
  (isLogicalContext(node.parent) &&
116
120
 
@@ -149,6 +153,9 @@ module.exports = {
149
153
  * @returns {boolean} `true` if the node needs to be parenthesized.
150
154
  */
151
155
  function needsParens(previousNode, node) {
156
+ if (previousNode.parent.type === "ChainExpression") {
157
+ return needsParens(previousNode.parent, node);
158
+ }
152
159
  if (isParenthesized(previousNode)) {
153
160
 
154
161
  // parentheses around the previous node will stay, so there is no need for an additional pair
@@ -100,10 +100,18 @@ module.exports = {
100
100
  * @private
101
101
  */
102
102
  function isImmediateFunctionPrototypeMethodCall(node) {
103
- return node.type === "CallExpression" &&
104
- node.callee.type === "MemberExpression" &&
105
- node.callee.object.type === "FunctionExpression" &&
106
- ["call", "apply"].includes(astUtils.getStaticPropertyName(node.callee));
103
+ const callNode = astUtils.skipChainExpression(node);
104
+
105
+ if (callNode.type !== "CallExpression") {
106
+ return false;
107
+ }
108
+ const callee = astUtils.skipChainExpression(callNode.callee);
109
+
110
+ return (
111
+ callee.type === "MemberExpression" &&
112
+ callee.object.type === "FunctionExpression" &&
113
+ ["call", "apply"].includes(astUtils.getStaticPropertyName(callee))
114
+ );
107
115
  }
108
116
 
109
117
  /**
@@ -360,7 +368,9 @@ module.exports = {
360
368
  * @returns {boolean} `true` if the given node is an IIFE
361
369
  */
362
370
  function isIIFE(node) {
363
- return node.type === "CallExpression" && node.callee.type === "FunctionExpression";
371
+ const maybeCallNode = astUtils.skipChainExpression(node);
372
+
373
+ return maybeCallNode.type === "CallExpression" && maybeCallNode.callee.type === "FunctionExpression";
364
374
  }
365
375
 
366
376
  /**
@@ -466,13 +476,16 @@ module.exports = {
466
476
 
467
477
  if (
468
478
  hasDoubleExcessParens(callee) ||
469
- !isIIFE(node) && !hasNewParensException && !(
479
+ !isIIFE(node) &&
480
+ !hasNewParensException &&
481
+ !(
470
482
 
471
483
  // Allow extra parens around a new expression if they are intervening parentheses.
472
484
  node.type === "NewExpression" &&
473
485
  callee.type === "MemberExpression" &&
474
486
  doesMemberExpressionContainCallExpression(callee)
475
- )
487
+ ) &&
488
+ !(!node.optional && callee.type === "ChainExpression")
476
489
  ) {
477
490
  report(node.callee);
478
491
  }
@@ -1004,6 +1017,13 @@ module.exports = {
1004
1017
  report(node.object);
1005
1018
  }
1006
1019
 
1020
+ if (nodeObjHasExcessParens &&
1021
+ node.optional &&
1022
+ node.object.type === "ChainExpression"
1023
+ ) {
1024
+ report(node.object);
1025
+ }
1026
+
1007
1027
  if (node.computed && hasExcessParens(node.property)) {
1008
1028
  report(node.property);
1009
1029
  }
@@ -47,12 +47,14 @@ function isDoubleLogicalNegating(node) {
47
47
  * @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
48
48
  */
49
49
  function isBinaryNegatingOfIndexOf(node) {
50
+ if (node.operator !== "~") {
51
+ return false;
52
+ }
53
+ const callNode = astUtils.skipChainExpression(node.argument);
54
+
50
55
  return (
51
- node.operator === "~" &&
52
- node.argument.type === "CallExpression" &&
53
- node.argument.callee.type === "MemberExpression" &&
54
- node.argument.callee.property.type === "Identifier" &&
55
- INDEX_OF_PATTERN.test(node.argument.callee.property.name)
56
+ callNode.type === "CallExpression" &&
57
+ astUtils.isSpecificMemberAccess(callNode.callee, null, INDEX_OF_PATTERN)
56
58
  );
57
59
  }
58
60
 
@@ -246,7 +248,10 @@ module.exports = {
246
248
  // ~foo.indexOf(bar)
247
249
  operatorAllowed = options.allow.indexOf("~") >= 0;
248
250
  if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
249
- const recommendation = `${sourceCode.getText(node.argument)} !== -1`;
251
+
252
+ // `foo?.indexOf(bar) !== -1` will be true (== found) if the `foo` is nullish. So use `>= 0` in that case.
253
+ const comparison = node.argument.type === "ChainExpression" ? ">= 0" : "!== -1";
254
+ const recommendation = `${sourceCode.getText(node.argument)} ${comparison}`;
250
255
 
251
256
  report(node, recommendation, false);
252
257
  }
@@ -35,8 +35,8 @@ module.exports = {
35
35
  },
36
36
 
37
37
  create(context) {
38
- const EVAL_LIKE_FUNCS = Object.freeze(["setTimeout", "execScript", "setInterval"]);
39
38
  const GLOBAL_CANDIDATES = Object.freeze(["global", "window", "globalThis"]);
39
+ const EVAL_LIKE_FUNC_PATTERN = /^(?:set(?:Interval|Timeout)|execScript)$/u;
40
40
 
41
41
  /**
42
42
  * Checks whether a node is evaluated as a string or not.
@@ -56,28 +56,6 @@ module.exports = {
56
56
  return false;
57
57
  }
58
58
 
59
- /**
60
- * Checks whether a node is an Identifier node named one of the specified names.
61
- * @param {ASTNode} node A node to check.
62
- * @param {string[]} specifiers Array of specified name.
63
- * @returns {boolean} True if the node is a Identifier node which has specified name.
64
- */
65
- function isSpecifiedIdentifier(node, specifiers) {
66
- return node.type === "Identifier" && specifiers.includes(node.name);
67
- }
68
-
69
- /**
70
- * Checks a given node is a MemberExpression node which has the specified name's
71
- * property.
72
- * @param {ASTNode} node A node to check.
73
- * @param {string[]} specifiers Array of specified name.
74
- * @returns {boolean} `true` if the node is a MemberExpression node which has
75
- * the specified name's property
76
- */
77
- function isSpecifiedMember(node, specifiers) {
78
- return node.type === "MemberExpression" && specifiers.includes(astUtils.getStaticPropertyName(node));
79
- }
80
-
81
59
  /**
82
60
  * Reports if the `CallExpression` node has evaluated argument.
83
61
  * @param {ASTNode} node A CallExpression to check.
@@ -114,14 +92,15 @@ module.exports = {
114
92
  const identifier = ref.identifier;
115
93
  let node = identifier.parent;
116
94
 
117
- while (isSpecifiedMember(node, [name])) {
95
+ while (astUtils.isSpecificMemberAccess(node, null, name)) {
118
96
  node = node.parent;
119
97
  }
120
98
 
121
- if (isSpecifiedMember(node, EVAL_LIKE_FUNCS)) {
122
- const parent = node.parent;
99
+ if (astUtils.isSpecificMemberAccess(node, null, EVAL_LIKE_FUNC_PATTERN)) {
100
+ const calleeNode = node.parent.type === "ChainExpression" ? node.parent : node;
101
+ const parent = calleeNode.parent;
123
102
 
124
- if (parent.type === "CallExpression" && parent.callee === node) {
103
+ if (parent.type === "CallExpression" && parent.callee === calleeNode) {
125
104
  reportImpliedEvalCallExpression(parent);
126
105
  }
127
106
  }
@@ -134,7 +113,7 @@ module.exports = {
134
113
 
135
114
  return {
136
115
  CallExpression(node) {
137
- if (isSpecifiedIdentifier(node.callee, EVAL_LIKE_FUNCS)) {
116
+ if (astUtils.isSpecificId(node.callee, EVAL_LIKE_FUNC_PATTERN)) {
138
117
  reportImpliedEvalCallExpression(node);
139
118
  }
140
119
  },
@@ -9,16 +9,12 @@
9
9
  // Helpers
10
10
  //------------------------------------------------------------------------------
11
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
- ])
12
+ const { findVariable } = require("eslint-utils");
13
+ const astUtils = require("./utils/ast-utils");
14
+
15
+ const WellKnownMutationFunctions = {
16
+ Object: /^(?:assign|definePropert(?:y|ies)|freeze|setPrototypeOf)$/u,
17
+ Reflect: /^(?:(?:define|delete)Property|set(?:PrototypeOf)?)$/u
22
18
  };
23
19
 
24
20
  /**
@@ -56,17 +52,20 @@ function isAssignmentLeft(node) {
56
52
  * @returns {boolean} `true` if the node is the operand of mutation unary operator.
57
53
  */
58
54
  function isOperandOfMutationUnaryOperator(node) {
59
- const { parent } = node;
55
+ const argumentNode = node.parent.type === "ChainExpression"
56
+ ? node.parent
57
+ : node;
58
+ const { parent } = argumentNode;
60
59
 
61
60
  return (
62
61
  (
63
62
  parent.type === "UpdateExpression" &&
64
- parent.argument === node
63
+ parent.argument === argumentNode
65
64
  ) ||
66
65
  (
67
66
  parent.type === "UnaryExpression" &&
68
67
  parent.operator === "delete" &&
69
- parent.argument === node
68
+ parent.argument === argumentNode
70
69
  )
71
70
  );
72
71
  }
@@ -92,35 +91,37 @@ function isIterationVariable(node) {
92
91
  }
93
92
 
94
93
  /**
95
- * Check if a given node is the iteration variable of `for-in`/`for-of` syntax.
94
+ * Check if a given node is at the first argument of a well-known mutation function.
95
+ * - `Object.assign`
96
+ * - `Object.defineProperty`
97
+ * - `Object.defineProperties`
98
+ * - `Object.freeze`
99
+ * - `Object.setPrototypeOf`
100
+ * - `Refrect.defineProperty`
101
+ * - `Refrect.deleteProperty`
102
+ * - `Refrect.set`
103
+ * - `Refrect.setPrototypeOf`
96
104
  * @param {ASTNode} node The node to check.
97
105
  * @param {Scope} scope A `escope.Scope` object to find variable (whichever).
98
- * @returns {boolean} `true` if the node is the iteration variable.
106
+ * @returns {boolean} `true` if the node is at the first argument of a well-known mutation function.
99
107
  */
100
108
  function isArgumentOfWellKnownMutationFunction(node, scope) {
101
109
  const { parent } = node;
102
110
 
111
+ if (parent.type !== "CallExpression" || parent.arguments[0] !== node) {
112
+ return false;
113
+ }
114
+ const callee = astUtils.skipChainExpression(parent.callee);
115
+
103
116
  if (
104
- parent.type === "CallExpression" &&
105
- parent.arguments[0] === node &&
106
- parent.callee.type === "MemberExpression" &&
107
- parent.callee.object.type === "Identifier"
117
+ !astUtils.isSpecificMemberAccess(callee, "Object", WellKnownMutationFunctions.Object) &&
118
+ !astUtils.isSpecificMemberAccess(callee, "Reflect", WellKnownMutationFunctions.Reflect)
108
119
  ) {
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
- }
120
+ return false;
121
121
  }
122
+ const variable = findVariable(scope, callee.object);
122
123
 
123
- return false;
124
+ return variable !== null && variable.scope.type === "global";
124
125
  }
125
126
 
126
127
  /**
@@ -91,7 +91,7 @@ module.exports = {
91
91
  const locStart = node.loc.start;
92
92
  const locEnd = node.loc.end;
93
93
 
94
- errors = errors.filter(({ loc: errorLoc }) => {
94
+ errors = errors.filter(({ loc: { start: errorLoc } }) => {
95
95
  if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) {
96
96
  if (errorLoc.column >= locStart.column && (errorLoc.column <= locEnd.column || errorLoc.line < locEnd.line)) {
97
97
  return false;
@@ -160,15 +160,19 @@ module.exports = {
160
160
  let match;
161
161
 
162
162
  while ((match = IRREGULAR_WHITESPACE.exec(sourceLine)) !== null) {
163
- const location = {
164
- line: lineNumber,
165
- column: match.index
166
- };
167
-
168
163
  errors.push({
169
164
  node,
170
165
  messageId: "noIrregularWhitespace",
171
- loc: location
166
+ loc: {
167
+ start: {
168
+ line: lineNumber,
169
+ column: match.index
170
+ },
171
+ end: {
172
+ line: lineNumber,
173
+ column: match.index + match[0].length
174
+ }
175
+ }
172
176
  });
173
177
  }
174
178
  });
@@ -189,16 +193,22 @@ module.exports = {
189
193
 
190
194
  while ((match = IRREGULAR_LINE_TERMINATORS.exec(source)) !== null) {
191
195
  const lineIndex = linebreaks.indexOf(match[0], lastLineIndex + 1) || 0;
192
- const location = {
193
- line: lineIndex + 1,
194
- column: sourceLines[lineIndex].length
195
- };
196
196
 
197
197
  errors.push({
198
198
  node,
199
199
  messageId: "noIrregularWhitespace",
200
- loc: location
200
+ loc: {
201
+ start: {
202
+ line: lineIndex + 1,
203
+ column: sourceLines[lineIndex].length
204
+ },
205
+ end: {
206
+ line: lineIndex + 2,
207
+ column: 0
208
+ }
209
+ }
201
210
  });
211
+
202
212
  lastLineIndex = lineIndex;
203
213
  }
204
214
  }
@@ -5,7 +5,7 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const { isNumericLiteral } = require("./utils/ast-utils");
8
+ const astUtils = require("./utils/ast-utils");
9
9
 
10
10
  // Maximum array length by the ECMAScript Specification.
11
11
  const MAX_ARRAY_LENGTH = 2 ** 32 - 1;
@@ -100,12 +100,8 @@ module.exports = {
100
100
 
101
101
  return parent.type === "CallExpression" && fullNumberNode === parent.arguments[1] &&
102
102
  (
103
- parent.callee.name === "parseInt" ||
104
- (
105
- parent.callee.type === "MemberExpression" &&
106
- parent.callee.object.name === "Number" &&
107
- parent.callee.property.name === "parseInt"
108
- )
103
+ astUtils.isSpecificId(parent.callee, "parseInt") ||
104
+ astUtils.isSpecificMemberAccess(parent.callee, "Number", "parseInt")
109
105
  );
110
106
  }
111
107
 
@@ -157,7 +153,7 @@ module.exports = {
157
153
 
158
154
  return {
159
155
  Literal(node) {
160
- if (!isNumericLiteral(node)) {
156
+ if (!astUtils.isNumericLiteral(node)) {
161
157
  return;
162
158
  }
163
159
 
@@ -24,10 +24,13 @@ const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect"];
24
24
  * @returns {string} name to report
25
25
  */
26
26
  function getReportNodeName(node) {
27
- if (node.callee.type === "MemberExpression") {
28
- return getPropertyName(node.callee);
27
+ if (node.type === "ChainExpression") {
28
+ return getReportNodeName(node.expression);
29
29
  }
30
- return node.callee.name;
30
+ if (node.type === "MemberExpression") {
31
+ return getPropertyName(node);
32
+ }
33
+ return node.name;
31
34
  }
32
35
 
33
36
  //------------------------------------------------------------------------------
@@ -69,7 +72,7 @@ module.exports = {
69
72
  }
70
73
 
71
74
  for (const { node, path } of tracker.iterateGlobalReferences(traceMap)) {
72
- const name = getReportNodeName(node);
75
+ const name = getReportNodeName(node.callee);
73
76
  const ref = path[0];
74
77
  const messageId = name === ref ? "unexpectedCall" : "unexpectedRefCall";
75
78
 
@@ -4,6 +4,12 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const astUtils = require("./utils/ast-utils");
12
+
7
13
  //------------------------------------------------------------------------------
8
14
  // Rule Definition
9
15
  //------------------------------------------------------------------------------
@@ -39,15 +45,19 @@ module.exports = {
39
45
  * @returns {void}
40
46
  */
41
47
  function disallowBuiltIns(node) {
42
- if (node.callee.type !== "MemberExpression" || node.callee.computed) {
48
+
49
+ // TODO: just use `astUtils.getStaticPropertyName(node.callee)`
50
+ const callee = astUtils.skipChainExpression(node.callee);
51
+
52
+ if (callee.type !== "MemberExpression" || callee.computed) {
43
53
  return;
44
54
  }
45
- const propName = node.callee.property.name;
55
+ const propName = callee.property.name;
46
56
 
47
57
  if (DISALLOWED_PROPS.indexOf(propName) > -1) {
48
58
  context.report({
49
59
  messageId: "prototypeBuildIn",
50
- loc: node.callee.property.loc,
60
+ loc: callee.property.loc,
51
61
  data: { prop: propName },
52
62
  node
53
63
  });