eslint 7.2.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 (76) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +14 -7
  3. package/lib/init/config-initializer.js +89 -68
  4. package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
  5. package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
  6. package/lib/linter/code-path-analysis/code-path-state.js +59 -0
  7. package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
  8. package/lib/rule-tester/rule-tester.js +9 -0
  9. package/lib/rules/accessor-pairs.js +1 -14
  10. package/lib/rules/array-callback-return.js +26 -17
  11. package/lib/rules/arrow-body-style.js +43 -8
  12. package/lib/rules/arrow-parens.js +91 -108
  13. package/lib/rules/camelcase.js +47 -0
  14. package/lib/rules/comma-dangle.js +2 -1
  15. package/lib/rules/consistent-return.js +1 -12
  16. package/lib/rules/constructor-super.js +1 -0
  17. package/lib/rules/curly.js +8 -1
  18. package/lib/rules/dot-location.js +20 -14
  19. package/lib/rules/dot-notation.js +36 -33
  20. package/lib/rules/func-call-spacing.js +42 -6
  21. package/lib/rules/func-name-matching.js +1 -4
  22. package/lib/rules/global-require.js +2 -1
  23. package/lib/rules/id-blacklist.js +14 -11
  24. package/lib/rules/id-denylist.js +230 -0
  25. package/lib/rules/id-match.js +2 -1
  26. package/lib/rules/indent.js +19 -0
  27. package/lib/rules/index.js +3 -0
  28. package/lib/rules/key-spacing.js +6 -2
  29. package/lib/rules/keyword-spacing.js +2 -2
  30. package/lib/rules/max-len.js +13 -2
  31. package/lib/rules/max-lines.js +34 -8
  32. package/lib/rules/new-cap.js +10 -14
  33. package/lib/rules/newline-per-chained-call.js +15 -5
  34. package/lib/rules/no-alert.js +10 -3
  35. package/lib/rules/no-eval.js +8 -38
  36. package/lib/rules/no-extend-native.js +37 -40
  37. package/lib/rules/no-extra-bind.js +57 -17
  38. package/lib/rules/no-extra-boolean-cast.js +7 -0
  39. package/lib/rules/no-extra-parens.js +48 -10
  40. package/lib/rules/no-implicit-coercion.js +11 -6
  41. package/lib/rules/no-implied-eval.js +7 -28
  42. package/lib/rules/no-import-assign.js +33 -32
  43. package/lib/rules/no-irregular-whitespace.js +22 -12
  44. package/lib/rules/no-magic-numbers.js +4 -8
  45. package/lib/rules/no-obj-calls.js +7 -4
  46. package/lib/rules/no-promise-executor-return.js +121 -0
  47. package/lib/rules/no-prototype-builtins.js +13 -3
  48. package/lib/rules/no-self-assign.js +3 -53
  49. package/lib/rules/no-setter-return.js +5 -8
  50. package/lib/rules/no-unexpected-multiline.js +2 -2
  51. package/lib/rules/no-unneeded-ternary.js +0 -2
  52. package/lib/rules/no-unreachable-loop.js +150 -0
  53. package/lib/rules/no-unused-expressions.js +55 -23
  54. package/lib/rules/no-unused-vars.js +2 -1
  55. package/lib/rules/no-useless-call.js +10 -7
  56. package/lib/rules/no-whitespace-before-property.js +16 -4
  57. package/lib/rules/object-curly-newline.js +4 -4
  58. package/lib/rules/object-property-newline.js +1 -1
  59. package/lib/rules/operator-assignment.js +3 -42
  60. package/lib/rules/operator-linebreak.js +2 -5
  61. package/lib/rules/padded-blocks.js +2 -1
  62. package/lib/rules/padding-line-between-statements.js +2 -2
  63. package/lib/rules/prefer-arrow-callback.js +90 -25
  64. package/lib/rules/prefer-exponentiation-operator.js +1 -1
  65. package/lib/rules/prefer-numeric-literals.js +4 -13
  66. package/lib/rules/prefer-promise-reject-errors.js +1 -3
  67. package/lib/rules/prefer-regex-literals.js +68 -13
  68. package/lib/rules/prefer-spread.js +2 -6
  69. package/lib/rules/radix.js +5 -2
  70. package/lib/rules/semi-spacing.js +1 -0
  71. package/lib/rules/sort-imports.js +28 -0
  72. package/lib/rules/use-isnan.js +1 -1
  73. package/lib/rules/utils/ast-utils.js +317 -153
  74. package/lib/rules/wrap-iife.js +9 -2
  75. package/lib/rules/yoda.js +2 -55
  76. package/package.json +7 -7
