diff --git a/crates/oxc_linter/src/rules/oxc/no_async_await.rs b/crates/oxc_linter/src/rules/oxc/no_async_await.rs index 6d64423088c65..63ed3bd0d6c6c 100644 --- a/crates/oxc_linter/src/rules/oxc/no_async_await.rs +++ b/crates/oxc_linter/src/rules/oxc/no_async_await.rs @@ -1,14 +1,13 @@ use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; -use oxc_semantic::NodeId; -use oxc_span::Span; +use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -fn no_async_await_diagnostic(span: Span) -> OxcDiagnostic { - OxcDiagnostic::warn("Unexpected async/await") - .with_help("Async/await is not allowed") +fn no_async_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("async is not allowed") + .with_help("Remove the `async` keyword") .with_label(span) } @@ -37,12 +36,40 @@ impl Rule for NoAsyncAwait { match node.kind() { AstKind::Function(func_decl) => { if func_decl.r#async { - report(node.id(), func_decl.span, ctx); + let parent_kind = ctx.nodes().parent_kind(node.id()); + let async_span = match &func_decl.id { + // named function like `async function run() {}` + Some(id) => Span::new(func_decl.span.start, id.span.end), + // anonymous function like `async function() {}` + None => match parent_kind { + // Actually part of a method definition like: + // ``` + // class Foo { + // async bar() {} + // } + // ``` + Some(AstKind::MethodDefinition(method_def)) => { + Span::new(method_def.span.start, method_def.key.span().start) + } + // The function is part of an object property like: + // ``` + // const obj = { + // async foo() {} + // }; + // ``` + Some(AstKind::ObjectProperty(obj_prop)) => { + Span::new(obj_prop.span.start, obj_prop.key.span().start) + } + _ => func_decl.span, + }, + }; + report_on_async_span(async_span, ctx); } } AstKind::ArrowFunctionExpression(arrow_expr) => { if arrow_expr.r#async { - report(node.id(), arrow_expr.span, ctx); + let async_span = Span::new(arrow_expr.span.start, arrow_expr.params.span.start); + report_on_async_span(async_span, ctx); } } _ => {} @@ -50,16 +77,17 @@ impl Rule for NoAsyncAwait { } } -fn report(node_id: NodeId, func_span: Span, ctx: &LintContext<'_>) { - /// "async".len() - const ASYNC_LEN: u32 = 5; +/// "async".len() +const ASYNC_LEN: u32 = 5; - let parent = ctx.nodes().parent_kind(node_id); - if let Some(AstKind::ObjectProperty(obj_prop)) = parent { - ctx.diagnostic(no_async_await_diagnostic(Span::sized(obj_prop.span.start, ASYNC_LEN))); - } else { - ctx.diagnostic(no_async_await_diagnostic(Span::sized(func_span.start, ASYNC_LEN))); - } +#[allow(clippy::cast_possible_truncation)] +fn report_on_async_span(async_span: Span, ctx: &LintContext<'_>) { + // find the `async` keyword within the span and report on it + let Some(async_keyword_offset) = ctx.source_range(async_span).find("async") else { + return; + }; + let async_keyword_span = Span::sized(async_span.start + async_keyword_offset as u32, ASYNC_LEN); + ctx.diagnostic(no_async_diagnostic(async_keyword_span)); } #[test] @@ -71,6 +99,9 @@ fn test() { "const foo = () => {}", "function foo () { return bar(); }", "class Foo { foo() {} }", + "class async { }", + "const async = {};", + "class async { async() { async(); } }", ]; let fail = vec![ @@ -83,7 +114,6 @@ fn test() { async test() {} }; ", - // FIXME: diagnostics on method `foo` have incorrect spans " class Foo { async foo() {} @@ -94,12 +124,29 @@ fn test() { public async foo() {} } ", - // this one is fine " const obj = { async foo() {} } ", + " + class async { + async async() { + async(); + } + } + ", + " + class async { + async async() { + function async() { + const async = { + async: async () => {}, + } + } + } + } + ", ]; Tester::new(NoAsyncAwait::NAME, NoAsyncAwait::PLUGIN, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/snapshots/oxc_no_async_await.snap b/crates/oxc_linter/src/snapshots/oxc_no_async_await.snap index 8f989d67a780e..3d14b9bb6eaeb 100644 --- a/crates/oxc_linter/src/snapshots/oxc_no_async_await.snap +++ b/crates/oxc_linter/src/snapshots/oxc_no_async_await.snap @@ -1,66 +1,93 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ oxc(no-async-await): Unexpected async/await + ⚠ oxc(no-async-await): async is not allowed ╭─[no_async_await.tsx:1:1] 1 │ async function foo() {} · ───── ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword - ⚠ oxc(no-async-await): Unexpected async/await + ⚠ oxc(no-async-await): async is not allowed ╭─[no_async_await.tsx:1:13] 1 │ const foo = async () => {} · ───── ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword - ⚠ oxc(no-async-await): Unexpected async/await + ⚠ oxc(no-async-await): async is not allowed ╭─[no_async_await.tsx:1:1] 1 │ async () => {} · ───── ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword - ⚠ oxc(no-async-await): Unexpected async/await + ⚠ oxc(no-async-await): async is not allowed ╭─[no_async_await.tsx:1:14] 1 │ const test = async () => {}; · ───── ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword - ⚠ oxc(no-async-await): Unexpected async/await + ⚠ oxc(no-async-await): async is not allowed ╭─[no_async_await.tsx:3:17] 2 │ const test = { 3 │ async test() {} · ───── 4 │ }; ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword - ⚠ oxc(no-async-await): Unexpected async/await - ╭─[no_async_await.tsx:3:22] + ⚠ oxc(no-async-await): async is not allowed + ╭─[no_async_await.tsx:3:13] 2 │ class Foo { 3 │ async foo() {} - · ───── + · ───── 4 │ } ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword - ⚠ oxc(no-async-await): Unexpected async/await - ╭─[no_async_await.tsx:3:29] + ⚠ oxc(no-async-await): async is not allowed + ╭─[no_async_await.tsx:3:20] 2 │ class Foo { 3 │ public async foo() {} - · ───── + · ───── 4 │ } ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword - ⚠ oxc(no-async-await): Unexpected async/await + ⚠ oxc(no-async-await): async is not allowed ╭─[no_async_await.tsx:3:13] 2 │ const obj = { 3 │ async foo() {} · ───── 4 │ } ╰──── - help: Async/await is not allowed + help: Remove the `async` keyword + + ⚠ oxc(no-async-await): async is not allowed + ╭─[no_async_await.tsx:3:13] + 2 │ class async { + 3 │ async async() { + · ───── + 4 │ async(); + ╰──── + help: Remove the `async` keyword + + ⚠ oxc(no-async-await): async is not allowed + ╭─[no_async_await.tsx:3:13] + 2 │ class async { + 3 │ async async() { + · ───── + 4 │ function async() { + ╰──── + help: Remove the `async` keyword + + ⚠ oxc(no-async-await): async is not allowed + ╭─[no_async_await.tsx:6:32] + 5 │ const async = { + 6 │ async: async () => {}, + · ───── + 7 │ } + ╰──── + help: Remove the `async` keyword