]> git.proxmox.com Git - pve-eslint.git/blame - eslint/docs/src/extend/code-path-analysis.md
import 8.41.0 source
[pve-eslint.git] / eslint / docs / src / extend / code-path-analysis.md
CommitLineData
8f9d1d4d
DC
1---
2title: Code Path Analysis Details
8f9d1d4d
DC
3
4---
eb39fafa
DC
5
6ESLint's rules can use code paths.
7The code path is execution routes of programs.
8It forks/joins at such as `if` statements.
9
10```js
11if (a && b) {
12 foo();
13}
14bar();
15```
16
f2a92ac6 17:::img-container
8f9d1d4d 18![Code Path Example](../assets/images/code-path-analysis/helo.svg)
f2a92ac6 19:::
eb39fafa
DC
20
21## Objects
22
23Program is expressed with several code paths.
24A code path is expressed with objects of two kinds: `CodePath` and `CodePathSegment`.
25
26### `CodePath`
27
28`CodePath` expresses whole of one code path.
29This object exists for each function and the global.
30This has references of both the initial segment and the final segments of a code path.
31
32`CodePath` has the following properties:
33
34* `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each code path.
609c276f 35* `origin` (`string`) - The reason that the code path was started. May be `"program"`, `"function"`, `"class-field-initializer"`, or `"class-static-block"`.
eb39fafa
DC
36* `initialSegment` (`CodePathSegment`) - The initial segment of this code path.
37* `finalSegments` (`CodePathSegment[]`) - The final segments which includes both returned and thrown.
38* `returnedSegments` (`CodePathSegment[]`) - The final segments which includes only returned.
39* `thrownSegments` (`CodePathSegment[]`) - The final segments which includes only thrown.
40* `currentSegments` (`CodePathSegment[]`) - Segments of the current position.
41* `upper` (`CodePath|null`) - The code path of the upper function/global scope.
42* `childCodePaths` (`CodePath[]`) - Code paths of functions this code path contains.
43
44### `CodePathSegment`
45
46`CodePathSegment` is a part of a code path.
47A code path is expressed with plural `CodePathSegment` objects, it's similar to doubly linked list.
48Difference from doubly linked list is what there are forking and merging (the next/prev are plural).
49
50`CodePathSegment` has the following properties:
51
52* `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each segment.
53* `nextSegments` (`CodePathSegment[]`) - The next segments. If forking, there are two or more. If final, there is nothing.
54* `prevSegments` (`CodePathSegment[]`) - The previous segments. If merging, there are two or more. If initial, there is nothing.
55* `reachable` (`boolean`) - A flag which shows whether or not it's reachable. This becomes `false` when preceded by `return`, `throw`, `break`, or `continue`.
56
57## Events
58
59There are five events related to code paths, and you can define event handlers in rules.
60
61```js
62module.exports = function(context) {
63 return {
64 /**
65 * This is called at the start of analyzing a code path.
66 * In this time, the code path object has only the initial segment.
67 *
68 * @param {CodePath} codePath - The new code path.
69 * @param {ASTNode} node - The current node.
70 * @returns {void}
71 */
72 "onCodePathStart": function(codePath, node) {
73 // do something with codePath
74 },
75
76 /**
77 * This is called at the end of analyzing a code path.
78 * In this time, the code path object is complete.
79 *
80 * @param {CodePath} codePath - The completed code path.
81 * @param {ASTNode} node - The current node.
82 * @returns {void}
83 */
84 "onCodePathEnd": function(codePath, node) {
85 // do something with codePath
86 },
87
88 /**
89 * This is called when a code path segment was created.
90 * It meant the code path is forked or merged.
91 * In this time, the segment has the previous segments and has been
92 * judged reachable or not.
93 *
94 * @param {CodePathSegment} segment - The new code path segment.
95 * @param {ASTNode} node - The current node.
96 * @returns {void}
97 */
98 "onCodePathSegmentStart": function(segment, node) {
99 // do something with segment
100 },
101
102 /**
8f9d1d4d 103 * This is called when a code path segment was left.
eb39fafa
DC
104 * In this time, the segment does not have the next segments yet.
105 *
8f9d1d4d 106 * @param {CodePathSegment} segment - The left code path segment.
eb39fafa
DC
107 * @param {ASTNode} node - The current node.
108 * @returns {void}
109 */
110 "onCodePathSegmentEnd": function(segment, node) {
111 // do something with segment
112 },
113
114 /**
115 * This is called when a code path segment was looped.
116 * Usually segments have each previous segments when created,
117 * but when looped, a segment is added as a new previous segment into a
118 * existing segment.
119 *
120 * @param {CodePathSegment} fromSegment - A code path segment of source.
121 * @param {CodePathSegment} toSegment - A code path segment of destination.
122 * @param {ASTNode} node - The current node.
123 * @returns {void}
124 */
125 "onCodePathSegmentLoop": function(fromSegment, toSegment, node) {
126 // do something with segment
127 }
128 };
129};
130```
131
132### About `onCodePathSegmentLoop`
133
134This event is always fired when the next segment has existed already.
135That timing is the end of loops mainly.
136
137For Example 1:
138
139```js
140while (a) {
141 a = foo();
142}
143bar();
144```
145
1461. First, the analysis advances to the end of loop.
147
f2a92ac6 148:::img-container
8f9d1d4d 149 ![Loop Event's Example 1](../assets/images/code-path-analysis/loop-event-example-while-1.svg)
f2a92ac6 150:::
eb39fafa
DC
151
1522. Second, it creates the looping path.
153 At this time, the next segment has existed already, so the `onCodePathSegmentStart` event is not fired.
154 It fires `onCodePathSegmentLoop` instead.
155
f2a92ac6 156:::img-container
8f9d1d4d 157 ![Loop Event's Example 2](../assets/images/code-path-analysis/loop-event-example-while-2.svg)
f2a92ac6 158:::
eb39fafa
DC
159
1603. Last, it advances to the end.
161
f2a92ac6 162:::img-container
8f9d1d4d 163 ![Loop Event's Example 3](../assets/images/code-path-analysis/loop-event-example-while-3.svg)
f2a92ac6 164:::
eb39fafa
DC
165
166For example 2:
167
168```js
169for (let i = 0; i < 10; ++i) {
170 foo(i);
171}
172bar();
173```
174
1751. `for` statements are more complex.
176 First, the analysis advances to `ForStatement.update`.
177 The `update` segment is hovered at first.
178
f2a92ac6 179:::img-container
8f9d1d4d 180 ![Loop Event's Example 1](../assets/images/code-path-analysis/loop-event-example-for-1.svg)
f2a92ac6 181:::
eb39fafa
DC
182
1832. Second, it advances to `ForStatement.body`.
184 Of course the `body` segment is preceded by the `test` segment.
185 It keeps the `update` segment hovering.
186
f2a92ac6 187:::img-container
8f9d1d4d 188 ![Loop Event's Example 2](../assets/images/code-path-analysis/loop-event-example-for-2.svg)
f2a92ac6 189:::
eb39fafa
DC
190
1913. Third, it creates the looping path from `body` segment to `update` segment.
192 At this time, the next segment has existed already, so the `onCodePathSegmentStart` event is not fired.
193 It fires `onCodePathSegmentLoop` instead.
194
f2a92ac6 195:::img-container
8f9d1d4d 196 ![Loop Event's Example 3](../assets/images/code-path-analysis/loop-event-example-for-3.svg)
f2a92ac6 197:::
eb39fafa
DC
198
1994. Fourth, also it creates the looping path from `update` segment to `test` segment.
200 At this time, the next segment has existed already, so the `onCodePathSegmentStart` event is not fired.
201 It fires `onCodePathSegmentLoop` instead.
202
f2a92ac6 203:::img-container
8f9d1d4d 204 ![Loop Event's Example 4](../assets/images/code-path-analysis/loop-event-example-for-4.svg)
f2a92ac6 205:::
eb39fafa
DC
206
2075. Last, it advances to the end.
208
f2a92ac6 209:::img-container
8f9d1d4d 210 ![Loop Event's Example 5](../assets/images/code-path-analysis/loop-event-example-for-5.svg)
f2a92ac6 211:::
eb39fafa 212
eb39fafa
DC
213## Usage Examples
214
215### To check whether or not this is reachable
216
217```js
eb39fafa
DC
218function isReachable(segment) {
219 return segment.reachable;
220}
221
222module.exports = function(context) {
223 var codePathStack = [];
224
225 return {
226 // Stores CodePath objects.
227 "onCodePathStart": function(codePath) {
228 codePathStack.push(codePath);
229 },
230 "onCodePathEnd": function(codePath) {
231 codePathStack.pop();
232 },
233
234 // Checks reachable or not.
235 "ExpressionStatement": function(node) {
5422a9cc 236 var codePath = codePathStack[codePathStack.length - 1];
eb39fafa
DC
237
238 // Checks the current code path segments.
239 if (!codePath.currentSegments.some(isReachable)) {
240 context.report({message: "Unreachable!", node: node});
241 }
242 }
243 };
244};
245```
246
247See Also:
609c276f
TL
248[no-unreachable](https://github.com/eslint/eslint/blob/HEAD/lib/rules/no-unreachable.js),
249[no-fallthrough](https://github.com/eslint/eslint/blob/HEAD/lib/rules/no-fallthrough.js),
250[consistent-return](https://github.com/eslint/eslint/blob/HEAD/lib/rules/consistent-return.js)
eb39fafa
DC
251
252### To check state of a code path
253
254This example is checking whether or not the parameter `cb` is called in every path.
255Instances of `CodePath` and `CodePathSegment` are shared to every rule.
256So a rule must not modify those instances.
257Please use a map of information instead.
258
259```js
eb39fafa
DC
260function hasCb(node, context) {
261 if (node.type.indexOf("Function") !== -1) {
f2a92ac6
DC
262 const sourceCode = context.sourceCode;
263 return sourceCode.getDeclaredVariables(node).some(function(v) {
eb39fafa
DC
264 return v.type === "Parameter" && v.name === "cb";
265 });
266 }
267 return false;
268}
269
270function isCbCalled(info) {
271 return info.cbCalled;
272}
273
274module.exports = function(context) {
275 var funcInfoStack = [];
276 var segmentInfoMap = Object.create(null);
277
278 return {
279 // Checks `cb`.
280 "onCodePathStart": function(codePath, node) {
281 funcInfoStack.push({
282 codePath: codePath,
283 hasCb: hasCb(node, context)
284 });
285 },
286 "onCodePathEnd": function(codePath, node) {
287 funcInfoStack.pop();
288
289 // Checks `cb` was called in every paths.
290 var cbCalled = codePath.finalSegments.every(function(segment) {
291 var info = segmentInfoMap[segment.id];
292 return info.cbCalled;
293 });
294
295 if (!cbCalled) {
296 context.report({
297 message: "`cb` should be called in every path.",
298 node: node
299 });
300 }
301 },
302
303 // Manages state of code paths.
304 "onCodePathSegmentStart": function(segment) {
5422a9cc
TL
305 var funcInfo = funcInfoStack[funcInfoStack.length - 1];
306
eb39fafa 307 // Ignores if `cb` doesn't exist.
5422a9cc 308 if (!funcInfo.hasCb) {
eb39fafa
DC
309 return;
310 }
311
312 // Initialize state of this path.
313 var info = segmentInfoMap[segment.id] = {
314 cbCalled: false
315 };
316
317 // If there are the previous paths, merges state.
318 // Checks `cb` was called in every previous path.
319 if (segment.prevSegments.length > 0) {
320 info.cbCalled = segment.prevSegments.every(isCbCalled);
321 }
322 },
323
324 // Checks reachable or not.
325 "CallExpression": function(node) {
5422a9cc 326 var funcInfo = funcInfoStack[funcInfoStack.length - 1];
eb39fafa
DC
327
328 // Ignores if `cb` doesn't exist.
329 if (!funcInfo.hasCb) {
330 return;
331 }
332
333 // Sets marks that `cb` was called.
334 var callee = node.callee;
335 if (callee.type === "Identifier" && callee.name === "cb") {
336 funcInfo.codePath.currentSegments.forEach(function(segment) {
337 var info = segmentInfoMap[segment.id];
338 info.cbCalled = true;
339 });
340 }
341 }
342 };
343};
344```
345
346See Also:
609c276f
TL
347[constructor-super](https://github.com/eslint/eslint/blob/HEAD/lib/rules/constructor-super.js),
348[no-this-before-super](https://github.com/eslint/eslint/blob/HEAD/lib/rules/no-this-before-super.js)
eb39fafa
DC
349
350## Code Path Examples
351
352### Hello World
353
354```js
355console.log("Hello world!");
356```
357
f2a92ac6 358:::img-container
8f9d1d4d 359![Hello World](../assets/images/code-path-analysis/example-hello-world.svg)
f2a92ac6 360:::
eb39fafa
DC
361
362### `IfStatement`
363
364```js
365if (a) {
366 foo();
367} else {
368 bar();
369}
370```
371
f2a92ac6 372:::img-container
8f9d1d4d 373![`IfStatement`](../assets/images/code-path-analysis/example-ifstatement.svg)
f2a92ac6 374:::
eb39fafa
DC
375
376### `IfStatement` (chain)
377
378```js
379if (a) {
380 foo();
381} else if (b) {
382 bar();
383} else if (c) {
384 hoge();
385}
386```
387
f2a92ac6 388:::img-container
8f9d1d4d 389![`IfStatement` (chain)](../assets/images/code-path-analysis/example-ifstatement-chain.svg)
f2a92ac6 390:::
eb39fafa
DC
391
392### `SwitchStatement`
393
394```js
395switch (a) {
396 case 0:
397 foo();
398 break;
399
400 case 1:
401 case 2:
402 bar();
403 // fallthrough
404
405 case 3:
406 hoge();
407 break;
408}
409```
410
f2a92ac6 411:::img-container
8f9d1d4d 412![`SwitchStatement`](../assets/images/code-path-analysis/example-switchstatement.svg)
f2a92ac6 413:::
eb39fafa
DC
414
415### `SwitchStatement` (has `default`)
416
417```js
418switch (a) {
419 case 0:
420 foo();
421 break;
422
423 case 1:
424 case 2:
425 bar();
426 // fallthrough
427
428 case 3:
429 hoge();
430 break;
431
432 default:
433 fuga();
434 break;
435}
436```
437
f2a92ac6 438:::img-container
8f9d1d4d 439![`SwitchStatement` (has `default`)](../assets/images/code-path-analysis/example-switchstatement-has-default.svg)
f2a92ac6 440:::
eb39fafa
DC
441
442### `TryStatement` (try-catch)
443
444```js
445try {
446 foo();
447 if (a) {
448 throw new Error();
449 }
450 bar();
451} catch (err) {
452 hoge(err);
453}
454last();
455```
456
457It creates the paths from `try` block to `catch` block at:
458
459* `throw` statements.
460* The first throwable node (e.g. a function call) in the `try` block.
461* The end of the `try` block.
462
f2a92ac6 463:::img-container
8f9d1d4d 464![`TryStatement` (try-catch)](../assets/images/code-path-analysis/example-trystatement-try-catch.svg)
f2a92ac6 465:::
eb39fafa
DC
466
467### `TryStatement` (try-finally)
468
469```js
470try {
471 foo();
472 bar();
473} finally {
474 fuga();
475}
476last();
477```
478
479If there is not `catch` block, `finally` block has two current segments.
480At this time, `CodePath.currentSegments.length` is `2`.
481One is the normal path, and another is the leaving path (`throw` or `return`).
482
f2a92ac6 483:::img-container
8f9d1d4d 484![`TryStatement` (try-finally)](../assets/images/code-path-analysis/example-trystatement-try-finally.svg)
f2a92ac6 485:::
eb39fafa
DC
486
487### `TryStatement` (try-catch-finally)
488
489```js
490try {
491 foo();
492 bar();
493} catch (err) {
494 hoge(err);
495} finally {
496 fuga();
497}
498last();
499```
500
f2a92ac6 501:::img-container
8f9d1d4d 502![`TryStatement` (try-catch-finally)](../assets/images/code-path-analysis/example-trystatement-try-catch-finally.svg)
f2a92ac6 503:::
eb39fafa
DC
504
505### `WhileStatement`
506
507```js
508while (a) {
509 foo();
510 if (b) {
511 continue;
512 }
513 bar();
514}
515```
516
f2a92ac6 517:::img-container
8f9d1d4d 518![`WhileStatement`](../assets/images/code-path-analysis/example-whilestatement.svg)
f2a92ac6 519:::
eb39fafa
DC
520
521### `DoWhileStatement`
522
523```js
524do {
525 foo();
526 bar();
527} while (a);
528```
529
f2a92ac6 530:::img-container
8f9d1d4d 531![`DoWhileStatement`](../assets/images/code-path-analysis/example-dowhilestatement.svg)
f2a92ac6 532:::
eb39fafa
DC
533
534### `ForStatement`
535
536```js
537for (let i = 0; i < 10; ++i) {
538 foo();
539 if (b) {
540 break;
541 }
542 bar();
543}
544```
545
f2a92ac6 546:::img-container
8f9d1d4d 547![`ForStatement`](../assets/images/code-path-analysis/example-forstatement.svg)
f2a92ac6 548:::
eb39fafa
DC
549
550### `ForStatement` (for ever)
551
552```js
553for (;;) {
554 foo();
555}
556bar();
557```
558
f2a92ac6 559:::img-container
8f9d1d4d 560![`ForStatement` (for ever)](../assets/images/code-path-analysis/example-forstatement-for-ever.svg)
f2a92ac6 561:::
eb39fafa
DC
562
563### `ForInStatement`
564
565```js
566for (let key in obj) {
567 foo(key);
568}
569```
570
f2a92ac6 571:::img-container
8f9d1d4d 572![`ForInStatement`](../assets/images/code-path-analysis/example-forinstatement.svg)
f2a92ac6 573:::
eb39fafa
DC
574
575### When there is a function
576
577```js
578function foo(a) {
579 if (a) {
580 return;
581 }
582 bar();
583}
584
585foo(false);
586```
587
588It creates two code paths.
589
590* The global's
591
f2a92ac6 592:::img-container
8f9d1d4d 593 ![When there is a function](../assets/images/code-path-analysis/example-when-there-is-a-function-g.svg)
f2a92ac6 594:::
eb39fafa
DC
595
596* The function's
597
f2a92ac6 598:::img-container
8f9d1d4d 599 ![When there is a function](../assets/images/code-path-analysis/example-when-there-is-a-function-f.svg)
f2a92ac6 600:::