Skip to content

Commit 44d3ec7

Browse files
committed
fix(no-floating-promises): avoid recursive checker calls
1 parent 4d71eb8 commit 44d3ec7

3 files changed

Lines changed: 63 additions & 2 deletions

File tree

internal/rule_tester/__snapshots__/no-floating-promises.snap

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,3 +1684,26 @@ Help: The promise must end with a call to .catch, or end with a call to .then wi
16841684
Suggestion 1: [floatingFixVoid] Add void operator to ignore.
16851685
Suggestion 2: [floatingFixAwait] Add await operator.
16861686
---
1687+
1688+
[TestNoFloatingPromisesRule/invalid-101 - 1]
1689+
Diagnostic 1: floatingVoid (14:9 - 20:13)
1690+
Message: Promises must be awaited, add void operator to ignore.
1691+
Help: The promise must end with a call to .catch, or end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator.
1692+
13 |
1693+
14 | (async () => {
1694+
| ~~~~~~~~~~~~~~
1695+
15 | wrapNode(() => {
1696+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
1697+
16 | const node = createNode();
1698+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1699+
17 |
1700+
18 | return wrapNode<typeof node.getNextNode<any>>(node.getNextNode);
1701+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1702+
19 | });
1703+
| ~~~~~~~~~~~~~
1704+
20 | })();
1705+
| ~~~~~~~~~~~~~
1706+
21 |
1707+
Suggestion 1: [floatingFixVoid] Add void operator to ignore.
1708+
Suggestion 2: [floatingFixAwait] Add await operator.
1709+
---

internal/rules/no_floating_promises/no_floating_promises.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,42 @@ var NoFloatingPromisesRule = rule.Rule{
200200
}
201201
return false
202202
}
203+
arrayReturnContainsTypeParameter := func(t *checker.Type) bool {
204+
for _, typePart := range utils.UnionTypeParts(t) {
205+
apparent := checker.Checker_getApparentType(ctx.TypeChecker, typePart)
206+
if !checker.Checker_isArrayType(ctx.TypeChecker, apparent) && !checker.IsTupleType(apparent) {
207+
continue
208+
}
209+
for _, typeArg := range checker.Checker_getTypeArguments(ctx.TypeChecker, apparent) {
210+
if typeArg.Flags()&checker.TypeFlagsTypeParameter != 0 {
211+
return true
212+
}
213+
}
214+
}
215+
return false
216+
}
217+
callReturnsKnownNonPromise := func(callExpr *ast.CallExpression) bool {
218+
calleeType := ctx.TypeChecker.GetTypeAtLocation(callExpr.Expression)
219+
signatures := utils.GetCallSignatures(ctx.TypeChecker, calleeType)
220+
if len(signatures) == 0 {
221+
return false
222+
}
223+
224+
for _, signature := range signatures {
225+
returnType := checker.Checker_getReturnTypeOfSignature(ctx.TypeChecker, signature)
226+
if returnType == nil || returnType.Flags()&checker.TypeFlagsTypeParameter != 0 {
227+
return false
228+
}
229+
if arrayReturnContainsTypeParameter(returnType) {
230+
return false
231+
}
232+
if isPromiseArray(callExpr.AsNode(), returnType) || isPromiseLike(callExpr.AsNode(), returnType) {
233+
return false
234+
}
235+
}
236+
237+
return true
238+
}
203239

204240
isKnownSafePromiseReturn := func(node *ast.Node) bool {
205241
if len(opts.AllowForKnownSafeCalls) == 0 {
@@ -296,6 +332,10 @@ var NoFloatingPromisesRule = rule.Rule{
296332
// Check the type. At this point it can't be unhandled if it isn't a promise
297333
// or array thereof.
298334

335+
if ast.IsCallExpression(node) && callReturnsKnownNonPromise(node.AsCallExpression()) {
336+
return false, false, false
337+
}
338+
299339
t := ctx.TypeChecker.GetTypeAtLocation(node)
300340
if isPromiseArray(node, t) {
301341
return true, false, true

internal/rules/no_floating_promises/no_floating_promises_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,6 @@ myAsyncFunction();
807807
Options: rule_tester.OptionsFromJSON[NoFloatingPromisesOptions](`{"allowForKnownSafeCalls": ["myAsyncFunction"]}`),
808808
},
809809
{
810-
Skip: true,
811810
Code: `
812811
interface CustomNode<P> {
813812
getNextNode: () => CustomNode<P>;
@@ -5606,7 +5605,6 @@ await Promise.reject('foo').then(...[], () => {});
56065605
},
56075606
},
56085607
{
5609-
Skip: true,
56105608
Code: `
56115609
interface CustomNode<P> {
56125610
getNextNode: () => CustomNode<P>;

0 commit comments

Comments
 (0)