• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Rule to disallow uses of await inside of loops.
3 * @author Nat Mote (nmote)
4 */
5"use strict";
6
7/**
8 * Check whether it should stop traversing ancestors at the given node.
9 * @param {ASTNode} node A node to check.
10 * @returns {boolean} `true` if it should stop traversing.
11 */
12function isBoundary(node) {
13    const t = node.type;
14
15    return (
16        t === "FunctionDeclaration" ||
17        t === "FunctionExpression" ||
18        t === "ArrowFunctionExpression" ||
19
20        /*
21         * Don't report the await expressions on for-await-of loop since it's
22         * asynchronous iteration intentionally.
23         */
24        (t === "ForOfStatement" && node.await === true)
25    );
26}
27
28/**
29 * Check whether the given node is in loop.
30 * @param {ASTNode} node A node to check.
31 * @param {ASTNode} parent A parent node to check.
32 * @returns {boolean} `true` if the node is in loop.
33 */
34function isLooped(node, parent) {
35    switch (parent.type) {
36        case "ForStatement":
37            return (
38                node === parent.test ||
39                node === parent.update ||
40                node === parent.body
41            );
42
43        case "ForOfStatement":
44        case "ForInStatement":
45            return node === parent.body;
46
47        case "WhileStatement":
48        case "DoWhileStatement":
49            return node === parent.test || node === parent.body;
50
51        default:
52            return false;
53    }
54}
55
56module.exports = {
57    meta: {
58        type: "problem",
59
60        docs: {
61            description: "disallow `await` inside of loops",
62            category: "Possible Errors",
63            recommended: false,
64            url: "https://eslint.org/docs/rules/no-await-in-loop"
65        },
66
67        schema: [],
68
69        messages: {
70            unexpectedAwait: "Unexpected `await` inside a loop."
71        }
72    },
73    create(context) {
74
75        /**
76         * Validate an await expression.
77         * @param {ASTNode} awaitNode An AwaitExpression or ForOfStatement node to validate.
78         * @returns {void}
79         */
80        function validate(awaitNode) {
81            if (awaitNode.type === "ForOfStatement" && !awaitNode.await) {
82                return;
83            }
84
85            let node = awaitNode;
86            let parent = node.parent;
87
88            while (parent && !isBoundary(parent)) {
89                if (isLooped(node, parent)) {
90                    context.report({
91                        node: awaitNode,
92                        messageId: "unexpectedAwait"
93                    });
94                    return;
95                }
96                node = parent;
97                parent = parent.parent;
98            }
99        }
100
101        return {
102            AwaitExpression: validate,
103            ForOfStatement: validate
104        };
105    }
106};
107