@@ -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
  }
@@ -710,6 +723,20 @@ module.exports = {
710
723
  reportsBuffer.reports = reportsBuffer.reports.filter(r => r.node !== node);
711
724
  }
712
725
 
726
+ /**
727
+ * Checks whether a node is a MemberExpression at NewExpression's callee.
728
+ * @param {ASTNode} node node to check.
729
+ * @returns {boolean} True if the node is a MemberExpression at NewExpression's callee. false otherwise.
730
+ */
731
+ function isMemberExpInNewCallee(node) {
732
+ if (node.type === "MemberExpression") {
733
+ return node.parent.type === "NewExpression" && node.parent.callee === node
734
+ ? true
735
+ : node.parent.object === node && isMemberExpInNewCallee(node.parent);
736
+ }
737
+ return false;
738
+ }
739
+
713
740
  return {
714
741
  ArrayExpression(node) {
715
742
  node.elements
@@ -950,7 +977,11 @@ module.exports = {
950
977
  LogicalExpression: checkBinaryLogical,
951
978
 
952
979
  MemberExpression(node) {
953
- const nodeObjHasExcessParens = hasExcessParens(node.object) &&
980
+ const shouldAllowWrapOnce = isMemberExpInNewCallee(node) &&
981
+ doesMemberExpressionContainCallExpression(node);
982
+ const nodeObjHasExcessParens = shouldAllowWrapOnce
983
+ ? hasDoubleExcessParens(node.object)
984
+ : hasExcessParens(node.object) &&
954
985
  !(
955
986
  isImmediateFunctionPrototypeMethodCall(node.parent) &&
956
987
  node.parent.callee === node &&
@@ -974,8 +1005,8 @@ module.exports = {
974
1005
  }
975
1006
 
976
1007
  if (nodeObjHasExcessParens &&
977
- node.object.type === "CallExpression" &&
978
- node.parent.type !== "NewExpression") {
1008
+ node.object.type === "CallExpression"
1009
+ ) {
979
1010
  report(node.object);
980
1011
  }
981
1012
 
@@ -986,6 +1017,13 @@ module.exports = {
986
1017
  report(node.object);
987
1018
  }
988
1019
 
1020
+ if (nodeObjHasExcessParens &&
1021
+ node.optional &&
1022
+ node.object.type === "ChainExpression"
1023
+ ) {
1024
+ report(node.object);
1025
+ }
1026
+
989
1027
  if (node.computed && hasExcessParens(node.property)) {
990
1028
  report(node.property);
991
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
 
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview Rule to disallow returning values from Promise executor functions
3
+ * @author Milos Djermanovic
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const { findVariable } = require("eslint-utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ const functionTypesToCheck = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
19
+
20
+ /**
21
+ * Determines whether the given identifier node is a reference to a global variable.
22
+ * @param {ASTNode} node `Identifier` node to check.
23
+ * @param {Scope} scope Scope to which the node belongs.
24
+ * @returns {boolean} True if the identifier is a reference to a global variable.
25
+ */
26
+ function isGlobalReference(node, scope) {
27
+ const variable = findVariable(scope, node);
28
+
29
+ return variable !== null && variable.scope.type === "global" && variable.defs.length === 0;
30
+ }
31
+
32
+ /**
33
+ * Finds function's outer scope.
34
+ * @param {Scope} scope Function's own scope.
35
+ * @returns {Scope} Function's outer scope.
36
+ */
37
+ function getOuterScope(scope) {
38
+ const upper = scope.upper;
39
+
40
+ if (upper.type === "function-expression-name") {
41
+ return upper.upper;
42
+ }
43
+ return upper;
44
+ }
45
+
46
+ /**
47
+ * Determines whether the given function node is used as a Promise executor.
48
+ * @param {ASTNode} node The node to check.
49
+ * @param {Scope} scope Function's own scope.
50
+ * @returns {boolean} `true` if the node is a Promise executor.
51
+ */
52
+ function isPromiseExecutor(node, scope) {
53
+ const parent = node.parent;
54
+
55
+ return parent.type === "NewExpression" &&
56
+ parent.arguments[0] === node &&
57
+ parent.callee.type === "Identifier" &&
58
+ parent.callee.name === "Promise" &&
59
+ isGlobalReference(parent.callee, getOuterScope(scope));
60
+ }
61
+
62
+ //------------------------------------------------------------------------------
63
+ // Rule Definition
64
+ //------------------------------------------------------------------------------
65
+
66
+ module.exports = {
67
+ meta: {
68
+ type: "problem",
69
+
70
+ docs: {
71
+ description: "disallow returning values from Promise executor functions",
72
+ category: "Possible Errors",
73
+ recommended: false,
74
+ url: "https://eslint.org/docs/rules/no-promise-executor-return"
75
+ },
76
+
77
+ schema: [],
78
+
79
+ messages: {
80
+ returnsValue: "Return values from promise executor functions cannot be read."
81
+ }
82
+ },
83
+
84
+ create(context) {
85
+
86
+ let funcInfo = null;
87
+
88
+ /**
89
+ * Reports the given node.
90
+ * @param {ASTNode} node Node to report.
91
+ * @returns {void}
92
+ */
93
+ function report(node) {
94
+ context.report({ node, messageId: "returnsValue" });
95
+ }
96
+
97
+ return {
98
+
99
+ onCodePathStart(_, node) {
100
+ funcInfo = {
101
+ upper: funcInfo,
102
+ shouldCheck: functionTypesToCheck.has(node.type) && isPromiseExecutor(node, context.getScope())
103
+ };
104
+
105
+ if (funcInfo.shouldCheck && node.type === "ArrowFunctionExpression" && node.expression) {
106
+ report(node.body);
107
+ }
108
+ },
109
+
110
+ onCodePathEnd() {
111
+ funcInfo = funcInfo.upper;
112
+ },
113
+
114
+ ReturnStatement(node) {
115
+ if (funcInfo.shouldCheck && node.argument) {
116
+ report(node);
117
+ }
118
+ }
119
+ };
120
+ }
121
+ };
@@ -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
  });
@@ -17,56 +17,6 @@ const astUtils = require("./utils/ast-utils");
17
17
 
18
18
  const SPACES = /\s+/gu;
19
19
 
20
- /**
21
- * Checks whether the property of 2 given member expression nodes are the same
22
- * property or not.
23
- * @param {ASTNode} left A member expression node to check.
24
- * @param {ASTNode} right Another member expression node to check.
25
- * @returns {boolean} `true` if the member expressions have the same property.
26
- */
27
- function isSameProperty(left, right) {
28
- if (left.property.type === "Identifier" &&
29
- left.property.type === right.property.type &&
30
- left.property.name === right.property.name &&
31
- left.computed === right.computed
32
- ) {
33
- return true;
34
- }
35
-
36
- const lname = astUtils.getStaticPropertyName(left);
37
- const rname = astUtils.getStaticPropertyName(right);
38
-
39
- return lname !== null && lname === rname;
40
- }
41
-
42
- /**
43
- * Checks whether 2 given member expression nodes are the reference to the same
44
- * property or not.
45
- * @param {ASTNode} left A member expression node to check.
46
- * @param {ASTNode} right Another member expression node to check.
47
- * @returns {boolean} `true` if the member expressions are the reference to the
48
- * same property or not.
49
- */
50
- function isSameMember(left, right) {
51
- if (!isSameProperty(left, right)) {
52
- return false;
53
- }
54
-
55
- const lobj = left.object;
56
- const robj = right.object;
57
-
58
- if (lobj.type !== robj.type) {
59
- return false;
60
- }
61
- if (lobj.type === "MemberExpression") {
62
- return isSameMember(lobj, robj);
63
- }
64
- if (lobj.type === "ThisExpression") {
65
- return true;
66
- }
67
- return lobj.type === "Identifier" && lobj.name === robj.name;
68
- }
69
-
70
20
  /**
71
21
  * Traverses 2 Pattern nodes in parallel, then reports self-assignments.
72
22
  * @param {ASTNode|null} left A left node to traverse. This is a Pattern or
@@ -162,9 +112,9 @@ function eachSelfAssignment(left, right, props, report) {
162
112
  }
163
113
  } else if (
164
114
  props &&
165
- left.type === "MemberExpression" &&
166
- right.type === "MemberExpression" &&
167
- isSameMember(left, right)
115
+ astUtils.skipChainExpression(left).type === "MemberExpression" &&
116
+ astUtils.skipChainExpression(right).type === "MemberExpression" &&
117
+ astUtils.isSameReference(left, right)
168
118
  ) {
169
119
  report(right);
170
120
  }