eslint 8.47.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-segment.js +52 -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 +54 -20
- package/lib/rule-tester/rule-tester.js +117 -22
- 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/for-direction.js +15 -8
- 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-promise-executor-return.js +154 -16
- 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/messages/eslintrc-incompat.js +1 -1
- 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 = [];
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @fileoverview
|
2
|
+
* @fileoverview The CodePathSegment class.
|
3
3
|
* @author Toru Nagashima
|
4
4
|
*/
|
5
5
|
|
@@ -30,10 +30,22 @@ function isReachable(segment) {
|
|
30
30
|
|
31
31
|
/**
|
32
32
|
* A code path segment.
|
33
|
+
*
|
34
|
+
* Each segment is arranged in a series of linked lists (implemented by arrays)
|
35
|
+
* that keep track of the previous and next segments in a code path. In this way,
|
36
|
+
* you can navigate between all segments in any code path so long as you have a
|
37
|
+
* reference to any segment in that code path.
|
38
|
+
*
|
39
|
+
* When first created, the segment is in a detached state, meaning that it knows the
|
40
|
+
* segments that came before it but those segments don't know that this new segment
|
41
|
+
* follows it. Only when `CodePathSegment#markUsed()` is called on a segment does it
|
42
|
+
* officially become part of the code path by updating the previous segments to know
|
43
|
+
* that this new segment follows.
|
33
44
|
*/
|
34
45
|
class CodePathSegment {
|
35
46
|
|
36
47
|
/**
|
48
|
+
* Creates a new instance.
|
37
49
|
* @param {string} id An identifier.
|
38
50
|
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
|
39
51
|
* This array includes unreachable segments.
|
@@ -49,27 +61,25 @@ class CodePathSegment {
|
|
49
61
|
this.id = id;
|
50
62
|
|
51
63
|
/**
|
52
|
-
* An array of the next segments.
|
64
|
+
* An array of the next reachable segments.
|
53
65
|
* @type {CodePathSegment[]}
|
54
66
|
*/
|
55
67
|
this.nextSegments = [];
|
56
68
|
|
57
69
|
/**
|
58
|
-
* An array of the previous segments.
|
70
|
+
* An array of the previous reachable segments.
|
59
71
|
* @type {CodePathSegment[]}
|
60
72
|
*/
|
61
73
|
this.prevSegments = allPrevSegments.filter(isReachable);
|
62
74
|
|
63
75
|
/**
|
64
|
-
* An array of
|
65
|
-
* This array includes unreachable segments.
|
76
|
+
* An array of all next segments including reachable and unreachable.
|
66
77
|
* @type {CodePathSegment[]}
|
67
78
|
*/
|
68
79
|
this.allNextSegments = [];
|
69
80
|
|
70
81
|
/**
|
71
|
-
* An array of
|
72
|
-
* This array includes unreachable segments.
|
82
|
+
* An array of all previous segments including reachable and unreachable.
|
73
83
|
* @type {CodePathSegment[]}
|
74
84
|
*/
|
75
85
|
this.allPrevSegments = allPrevSegments;
|
@@ -83,7 +93,11 @@ class CodePathSegment {
|
|
83
93
|
// Internal data.
|
84
94
|
Object.defineProperty(this, "internal", {
|
85
95
|
value: {
|
96
|
+
|
97
|
+
// determines if the segment has been attached to the code path
|
86
98
|
used: false,
|
99
|
+
|
100
|
+
// array of previous segments coming from the end of a loop
|
87
101
|
loopedPrevSegments: []
|
88
102
|
}
|
89
103
|
});
|
@@ -113,9 +127,10 @@ class CodePathSegment {
|
|
113
127
|
}
|
114
128
|
|
115
129
|
/**
|
116
|
-
* Creates a segment
|
130
|
+
* Creates a new segment and appends it after the given segments.
|
117
131
|
* @param {string} id An identifier.
|
118
|
-
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments
|
132
|
+
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments
|
133
|
+
* to append to.
|
119
134
|
* @returns {CodePathSegment} The created segment.
|
120
135
|
*/
|
121
136
|
static newNext(id, allPrevSegments) {
|
@@ -127,7 +142,7 @@ class CodePathSegment {
|
|
127
142
|
}
|
128
143
|
|
129
144
|
/**
|
130
|
-
* Creates an unreachable segment
|
145
|
+
* Creates an unreachable segment and appends it after the given segments.
|
131
146
|
* @param {string} id An identifier.
|
132
147
|
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
|
133
148
|
* @returns {CodePathSegment} The created segment.
|
@@ -137,7 +152,7 @@ class CodePathSegment {
|
|
137
152
|
|
138
153
|
/*
|
139
154
|
* In `if (a) return a; foo();` case, the unreachable segment preceded by
|
140
|
-
* the return statement is not used but must not be
|
155
|
+
* the return statement is not used but must not be removed.
|
141
156
|
*/
|
142
157
|
CodePathSegment.markUsed(segment);
|
143
158
|
|
@@ -157,7 +172,7 @@ class CodePathSegment {
|
|
157
172
|
}
|
158
173
|
|
159
174
|
/**
|
160
|
-
*
|
175
|
+
* Marks a given segment as used.
|
161
176
|
*
|
162
177
|
* And this function registers the segment into the previous segments as a next.
|
163
178
|
* @param {CodePathSegment} segment A segment to mark.
|
@@ -172,6 +187,13 @@ class CodePathSegment {
|
|
172
187
|
let i;
|
173
188
|
|
174
189
|
if (segment.reachable) {
|
190
|
+
|
191
|
+
/*
|
192
|
+
* If the segment is reachable, then it's officially part of the
|
193
|
+
* code path. This loops through all previous segments to update
|
194
|
+
* their list of next segments. Because the segment is reachable,
|
195
|
+
* it's added to both `nextSegments` and `allNextSegments`.
|
196
|
+
*/
|
175
197
|
for (i = 0; i < segment.allPrevSegments.length; ++i) {
|
176
198
|
const prevSegment = segment.allPrevSegments[i];
|
177
199
|
|
@@ -179,6 +201,13 @@ class CodePathSegment {
|
|
179
201
|
prevSegment.nextSegments.push(segment);
|
180
202
|
}
|
181
203
|
} else {
|
204
|
+
|
205
|
+
/*
|
206
|
+
* If the segment is not reachable, then it's not officially part of the
|
207
|
+
* code path. This loops through all previous segments to update
|
208
|
+
* their list of next segments. Because the segment is not reachable,
|
209
|
+
* it's added only to `allNextSegments`.
|
210
|
+
*/
|
182
211
|
for (i = 0; i < segment.allPrevSegments.length; ++i) {
|
183
212
|
segment.allPrevSegments[i].allNextSegments.push(segment);
|
184
213
|
}
|
@@ -196,19 +225,20 @@ class CodePathSegment {
|
|
196
225
|
}
|
197
226
|
|
198
227
|
/**
|
199
|
-
*
|
200
|
-
*
|
201
|
-
*
|
228
|
+
* Creates a new array based on an array of segments. If any segment in the
|
229
|
+
* array is unused, then it is replaced by all of its previous segments.
|
230
|
+
* All used segments are returned as-is without replacement.
|
231
|
+
* @param {CodePathSegment[]} segments The array of segments to flatten.
|
232
|
+
* @returns {CodePathSegment[]} The flattened array.
|
202
233
|
*/
|
203
234
|
static flattenUnusedSegments(segments) {
|
204
|
-
const done =
|
205
|
-
const retv = [];
|
235
|
+
const done = new Set();
|
206
236
|
|
207
237
|
for (let i = 0; i < segments.length; ++i) {
|
208
238
|
const segment = segments[i];
|
209
239
|
|
210
240
|
// Ignores duplicated.
|
211
|
-
if (done
|
241
|
+
if (done.has(segment)) {
|
212
242
|
continue;
|
213
243
|
}
|
214
244
|
|
@@ -217,18 +247,16 @@ class CodePathSegment {
|
|
217
247
|
for (let j = 0; j < segment.allPrevSegments.length; ++j) {
|
218
248
|
const prevSegment = segment.allPrevSegments[j];
|
219
249
|
|
220
|
-
if (!done
|
221
|
-
done
|
222
|
-
retv.push(prevSegment);
|
250
|
+
if (!done.has(prevSegment)) {
|
251
|
+
done.add(prevSegment);
|
223
252
|
}
|
224
253
|
}
|
225
254
|
} else {
|
226
|
-
done
|
227
|
-
retv.push(segment);
|
255
|
+
done.add(segment);
|
228
256
|
}
|
229
257
|
}
|
230
258
|
|
231
|
-
return
|
259
|
+
return [...done];
|
232
260
|
}
|
233
261
|
}
|
234
262
|
|
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
|
|
@@ -32,6 +34,7 @@ const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
|
|
32
34
|
|
33
35
|
/** @typedef {import("../shared/types").Parser} Parser */
|
34
36
|
/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
|
37
|
+
/** @typedef {import("../shared/types").Rule} Rule */
|
35
38
|
|
36
39
|
|
37
40
|
/**
|
@@ -273,6 +276,21 @@ function getCommentsDeprecation() {
|
|
273
276
|
);
|
274
277
|
}
|
275
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
|
+
|
276
294
|
//------------------------------------------------------------------------------
|
277
295
|
// Public Interface
|
278
296
|
//------------------------------------------------------------------------------
|
@@ -446,7 +464,7 @@ class FlatRuleTester {
|
|
446
464
|
/**
|
447
465
|
* Adds a new rule test to execute.
|
448
466
|
* @param {string} ruleName The name of the rule to run.
|
449
|
-
* @param {Function} rule The rule to test.
|
467
|
+
* @param {Function | Rule} rule The rule to test.
|
450
468
|
* @param {{
|
451
469
|
* valid: (ValidTestCase | string)[],
|
452
470
|
* invalid: InvalidTestCase[]
|
@@ -663,6 +681,7 @@ class FlatRuleTester {
|
|
663
681
|
|
664
682
|
// Verify the code.
|
665
683
|
const { getComments } = SourceCode.prototype;
|
684
|
+
const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
|
666
685
|
let messages;
|
667
686
|
|
668
687
|
// check for validation errors
|
@@ -676,11 +695,20 @@ class FlatRuleTester {
|
|
676
695
|
|
677
696
|
try {
|
678
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
|
+
|
679
705
|
messages = linter.verify(code, configs, filename);
|
680
706
|
} finally {
|
681
707
|
SourceCode.prototype.getComments = getComments;
|
708
|
+
Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
|
682
709
|
}
|
683
710
|
|
711
|
+
|
684
712
|
const fatalErrorMessage = messages.find(m => m.fatal);
|
685
713
|
|
686
714
|
assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
|
@@ -1011,29 +1039,35 @@ class FlatRuleTester {
|
|
1011
1039
|
/*
|
1012
1040
|
* This creates a mocha test suite and pipes all supplied info through
|
1013
1041
|
* one of the templates above.
|
1042
|
+
* The test suites for valid/invalid are created conditionally as
|
1043
|
+
* test runners (eg. vitest) fail for empty test suites.
|
1014
1044
|
*/
|
1015
1045
|
this.constructor.describe(ruleName, () => {
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1046
|
+
if (test.valid.length > 0) {
|
1047
|
+
this.constructor.describe("valid", () => {
|
1048
|
+
test.valid.forEach(valid => {
|
1049
|
+
this.constructor[valid.only ? "itOnly" : "it"](
|
1050
|
+
sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
|
1051
|
+
() => {
|
1052
|
+
testValidTemplate(valid);
|
1053
|
+
}
|
1054
|
+
);
|
1055
|
+
});
|
1024
1056
|
});
|
1025
|
-
}
|
1057
|
+
}
|
1026
1058
|
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1059
|
+
if (test.invalid.length > 0) {
|
1060
|
+
this.constructor.describe("invalid", () => {
|
1061
|
+
test.invalid.forEach(invalid => {
|
1062
|
+
this.constructor[invalid.only ? "itOnly" : "it"](
|
1063
|
+
sanitize(invalid.name || invalid.code),
|
1064
|
+
() => {
|
1065
|
+
testInvalidTemplate(invalid);
|
1066
|
+
}
|
1067
|
+
);
|
1068
|
+
});
|
1035
1069
|
});
|
1036
|
-
}
|
1070
|
+
}
|
1037
1071
|
});
|
1038
1072
|
}
|
1039
1073
|
}
|