/
no-return-await.js
135 lines (111 loc) · 4.99 KB
/
no-return-await.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/**
* @fileoverview Disallows unnecessary `return await`
* @author Jordan Harband
* @deprecated in ESLint v8.46.0
*/
"use strict";
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
hasSuggestions: true,
type: "suggestion",
docs: {
description: "Disallow unnecessary `return await`",
recommended: false,
url: "https://eslint.org/docs/latest/rules/no-return-await"
},
fixable: null,
deprecated: true,
replacedBy: [],
schema: [
],
messages: {
removeAwait: "Remove redundant `await`.",
redundantUseOfAwait: "Redundant use of `await` on a return value."
}
},
create(context) {
/**
* Reports a found unnecessary `await` expression.
* @param {ASTNode} node The node representing the `await` expression to report
* @returns {void}
*/
function reportUnnecessaryAwait(node) {
context.report({
node: context.sourceCode.getFirstToken(node),
loc: node.loc,
messageId: "redundantUseOfAwait",
suggest: [
{
messageId: "removeAwait",
fix(fixer) {
const sourceCode = context.sourceCode;
const [awaitToken, tokenAfterAwait] = sourceCode.getFirstTokens(node, 2);
const areAwaitAndAwaitedExpressionOnTheSameLine = awaitToken.loc.start.line === tokenAfterAwait.loc.start.line;
if (!areAwaitAndAwaitedExpressionOnTheSameLine) {
return null;
}
const [startOfAwait, endOfAwait] = awaitToken.range;
const characterAfterAwait = sourceCode.text[endOfAwait];
const trimLength = characterAfterAwait === " " ? 1 : 0;
const range = [startOfAwait, endOfAwait + trimLength];
return fixer.removeRange(range);
}
}
]
});
}
/**
* Determines whether a thrown error from this node will be caught/handled within this function rather than immediately halting
* this function. For example, a statement in a `try` block will always have an error handler. A statement in
* a `catch` block will only have an error handler if there is also a `finally` block.
* @param {ASTNode} node A node representing a location where an could be thrown
* @returns {boolean} `true` if a thrown error will be caught/handled in this function
*/
function hasErrorHandler(node) {
let ancestor = node;
while (!astUtils.isFunction(ancestor) && ancestor.type !== "Program") {
if (ancestor.parent.type === "TryStatement" && (ancestor === ancestor.parent.block || ancestor === ancestor.parent.handler && ancestor.parent.finalizer)) {
return true;
}
ancestor = ancestor.parent;
}
return false;
}
/**
* Checks if a node is placed in tail call position. Once `return` arguments (or arrow function expressions) can be a complex expression,
* 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.
* @param {ASTNode} node A node representing the `await` expression to check
* @returns {boolean} The checking result
*/
function isInTailCallPosition(node) {
if (node.parent.type === "ArrowFunctionExpression") {
return true;
}
if (node.parent.type === "ReturnStatement") {
return !hasErrorHandler(node.parent);
}
if (node.parent.type === "ConditionalExpression" && (node === node.parent.consequent || node === node.parent.alternate)) {
return isInTailCallPosition(node.parent);
}
if (node.parent.type === "LogicalExpression" && node === node.parent.right) {
return isInTailCallPosition(node.parent);
}
if (node.parent.type === "SequenceExpression" && node === node.parent.expressions.at(-1)) {
return isInTailCallPosition(node.parent);
}
return false;
}
return {
AwaitExpression(node) {
if (isInTailCallPosition(node) && !hasErrorHandler(node)) {
reportUnnecessaryAwait(node);
}
}
};
}
};