eslint 8.47.0 → 8.57.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 +18 -13
- package/bin/eslint.js +38 -5
- package/conf/rule-type-list.json +25 -33
- package/lib/api.js +29 -1
- package/lib/cli-engine/cli-engine.js +2 -2
- package/lib/cli-engine/lint-result-cache.js +18 -6
- package/lib/cli.js +36 -6
- package/lib/config/flat-config-schema.js +124 -61
- package/lib/config/rule-validator.js +2 -1
- package/lib/eslint/eslint-helpers.js +9 -11
- package/lib/eslint/eslint.js +7 -0
- package/lib/eslint/flat-eslint.js +33 -18
- package/lib/linter/apply-disable-directives.js +127 -13
- 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-state.js +1108 -243
- package/lib/linter/code-path-analysis/code-path.js +128 -33
- package/lib/linter/code-path-analysis/fork-context.js +173 -72
- package/lib/linter/config-comment-parser.js +36 -2
- package/lib/linter/linter.js +183 -82
- package/lib/options.js +24 -3
- package/lib/rule-tester/flat-rule-tester.js +113 -25
- package/lib/rule-tester/rule-tester.js +176 -23
- package/lib/rules/array-bracket-newline.js +3 -0
- package/lib/rules/array-bracket-spacing.js +3 -0
- package/lib/rules/array-callback-return.js +175 -25
- package/lib/rules/array-element-newline.js +3 -0
- package/lib/rules/arrow-parens.js +3 -0
- package/lib/rules/arrow-spacing.js +3 -0
- package/lib/rules/block-spacing.js +3 -0
- package/lib/rules/brace-style.js +3 -0
- package/lib/rules/comma-dangle.js +3 -0
- package/lib/rules/comma-spacing.js +3 -0
- package/lib/rules/comma-style.js +3 -0
- package/lib/rules/computed-property-spacing.js +3 -0
- package/lib/rules/consistent-return.js +32 -7
- package/lib/rules/constructor-super.js +37 -14
- package/lib/rules/dot-location.js +3 -0
- package/lib/rules/eol-last.js +3 -0
- package/lib/rules/for-direction.js +38 -24
- package/lib/rules/func-call-spacing.js +3 -0
- package/lib/rules/function-call-argument-newline.js +3 -0
- package/lib/rules/function-paren-newline.js +3 -0
- package/lib/rules/generator-star-spacing.js +3 -0
- package/lib/rules/getter-return.js +33 -8
- package/lib/rules/implicit-arrow-linebreak.js +3 -0
- package/lib/rules/indent.js +3 -0
- package/lib/rules/index.js +1 -0
- package/lib/rules/jsx-quotes.js +3 -0
- package/lib/rules/key-spacing.js +3 -0
- package/lib/rules/keyword-spacing.js +3 -0
- package/lib/rules/linebreak-style.js +3 -0
- package/lib/rules/lines-around-comment.js +3 -0
- package/lib/rules/lines-between-class-members.js +95 -7
- package/lib/rules/logical-assignment-operators.js +31 -3
- package/lib/rules/max-len.js +3 -0
- package/lib/rules/max-statements-per-line.js +3 -0
- package/lib/rules/multiline-ternary.js +3 -0
- package/lib/rules/new-parens.js +3 -0
- package/lib/rules/newline-per-chained-call.js +3 -0
- package/lib/rules/no-array-constructor.js +85 -6
- package/lib/rules/no-confusing-arrow.js +3 -0
- package/lib/rules/no-console.js +74 -2
- package/lib/rules/no-extra-parens.js +3 -0
- package/lib/rules/no-extra-semi.js +3 -0
- package/lib/rules/no-fallthrough.js +42 -14
- package/lib/rules/no-floating-decimal.js +3 -0
- package/lib/rules/no-invalid-this.js +1 -1
- package/lib/rules/no-misleading-character-class.js +65 -15
- package/lib/rules/no-mixed-operators.js +3 -0
- package/lib/rules/no-mixed-spaces-and-tabs.js +3 -0
- package/lib/rules/no-multi-spaces.js +3 -0
- package/lib/rules/no-multiple-empty-lines.js +3 -0
- package/lib/rules/no-new-object.js +7 -0
- package/lib/rules/no-object-constructor.js +117 -0
- package/lib/rules/no-promise-executor-return.js +157 -16
- package/lib/rules/no-prototype-builtins.js +90 -2
- package/lib/rules/no-restricted-imports.js +54 -31
- package/lib/rules/no-restricted-properties.js +15 -28
- package/lib/rules/no-tabs.js +3 -0
- package/lib/rules/no-this-before-super.js +38 -11
- package/lib/rules/no-trailing-spaces.js +3 -0
- 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/no-whitespace-before-property.js +3 -0
- package/lib/rules/nonblock-statement-body-position.js +3 -0
- package/lib/rules/object-curly-newline.js +3 -0
- package/lib/rules/object-curly-spacing.js +3 -0
- package/lib/rules/object-property-newline.js +3 -0
- package/lib/rules/one-var-declaration-per-line.js +3 -0
- package/lib/rules/operator-linebreak.js +3 -0
- package/lib/rules/padded-blocks.js +3 -0
- package/lib/rules/padding-line-between-statements.js +3 -0
- package/lib/rules/quote-props.js +3 -0
- package/lib/rules/quotes.js +3 -0
- package/lib/rules/require-atomic-updates.js +21 -7
- package/lib/rules/rest-spread-spacing.js +3 -0
- package/lib/rules/semi-spacing.js +3 -0
- package/lib/rules/semi-style.js +3 -0
- package/lib/rules/semi.js +3 -0
- package/lib/rules/space-before-blocks.js +3 -0
- package/lib/rules/space-before-function-paren.js +3 -0
- package/lib/rules/space-in-parens.js +3 -0
- package/lib/rules/space-infix-ops.js +3 -0
- package/lib/rules/space-unary-ops.js +3 -0
- package/lib/rules/spaced-comment.js +3 -0
- package/lib/rules/switch-colon-spacing.js +3 -0
- package/lib/rules/template-curly-spacing.js +3 -0
- package/lib/rules/template-tag-spacing.js +3 -0
- package/lib/rules/utils/ast-utils.js +111 -1
- package/lib/rules/wrap-iife.js +3 -0
- package/lib/rules/wrap-regex.js +3 -0
- package/lib/rules/yield-star-spacing.js +3 -0
- package/lib/shared/severity.js +49 -0
- package/lib/source-code/source-code.js +329 -3
- package/messages/eslintrc-incompat.js +1 -1
- package/package.json +24 -17
@@ -30,7 +30,7 @@ function compareLocations(itemA, itemB) {
|
|
30
30
|
|
31
31
|
/**
|
32
32
|
* Groups a set of directives into sub-arrays by their parent comment.
|
33
|
-
* @param {Directive
|
33
|
+
* @param {Iterable<Directive>} directives Unused directives to be removed.
|
34
34
|
* @returns {Directive[][]} Directives grouped by their parent comment.
|
35
35
|
*/
|
36
36
|
function groupByParentComment(directives) {
|
@@ -87,7 +87,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
|
|
87
87
|
return directives.map(directive => {
|
88
88
|
const { ruleId } = directive;
|
89
89
|
|
90
|
-
const regex = new RegExp(String.raw`(?:^|\s*,\s*)${escapeRegExp(ruleId)}(?:\s*,\s*|$)`, "u");
|
90
|
+
const regex = new RegExp(String.raw`(?:^|\s*,\s*)(?<quote>['"]?)${escapeRegExp(ruleId)}\k<quote>(?:\s*,\s*|$)`, "u");
|
91
91
|
const match = regex.exec(listText);
|
92
92
|
const matchedText = match[0];
|
93
93
|
const matchStartOffset = listStartOffset + match.index;
|
@@ -177,10 +177,10 @@ function createCommentRemoval(directives, commentToken) {
|
|
177
177
|
|
178
178
|
/**
|
179
179
|
* Parses details from directives to create output Problems.
|
180
|
-
* @param {Directive
|
180
|
+
* @param {Iterable<Directive>} allDirectives Unused directives to be removed.
|
181
181
|
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
182
182
|
*/
|
183
|
-
function
|
183
|
+
function processUnusedDirectives(allDirectives) {
|
184
184
|
const directiveGroups = groupByParentComment(allDirectives);
|
185
185
|
|
186
186
|
return directiveGroups.flatMap(
|
@@ -199,6 +199,95 @@ function processUnusedDisableDirectives(allDirectives) {
|
|
199
199
|
);
|
200
200
|
}
|
201
201
|
|
202
|
+
/**
|
203
|
+
* Collect eslint-enable comments that are removing suppressions by eslint-disable comments.
|
204
|
+
* @param {Directive[]} directives The directives to check.
|
205
|
+
* @returns {Set<Directive>} The used eslint-enable comments
|
206
|
+
*/
|
207
|
+
function collectUsedEnableDirectives(directives) {
|
208
|
+
|
209
|
+
/**
|
210
|
+
* A Map of `eslint-enable` keyed by ruleIds that may be marked as used.
|
211
|
+
* If `eslint-enable` does not have a ruleId, the key will be `null`.
|
212
|
+
* @type {Map<string|null, Directive>}
|
213
|
+
*/
|
214
|
+
const enabledRules = new Map();
|
215
|
+
|
216
|
+
/**
|
217
|
+
* A Set of `eslint-enable` marked as used.
|
218
|
+
* It is also the return value of `collectUsedEnableDirectives` function.
|
219
|
+
* @type {Set<Directive>}
|
220
|
+
*/
|
221
|
+
const usedEnableDirectives = new Set();
|
222
|
+
|
223
|
+
/*
|
224
|
+
* Checks the directives backwards to see if the encountered `eslint-enable` is used by the previous `eslint-disable`,
|
225
|
+
* and if so, stores the `eslint-enable` in `usedEnableDirectives`.
|
226
|
+
*/
|
227
|
+
for (let index = directives.length - 1; index >= 0; index--) {
|
228
|
+
const directive = directives[index];
|
229
|
+
|
230
|
+
if (directive.type === "disable") {
|
231
|
+
if (enabledRules.size === 0) {
|
232
|
+
continue;
|
233
|
+
}
|
234
|
+
if (directive.ruleId === null) {
|
235
|
+
|
236
|
+
// If encounter `eslint-disable` without ruleId,
|
237
|
+
// mark all `eslint-enable` currently held in enabledRules as used.
|
238
|
+
// e.g.
|
239
|
+
// /* eslint-disable */ <- current directive
|
240
|
+
// /* eslint-enable rule-id1 */ <- used
|
241
|
+
// /* eslint-enable rule-id2 */ <- used
|
242
|
+
// /* eslint-enable */ <- used
|
243
|
+
for (const enableDirective of enabledRules.values()) {
|
244
|
+
usedEnableDirectives.add(enableDirective);
|
245
|
+
}
|
246
|
+
enabledRules.clear();
|
247
|
+
} else {
|
248
|
+
const enableDirective = enabledRules.get(directive.ruleId);
|
249
|
+
|
250
|
+
if (enableDirective) {
|
251
|
+
|
252
|
+
// If encounter `eslint-disable` with ruleId, and there is an `eslint-enable` with the same ruleId in enabledRules,
|
253
|
+
// mark `eslint-enable` with ruleId as used.
|
254
|
+
// e.g.
|
255
|
+
// /* eslint-disable rule-id */ <- current directive
|
256
|
+
// /* eslint-enable rule-id */ <- used
|
257
|
+
usedEnableDirectives.add(enableDirective);
|
258
|
+
} else {
|
259
|
+
const enabledDirectiveWithoutRuleId = enabledRules.get(null);
|
260
|
+
|
261
|
+
if (enabledDirectiveWithoutRuleId) {
|
262
|
+
|
263
|
+
// If encounter `eslint-disable` with ruleId, and there is no `eslint-enable` with the same ruleId in enabledRules,
|
264
|
+
// mark `eslint-enable` without ruleId as used.
|
265
|
+
// e.g.
|
266
|
+
// /* eslint-disable rule-id */ <- current directive
|
267
|
+
// /* eslint-enable */ <- used
|
268
|
+
usedEnableDirectives.add(enabledDirectiveWithoutRuleId);
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
} else if (directive.type === "enable") {
|
273
|
+
if (directive.ruleId === null) {
|
274
|
+
|
275
|
+
// If encounter `eslint-enable` without ruleId, the `eslint-enable` that follows it are unused.
|
276
|
+
// So clear enabledRules.
|
277
|
+
// e.g.
|
278
|
+
// /* eslint-enable */ <- current directive
|
279
|
+
// /* eslint-enable rule-id *// <- unused
|
280
|
+
// /* eslint-enable */ <- unused
|
281
|
+
enabledRules.clear();
|
282
|
+
enabledRules.set(null, directive);
|
283
|
+
} else {
|
284
|
+
enabledRules.set(directive.ruleId, directive);
|
285
|
+
}
|
286
|
+
}
|
287
|
+
}
|
288
|
+
return usedEnableDirectives;
|
289
|
+
}
|
290
|
+
|
202
291
|
/**
|
203
292
|
* This is the same as the exported function, except that it
|
204
293
|
* doesn't handle disable-line and disable-next-line directives, and it always reports unused
|
@@ -206,7 +295,7 @@ function processUnusedDisableDirectives(allDirectives) {
|
|
206
295
|
* @param {Object} options options for applying directives. This is the same as the options
|
207
296
|
* for the exported function, except that `reportUnusedDisableDirectives` is not supported
|
208
297
|
* (this function always reports unused disable directives).
|
209
|
-
* @returns {{problems: LintMessage[],
|
298
|
+
* @returns {{problems: LintMessage[], unusedDirectives: LintMessage[]}} An object with a list
|
210
299
|
* of problems (including suppressed ones) and unused eslint-disable directives
|
211
300
|
*/
|
212
301
|
function applyDirectives(options) {
|
@@ -258,17 +347,42 @@ function applyDirectives(options) {
|
|
258
347
|
const unusedDisableDirectivesToReport = options.directives
|
259
348
|
.filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
|
260
349
|
|
261
|
-
const processed = processUnusedDisableDirectives(unusedDisableDirectivesToReport);
|
262
350
|
|
263
|
-
const
|
351
|
+
const unusedEnableDirectivesToReport = new Set(
|
352
|
+
options.directives.filter(directive => directive.unprocessedDirective.type === "enable")
|
353
|
+
);
|
354
|
+
|
355
|
+
/*
|
356
|
+
* If directives has the eslint-enable directive,
|
357
|
+
* check whether the eslint-enable comment is used.
|
358
|
+
*/
|
359
|
+
if (unusedEnableDirectivesToReport.size > 0) {
|
360
|
+
for (const directive of collectUsedEnableDirectives(options.directives)) {
|
361
|
+
unusedEnableDirectivesToReport.delete(directive);
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
|
366
|
+
.concat(processUnusedDirectives(unusedEnableDirectivesToReport));
|
367
|
+
|
368
|
+
const unusedDirectives = processed
|
264
369
|
.map(({ description, fix, unprocessedDirective }) => {
|
265
370
|
const { parentComment, type, line, column } = unprocessedDirective;
|
266
371
|
|
372
|
+
let message;
|
373
|
+
|
374
|
+
if (type === "enable") {
|
375
|
+
message = description
|
376
|
+
? `Unused eslint-enable directive (no matching eslint-disable directives were found for ${description}).`
|
377
|
+
: "Unused eslint-enable directive (no matching eslint-disable directives were found).";
|
378
|
+
} else {
|
379
|
+
message = description
|
380
|
+
? `Unused eslint-disable directive (no problems were reported from ${description}).`
|
381
|
+
: "Unused eslint-disable directive (no problems were reported).";
|
382
|
+
}
|
267
383
|
return {
|
268
384
|
ruleId: null,
|
269
|
-
message
|
270
|
-
? `Unused eslint-disable directive (no problems were reported from ${description}).`
|
271
|
-
: "Unused eslint-disable directive (no problems were reported).",
|
385
|
+
message,
|
272
386
|
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
|
273
387
|
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
|
274
388
|
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
@@ -277,7 +391,7 @@ function applyDirectives(options) {
|
|
277
391
|
};
|
278
392
|
});
|
279
393
|
|
280
|
-
return { problems,
|
394
|
+
return { problems, unusedDirectives };
|
281
395
|
}
|
282
396
|
|
283
397
|
/**
|
@@ -344,8 +458,8 @@ module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirec
|
|
344
458
|
|
345
459
|
return reportUnusedDisableDirectives !== "off"
|
346
460
|
? lineDirectivesResult.problems
|
347
|
-
.concat(blockDirectivesResult.
|
348
|
-
.concat(lineDirectivesResult.
|
461
|
+
.concat(blockDirectivesResult.unusedDirectives)
|
462
|
+
.concat(lineDirectivesResult.unusedDirectives)
|
349
463
|
.sort(compareLocations)
|
350
464
|
: lineDirectivesResult.problems;
|
351
465
|
};
|
@@ -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
|
|