1 | "use strict";
|
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3 | if (k2 === undefined) k2 = k;
|
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5 | }) : (function(o, m, k, k2) {
|
6 | if (k2 === undefined) k2 = k;
|
7 | o[k2] = m[k];
|
8 | }));
|
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
11 | }) : function(o, v) {
|
12 | o["default"] = v;
|
13 | });
|
14 | var __importStar = (this && this.__importStar) || function (mod) {
|
15 | if (mod && mod.__esModule) return mod;
|
16 | var result = {};
|
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
18 | __setModuleDefault(result, mod);
|
19 | return result;
|
20 | };
|
21 | Object.defineProperty(exports, "__esModule", { value: true });
|
22 | const tsutils = __importStar(require("tsutils"));
|
23 | const utils_1 = require("@typescript-eslint/utils");
|
24 | const util = __importStar(require("../util"));
|
25 | exports.default = util.createRule({
|
26 | name: 'no-floating-promises',
|
27 | meta: {
|
28 | docs: {
|
29 | description: 'Requires Promise-like values to be handled appropriately',
|
30 | recommended: 'error',
|
31 | suggestion: true,
|
32 | requiresTypeChecking: true,
|
33 | },
|
34 | hasSuggestions: true,
|
35 | messages: {
|
36 | floating: 'Promises must be awaited, end with a call to .catch, or end with a call to .then with a rejection handler.',
|
37 | floatingVoid: 'Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler' +
|
38 | ' or be explicitly marked as ignored with the `void` operator.',
|
39 | floatingFixVoid: 'Add void operator to ignore.',
|
40 | },
|
41 | schema: [
|
42 | {
|
43 | type: 'object',
|
44 | properties: {
|
45 | ignoreVoid: { type: 'boolean' },
|
46 | ignoreIIFE: { type: 'boolean' },
|
47 | },
|
48 | additionalProperties: false,
|
49 | },
|
50 | ],
|
51 | type: 'problem',
|
52 | },
|
53 | defaultOptions: [
|
54 | {
|
55 | ignoreVoid: true,
|
56 | ignoreIIFE: false,
|
57 | },
|
58 | ],
|
59 | create(context, [options]) {
|
60 | const parserServices = util.getParserServices(context);
|
61 | const checker = parserServices.program.getTypeChecker();
|
62 | const sourceCode = context.getSourceCode();
|
63 | return {
|
64 | ExpressionStatement(node) {
|
65 | if (options.ignoreIIFE && isAsyncIife(node)) {
|
66 | return;
|
67 | }
|
68 | let expression = node.expression;
|
69 | if (expression.type === utils_1.AST_NODE_TYPES.ChainExpression) {
|
70 | expression = expression.expression;
|
71 | }
|
72 | if (isUnhandledPromise(checker, expression)) {
|
73 | if (options.ignoreVoid) {
|
74 | context.report({
|
75 | node,
|
76 | messageId: 'floatingVoid',
|
77 | suggest: [
|
78 | {
|
79 | messageId: 'floatingFixVoid',
|
80 | fix(fixer) {
|
81 | let code = sourceCode.getText(node);
|
82 | code = `void ${code}`;
|
83 | return fixer.replaceText(node, code);
|
84 | },
|
85 | },
|
86 | ],
|
87 | });
|
88 | }
|
89 | else {
|
90 | context.report({
|
91 | node,
|
92 | messageId: 'floating',
|
93 | });
|
94 | }
|
95 | }
|
96 | },
|
97 | };
|
98 | function isAsyncIife(node) {
|
99 | if (node.expression.type !== utils_1.AST_NODE_TYPES.CallExpression) {
|
100 | return false;
|
101 | }
|
102 | return (node.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
103 | (node.expression.callee.type ===
|
104 | utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
105 | node.expression.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression));
|
106 | }
|
107 | function isUnhandledPromise(checker, node) {
|
108 |
|
109 | if (node.type === utils_1.AST_NODE_TYPES.SequenceExpression) {
|
110 |
|
111 |
|
112 |
|
113 | return node.expressions.some(item => isUnhandledPromise(checker, item));
|
114 | }
|
115 | if (!options.ignoreVoid &&
|
116 | node.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
117 | node.operator === 'void') {
|
118 |
|
119 |
|
120 | return isUnhandledPromise(checker, node.argument);
|
121 | }
|
122 |
|
123 | if (!isPromiseLike(checker, parserServices.esTreeNodeToTSNodeMap.get(node))) {
|
124 | return false;
|
125 | }
|
126 | if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
127 |
|
128 |
|
129 | return (!isPromiseCatchCallWithHandler(node) &&
|
130 | !isPromiseThenCallWithRejectionHandler(node) &&
|
131 | !isPromiseFinallyCallWithHandler(node));
|
132 | }
|
133 | else if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
134 |
|
135 |
|
136 | return (isUnhandledPromise(checker, node.alternate) ||
|
137 | isUnhandledPromise(checker, node.consequent));
|
138 | }
|
139 | else if (node.type === utils_1.AST_NODE_TYPES.MemberExpression ||
|
140 | node.type === utils_1.AST_NODE_TYPES.Identifier ||
|
141 | node.type === utils_1.AST_NODE_TYPES.NewExpression) {
|
142 |
|
143 |
|
144 |
|
145 | return true;
|
146 | }
|
147 |
|
148 |
|
149 |
|
150 | return false;
|
151 | }
|
152 | },
|
153 | });
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | function isPromiseLike(checker, node) {
|
159 | const type = checker.getTypeAtLocation(node);
|
160 | for (const ty of tsutils.unionTypeParts(checker.getApparentType(type))) {
|
161 | const then = ty.getProperty('then');
|
162 | if (then === undefined) {
|
163 | continue;
|
164 | }
|
165 | const thenType = checker.getTypeOfSymbolAtLocation(then, node);
|
166 | if (hasMatchingSignature(thenType, signature => signature.parameters.length >= 2 &&
|
167 | isFunctionParam(checker, signature.parameters[0], node) &&
|
168 | isFunctionParam(checker, signature.parameters[1], node))) {
|
169 | return true;
|
170 | }
|
171 | }
|
172 | return false;
|
173 | }
|
174 | function hasMatchingSignature(type, matcher) {
|
175 | for (const t of tsutils.unionTypeParts(type)) {
|
176 | if (t.getCallSignatures().some(matcher)) {
|
177 | return true;
|
178 | }
|
179 | }
|
180 | return false;
|
181 | }
|
182 | function isFunctionParam(checker, param, node) {
|
183 | const type = checker.getApparentType(checker.getTypeOfSymbolAtLocation(param, node));
|
184 | for (const t of tsutils.unionTypeParts(type)) {
|
185 | if (t.getCallSignatures().length !== 0) {
|
186 | return true;
|
187 | }
|
188 | }
|
189 | return false;
|
190 | }
|
191 | function isPromiseCatchCallWithHandler(expression) {
|
192 | return (expression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
193 | expression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
194 | expression.callee.property.name === 'catch' &&
|
195 | expression.arguments.length >= 1);
|
196 | }
|
197 | function isPromiseThenCallWithRejectionHandler(expression) {
|
198 | return (expression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
199 | expression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
200 | expression.callee.property.name === 'then' &&
|
201 | expression.arguments.length >= 2);
|
202 | }
|
203 | function isPromiseFinallyCallWithHandler(expression) {
|
204 | return (expression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
205 | expression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
206 | expression.callee.property.name === 'finally' &&
|
207 | expression.arguments.length >= 1);
|
208 | }
|
209 |
|
\ | No newline at end of file |