UNPKG

3.82 kBJavaScriptView Raw
1/**
2 * @fileoverview Disallows unnecessary `return await`
3 * @author Jordan Harband
4 */
5"use strict";
6
7const astUtils = require("../util/ast-utils");
8
9//------------------------------------------------------------------------------
10// Rule Definition
11//------------------------------------------------------------------------------
12
13const message = "Redundant use of `await` on a return value.";
14
15module.exports = {
16 meta: {
17 type: "suggestion",
18
19 docs: {
20 description: "disallow unnecessary `return await`",
21 category: "Best Practices",
22
23 recommended: false,
24
25 url: "https://eslint.org/docs/rules/no-return-await"
26 },
27
28 fixable: null,
29
30 schema: [
31 ]
32 },
33
34 create(context) {
35
36 /**
37 * Reports a found unnecessary `await` expression.
38 * @param {ASTNode} node The node representing the `await` expression to report
39 * @returns {void}
40 */
41 function reportUnnecessaryAwait(node) {
42 context.report({
43 node: context.getSourceCode().getFirstToken(node),
44 loc: node.loc,
45 message
46 });
47 }
48
49 /**
50 * Determines whether a thrown error from this node will be caught/handled within this function rather than immediately halting
51 * this function. For example, a statement in a `try` block will always have an error handler. A statement in
52 * a `catch` block will only have an error handler if there is also a `finally` block.
53 * @param {ASTNode} node A node representing a location where an could be thrown
54 * @returns {boolean} `true` if a thrown error will be caught/handled in this function
55 */
56 function hasErrorHandler(node) {
57 let ancestor = node;
58
59 while (!astUtils.isFunction(ancestor) && ancestor.type !== "Program") {
60 if (ancestor.parent.type === "TryStatement" && (ancestor === ancestor.parent.block || ancestor === ancestor.parent.handler && ancestor.parent.finalizer)) {
61 return true;
62 }
63 ancestor = ancestor.parent;
64 }
65 return false;
66 }
67
68 /**
69 * Checks if a node is placed in tail call position. Once `return` arguments (or arrow function expressions) can be a complex expression,
70 * an `await` expression could or could not be unnecessary by the definition of this rule. So we're looking for `await` expressions that are in tail position.
71 * @param {ASTNode} node A node representing the `await` expression to check
72 * @returns {boolean} The checking result
73 */
74 function isInTailCallPosition(node) {
75 if (node.parent.type === "ArrowFunctionExpression") {
76 return true;
77 }
78 if (node.parent.type === "ReturnStatement") {
79 return !hasErrorHandler(node.parent);
80 }
81 if (node.parent.type === "ConditionalExpression" && (node === node.parent.consequent || node === node.parent.alternate)) {
82 return isInTailCallPosition(node.parent);
83 }
84 if (node.parent.type === "LogicalExpression" && node === node.parent.right) {
85 return isInTailCallPosition(node.parent);
86 }
87 if (node.parent.type === "SequenceExpression" && node === node.parent.expressions[node.parent.expressions.length - 1]) {
88 return isInTailCallPosition(node.parent);
89 }
90 return false;
91 }
92
93 return {
94 AwaitExpression(node) {
95 if (isInTailCallPosition(node) && !hasErrorHandler(node)) {
96 reportUnnecessaryAwait(node);
97 }
98 }
99 };
100 }
101};