]> git.proxmox.com Git - pve-eslint.git/blame - eslint/tests/lib/linter/code-path-analysis/code-path.js
import 8.23.1 source
[pve-eslint.git] / eslint / tests / lib / linter / code-path-analysis / code-path.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview Tests for CodePath.
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const assert = require("assert"),
13 { Linter } = require("../../../../lib/linter");
14const linter = new Linter();
15
16//------------------------------------------------------------------------------
17// Helpers
18//------------------------------------------------------------------------------
19
20/**
21 * Gets the code path of a given source code.
22 * @param {string} code A source code.
23 * @returns {CodePath[]} A list of created code paths.
24 */
25function parseCodePaths(code) {
26 const retv = [];
27
28 linter.defineRule("test", () => ({
29 onCodePathStart(codePath) {
30 retv.push(codePath);
31 }
32 }));
609c276f
TL
33
34 linter.verify(code, {
35 rules: { test: 2 },
36 parserOptions: { ecmaVersion: "latest" }
37 });
eb39fafa
DC
38
39 return retv;
40}
41
42/**
43 * Traverses a given code path then returns the order of traversing.
44 * @param {CodePath} codePath A code path to traverse.
45 * @param {Object|undefined} [options] The option object of
46 * `codePath.traverseSegments()` method.
47 * @param {Function|undefined} [callback] The callback function of
48 * `codePath.traverseSegments()` method.
49 * @returns {string[]} The list of segment's ids in the order traversed.
50 */
51function getOrderOfTraversing(codePath, options, callback) {
52 const retv = [];
53
54 codePath.traverseSegments(options, (segment, controller) => {
55 retv.push(segment.id);
56 if (callback) {
8f9d1d4d 57 callback(segment, controller); // eslint-disable-line n/callback-return -- At end of inner function
eb39fafa
DC
58 }
59 });
60
61 return retv;
62}
63
64//------------------------------------------------------------------------------
65// Tests
66//------------------------------------------------------------------------------
67
68describe("CodePathAnalyzer", () => {
609c276f
TL
69
70 /*
71 * If you need to output the code paths and DOT graph information for a
8f9d1d4d 72 * particular piece of code, update and uncomment the following test and
609c276f
TL
73 * then run:
74 * DEBUG=eslint:code-path npx mocha tests/lib/linter/code-path-analysis/
75 *
76 * All the information you need will be output to the console.
77 */
78 /*
79 * it.only("test", () => {
80 * const codePaths = parseCodePaths("class Foo { a = () => b }");
81 * });
82 */
83
84 describe("CodePath#origin", () => {
85
86 it("should be 'program' when code path starts at root node", () => {
87 const codePath = parseCodePaths("foo(); bar(); baz();")[0];
88
89 assert.strictEqual(codePath.origin, "program");
90 });
91
92 it("should be 'function' when code path starts inside a function", () => {
93 const codePath = parseCodePaths("function foo() {}")[1];
94
95 assert.strictEqual(codePath.origin, "function");
96 });
97
98 it("should be 'function' when code path starts inside an arrow function", () => {
99 const codePath = parseCodePaths("let foo = () => {}")[1];
100
101 assert.strictEqual(codePath.origin, "function");
102 });
103
104 it("should be 'class-field-initializer' when code path starts inside a class field initializer", () => {
105 const codePath = parseCodePaths("class Foo { a=1; }")[1];
106
107 assert.strictEqual(codePath.origin, "class-field-initializer");
108 });
109
110 it("should be 'class-static-block' when code path starts inside a class static block", () => {
111 const codePath = parseCodePaths("class Foo { static { this.a=1; } }")[1];
112
113 assert.strictEqual(codePath.origin, "class-static-block");
114 });
115 });
116
eb39fafa 117 describe(".traverseSegments()", () => {
609c276f 118
eb39fafa 119 describe("should traverse segments from the first to the end:", () => {
609c276f 120 /* eslint-disable internal-rules/multiline-comment-style -- Commenting out */
eb39fafa
DC
121 it("simple", () => {
122 const codePath = parseCodePaths("foo(); bar(); baz();")[0];
123 const order = getOrderOfTraversing(codePath);
124
125 assert.deepStrictEqual(order, ["s1_1"]);
126
127 /*
128 digraph {
129 node[shape=box,style="rounded,filled",fillcolor=white];
130 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
131 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
132 s1_1[label="Program\nExpressionStatement\nCallExpression\nIdentifier (foo)\nExpressionStatement\nCallExpression\nIdentifier (bar)\nExpressionStatement\nCallExpression\nIdentifier (baz)"];
133 initial->s1_1->final;
134 }
135 */
136 });
137
138 it("if", () => {
139 const codePath = parseCodePaths("if (a) foo(); else bar(); baz();")[0];
140 const order = getOrderOfTraversing(codePath);
141
142 assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4"]);
143
144 /*
145 digraph {
146 node[shape=box,style="rounded,filled",fillcolor=white];
147 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
148 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
149 s1_1[label="Program\nIfStatement\nIdentifier (a)"];
150 s1_2[label="ExpressionStatement\nCallExpression\nIdentifier (foo)"];
151 s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (baz)"];
152 s1_3[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
153 initial->s1_1->s1_2->s1_4;
154 s1_1->s1_3->s1_4->final;
155 }
156 */
157 });
158
159 it("switch", () => {
160 const codePath = parseCodePaths("switch (a) { case 0: foo(); break; case 1: bar(); } baz();")[0];
161 const order = getOrderOfTraversing(codePath);
162
163 assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_4", "s1_5", "s1_6"]);
164
165 /*
166 digraph {
167 node[shape=box,style="rounded,filled",fillcolor=white];
168 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
169 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
170 s1_1[label="Program\nSwitchStatement\nIdentifier (a)\nSwitchCase\nLiteral (0)"];
171 s1_2[label="ExpressionStatement\nCallExpression\nIdentifier (foo)\nBreakStatement"];
172 s1_3[style="rounded,dashed,filled",fillcolor="#FF9800",label="<<unreachable>>\nSwitchCase:exit"];
173 s1_5[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
174 s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (baz)"];
175 s1_4[label="SwitchCase\nLiteral (1)"];
176 initial->s1_1->s1_2->s1_3->s1_5->s1_6;
177 s1_1->s1_4->s1_5;
178 s1_2->s1_6;
179 s1_4->s1_6->final;
180 }
181 */
182 });
183
184 it("while", () => {
185 const codePath = parseCodePaths("while (a) foo(); bar();")[0];
186 const order = getOrderOfTraversing(codePath);
187
188 assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4"]);
189
190 /*
191 digraph {
192 node[shape=box,style="rounded,filled",fillcolor=white];
193 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
194 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
195 s1_1[label="Program\nWhileStatement"];
196 s1_2[label="Identifier (a)"];
197 s1_3[label="ExpressionStatement\nCallExpression\nIdentifier (foo)"];
198 s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
199 initial->s1_1->s1_2->s1_3->s1_2->s1_4->final;
200 }
201 */
202 });
203
204 it("for", () => {
205 const codePath = parseCodePaths("for (var i = 0; i < 10; ++i) foo(i); bar();")[0];
206 const order = getOrderOfTraversing(codePath);
207
208 assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4", "s1_5"]);
209
210 /*
211 digraph {
212 node[shape=box,style="rounded,filled",fillcolor=white];
213 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
214 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
215 s1_1[label="Program\nForStatement\nVariableDeclaration\nVariableDeclarator\nIdentifier (i)\nLiteral (0)"];
216 s1_2[label="BinaryExpression\nIdentifier (i)\nLiteral (10)"];
217 s1_3[label="ExpressionStatement\nCallExpression\nIdentifier (foo)\nIdentifier (i)"];
218 s1_4[label="UpdateExpression\nIdentifier (i)"];
219 s1_5[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
220 initial->s1_1->s1_2->s1_3->s1_4->s1_2->s1_5->final;
221 }
222 */
223 });
224
225 it("for-in", () => {
226 const codePath = parseCodePaths("for (var key in obj) foo(key); bar();")[0];
227 const order = getOrderOfTraversing(codePath);
228
229 assert.deepStrictEqual(order, ["s1_1", "s1_3", "s1_2", "s1_4", "s1_5"]);
230
231 /*
232 digraph {
233 node[shape=box,style="rounded,filled",fillcolor=white];
234 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
235 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
236 s1_1[label="Program\nForInStatement"];
237 s1_3[label="Identifier (obj)"];
238 s1_2[label="VariableDeclaration\nVariableDeclarator\nIdentifier (key)"];
239 s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (foo)\nIdentifier (key)"];
240 s1_5[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
241 initial->s1_1->s1_3->s1_2->s1_4->s1_2;
242 s1_3->s1_5;
243 s1_4->s1_5->final;
244 }
245 */
246 });
247
248 it("try-catch", () => {
249 const codePath = parseCodePaths("try { foo(); } catch (e) { bar(); } baz();")[0];
250 const order = getOrderOfTraversing(codePath);
251
252 assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4"]);
253
254 /*
255 digraph {
256 node[shape=box,style="rounded,filled",fillcolor=white];
257 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
258 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
259 s1_1[label="Program\nTryStatement\nBlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"];
260 s1_2[label="CallExpression:exit\nExpressionStatement:exit\nBlockStatement:exit"];
261 s1_3[label="CatchClause\nIdentifier (e)\nBlockStatement\nExpressionStatement\nCallExpression\nIdentifier (bar)"];
262 s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (baz)"];
263 initial->s1_1->s1_2->s1_3->s1_4;
264 s1_1->s1_3;
265 s1_2->s1_4->final;
266 }
267 */
268 });
269 });
270
271 it("should traverse segments from `options.first` to `options.last`.", () => {
272 const codePath = parseCodePaths("if (a) { if (b) { foo(); } bar(); } else { out1(); } out2();")[0];
273 const order = getOrderOfTraversing(codePath, {
274 first: codePath.initialSegment.nextSegments[0],
275 last: codePath.initialSegment.nextSegments[0].nextSegments[1]
276 });
277
278 assert.deepStrictEqual(order, ["s1_2", "s1_3", "s1_4"]);
279
280 /*
281 digraph {
282 node[shape=box,style="rounded,filled",fillcolor=white];
283 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
284 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
285 s1_1[label="Program\nIfStatement\nIdentifier (a)"];
286 s1_2[label="BlockStatement\nIfStatement\nIdentifier (b)"];
287 s1_3[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"];
288 s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
289 s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (out2)"];
290 s1_5[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (out1)"];
291 initial->s1_1->s1_2->s1_3->s1_4->s1_6;
292 s1_1->s1_5->s1_6;
293 s1_2->s1_4;
294 s1_6->final;
295 }
296 */
297 });
298
299 it("should stop immediately when 'controller.break()' was called.", () => {
300 const codePath = parseCodePaths("if (a) { if (b) { foo(); } bar(); } else { out1(); } out2();")[0];
301 const order = getOrderOfTraversing(codePath, null, (segment, controller) => {
302 if (segment.id === "s1_2") {
303 controller.break();
304 }
305 });
306
307 assert.deepStrictEqual(order, ["s1_1", "s1_2"]);
308
309 /*
310 digraph {
311 node[shape=box,style="rounded,filled",fillcolor=white];
312 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
313 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
314 s1_1[label="Program\nIfStatement\nIdentifier (a)"];
315 s1_2[label="BlockStatement\nIfStatement\nIdentifier (b)"];
316 s1_3[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"];
317 s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
318 s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (out2)"];
319 s1_5[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (out1)"];
320 initial->s1_1->s1_2->s1_3->s1_4->s1_6;
321 s1_1->s1_5->s1_6;
322 s1_2->s1_4;
323 s1_6->final;
324 }
325 */
326 });
327
328 it("should skip the current branch when 'controller.skip()' was called.", () => {
329 const codePath = parseCodePaths("if (a) { if (b) { foo(); } bar(); } else { out1(); } out2();")[0];
330 const order = getOrderOfTraversing(codePath, null, (segment, controller) => {
331 if (segment.id === "s1_2") {
332 controller.skip();
333 }
334 });
335
336 assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_5", "s1_6"]);
337
338 /*
339 digraph {
340 node[shape=box,style="rounded,filled",fillcolor=white];
341 initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
342 final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
343 s1_1[label="Program\nIfStatement\nIdentifier (a)"];
344 s1_2[label="BlockStatement\nIfStatement\nIdentifier (b)"];
345 s1_3[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"];
346 s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"];
347 s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (out2)"];
348 s1_5[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (out1)"];
349 initial->s1_1->s1_2->s1_3->s1_4->s1_6;
350 s1_1->s1_5->s1_6;
351 s1_2->s1_4;
352 s1_6->final;
353 }
354 */
355 });
356
609c276f 357 /* eslint-enable internal-rules/multiline-comment-style -- Commenting out */
eb39fafa
DC
358 });
359});