mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(lint/eslint): implement require-await (#3406)
Implements https://eslint.org/docs/latest/rules/require-await. Related to https://github.com/oxc-project/oxc/issues/479. --------- Co-authored-by: wenzhe <mysteryven@gmail.com>
This commit is contained in:
parent
147864cfeb
commit
14ef4df349
3 changed files with 228 additions and 0 deletions
|
|
@ -107,6 +107,7 @@ mod eslint {
|
|||
pub mod no_with;
|
||||
pub mod prefer_exponentiation_operator;
|
||||
pub mod radix;
|
||||
pub mod require_await;
|
||||
pub mod require_yield;
|
||||
pub mod symbol_description;
|
||||
pub mod unicode_bom;
|
||||
|
|
@ -415,6 +416,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
eslint::no_caller,
|
||||
eslint::no_case_declarations,
|
||||
eslint::no_class_assign,
|
||||
eslint::require_await,
|
||||
eslint::no_compare_neg_zero,
|
||||
eslint::no_cond_assign,
|
||||
eslint::no_console,
|
||||
|
|
|
|||
163
crates/oxc_linter/src/rules/eslint/require_await.rs
Normal file
163
crates/oxc_linter/src/rules/eslint/require_await.rs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
use oxc_ast::{
|
||||
ast::{ArrowFunctionExpression, AwaitExpression, ForOfStatement, Function, PropertyKey},
|
||||
AstKind, Visit,
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::ScopeFlags;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct RequireAwait;
|
||||
|
||||
fn require_await_diagnostic(span0: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("eslint(require-await): Async function has no 'await' expression.")
|
||||
.with_labels([span0.into()])
|
||||
}
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
/// Disallow async functions which have no await expression.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
///
|
||||
/// ### Example
|
||||
/// ```javascript
|
||||
/// async function foo() {
|
||||
/// doSomething();
|
||||
/// }
|
||||
/// ```
|
||||
RequireAwait,
|
||||
pedantic,
|
||||
);
|
||||
|
||||
impl Rule for RequireAwait {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if let AstKind::FunctionBody(body) = node.kind() {
|
||||
if body.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(parent) = ctx.nodes().parent_node(node.id()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
match parent.kind() {
|
||||
AstKind::Function(func) => {
|
||||
if func.r#async && !func.generator {
|
||||
let mut finder = AwaitFinder { found: false };
|
||||
finder.visit_function_body(body);
|
||||
if !finder.found {
|
||||
if let Some(AstKind::ObjectProperty(p)) =
|
||||
ctx.nodes().parent_kind(parent.id())
|
||||
{
|
||||
if let PropertyKey::StaticIdentifier(iden) = &p.key {
|
||||
ctx.diagnostic(require_await_diagnostic(iden.span));
|
||||
} else {
|
||||
ctx.diagnostic(require_await_diagnostic(func.span));
|
||||
}
|
||||
} else {
|
||||
ctx.diagnostic(require_await_diagnostic(
|
||||
func.id.as_ref().map_or(func.span, |ident| ident.span),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AstKind::ArrowFunctionExpression(func) => {
|
||||
if func.r#async {
|
||||
let mut finder = AwaitFinder { found: false };
|
||||
finder.visit_function_body(body);
|
||||
if !finder.found {
|
||||
ctx.diagnostic(require_await_diagnostic(func.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AwaitFinder {
|
||||
found: bool,
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for AwaitFinder {
|
||||
fn visit_await_expression(&mut self, _expr: &AwaitExpression) {
|
||||
if self.found {
|
||||
return;
|
||||
}
|
||||
self.found = true;
|
||||
}
|
||||
fn visit_for_of_statement(&mut self, stmt: &ForOfStatement) {
|
||||
if stmt.r#await {
|
||||
self.found = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_arrow_expression(&mut self, _expr: &ArrowFunctionExpression<'a>) {}
|
||||
fn visit_function(&mut self, _func: &Function<'a>, _flags: Option<ScopeFlags>) {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
"async function foo() { await doSomething() }",
|
||||
"(async function() { await doSomething() })",
|
||||
"async () => { await doSomething() }",
|
||||
"async () => await doSomething()",
|
||||
"({ async foo() { await doSomething() } })",
|
||||
"class A { async foo() { await doSomething() } }",
|
||||
"(class { async foo() { await doSomething() } })",
|
||||
"async function foo() { await (async () => { await doSomething() }) }",
|
||||
"async function foo() {}",
|
||||
"async () => {}",
|
||||
"function foo() { doSomething() }",
|
||||
"async function foo() { for await (x of xs); }",
|
||||
"await foo()",
|
||||
"
|
||||
for await (let num of asyncIterable) {
|
||||
console.log(num);
|
||||
}
|
||||
",
|
||||
"async function* run() { yield * anotherAsyncGenerator() }",
|
||||
"async function* run() {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
yield 'Hello';
|
||||
console.log('World');
|
||||
}
|
||||
",
|
||||
"
|
||||
async function foo() {
|
||||
{
|
||||
await doSomething()
|
||||
}
|
||||
}
|
||||
",
|
||||
"async function* run() { }",
|
||||
"const foo = async function *(){}",
|
||||
r#"const foo = async function *(){ console.log("bar") }"#,
|
||||
r#"async function* run() { console.log("bar") }"#,
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
"async function foo() { doSomething() }",
|
||||
"(async function() { doSomething() })",
|
||||
"async () => { doSomething() }",
|
||||
"async () => doSomething()",
|
||||
"({ async foo() { doSomething() } })",
|
||||
"class A { async foo() { doSomething() } }",
|
||||
"(class { async foo() { doSomething() } })",
|
||||
"(class { async ''() { doSomething() } })",
|
||||
"async function foo() { async () => { await doSomething() } }",
|
||||
"async function foo() { await (async () => { doSomething() }) }",
|
||||
];
|
||||
|
||||
Tester::new(RequireAwait::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
63
crates/oxc_linter/src/snapshots/require_await.snap
Normal file
63
crates/oxc_linter/src/snapshots/require_await.snap
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
expression: require_await
|
||||
---
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:16]
|
||||
1 │ async function foo() { doSomething() }
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:2]
|
||||
1 │ (async function() { doSomething() })
|
||||
· ──────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:1]
|
||||
1 │ async () => { doSomething() }
|
||||
· ─────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:1]
|
||||
1 │ async () => doSomething()
|
||||
· ─────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:10]
|
||||
1 │ ({ async foo() { doSomething() } })
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:20]
|
||||
1 │ class A { async foo() { doSomething() } }
|
||||
· ────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:19]
|
||||
1 │ (class { async foo() { doSomething() } })
|
||||
· ────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:18]
|
||||
1 │ (class { async ''() { doSomething() } })
|
||||
· ────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:16]
|
||||
1 │ async function foo() { async () => { await doSomething() } }
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint(require-await): Async function has no 'await' expression.
|
||||
╭─[require_await.tsx:1:31]
|
||||
1 │ async function foo() { await (async () => { doSomething() }) }
|
||||
· ─────────────────────────────
|
||||
╰────
|
||||
Loading…
Reference in a new issue