]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/rules/require-atomic-updates.js
import 8.3.0 source
[pve-eslint.git] / eslint / tests / lib / rules / require-atomic-updates.js
1 /**
2 * @fileoverview disallow assignments that can lead to race conditions due to usage of `await` or `yield`
3 * @author Teddy Katz
4 */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const rule = require("../../../lib/rules/require-atomic-updates");
12 const { RuleTester } = require("../../../lib/rule-tester");
13
14 //------------------------------------------------------------------------------
15 // Tests
16 //------------------------------------------------------------------------------
17
18 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
19
20 const VARIABLE_ERROR = {
21 messageId: "nonAtomicUpdate",
22 data: { value: "foo" },
23 type: "AssignmentExpression"
24 };
25
26 const STATIC_PROPERTY_ERROR = {
27 messageId: "nonAtomicObjectUpdate",
28 data: { value: "foo.bar", object: "foo" },
29 type: "AssignmentExpression"
30 };
31
32 const COMPUTED_PROPERTY_ERROR = {
33 messageId: "nonAtomicObjectUpdate",
34 data: { value: "foo[bar].baz", object: "foo" },
35 type: "AssignmentExpression"
36 };
37
38 const PRIVATE_PROPERTY_ERROR = {
39 messageId: "nonAtomicObjectUpdate",
40 data: { value: "foo.#bar", object: "foo" },
41 type: "AssignmentExpression"
42 };
43
44 ruleTester.run("require-atomic-updates", rule, {
45
46 valid: [
47 "let foo; async function x() { foo += bar; }",
48 "let foo; async function x() { foo = foo + bar; }",
49 "let foo; async function x() { foo = await bar + foo; }",
50 "async function x() { let foo; foo += await bar; }",
51 "let foo; async function x() { foo = (await result)(foo); }",
52 "let foo; async function x() { foo = bar(await something, foo) }",
53 "function* x() { let foo; foo += yield bar; }",
54 "const foo = {}; async function x() { foo.bar = await baz; }",
55 "const foo = []; async function x() { foo[x] += 1; }",
56 "let foo; function* x() { foo = bar + foo; }",
57 "async function x() { let foo; bar(() => baz += 1); foo += await amount; }",
58 "let foo; async function x() { foo = condition ? foo : await bar; }",
59 "async function x() { let foo; bar(() => { let foo; blah(foo); }); foo += await result; }",
60 "let foo; async function x() { foo = foo + 1; await bar; }",
61 "async function x() { foo += await bar; }",
62
63
64 /*
65 * Ensure rule doesn't take exponential time in the number of branches
66 * (see https://github.com/eslint/eslint/issues/10893)
67 */
68 `
69 async function foo() {
70 if (1);
71 if (2);
72 if (3);
73 if (4);
74 if (5);
75 if (6);
76 if (7);
77 if (8);
78 if (9);
79 if (10);
80 if (11);
81 if (12);
82 if (13);
83 if (14);
84 if (15);
85 if (16);
86 if (17);
87 if (18);
88 if (19);
89 if (20);
90 }
91 `,
92 `
93 async function foo() {
94 return [
95 1 ? a : b,
96 2 ? a : b,
97 3 ? a : b,
98 4 ? a : b,
99 5 ? a : b,
100 6 ? a : b,
101 7 ? a : b,
102 8 ? a : b,
103 9 ? a : b,
104 10 ? a : b,
105 11 ? a : b,
106 12 ? a : b,
107 13 ? a : b,
108 14 ? a : b,
109 15 ? a : b,
110 16 ? a : b,
111 17 ? a : b,
112 18 ? a : b,
113 19 ? a : b,
114 20 ? a : b
115 ];
116 }
117 `,
118
119 // https://github.com/eslint/eslint/issues/11194
120 `
121 async function f() {
122 let records
123 records = await a.records
124 g(() => { records })
125 }
126 `,
127
128 // https://github.com/eslint/eslint/issues/11687
129 `
130 async function f() {
131 try {
132 this.foo = doSomething();
133 } catch (e) {
134 this.foo = null;
135 await doElse();
136 }
137 }
138 `,
139
140 // https://github.com/eslint/eslint/issues/11723
141 `
142 async function f(foo) {
143 let bar = await get(foo.id);
144 bar.prop = foo.prop;
145 }
146 `,
147 `
148 async function f(foo) {
149 let bar = await get(foo.id);
150 foo = bar.prop;
151 }
152 `,
153 `
154 async function f() {
155 let foo = {}
156 let bar = await get(foo.id);
157 foo.prop = bar.prop;
158 }
159 `,
160
161 // https://github.com/eslint/eslint/issues/11954
162 `
163 let count = 0
164 let queue = []
165 async function A(...args) {
166 count += 1
167 await new Promise(resolve=>resolve())
168 count -= 1
169 return
170 }
171 `,
172
173 // https://github.com/eslint/eslint/issues/14208
174 `
175 async function foo(e) {
176 }
177
178 async function run() {
179 const input = [];
180 const props = [];
181
182 for(const entry of input) {
183 const prop = props.find(a => a.id === entry.id) || null;
184 await foo(entry);
185 }
186
187 for(const entry of input) {
188 const prop = props.find(a => a.id === entry.id) || null;
189 }
190
191 for(const entry2 of input) {
192 const prop = props.find(a => a.id === entry2.id) || null;
193 }
194 }
195 `,
196
197 `
198 async function run() {
199 {
200 let entry;
201 await entry;
202 }
203 {
204 let entry;
205 () => entry;
206
207 entry = 1;
208 }
209 }
210 `,
211
212 `
213 async function run() {
214 await a;
215 b = 1;
216 }
217 `,
218
219 // allowProperties
220 {
221 code: `
222 async function a(foo) {
223 if (foo.bar) {
224 foo.bar = await something;
225 }
226 }
227 `,
228 options: [{ allowProperties: true }]
229 },
230 {
231 code: `
232 function* g(foo) {
233 baz = foo.bar;
234 yield something;
235 foo.bar = 1;
236 }
237 `,
238 options: [{ allowProperties: true }]
239 }
240 ],
241
242 invalid: [
243 {
244 code: "let foo; async function x() { foo += await amount; }",
245 errors: [{ messageId: "nonAtomicUpdate", data: { value: "foo" } }]
246 },
247 {
248 code: "if (1); let foo; async function x() { foo += await amount; }",
249 errors: [{ messageId: "nonAtomicUpdate", data: { value: "foo" } }]
250 },
251 {
252 code: "let foo; async function x() { while (condition) { foo += await amount; } }",
253 errors: [VARIABLE_ERROR]
254 },
255 {
256 code: "let foo; async function x() { foo = foo + await amount; }",
257 errors: [VARIABLE_ERROR]
258 },
259 {
260 code: "let foo; async function x() { foo = foo + (bar ? baz : await amount); }",
261 errors: [VARIABLE_ERROR]
262 },
263 {
264 code: "let foo; async function x() { foo = foo + (bar ? await amount : baz); }",
265 errors: [VARIABLE_ERROR]
266 },
267 {
268 code: "let foo; async function x() { foo = condition ? foo + await amount : somethingElse; }",
269 errors: [VARIABLE_ERROR]
270 },
271 {
272 code: "let foo; async function x() { foo = (condition ? foo : await bar) + await bar; }",
273 errors: [VARIABLE_ERROR]
274 },
275 {
276 code: "let foo; async function x() { foo += bar + await amount; }",
277 errors: [VARIABLE_ERROR]
278 },
279 {
280 code: "async function x() { let foo; bar(() => foo); foo += await amount; }",
281 errors: [VARIABLE_ERROR]
282 },
283 {
284 code: "let foo; function* x() { foo += yield baz }",
285 errors: [VARIABLE_ERROR]
286 },
287 {
288 code: "let foo; async function x() { foo = bar(foo, await something) }",
289 errors: [VARIABLE_ERROR]
290 },
291 {
292 code: "const foo = {}; async function x() { foo.bar += await baz }",
293 errors: [STATIC_PROPERTY_ERROR]
294 },
295 {
296 code: "const foo = []; async function x() { foo[bar].baz += await result; }",
297 errors: [COMPUTED_PROPERTY_ERROR]
298 },
299 {
300 code: "const foo = {}; class C { #bar; async wrap() { foo.#bar += await baz } }",
301 errors: [PRIVATE_PROPERTY_ERROR]
302 },
303 {
304 code: "let foo; async function* x() { foo = (yield foo) + await bar; }",
305 errors: [VARIABLE_ERROR]
306 },
307 {
308 code: "let foo; async function x() { foo = foo + await result(foo); }",
309 errors: [VARIABLE_ERROR]
310 },
311 {
312 code: "let foo; async function x() { foo = await result(foo, await somethingElse); }",
313 errors: [VARIABLE_ERROR]
314 },
315 {
316 code: "function* x() { let foo; yield async function y() { foo += await bar; } }",
317 errors: [VARIABLE_ERROR]
318 },
319 {
320 code: "let foo; async function* x() { foo = await foo + (yield bar); }",
321 errors: [VARIABLE_ERROR]
322 },
323 {
324 code: "let foo; async function x() { foo = bar + await foo; }",
325 errors: [VARIABLE_ERROR]
326 },
327 {
328 code: "let foo = {}; async function x() { foo[bar].baz = await (foo.bar += await foo[bar].baz) }",
329 errors: [COMPUTED_PROPERTY_ERROR, STATIC_PROPERTY_ERROR]
330 },
331 {
332 code: "let foo = ''; async function x() { foo += await bar; }",
333 errors: [VARIABLE_ERROR]
334 },
335 {
336 code: "let foo = 0; async function x() { foo = (a ? b : foo) + await bar; if (baz); }",
337 errors: [VARIABLE_ERROR]
338 },
339 {
340 code: "let foo = 0; async function x() { foo = (a ? b ? c ? d ? foo : e : f : g : h) + await bar; if (baz); }",
341 errors: [VARIABLE_ERROR]
342 },
343
344 // https://github.com/eslint/eslint/issues/11723
345 {
346 code: `
347 async function f(foo) {
348 let buz = await get(foo.id);
349 foo.bar = buz.bar;
350 }
351 `,
352 errors: [STATIC_PROPERTY_ERROR]
353 },
354
355 // https://github.com/eslint/eslint/issues/15076
356 {
357 code: `
358 async () => {
359 opts.spec = process.stdin;
360 try {
361 const { exit_code } = await run(opts);
362 process.exitCode = exit_code;
363 } catch (e) {
364 process.exitCode = 1;
365 }
366 };
367 `,
368 env: { node: true },
369 errors: [
370 {
371 messageId: "nonAtomicObjectUpdate",
372 data: { value: "process.exitCode", object: "process" },
373 type: "AssignmentExpression",
374 line: 6
375 },
376 {
377 messageId: "nonAtomicObjectUpdate",
378 data: { value: "process.exitCode", object: "process" },
379 type: "AssignmentExpression",
380 line: 8
381 }
382 ]
383 },
384
385 // allowProperties
386 {
387 code: `
388 async function a(foo) {
389 if (foo.bar) {
390 foo.bar = await something;
391 }
392 }
393 `,
394 errors: [STATIC_PROPERTY_ERROR]
395 },
396 {
397 code: `
398 function* g(foo) {
399 baz = foo.bar;
400 yield something;
401 foo.bar = 1;
402 }
403 `,
404 errors: [STATIC_PROPERTY_ERROR]
405 },
406 {
407 code: `
408 async function a(foo) {
409 if (foo.bar) {
410 foo.bar = await something;
411 }
412 }
413 `,
414 options: [{}],
415 errors: [STATIC_PROPERTY_ERROR]
416
417 },
418 {
419 code: `
420 function* g(foo) {
421 baz = foo.bar;
422 yield something;
423 foo.bar = 1;
424 }
425 `,
426 options: [{}],
427 errors: [STATIC_PROPERTY_ERROR]
428 },
429 {
430 code: `
431 async function a(foo) {
432 if (foo.bar) {
433 foo.bar = await something;
434 }
435 }
436 `,
437 options: [{ allowProperties: false }],
438 errors: [STATIC_PROPERTY_ERROR]
439
440 },
441 {
442 code: `
443 function* g(foo) {
444 baz = foo.bar;
445 yield something;
446 foo.bar = 1;
447 }
448 `,
449 options: [{ allowProperties: false }],
450 errors: [STATIC_PROPERTY_ERROR]
451 },
452 {
453 code: `
454 let foo;
455 async function a() {
456 if (foo) {
457 foo = await something;
458 }
459 }
460 `,
461 options: [{ allowProperties: true }],
462 errors: [VARIABLE_ERROR]
463
464 },
465 {
466 code: `
467 let foo;
468 function* g() {
469 baz = foo;
470 yield something;
471 foo = 1;
472 }
473 `,
474 options: [{ allowProperties: true }],
475 errors: [VARIABLE_ERROR]
476 }
477 ]
478 });