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.
Files changed (118) hide show
  1. package/README.md +18 -13
  2. package/bin/eslint.js +38 -5
  3. package/conf/rule-type-list.json +25 -33
  4. package/lib/api.js +29 -1
  5. package/lib/cli-engine/cli-engine.js +2 -2
  6. package/lib/cli-engine/lint-result-cache.js +18 -6
  7. package/lib/cli.js +36 -6
  8. package/lib/config/flat-config-schema.js +124 -61
  9. package/lib/config/rule-validator.js +2 -1
  10. package/lib/eslint/eslint-helpers.js +9 -11
  11. package/lib/eslint/eslint.js +7 -0
  12. package/lib/eslint/flat-eslint.js +33 -18
  13. package/lib/linter/apply-disable-directives.js +127 -13
  14. package/lib/linter/code-path-analysis/code-path-analyzer.js +32 -24
  15. package/lib/linter/code-path-analysis/code-path-segment.js +52 -24
  16. package/lib/linter/code-path-analysis/code-path-state.js +1108 -243
  17. package/lib/linter/code-path-analysis/code-path.js +128 -33
  18. package/lib/linter/code-path-analysis/fork-context.js +173 -72
  19. package/lib/linter/config-comment-parser.js +36 -2
  20. package/lib/linter/linter.js +183 -82
  21. package/lib/options.js +24 -3
  22. package/lib/rule-tester/flat-rule-tester.js +113 -25
  23. package/lib/rule-tester/rule-tester.js +176 -23
  24. package/lib/rules/array-bracket-newline.js +3 -0
  25. package/lib/rules/array-bracket-spacing.js +3 -0
  26. package/lib/rules/array-callback-return.js +175 -25
  27. package/lib/rules/array-element-newline.js +3 -0
  28. package/lib/rules/arrow-parens.js +3 -0
  29. package/lib/rules/arrow-spacing.js +3 -0
  30. package/lib/rules/block-spacing.js +3 -0
  31. package/lib/rules/brace-style.js +3 -0
  32. package/lib/rules/comma-dangle.js +3 -0
  33. package/lib/rules/comma-spacing.js +3 -0
  34. package/lib/rules/comma-style.js +3 -0
  35. package/lib/rules/computed-property-spacing.js +3 -0
  36. package/lib/rules/consistent-return.js +32 -7
  37. package/lib/rules/constructor-super.js +37 -14
  38. package/lib/rules/dot-location.js +3 -0
  39. package/lib/rules/eol-last.js +3 -0
  40. package/lib/rules/for-direction.js +38 -24
  41. package/lib/rules/func-call-spacing.js +3 -0
  42. package/lib/rules/function-call-argument-newline.js +3 -0
  43. package/lib/rules/function-paren-newline.js +3 -0
  44. package/lib/rules/generator-star-spacing.js +3 -0
  45. package/lib/rules/getter-return.js +33 -8
  46. package/lib/rules/implicit-arrow-linebreak.js +3 -0
  47. package/lib/rules/indent.js +3 -0
  48. package/lib/rules/index.js +1 -0
  49. package/lib/rules/jsx-quotes.js +3 -0
  50. package/lib/rules/key-spacing.js +3 -0
  51. package/lib/rules/keyword-spacing.js +3 -0
  52. package/lib/rules/linebreak-style.js +3 -0
  53. package/lib/rules/lines-around-comment.js +3 -0
  54. package/lib/rules/lines-between-class-members.js +95 -7
  55. package/lib/rules/logical-assignment-operators.js +31 -3
  56. package/lib/rules/max-len.js +3 -0
  57. package/lib/rules/max-statements-per-line.js +3 -0
  58. package/lib/rules/multiline-ternary.js +3 -0
  59. package/lib/rules/new-parens.js +3 -0
  60. package/lib/rules/newline-per-chained-call.js +3 -0
  61. package/lib/rules/no-array-constructor.js +85 -6
  62. package/lib/rules/no-confusing-arrow.js +3 -0
  63. package/lib/rules/no-console.js +74 -2
  64. package/lib/rules/no-extra-parens.js +3 -0
  65. package/lib/rules/no-extra-semi.js +3 -0
  66. package/lib/rules/no-fallthrough.js +42 -14
  67. package/lib/rules/no-floating-decimal.js +3 -0
  68. package/lib/rules/no-invalid-this.js +1 -1
  69. package/lib/rules/no-misleading-character-class.js +65 -15
  70. package/lib/rules/no-mixed-operators.js +3 -0
  71. package/lib/rules/no-mixed-spaces-and-tabs.js +3 -0
  72. package/lib/rules/no-multi-spaces.js +3 -0
  73. package/lib/rules/no-multiple-empty-lines.js +3 -0
  74. package/lib/rules/no-new-object.js +7 -0
  75. package/lib/rules/no-object-constructor.js +117 -0
  76. package/lib/rules/no-promise-executor-return.js +157 -16
  77. package/lib/rules/no-prototype-builtins.js +90 -2
  78. package/lib/rules/no-restricted-imports.js +54 -31
  79. package/lib/rules/no-restricted-properties.js +15 -28
  80. package/lib/rules/no-tabs.js +3 -0
  81. package/lib/rules/no-this-before-super.js +38 -11
  82. package/lib/rules/no-trailing-spaces.js +3 -0
  83. package/lib/rules/no-unreachable-loop.js +47 -12
  84. package/lib/rules/no-unreachable.js +39 -10
  85. package/lib/rules/no-useless-return.js +35 -4
  86. package/lib/rules/no-whitespace-before-property.js +3 -0
  87. package/lib/rules/nonblock-statement-body-position.js +3 -0
  88. package/lib/rules/object-curly-newline.js +3 -0
  89. package/lib/rules/object-curly-spacing.js +3 -0
  90. package/lib/rules/object-property-newline.js +3 -0
  91. package/lib/rules/one-var-declaration-per-line.js +3 -0
  92. package/lib/rules/operator-linebreak.js +3 -0
  93. package/lib/rules/padded-blocks.js +3 -0
  94. package/lib/rules/padding-line-between-statements.js +3 -0
  95. package/lib/rules/quote-props.js +3 -0
  96. package/lib/rules/quotes.js +3 -0
  97. package/lib/rules/require-atomic-updates.js +21 -7
  98. package/lib/rules/rest-spread-spacing.js +3 -0
  99. package/lib/rules/semi-spacing.js +3 -0
  100. package/lib/rules/semi-style.js +3 -0
  101. package/lib/rules/semi.js +3 -0
  102. package/lib/rules/space-before-blocks.js +3 -0
  103. package/lib/rules/space-before-function-paren.js +3 -0
  104. package/lib/rules/space-in-parens.js +3 -0
  105. package/lib/rules/space-infix-ops.js +3 -0
  106. package/lib/rules/space-unary-ops.js +3 -0
  107. package/lib/rules/spaced-comment.js +3 -0
  108. package/lib/rules/switch-colon-spacing.js +3 -0
  109. package/lib/rules/template-curly-spacing.js +3 -0
  110. package/lib/rules/template-tag-spacing.js +3 -0
  111. package/lib/rules/utils/ast-utils.js +111 -1
  112. package/lib/rules/wrap-iife.js +3 -0
  113. package/lib/rules/wrap-regex.js +3 -0
  114. package/lib/rules/yield-star-spacing.js +3 -0
  115. package/lib/shared/severity.js +49 -0
  116. package/lib/source-code/source-code.js +329 -3
  117. package/messages/eslintrc-incompat.js +1 -1
  118. package/package.json +24 -17
