Skip to content

Commit 3f339ac

Browse files
committed
fix(linter): noFloatingPromises detects "maybe" Promises
1 parent bdfe3fd commit 3f339ac

File tree

4 files changed

+55
-1
lines changed

4 files changed

+55
-1
lines changed

crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ impl Rule for NoFloatingPromises {
180180

181181
// Uncomment the following line for debugging convenience:
182182
//let printed = format!("type of {expression:?} = {ty:?}");
183-
if !ty.is_promise_instance() {
183+
let is_maybe_promise =
184+
ty.is_promise_instance() || ty.has_variant(|ty| ty.is_promise_instance());
185+
if !is_maybe_promise {
184186
return None;
185187
}
186188

crates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/invalid.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,13 @@ async function testDestructuringAndCallingReturnsPromiseFromRest({
335335
import("some-module").then(() => {});
336336

337337
returnPromiseResult();
338+
339+
function returnMaybePromise(): Promise<void> | undefined {
340+
if (!false) {
341+
return;
342+
}
343+
344+
return Promise.resolve();
345+
}
346+
347+
returnMaybePromise();

crates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/invalid.ts.snap

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,16 @@ import("some-module").then(() => {});
342342
343343
returnPromiseResult();
344344
345+
function returnMaybePromise(): Promise<void> | undefined {
346+
if (!false) {
347+
return;
348+
}
349+
350+
return Promise.resolve();
351+
}
352+
353+
returnMaybePromise();
354+
345355
```
346356
347357
# Diagnostics
@@ -1604,6 +1614,23 @@ invalid.ts:337:1 lint/nursery/noFloatingPromises ━━━━━━━━━━
16041614
> 337 │ returnPromiseResult();
16051615
│ ^^^^^^^^^^^^^^^^^^^^^^
16061616
338 │
1617+
339 │ function returnMaybePromise(): Promise<void> | undefined {
1618+
1619+
i This happens when a Promise is not awaited, lacks a `.catch` or `.then` rejection handler, or is not explicitly ignored using the `void` operator.
1620+
1621+
1622+
```
1623+
1624+
```
1625+
invalid.ts:347:1 lint/nursery/noFloatingPromises ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1626+
1627+
i A "floating" Promise was found, meaning it is not properly handled and could lead to ignored errors or unexpected behavior.
1628+
1629+
345}
1630+
346 │
1631+
> 347 │ returnMaybePromise();
1632+
│ ^^^^^^^^^^^^^^^^^^^^^
1633+
348 │
16071634
16081635
i This happens when a Promise is not awaited, lacks a `.catch` or `.then` rejection handler, or is not explicitly ignored using the `void` operator.
16091636

crates/biome_js_type_info/src/type_info.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,21 @@ impl Type {
9595
self.id.id()
9696
}
9797

98+
/// Returns `true` if this type represents a **union type** that has a
99+
/// variant for which the given `predicate` returns `true`.
100+
///
101+
/// Returns `false` otherwise.
102+
pub fn has_variant(&self, predicate: impl Fn(Self) -> bool) -> bool {
103+
match self.deref() {
104+
TypeData::Union(union) => union
105+
.types()
106+
.iter()
107+
.filter_map(|ty| self.resolve(ty))
108+
.any(predicate),
109+
_ => false,
110+
}
111+
}
112+
98113
/// Returns whether this type is the `Promise` class.
99114
pub fn is_promise(&self) -> bool {
100115
self.id.is_global() && self.id() == PROMISE_ID

0 commit comments

Comments
 (0)