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 +2 -2
- package/lib/config/rule-validator.js +2 -1
- package/lib/linter/code-path-analysis/code-path-analyzer.js +32 -24
- package/lib/linter/code-path-analysis/code-path.js +1 -0
- package/lib/linter/linter.js +1 -0
- package/lib/rule-tester/flat-rule-tester.js +28 -1
- package/lib/rule-tester/rule-tester.js +87 -2
- package/lib/rules/array-callback-return.js +36 -11
- package/lib/rules/consistent-return.js +32 -7
- package/lib/rules/constructor-super.js +37 -14
- package/lib/rules/getter-return.js +33 -8
- package/lib/rules/lines-between-class-members.js +92 -7
- package/lib/rules/no-fallthrough.js +42 -14
- package/lib/rules/no-this-before-super.js +38 -11
- package/lib/rules/no-unreachable-loop.js +47 -12
- package/lib/rules/no-unreachable.js +39 -10
- package/lib/rules/no-useless-return.js +35 -4
- package/lib/rules/require-atomic-updates.js +21 -7
- package/package.json +11 -9
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
|
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
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
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
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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(
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
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 = [];
|
package/lib/linter/linter.js
CHANGED
@@ -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
|
-
|
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.
|
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
|
20
|
-
* @param {CodePathSegment}
|
21
|
-
* @returns {boolean}
|
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
|
24
|
-
|
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.
|
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
|
14
|
-
* @param {CodePathSegment}
|
15
|
-
* @returns {boolean}
|
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
|
18
|
-
|
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.
|
372
|
+
const segments = funcInfo.currentSegments;
|
348
373
|
let duplicate = false;
|
349
374
|
let info = null;
|
350
375
|
|
351
|
-
for (
|
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.
|
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.
|
425
|
+
const segments = funcInfo.currentSegments;
|
402
426
|
|
403
|
-
for (
|
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
|
21
|
-
* @param {CodePathSegment}
|
22
|
-
* @returns {boolean}
|
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
|
25
|
-
|
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.
|
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) {
|