eslint 8.48.0 → 8.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -288,8 +288,8 @@ The following companies, organizations, and individuals support ESLint's ongoing
288
288
  <h3>Platinum Sponsors</h3>
289
289
  <p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
290
290
  <p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
291
- <p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
292
- <p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://github.com/about"><img src="https://avatars.githubusercontent.com/u/9919?v=4" alt="GitHub" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
291
+ <p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://opensource.siemens.com"><img src="https://avatars.githubusercontent.com/u/624020?v=4" alt="Siemens" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
292
+ <p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://github.com/about"><img src="https://avatars.githubusercontent.com/u/9919?v=4" alt="GitHub" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
293
293
  <!--sponsorsend-->
294
294
 
295
295
  ## Technology Sponsors
@@ -9,7 +9,8 @@
9
9
  // Requirements
10
10
  //-----------------------------------------------------------------------------
11
11
 
12
- const ajv = require("../shared/ajv")();
12
+ const ajvImport = require("../shared/ajv");
13
+ const ajv = ajvImport();
13
14
  const {
14
15
  parseRuleId,
15
16
  getRuleFromConfig,
@@ -192,15 +192,18 @@ function forwardCurrentToHead(analyzer, node) {
192
192
  headSegment = headSegments[i];
193
193
 
194
194
  if (currentSegment !== headSegment && currentSegment) {
195
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
196
195
 
197
- if (currentSegment.reachable) {
198
- analyzer.emitter.emit(
199
- "onCodePathSegmentEnd",
200
- currentSegment,
201
- node
202
- );
203
- }
196
+ const eventName = currentSegment.reachable
197
+ ? "onCodePathSegmentEnd"
198
+ : "onUnreachableCodePathSegmentEnd";
199
+
200
+ debug.dump(`${eventName} ${currentSegment.id}`);
201
+
202
+ analyzer.emitter.emit(
203
+ eventName,
204
+ currentSegment,
205
+ node
206
+ );
204
207
  }
205
208
  }
206
209
 
@@ -213,16 +216,19 @@ function forwardCurrentToHead(analyzer, node) {
213
216
  headSegment = headSegments[i];
214
217
 
215
218
  if (currentSegment !== headSegment && headSegment) {
216
- debug.dump(`onCodePathSegmentStart ${headSegment.id}`);
219
+
220
+ const eventName = headSegment.reachable
221
+ ? "onCodePathSegmentStart"
222
+ : "onUnreachableCodePathSegmentStart";
223
+
224
+ debug.dump(`${eventName} ${headSegment.id}`);
217
225
 
218
226
  CodePathSegment.markUsed(headSegment);
219
- if (headSegment.reachable) {
220
- analyzer.emitter.emit(
221
- "onCodePathSegmentStart",
222
- headSegment,
223
- node
224
- );
225
- }
227
+ analyzer.emitter.emit(
228
+ eventName,
229
+ headSegment,
230
+ node
231
+ );
226
232
  }
227
233
  }
228
234
 
@@ -241,15 +247,17 @@ function leaveFromCurrentSegment(analyzer, node) {
241
247
 
242
248
  for (let i = 0; i < currentSegments.length; ++i) {
243
249
  const currentSegment = currentSegments[i];
250
+ const eventName = currentSegment.reachable
251
+ ? "onCodePathSegmentEnd"
252
+ : "onUnreachableCodePathSegmentEnd";
244
253
 
245
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
246
- if (currentSegment.reachable) {
247
- analyzer.emitter.emit(
248
- "onCodePathSegmentEnd",
249
- currentSegment,
250
- node
251
- );
252
- }
254
+ debug.dump(`${eventName} ${currentSegment.id}`);
255
+
256
+ analyzer.emitter.emit(
257
+ eventName,
258
+ currentSegment,
259
+ node
260
+ );
253
261
  }
254
262
 
255
263
  state.currentSegments = [];
@@ -117,6 +117,7 @@ class CodePath {
117
117
  /**
118
118
  * Current code path segments.
119
119
  * @type {CodePathSegment[]}
120
+ * @deprecated
120
121
  */
121
122
  get currentSegments() {
122
123
  return this.internal.currentSegments;
@@ -898,6 +898,7 @@ const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
898
898
  getTokensBetween: "getTokensBetween"
899
899
  };
900
900
 
901
+
901
902
  const BASE_TRAVERSAL_CONTEXT = Object.freeze(
902
903
  Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).reduce(
903
904
  (contextInfo, methodName) =>
@@ -16,7 +16,9 @@ const
16
16
  equal = require("fast-deep-equal"),
17
17
  Traverser = require("../shared/traverser"),
18
18
  { getRuleOptionsSchema } = require("../config/flat-config-helpers"),
19
- { Linter, SourceCodeFixer, interpolate } = require("../linter");
19
+ { Linter, SourceCodeFixer, interpolate } = require("../linter"),
20
+ CodePath = require("../linter/code-path-analysis/code-path");
21
+
20
22
  const { FlatConfigArray } = require("../config/flat-config-array");
21
23
  const { defaultConfig } = require("../config/default-config");
22
24
 
@@ -274,6 +276,21 @@ function getCommentsDeprecation() {
274
276
  );
275
277
  }
276
278
 
279
+ /**
280
+ * Emit a deprecation warning if rule uses CodePath#currentSegments.
281
+ * @param {string} ruleName Name of the rule.
282
+ * @returns {void}
283
+ */
284
+ function emitCodePathCurrentSegmentsWarning(ruleName) {
285
+ if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
286
+ emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
287
+ process.emitWarning(
288
+ `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
289
+ "DeprecationWarning"
290
+ );
291
+ }
292
+ }
293
+
277
294
  //------------------------------------------------------------------------------
278
295
  // Public Interface
279
296
  //------------------------------------------------------------------------------
@@ -664,6 +681,7 @@ class FlatRuleTester {
664
681
 
665
682
  // Verify the code.
666
683
  const { getComments } = SourceCode.prototype;
684
+ const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
667
685
  let messages;
668
686
 
669
687
  // check for validation errors
@@ -677,11 +695,20 @@ class FlatRuleTester {
677
695
 
678
696
  try {
679
697
  SourceCode.prototype.getComments = getCommentsDeprecation;
698
+ Object.defineProperty(CodePath.prototype, "currentSegments", {
699
+ get() {
700
+ emitCodePathCurrentSegmentsWarning(ruleName);
701
+ return originalCurrentSegments.get.call(this);
702
+ }
703
+ });
704
+
680
705
  messages = linter.verify(code, configs, filename);
681
706
  } finally {
682
707
  SourceCode.prototype.getComments = getComments;
708
+ Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
683
709
  }
684
710
 
711
+
685
712
  const fatalErrorMessage = messages.find(m => m.fatal);
686
713
 
687
714
  assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
@@ -48,7 +48,8 @@ const
48
48
  equal = require("fast-deep-equal"),
49
49
  Traverser = require("../../lib/shared/traverser"),
50
50
  { getRuleOptionsSchema, validate } = require("../shared/config-validator"),
51
- { Linter, SourceCodeFixer, interpolate } = require("../linter");
51
+ { Linter, SourceCodeFixer, interpolate } = require("../linter"),
52
+ CodePath = require("../linter/code-path-analysis/code-path");
52
53
 
53
54
  const ajv = require("../shared/ajv")({ strictDefaults: true });
54
55
 
@@ -164,6 +165,30 @@ const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters
164
165
 
165
166
  const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
166
167
 
168
+ const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
169
+ getSource: "getText",
170
+ getSourceLines: "getLines",
171
+ getAllComments: "getAllComments",
172
+ getNodeByRangeIndex: "getNodeByRangeIndex",
173
+
174
+ // getComments: "getComments", -- already handled by a separate error
175
+ getCommentsBefore: "getCommentsBefore",
176
+ getCommentsAfter: "getCommentsAfter",
177
+ getCommentsInside: "getCommentsInside",
178
+ getJSDocComment: "getJSDocComment",
179
+ getFirstToken: "getFirstToken",
180
+ getFirstTokens: "getFirstTokens",
181
+ getLastToken: "getLastToken",
182
+ getLastTokens: "getLastTokens",
183
+ getTokenAfter: "getTokenAfter",
184
+ getTokenBefore: "getTokenBefore",
185
+ getTokenByRangeStart: "getTokenByRangeStart",
186
+ getTokens: "getTokens",
187
+ getTokensAfter: "getTokensAfter",
188
+ getTokensBefore: "getTokensBefore",
189
+ getTokensBetween: "getTokensBetween"
190
+ };
191
+
167
192
  /**
168
193
  * Clones a given value deeply.
169
194
  * Note: This ignores `parent` property.
@@ -335,6 +360,37 @@ function emitMissingSchemaWarning(ruleName) {
335
360
  }
336
361
  }
337
362
 
363
+ /**
364
+ * Emit a deprecation warning if a rule uses a deprecated `context` method.
365
+ * @param {string} ruleName Name of the rule.
366
+ * @param {string} methodName The name of the method on `context` that was used.
367
+ * @returns {void}
368
+ */
369
+ function emitDeprecatedContextMethodWarning(ruleName, methodName) {
370
+ if (!emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`]) {
371
+ emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`] = true;
372
+ process.emitWarning(
373
+ `"${ruleName}" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]}()\` instead.`,
374
+ "DeprecationWarning"
375
+ );
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Emit a deprecation warning if rule uses CodePath#currentSegments.
381
+ * @param {string} ruleName Name of the rule.
382
+ * @returns {void}
383
+ */
384
+ function emitCodePathCurrentSegmentsWarning(ruleName) {
385
+ if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
386
+ emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
387
+ process.emitWarning(
388
+ `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
389
+ "DeprecationWarning"
390
+ );
391
+ }
392
+ }
393
+
338
394
  //------------------------------------------------------------------------------
339
395
  // Public Interface
340
396
  //------------------------------------------------------------------------------
@@ -566,7 +622,27 @@ class RuleTester {
566
622
  freezeDeeply(context.settings);
567
623
  freezeDeeply(context.parserOptions);
568
624
 
569
- return (typeof rule === "function" ? rule : rule.create)(context);
625
+ const newContext = Object.freeze(
626
+ Object.create(
627
+ context,
628
+ Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
629
+ methodName,
630
+ {
631
+ value(...args) {
632
+
633
+ // emit deprecation warning
634
+ emitDeprecatedContextMethodWarning(ruleName, methodName);
635
+
636
+ // call the original method
637
+ return context[methodName].call(this, ...args);
638
+ },
639
+ enumerable: true
640
+ }
641
+ ]))
642
+ )
643
+ );
644
+
645
+ return (typeof rule === "function" ? rule : rule.create)(newContext);
570
646
  }
571
647
  }));
572
648
 
@@ -686,13 +762,22 @@ class RuleTester {
686
762
 
687
763
  // Verify the code.
688
764
  const { getComments } = SourceCode.prototype;
765
+ const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
689
766
  let messages;
690
767
 
691
768
  try {
692
769
  SourceCode.prototype.getComments = getCommentsDeprecation;
770
+ Object.defineProperty(CodePath.prototype, "currentSegments", {
771
+ get() {
772
+ emitCodePathCurrentSegmentsWarning(ruleName);
773
+ return originalCurrentSegments.get.call(this);
774
+ }
775
+ });
776
+
693
777
  messages = linter.verify(code, config, filename);
694
778
  } finally {
695
779
  SourceCode.prototype.getComments = getComments;
780
+ Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
696
781
  }
697
782
 
698
783
  const fatalErrorMessage = messages.find(m => m.fatal);
@@ -18,15 +18,6 @@ const astUtils = require("./utils/ast-utils");
18
18
  const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
19
19
  const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort|toSorted)$/u;
20
20
 
21
- /**
22
- * Checks a given code path segment is reachable.
23
- * @param {CodePathSegment} segment A segment to check.
24
- * @returns {boolean} `true` if the segment is reachable.
25
- */
26
- function isReachable(segment) {
27
- return segment.reachable;
28
- }
29
-
30
21
  /**
31
22
  * Checks a given node is a member access which has the specified name's
32
23
  * property.
@@ -38,6 +29,22 @@ function isTargetMethod(node) {
38
29
  return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
39
30
  }
40
31
 
32
+ /**
33
+ * Checks all segments in a set and returns true if any are reachable.
34
+ * @param {Set<CodePathSegment>} segments The segments to check.
35
+ * @returns {boolean} True if any segment is reachable; false otherwise.
36
+ */
37
+ function isAnySegmentReachable(segments) {
38
+
39
+ for (const segment of segments) {
40
+ if (segment.reachable) {
41
+ return true;
42
+ }
43
+ }
44
+
45
+ return false;
46
+ }
47
+
41
48
  /**
42
49
  * Returns a human-legible description of an array method
43
50
  * @param {string} arrayMethodName A method name to fully qualify
@@ -205,7 +212,7 @@ module.exports = {
205
212
  messageId = "expectedNoReturnValue";
206
213
  }
207
214
  } else {
208
- if (node.body.type === "BlockStatement" && funcInfo.codePath.currentSegments.some(isReachable)) {
215
+ if (node.body.type === "BlockStatement" && isAnySegmentReachable(funcInfo.currentSegments)) {
209
216
  messageId = funcInfo.hasReturn ? "expectedAtEnd" : "expectedInside";
210
217
  }
211
218
  }
@@ -242,7 +249,8 @@ module.exports = {
242
249
  methodName &&
243
250
  !node.async &&
244
251
  !node.generator,
245
- node
252
+ node,
253
+ currentSegments: new Set()
246
254
  };
247
255
  },
248
256
 
@@ -251,6 +259,23 @@ module.exports = {
251
259
  funcInfo = funcInfo.upper;
252
260
  },
253
261
 
262
+ onUnreachableCodePathSegmentStart(segment) {
263
+ funcInfo.currentSegments.add(segment);
264
+ },
265
+
266
+ onUnreachableCodePathSegmentEnd(segment) {
267
+ funcInfo.currentSegments.delete(segment);
268
+ },
269
+
270
+ onCodePathSegmentStart(segment) {
271
+ funcInfo.currentSegments.add(segment);
272
+ },
273
+
274
+ onCodePathSegmentEnd(segment) {
275
+ funcInfo.currentSegments.delete(segment);
276
+ },
277
+
278
+
254
279
  // Checks the return statement is valid.
255
280
  ReturnStatement(node) {
256
281
 
@@ -16,12 +16,19 @@ const { upperCaseFirst } = require("../shared/string-utils");
16
16
  //------------------------------------------------------------------------------
17
17
 
18
18
  /**
19
- * Checks whether or not a given code path segment is unreachable.
20
- * @param {CodePathSegment} segment A CodePathSegment to check.
21
- * @returns {boolean} `true` if the segment is unreachable.
19
+ * Checks all segments in a set and returns true if all are unreachable.
20
+ * @param {Set<CodePathSegment>} segments The segments to check.
21
+ * @returns {boolean} True if all segments are unreachable; false otherwise.
22
22
  */
23
- function isUnreachable(segment) {
24
- return !segment.reachable;
23
+ function areAllSegmentsUnreachable(segments) {
24
+
25
+ for (const segment of segments) {
26
+ if (segment.reachable) {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ return true;
25
32
  }
26
33
 
27
34
  /**
@@ -88,7 +95,7 @@ module.exports = {
88
95
  * When unreachable, all paths are returned or thrown.
89
96
  */
90
97
  if (!funcInfo.hasReturnValue ||
91
- funcInfo.codePath.currentSegments.every(isUnreachable) ||
98
+ areAllSegmentsUnreachable(funcInfo.currentSegments) ||
92
99
  astUtils.isES5Constructor(node) ||
93
100
  isClassConstructor(node)
94
101
  ) {
@@ -141,13 +148,31 @@ module.exports = {
141
148
  hasReturn: false,
142
149
  hasReturnValue: false,
143
150
  messageId: "",
144
- node
151
+ node,
152
+ currentSegments: new Set()
145
153
  };
146
154
  },
147
155
  onCodePathEnd() {
148
156
  funcInfo = funcInfo.upper;
149
157
  },
150
158
 
159
+ onUnreachableCodePathSegmentStart(segment) {
160
+ funcInfo.currentSegments.add(segment);
161
+ },
162
+
163
+ onUnreachableCodePathSegmentEnd(segment) {
164
+ funcInfo.currentSegments.delete(segment);
165
+ },
166
+
167
+ onCodePathSegmentStart(segment) {
168
+ funcInfo.currentSegments.add(segment);
169
+ },
170
+
171
+ onCodePathSegmentEnd(segment) {
172
+ funcInfo.currentSegments.delete(segment);
173
+ },
174
+
175
+
151
176
  // Reports a given return statement if it's inconsistent.
152
177
  ReturnStatement(node) {
153
178
  const argument = node.argument;
@@ -10,12 +10,19 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  /**
13
- * Checks whether a given code path segment is reachable or not.
14
- * @param {CodePathSegment} segment A code path segment to check.
15
- * @returns {boolean} `true` if the segment is reachable.
13
+ * Checks all segments in a set and returns true if any are reachable.
14
+ * @param {Set<CodePathSegment>} segments The segments to check.
15
+ * @returns {boolean} True if any segment is reachable; false otherwise.
16
16
  */
17
- function isReachable(segment) {
18
- return segment.reachable;
17
+ function isAnySegmentReachable(segments) {
18
+
19
+ for (const segment of segments) {
20
+ if (segment.reachable) {
21
+ return true;
22
+ }
23
+ }
24
+
25
+ return false;
19
26
  }
20
27
 
21
28
  /**
@@ -210,7 +217,8 @@ module.exports = {
210
217
  isConstructor: true,
211
218
  hasExtends: Boolean(superClass),
212
219
  superIsConstructor: isPossibleConstructor(superClass),
213
- codePath
220
+ codePath,
221
+ currentSegments: new Set()
214
222
  };
215
223
  } else {
216
224
  funcInfo = {
@@ -218,7 +226,8 @@ module.exports = {
218
226
  isConstructor: false,
219
227
  hasExtends: false,
220
228
  superIsConstructor: false,
221
- codePath
229
+ codePath,
230
+ currentSegments: new Set()
222
231
  };
223
232
  }
224
233
  },
@@ -261,6 +270,9 @@ module.exports = {
261
270
  * @returns {void}
262
271
  */
263
272
  onCodePathSegmentStart(segment) {
273
+
274
+ funcInfo.currentSegments.add(segment);
275
+
264
276
  if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
265
277
  return;
266
278
  }
@@ -281,6 +293,19 @@ module.exports = {
281
293
  }
282
294
  },
283
295
 
296
+ onUnreachableCodePathSegmentStart(segment) {
297
+ funcInfo.currentSegments.add(segment);
298
+ },
299
+
300
+ onUnreachableCodePathSegmentEnd(segment) {
301
+ funcInfo.currentSegments.delete(segment);
302
+ },
303
+
304
+ onCodePathSegmentEnd(segment) {
305
+ funcInfo.currentSegments.delete(segment);
306
+ },
307
+
308
+
284
309
  /**
285
310
  * Update information of the code path segment when a code path was
286
311
  * looped.
@@ -344,12 +369,11 @@ module.exports = {
344
369
 
345
370
  // Reports if needed.
346
371
  if (funcInfo.hasExtends) {
347
- const segments = funcInfo.codePath.currentSegments;
372
+ const segments = funcInfo.currentSegments;
348
373
  let duplicate = false;
349
374
  let info = null;
350
375
 
351
- for (let i = 0; i < segments.length; ++i) {
352
- const segment = segments[i];
376
+ for (const segment of segments) {
353
377
 
354
378
  if (segment.reachable) {
355
379
  info = segInfoMap[segment.id];
@@ -374,7 +398,7 @@ module.exports = {
374
398
  info.validNodes.push(node);
375
399
  }
376
400
  }
377
- } else if (funcInfo.codePath.currentSegments.some(isReachable)) {
401
+ } else if (isAnySegmentReachable(funcInfo.currentSegments)) {
378
402
  context.report({
379
403
  messageId: "unexpected",
380
404
  node
@@ -398,10 +422,9 @@ module.exports = {
398
422
  }
399
423
 
400
424
  // Returning argument is a substitute of 'super()'.
401
- const segments = funcInfo.codePath.currentSegments;
425
+ const segments = funcInfo.currentSegments;
402
426
 
403
- for (let i = 0; i < segments.length; ++i) {
404
- const segment = segments[i];
427
+ for (const segment of segments) {
405
428
 
406
429
  if (segment.reachable) {
407
430
  const info = segInfoMap[segment.id];
@@ -14,15 +14,23 @@ const astUtils = require("./utils/ast-utils");
14
14
  //------------------------------------------------------------------------------
15
15
  // Helpers
16
16
  //------------------------------------------------------------------------------
17
+
17
18
  const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
18
19
 
19
20
  /**
20
- * Checks a given code path segment is reachable.
21
- * @param {CodePathSegment} segment A segment to check.
22
- * @returns {boolean} `true` if the segment is reachable.
21
+ * Checks all segments in a set and returns true if any are reachable.
22
+ * @param {Set<CodePathSegment>} segments The segments to check.
23
+ * @returns {boolean} True if any segment is reachable; false otherwise.
23
24
  */
24
- function isReachable(segment) {
25
- return segment.reachable;
25
+ function isAnySegmentReachable(segments) {
26
+
27
+ for (const segment of segments) {
28
+ if (segment.reachable) {
29
+ return true;
30
+ }
31
+ }
32
+
33
+ return false;
26
34
  }
27
35
 
28
36
  //------------------------------------------------------------------------------
@@ -71,7 +79,8 @@ module.exports = {
71
79
  codePath: null,
72
80
  hasReturn: false,
73
81
  shouldCheck: false,
74
- node: null
82
+ node: null,
83
+ currentSegments: []
75
84
  };
76
85
 
77
86
  /**
@@ -85,7 +94,7 @@ module.exports = {
85
94
  */
86
95
  function checkLastSegment(node) {
87
96
  if (funcInfo.shouldCheck &&
88
- funcInfo.codePath.currentSegments.some(isReachable)
97
+ isAnySegmentReachable(funcInfo.currentSegments)
89
98
  ) {
90
99
  context.report({
91
100
  node,
@@ -144,7 +153,8 @@ module.exports = {
144
153
  codePath,
145
154
  hasReturn: false,
146
155
  shouldCheck: isGetter(node),
147
- node
156
+ node,
157
+ currentSegments: new Set()
148
158
  };
149
159
  },
150
160
 
@@ -152,6 +162,21 @@ module.exports = {
152
162
  onCodePathEnd() {
153
163
  funcInfo = funcInfo.upper;
154
164
  },
165
+ onUnreachableCodePathSegmentStart(segment) {
166
+ funcInfo.currentSegments.add(segment);
167
+ },
168
+
169
+ onUnreachableCodePathSegmentEnd(segment) {
170
+ funcInfo.currentSegments.delete(segment);
171
+ },
172
+
173
+ onCodePathSegmentStart(segment) {
174
+ funcInfo.currentSegments.add(segment);
175
+ },
176
+
177
+ onCodePathSegmentEnd(segment) {
178
+ funcInfo.currentSegments.delete(segment);
179
+ },
155
180
 
156
181
  // Checks the return statement is valid.
157
182
  ReturnStatement(node) {