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