@@ -80,7 +80,9 @@ class CodePath {
80
80
  }
81
81
 
82
82
  /**
83
- * The initial code path segment.
83
+ * The initial code path segment. This is the segment that is at the head
84
+ * of the code path.
85
+ * This is a passthrough to the underlying `CodePathState`.
84
86
  * @type {CodePathSegment}
85
87
  */
86
88
  get initialSegment() {
@@ -88,8 +90,10 @@ class CodePath {
88
90
  }
89
91
 
90
92
  /**
91
- * Final code path segments.
92
- * This array is a mix of `returnedSegments` and `thrownSegments`.
93
+ * Final code path segments. These are the terminal (tail) segments in the
94
+ * code path, which is the combination of `returnedSegments` and `thrownSegments`.
95
+ * All segments in this array are reachable.
96
+ * This is a passthrough to the underlying `CodePathState`.
93
97
  * @type {CodePathSegment[]}
94
98
  */
95
99
  get finalSegments() {
@@ -97,9 +101,14 @@ class CodePath {
97
101
  }
98
102
 
99
103
  /**
100
- * Final code path segments which is with `return` statements.
101
- * This array contains the last path segment if it's reachable.
102
- * Since the reachable last path returns `undefined`.
104
+ * Final code path segments that represent normal completion of the code path.
105
+ * For functions, this means both explicit `return` statements and implicit returns,
106
+ * such as the last reachable segment in a function that does not have an
107
+ * explicit `return` as this implicitly returns `undefined`. For scripts,
108
+ * modules, class field initializers, and class static blocks, this means
109
+ * all lines of code have been executed.
110
+ * These segments are also present in `finalSegments`.
111
+ * This is a passthrough to the underlying `CodePathState`.
103
112
  * @type {CodePathSegment[]}
104
113
  */
105
114
  get returnedSegments() {
@@ -107,7 +116,9 @@ class CodePath {
107
116
  }
108
117
 
109
118
  /**
110
- * Final code path segments which is with `throw` statements.
119
+ * Final code path segments that represent `throw` statements.
120
+ * This is a passthrough to the underlying `CodePathState`.
121
+ * These segments are also present in `finalSegments`.
111
122
  * @type {CodePathSegment[]}
112
123
  */
113
124
  get thrownSegments() {
@@ -115,8 +126,14 @@ class CodePath {
115
126
  }
116
127
 
117
128
  /**
118
- * Current code path segments.
129
+ * Tracks the traversal of the code path through each segment. This array
130
+ * starts empty and segments are added or removed as the code path is
131
+ * traversed. This array always ends up empty at the end of a code path
132
+ * traversal. The `CodePathState` uses this to track its progress through
133
+ * the code path.
134
+ * This is a passthrough to the underlying `CodePathState`.
119
135
  * @type {CodePathSegment[]}
136
+ * @deprecated
120
137
  */
121
138
  get currentSegments() {
122
139
  return this.internal.currentSegments;
@@ -125,46 +142,70 @@ class CodePath {
125
142
  /**
126
143
  * Traverses all segments in this code path.
127
144
  *
128
- * codePath.traverseSegments(function(segment, controller) {
145
+ * codePath.traverseSegments((segment, controller) => {
129
146
  * // do something.
130
147
  * });
131
148
  *
132
149
  * This method enumerates segments in order from the head.
133
150
  *
134
- * The `controller` object has two methods.
151
+ * The `controller` argument has two methods:
135
152
  *
136
- * - `controller.skip()` - Skip the following segments in this branch.
137
- * - `controller.break()` - Skip all following segments.
138
- * @param {Object} [options] Omittable.
139
- * @param {CodePathSegment} [options.first] The first segment to traverse.
140
- * @param {CodePathSegment} [options.last] The last segment to traverse.
153
+ * - `skip()` - skips the following segments in this branch
154
+ * - `break()` - skips all following segments in the traversal
155
+ *
156
+ * A note on the parameters: the `options` argument is optional. This means
157
+ * the first argument might be an options object or the callback function.
158
+ * @param {Object} [optionsOrCallback] Optional first and last segments to traverse.
159
+ * @param {CodePathSegment} [optionsOrCallback.first] The first segment to traverse.
160
+ * @param {CodePathSegment} [optionsOrCallback.last] The last segment to traverse.
141
161
  * @param {Function} callback A callback function.
142
162
  * @returns {void}
143
163
  */
144
- traverseSegments(options, callback) {
164
+ traverseSegments(optionsOrCallback, callback) {
165
+
166
+ // normalize the arguments into a callback and options
145
167
  let resolvedOptions;
146
168
  let resolvedCallback;
147
169
 
148
- if (typeof options === "function") {
149
- resolvedCallback = options;
170
+ if (typeof optionsOrCallback === "function") {
171
+ resolvedCallback = optionsOrCallback;
150
172
  resolvedOptions = {};
151
173
  } else {
152
- resolvedOptions = options || {};
174
+ resolvedOptions = optionsOrCallback || {};
153
175
  resolvedCallback = callback;
154
176
  }
155
177
 
178
+ // determine where to start traversing from based on the options
156
179
  const startSegment = resolvedOptions.first || this.internal.initialSegment;
157
180
  const lastSegment = resolvedOptions.last;
158
181
 
159
- let item = null;
182
+ // set up initial location information
183
+ let record = null;
160
184
  let index = 0;
161
185
  let end = 0;
162
186
  let segment = null;
163
- const visited = Object.create(null);
187
+
188
+ // segments that have already been visited during traversal
189
+ const visited = new Set();
190
+
191
+ // tracks the traversal steps
164
192
  const stack = [[startSegment, 0]];
193
+
194
+ // tracks the last skipped segment during traversal
165
195
  let skippedSegment = null;
196
+
197
+ // indicates if we exited early from the traversal
166
198
  let broken = false;
199
+
200
+ /**
201
+ * Maintains traversal state.
202
+ */
167
203
  const controller = {
204
+
205
+ /**
206
+ * Skip the following segments in this branch.
207
+ * @returns {void}
208
+ */
168
209
  skip() {
169
210
  if (stack.length <= 1) {
170
211
  broken = true;
@@ -172,32 +213,52 @@ class CodePath {
172
213
  skippedSegment = stack[stack.length - 2][0];
173
214
  }
174
215
  },
216
+
217
+ /**
218
+ * Stop traversal completely - do not traverse to any
219
+ * other segments.
220
+ * @returns {void}
221
+ */
175
222
  break() {
176
223
  broken = true;
177
224
  }
178
225
  };
179
226
 
180
227
  /**
181
- * Checks a given previous segment has been visited.
228
+ * Checks if a given previous segment has been visited.
182
229
  * @param {CodePathSegment} prevSegment A previous segment to check.
183
230
  * @returns {boolean} `true` if the segment has been visited.
184
231
  */
185
232
  function isVisited(prevSegment) {
186
233
  return (
187
- visited[prevSegment.id] ||
234
+ visited.has(prevSegment) ||
188
235
  segment.isLoopedPrevSegment(prevSegment)
189
236
  );
190
237
  }
191
238
 
239
+ // the traversal
192
240
  while (stack.length > 0) {
193
- item = stack[stack.length - 1];
194
- segment = item[0];
195
- index = item[1];
241
+
242
+ /*
243
+ * This isn't a pure stack. We use the top record all the time
244
+ * but don't always pop it off. The record is popped only if
245
+ * one of the following is true:
246
+ *
247
+ * 1) We have already visited the segment.
248
+ * 2) We have not visited *all* of the previous segments.
249
+ * 3) We have traversed past the available next segments.
250
+ *
251
+ * Otherwise, we just read the value and sometimes modify the
252
+ * record as we traverse.
253
+ */
254
+ record = stack[stack.length - 1];
255
+ segment = record[0];
256
+ index = record[1];
196
257
 
197
258
  if (index === 0) {
198
259
 
199
260
  // Skip if this segment has been visited already.
200
- if (visited[segment.id]) {
261
+ if (visited.has(segment)) {
201
262
  stack.pop();
202
263
  continue;
203
264
  }
@@ -211,18 +272,29 @@ class CodePath {
211
272
  continue;
212
273
  }
213
274
 
214
- // Reset the flag of skipping if all branches have been skipped.
275
+ // Reset the skipping flag if all branches have been skipped.
215
276
  if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
216
277
  skippedSegment = null;
217
278
  }
218
- visited[segment.id] = true;
279
+ visited.add(segment);
219
280
 
220
- // Call the callback when the first time.
281
+ /*
282
+ * If the most recent segment hasn't been skipped, then we call
283
+ * the callback, passing in the segment and the controller.
284
+ */
221
285
  if (!skippedSegment) {
222
286
  resolvedCallback.call(this, segment, controller);
287
+
288
+ // exit if we're at the last segment
223
289
  if (segment === lastSegment) {
224
290
  controller.skip();
225
291
  }
292
+
293
+ /*
294
+ * If the previous statement was executed, or if the callback
295
+ * called a method on the controller, we might need to exit the
296
+ * loop, so check for that and break accordingly.
297
+ */
226
298
  if (broken) {
227
299
  break;
228
300
  }
@@ -232,12 +304,35 @@ class CodePath {
232
304
  // Update the stack.
233
305
  end = segment.nextSegments.length - 1;
234
306
  if (index < end) {
235
- item[1] += 1;
307
+
308
+ /*
309
+ * If we haven't yet visited all of the next segments, update
310
+ * the current top record on the stack to the next index to visit
311
+ * and then push a record for the current segment on top.
312
+ *
313
+ * Setting the current top record's index lets us know how many
314
+ * times we've been here and ensures that the segment won't be
315
+ * reprocessed (because we only process segments with an index
316
+ * of 0).
317
+ */
318
+ record[1] += 1;
236
319
  stack.push([segment.nextSegments[index], 0]);
237
320
  } else if (index === end) {
238
- item[0] = segment.nextSegments[index];
239
- item[1] = 0;
321
+
322
+ /*
323
+ * If we are at the last next segment, then reset the top record
324
+ * in the stack to next segment and set its index to 0 so it will
325
+ * be processed next.
326
+ */
327
+ record[0] = segment.nextSegments[index];
328
+ record[1] = 0;
240
329
  } else {
330
+
331
+ /*
332
+ * If index > end, that means we have no more segments that need
333
+ * processing. So, we pop that record off of the stack in order to
334
+ * continue traversing at the next level up.
335
+ */
241
336
  stack.pop();
242
337
  }
243
338
  }
@@ -21,8 +21,8 @@ const assert = require("assert"),
21
21
  //------------------------------------------------------------------------------
22
22
 
23
23
  /**
24
- * Gets whether or not a given segment is reachable.
25
- * @param {CodePathSegment} segment A segment to get.
24
+ * Determines whether or not a given segment is reachable.
25
+ * @param {CodePathSegment} segment The segment to check.
26
26
  * @returns {boolean} `true` if the segment is reachable.
27
27
  */
28
28
  function isReachable(segment) {
@@ -30,32 +30,64 @@ function isReachable(segment) {
30
30
  }
31
31
 
32
32
  /**
33
- * Creates new segments from the specific range of `context.segmentsList`.
33
+ * Creates a new segment for each fork in the given context and appends it
34
+ * to the end of the specified range of segments. Ultimately, this ends up calling
35
+ * `new CodePathSegment()` for each of the forks using the `create` argument
36
+ * as a wrapper around special behavior.
37
+ *
38
+ * The `startIndex` and `endIndex` arguments specify a range of segments in
39
+ * `context` that should become `allPrevSegments` for the newly created
40
+ * `CodePathSegment` objects.
34
41
  *
35
42
  * When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
36
- * `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
37
- * This `h` is from `b`, `d`, and `f`.
38
- * @param {ForkContext} context An instance.
39
- * @param {number} begin The first index of the previous segments.
40
- * @param {number} end The last index of the previous segments.
41
- * @param {Function} create A factory function of new segments.
42
- * @returns {CodePathSegment[]} New segments.
43
+ * `end` is `-1`, this creates two new segments, `[g, h]`. This `g` is appended to
44
+ * the end of the path from `a`, `c`, and `e`. This `h` is appended to the end of
45
+ * `b`, `d`, and `f`.
46
+ * @param {ForkContext} context An instance from which the previous segments
47
+ * will be obtained.
48
+ * @param {number} startIndex The index of the first segment in the context
49
+ * that should be specified as previous segments for the newly created segments.
50
+ * @param {number} endIndex The index of the last segment in the context
51
+ * that should be specified as previous segments for the newly created segments.
52
+ * @param {Function} create A function that creates new `CodePathSegment`
53
+ * instances in a particular way. See the `CodePathSegment.new*` methods.
54
+ * @returns {Array<CodePathSegment>} An array of the newly-created segments.
43
55
  */
44
- function makeSegments(context, begin, end, create) {
56
+ function createSegments(context, startIndex, endIndex, create) {
57
+
58
+ /** @type {Array<Array<CodePathSegment>>} */
45
59
  const list = context.segmentsList;
46
60
 
47
- const normalizedBegin = begin >= 0 ? begin : list.length + begin;
48
- const normalizedEnd = end >= 0 ? end : list.length + end;
61
+ /*
62
+ * Both `startIndex` and `endIndex` work the same way: if the number is zero
63
+ * or more, then the number is used as-is. If the number is negative,
64
+ * then that number is added to the length of the segments list to
65
+ * determine the index to use. That means -1 for either argument
66
+ * is the last element, -2 is the second to last, and so on.
67
+ *
68
+ * So if `startIndex` is 0, `endIndex` is -1, and `list.length` is 3, the
69
+ * effective `startIndex` is 0 and the effective `endIndex` is 2, so this function
70
+ * will include items at indices 0, 1, and 2.
71
+ *
72
+ * Therefore, if `startIndex` is -1 and `endIndex` is -1, that means we'll only
73
+ * be using the last segment in `list`.
74
+ */
75
+ const normalizedBegin = startIndex >= 0 ? startIndex : list.length + startIndex;
76
+ const normalizedEnd = endIndex >= 0 ? endIndex : list.length + endIndex;
49
77
 
78
+ /** @type {Array<CodePathSegment>} */
50
79
  const segments = [];
51
80
 
52
81
  for (let i = 0; i < context.count; ++i) {
82
+
83
+ // this is passed into `new CodePathSegment` to add to code path.
53
84
  const allPrevSegments = [];
54
85
 
55
86
  for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
56
87
  allPrevSegments.push(list[j][i]);
57
88
  }
58
89
 
90
+ // note: `create` is just a wrapper that augments `new CodePathSegment`.
59
91
  segments.push(create(context.idGenerator.next(), allPrevSegments));
60
92
  }
61
93
 
@@ -63,28 +95,57 @@ function makeSegments(context, begin, end, create) {
63
95
  }
64
96
 
65
97
  /**
66
- * `segments` becomes doubly in a `finally` block. Then if a code path exits by a
67
- * control statement (such as `break`, `continue`) from the `finally` block, the
68
- * destination's segments may be half of the source segments. In that case, this
69
- * merges segments.
70
- * @param {ForkContext} context An instance.
71
- * @param {CodePathSegment[]} segments Segments to merge.
72
- * @returns {CodePathSegment[]} The merged segments.
98
+ * Inside of a `finally` block we end up with two parallel paths. If the code path
99
+ * exits by a control statement (such as `break` or `continue`) from the `finally`
100
+ * block, then we need to merge the remaining parallel paths back into one.
101
+ * @param {ForkContext} context The fork context to work on.
102
+ * @param {Array<CodePathSegment>} segments Segments to merge.
103
+ * @returns {Array<CodePathSegment>} The merged segments.
73
104
  */
74
105
  function mergeExtraSegments(context, segments) {
75
106
  let currentSegments = segments;
76
107
 
108
+ /*
109
+ * We need to ensure that the array returned from this function contains no more
110
+ * than the number of segments that the context allows. `context.count` indicates
111
+ * how many items should be in the returned array to ensure that the new segment
112
+ * entries will line up with the already existing segment entries.
113
+ */
77
114
  while (currentSegments.length > context.count) {
78
115
  const merged = [];
79
116
 
80
- for (let i = 0, length = currentSegments.length / 2 | 0; i < length; ++i) {
117
+ /*
118
+ * Because `context.count` is a factor of 2 inside of a `finally` block,
119
+ * we can divide the segment count by 2 to merge the paths together.
120
+ * This loops through each segment in the list and creates a new `CodePathSegment`
121
+ * that has the segment and the segment two slots away as previous segments.
122
+ *
123
+ * If `currentSegments` is [a,b,c,d], this will create new segments e and f, such
124
+ * that:
125
+ *
126
+ * When `i` is 0:
127
+ * a->e
128
+ * c->e
129
+ *
130
+ * When `i` is 1:
131
+ * b->f
132
+ * d->f
133
+ */
134
+ for (let i = 0, length = Math.floor(currentSegments.length / 2); i < length; ++i) {
81
135
  merged.push(CodePathSegment.newNext(
82
136
  context.idGenerator.next(),
83
137
  [currentSegments[i], currentSegments[i + length]]
84
138
  ));
85
139
  }
140
+
141
+ /*
142
+ * Go through the loop condition one more time to see if we have the
143
+ * number of segments for the context. If not, we'll keep merging paths
144
+ * of the merged segments until we get there.
145
+ */
86
146
  currentSegments = merged;
87
147
  }
148
+
88
149
  return currentSegments;
89
150
  }
90
151
 
@@ -93,25 +154,55 @@ function mergeExtraSegments(context, segments) {
93
154
  //------------------------------------------------------------------------------
94
155
 
95
156
  /**
96
- * A class to manage forking.
157
+ * Manages the forking of code paths.
97
158
  */
98
159
  class ForkContext {
99
160
 
100
161
  /**
162
+ * Creates a new instance.
101
163
  * @param {IdGenerator} idGenerator An identifier generator for segments.
102
- * @param {ForkContext|null} upper An upper fork context.
103
- * @param {number} count A number of parallel segments.
164
+ * @param {ForkContext|null} upper The preceding fork context.
165
+ * @param {number} count The number of parallel segments in each element
166
+ * of `segmentsList`.
104
167
  */
105
168
  constructor(idGenerator, upper, count) {
169
+
170
+ /**
171
+ * The ID generator that will generate segment IDs for any new
172
+ * segments that are created.
173
+ * @type {IdGenerator}
174
+ */
106
175
  this.idGenerator = idGenerator;
176
+
177
+ /**
178
+ * The preceding fork context.
179
+ * @type {ForkContext|null}
180
+ */
107
181
  this.upper = upper;
182
+
183
+ /**
184
+ * The number of elements in each element of `segmentsList`. In most
185
+ * cases, this is 1 but can be 2 when there is a `finally` present,
186
+ * which forks the code path outside of normal flow. In the case of nested
187
+ * `finally` blocks, this can be a multiple of 2.
188
+ * @type {number}
189
+ */
108
190
  this.count = count;
191
+
192
+ /**
193
+ * The segments within this context. Each element in this array has
194
+ * `count` elements that represent one step in each fork. For example,
195
+ * when `segmentsList` is `[[a, b], [c, d], [e, f]]`, there is one path
196
+ * a->c->e and one path b->d->f, and `count` is 2 because each element
197
+ * is an array with two elements.
198
+ * @type {Array<Array<CodePathSegment>>}
199
+ */
109
200
  this.segmentsList = [];
110
201
  }
111
202
 
112
203
  /**
113
- * The head segments.
114
- * @type {CodePathSegment[]}
204
+ * The segments that begin this fork context.
205
+ * @type {Array<CodePathSegment>}
115
206
  */
116
207
  get head() {
117
208
  const list = this.segmentsList;
@@ -120,7 +211,7 @@ class ForkContext {
120
211
  }
121
212
 
122
213
  /**
123
- * A flag which shows empty.
214
+ * Indicates if the context contains no segments.
124
215
  * @type {boolean}
125
216
  */
126
217
  get empty() {
@@ -128,7 +219,7 @@ class ForkContext {
128
219
  }
129
220
 
130
221
  /**
131
- * A flag which shows reachable.
222
+ * Indicates if there are any segments that are reachable.
132
223
  * @type {boolean}
133
224
  */
134
225
  get reachable() {
@@ -138,75 +229,82 @@ class ForkContext {
138
229
  }
139
230
 
140
231
  /**
141
- * Creates new segments from this context.
142
- * @param {number} begin The first index of previous segments.
143
- * @param {number} end The last index of previous segments.
144
- * @returns {CodePathSegment[]} New segments.
232
+ * Creates new segments in this context and appends them to the end of the
233
+ * already existing `CodePathSegment`s specified by `startIndex` and
234
+ * `endIndex`.
235
+ * @param {number} startIndex The index of the first segment in the context
236
+ * that should be specified as previous segments for the newly created segments.
237
+ * @param {number} endIndex The index of the last segment in the context
238
+ * that should be specified as previous segments for the newly created segments.
239
+ * @returns {Array<CodePathSegment>} An array of the newly created segments.
145
240
  */
146
- makeNext(begin, end) {
147
- return makeSegments(this, begin, end, CodePathSegment.newNext);
241
+ makeNext(startIndex, endIndex) {
242
+ return createSegments(this, startIndex, endIndex, CodePathSegment.newNext);
148
243
  }
149
244
 
150
245
  /**
151
- * Creates new segments from this context.
152
- * The new segments is always unreachable.
153
- * @param {number} begin The first index of previous segments.
154
- * @param {number} end The last index of previous segments.
155
- * @returns {CodePathSegment[]} New segments.
246
+ * Creates new unreachable segments in this context and appends them to the end of the
247
+ * already existing `CodePathSegment`s specified by `startIndex` and
248
+ * `endIndex`.
249
+ * @param {number} startIndex The index of the first segment in the context
250
+ * that should be specified as previous segments for the newly created segments.
251
+ * @param {number} endIndex The index of the last segment in the context
252
+ * that should be specified as previous segments for the newly created segments.
253
+ * @returns {Array<CodePathSegment>} An array of the newly created segments.
156
254
  */
157
- makeUnreachable(begin, end) {
158
- return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
255
+ makeUnreachable(startIndex, endIndex) {
256
+ return createSegments(this, startIndex, endIndex, CodePathSegment.newUnreachable);
159
257
  }
160
258
 
161
259
  /**
162
- * Creates new segments from this context.
163
- * The new segments don't have connections for previous segments.
164
- * But these inherit the reachable flag from this context.
165
- * @param {number} begin The first index of previous segments.
166
- * @param {number} end The last index of previous segments.
167
- * @returns {CodePathSegment[]} New segments.
260
+ * Creates new segments in this context and does not append them to the end
261
+ * of the already existing `CodePathSegment`s specified by `startIndex` and
262
+ * `endIndex`. The `startIndex` and `endIndex` are only used to determine if
263
+ * the new segments should be reachable. If any of the segments in this range
264
+ * are reachable then the new segments are also reachable; otherwise, the new
265
+ * segments are unreachable.
266
+ * @param {number} startIndex The index of the first segment in the context
267
+ * that should be considered for reachability.
268
+ * @param {number} endIndex The index of the last segment in the context
269
+ * that should be considered for reachability.
270
+ * @returns {Array<CodePathSegment>} An array of the newly created segments.
168
271
  */
169
- makeDisconnected(begin, end) {
170
- return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
272
+ makeDisconnected(startIndex, endIndex) {
273
+ return createSegments(this, startIndex, endIndex, CodePathSegment.newDisconnected);
171
274
  }
172
275
 
173
276
  /**
174
- * Adds segments into this context.
175
- * The added segments become the head.
176
- * @param {CodePathSegment[]} segments Segments to add.
277
+ * Adds segments to the head of this context.
278
+ * @param {Array<CodePathSegment>} segments The segments to add.
177
279
  * @returns {void}
178
280
  */
179
281
  add(segments) {
180
282
  assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
181
-
182
283
  this.segmentsList.push(mergeExtraSegments(this, segments));
183
284
  }
184
285
 
185
286
  /**
186
- * Replaces the head segments with given segments.
287
+ * Replaces the head segments with the given segments.
187
288
  * The current head segments are removed.
188
- * @param {CodePathSegment[]} segments Segments to add.
289
+ * @param {Array<CodePathSegment>} replacementHeadSegments The new head segments.
189
290
  * @returns {void}
190
291
  */
191
- replaceHead(segments) {
192
- assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
193
-
194
- this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
292
+ replaceHead(replacementHeadSegments) {
293
+ assert(
294
+ replacementHeadSegments.length >= this.count,
295
+ `${replacementHeadSegments.length} >= ${this.count}`
296
+ );
297
+ this.segmentsList.splice(-1, 1, mergeExtraSegments(this, replacementHeadSegments));
195
298
  }
196
299
 
197
300
  /**
198
301
  * Adds all segments of a given fork context into this context.
199
- * @param {ForkContext} context A fork context to add.
302
+ * @param {ForkContext} otherForkContext The fork context to add from.
200
303
  * @returns {void}
201
304
  */
202
- addAll(context) {
203
- assert(context.count === this.count);
204
-
205
- const source = context.segmentsList;
206
-
207
- for (let i = 0; i < source.length; ++i) {
208
- this.segmentsList.push(source[i]);
209
- }
305
+ addAll(otherForkContext) {
306
+ assert(otherForkContext.count === this.count);
307
+ this.segmentsList.push(...otherForkContext.segmentsList);
210
308
  }
211
309
 
212
310
  /**
@@ -218,7 +316,8 @@ class ForkContext {
218
316
  }
219
317
 
220
318
  /**
221
- * Creates the root fork context.
319
+ * Creates a new root context, meaning that there are no parent
320
+ * fork contexts.
222
321
  * @param {IdGenerator} idGenerator An identifier generator for segments.
223
322
  * @returns {ForkContext} New fork context.
224
323
  */
@@ -233,14 +332,16 @@ class ForkContext {
233
332
  /**
234
333
  * Creates an empty fork context preceded by a given context.
235
334
  * @param {ForkContext} parentContext The parent fork context.
236
- * @param {boolean} forkLeavingPath A flag which shows inside of `finally` block.
335
+ * @param {boolean} shouldForkLeavingPath Indicates that we are inside of
336
+ * a `finally` block and should therefore fork the path that leaves
337
+ * `finally`.
237
338
  * @returns {ForkContext} New fork context.
238
339
  */
239
- static newEmpty(parentContext, forkLeavingPath) {
340
+ static newEmpty(parentContext, shouldForkLeavingPath) {
240
341
  return new ForkContext(
241
342
  parentContext.idGenerator,
242
343
  parentContext,
243
- (forkLeavingPath ? 2 : 1) * parentContext.count
344
+ (shouldForkLeavingPath ? 2 : 1) * parentContext.count
244
345
  );
245
346
  }
246
347
  }