Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/use-await-yield-star.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@biomejs/biome": patch
---

Fixed [#8645](https://github.com/biomejs/biome/issues/8645).
[useAwait](https://biomejs.dev/linter/rules/use-await/) no longer reports `async` generator functions that use `yield*`, since `yield*` in an async generator delegates to an `AsyncIterable` and requires the `async` modifier.
13 changes: 12 additions & 1 deletion crates/biome_js_analyze/src/lint/suspicious/use_await.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use biome_analyze::{
use biome_console::markup;
use biome_diagnostics::Severity;
use biome_js_syntax::{
AnyFunctionLike, JsAwaitExpression, JsForOfStatement, JsLanguage, TextRange, WalkEvent,
AnyFunctionLike, JsAwaitExpression, JsForOfStatement, JsLanguage, JsYieldExpression,
TextRange, WalkEvent,
};
use biome_rowan::{AstNode, AstNodeList, Language, SyntaxNode, TextSize};
use biome_rule_options::use_await::UseAwaitOptions;
Expand Down Expand Up @@ -46,6 +47,11 @@ declare_lint_rule! {
///
/// // Nor does it warn about empty `async` functions
/// async function noop() { }
///
/// // Async generators that use `yield*` with an async iterable
/// async function* delegateToAsyncIterable() {
/// yield* otherAsyncIterable();
/// }
/// ```
pub UseAwait {
version: "1.4.0",
Expand Down Expand Up @@ -87,6 +93,11 @@ impl Visitor for MissingAwaitVisitor {
*has_await = true;
} else if let Some(for_of) = JsForOfStatement::cast_ref(node) {
*has_await = *has_await || for_of.await_token().is_some();
} else if let Some(yield_expr) = JsYieldExpression::cast_ref(node) {
*has_await = *has_await
|| yield_expr
.argument()
.is_some_and(|arg| arg.star_token().is_some());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,8 @@ class Sample {
return true;
}
}

async function* yieldWithoutStar() {
yield 1;
yield 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ class Sample {
}
}

async function* yieldWithoutStar() {
yield 1;
yield 2;
}

```

# Diagnostics
Expand Down Expand Up @@ -589,3 +594,35 @@ invalid.js:86:2 lint/suspicious/useAwait ━━━━━━━━━━━━━


```

```
invalid.js:92:1 lint/suspicious/useAwait ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! This async function lacks an await expression.

90 │ }
91 │
> 92 │ async function* yieldWithoutStar() {
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 93 │ yield 1;
> 94 │ yield 2;
> 95 │ }
│ ^
96 │

i Remove this async modifier, or add an await expression in the function.

90 │ }
91 │
> 92 │ async function* yieldWithoutStar() {
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 93 │ yield 1;
> 94 │ yield 2;
> 95 │ }
│ ^
96 │

i Async functions without await expressions may not need to be declared async.


```
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,12 @@ async function awaitExpressionWithForOf () {
return sum;
};

async function* yieldStarAsyncIterable() {
yield* otherAsyncGenerator();
}

async function* yieldStarWithAwait() {
const data = await fetch('/data');
yield* processData(data);
}

Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,14 @@ async function awaitExpressionWithForOf () {
return sum;
};

async function* yieldStarAsyncIterable() {
yield* otherAsyncGenerator();
}

async function* yieldStarWithAwait() {
const data = await fetch('/data');
yield* processData(data);
}


```