From e0f71eb1212181c16f0f3f081bf31f7ab6454252 Mon Sep 17 00:00:00 2001 From: Mikhail Baev Date: Fri, 13 Feb 2026 11:59:16 +0500 Subject: [PATCH 1/2] feat(linter/unicorn): implement suggestion for `unicorn/no-await-in-promise-methods` --- .../unicorn/no_await_in_promise_methods.rs | 76 +++++++++++++++---- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs b/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs index fcc3eddadaeb0..ec1d4ad92432e 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs @@ -49,7 +49,7 @@ declare_oxc_lint!( NoAwaitInPromiseMethods, unicorn, correctness, - pending + suggestion ); impl Rule for NoAwaitInPromiseMethods { @@ -68,6 +68,18 @@ impl Rule for NoAwaitInPromiseMethods { return; } + if call_expr.optional { + return; + } + + let Some(member_expr) = call_expr.callee.get_member_expr() else { + return; + }; + + if member_expr.optional() || member_expr.is_computed() { + return; + } + let Some(first_argument) = call_expr.arguments[0].as_expression() else { return; }; @@ -80,17 +92,31 @@ impl Rule for NoAwaitInPromiseMethods { if let Some(element_expr) = element.as_expression() && let Expression::AwaitExpression(await_expr) = element_expr.without_parentheses() { - let property_name = call_expr - .callee - .get_member_expr() - .expect("callee is a member expression") - .static_property_name() - .expect("callee is a static property"); - - ctx.diagnostic(no_await_in_promise_methods_diagnostic( - Span::sized(await_expr.span.start, 5), - property_name, - )); + let property_name = + member_expr.static_property_name().expect("callee is a static property"); + + ctx.diagnostic_with_suggestion( + no_await_in_promise_methods_diagnostic( + Span::sized(await_expr.span.start, 5), + property_name, + ), + |fixer| { + let await_span = Span::sized(await_expr.span.start, 5); + let source = fixer.source_text(); + let after_await = &source[(await_span.end as usize)..]; + + let trailing_spaces = after_await + .chars() + .take_while(|c| c.is_whitespace()) + .map(|c| { + u32::try_from(c.len_utf8()) + .expect("Failed to convert char len usize to u32") + }) + .sum(); + + fixer.delete_range(await_span.expand_right(trailing_spaces)) + }, + ); } } } @@ -111,15 +137,14 @@ fn test() { "Promise.all(notArrayExpression)", "Promise.all([,])", "Promise[all]([await promise])", - // TODO: Fix these commented-out tests. - // "Promise.all?.([await promise])", - // "Promise?.all([await promise])", + "Promise.all?.([await promise])", + "Promise?.all([await promise])", "Promise.notListedMethod([await promise])", "NotPromise.all([await promise])", "Promise.all([(await promise, 0)])", "new Promise.all([await promise])", "globalThis.Promise.all([await promise])", - // r#"Promise["all"]([await promise])"#, + r#"Promise["all"]([await promise])"#, ]; let fail = vec![ @@ -137,7 +162,26 @@ fn test() { "Promise.all([await /* comment*/ promise])", ]; + let fix = vec![ + ("Promise.all([await promise])", "Promise.all([promise])"), + ("Promise.allSettled([await promise])", "Promise.allSettled([promise])"), + ("Promise.any([await promise])", "Promise.any([promise])"), + ("Promise.race([await promise])", "Promise.race([promise])"), + ("Promise.all([, await promise])", "Promise.all([, promise])"), + ("Promise.all([await promise,])", "Promise.all([promise,])"), + ("Promise.all([await promise],)", "Promise.all([promise],)"), + ("Promise.all([await (0, promise)],)", "Promise.all([(0, promise)],)"), + ("Promise.all([await (( promise ))])", "Promise.all([(( promise ))])"), + ("Promise.all([await await promise])", "Promise.all([await promise])"), + ( + "Promise.all([...foo, await promise1, await promise2])", + "Promise.all([...foo, promise1, promise2])", + ), + ("Promise.all([await /* comment*/ promise])", "Promise.all([/* comment*/ promise])"), + ]; + Tester::new(NoAwaitInPromiseMethods::NAME, NoAwaitInPromiseMethods::PLUGIN, pass, fail) .change_rule_path_extension("mjs") + .expect_fix(fix) .test_and_snapshot(); } From 65a1c4d15eb4d85f8e8dc01dcb3df53f91352d71 Mon Sep 17 00:00:00 2001 From: Mikhail Baev Date: Fri, 13 Feb 2026 12:22:28 +0500 Subject: [PATCH 2/2] avoid creating await span twice --- .../src/rules/unicorn/no_await_in_promise_methods.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs b/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs index ec1d4ad92432e..bec596ed5445d 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_await_in_promise_methods.rs @@ -94,16 +94,13 @@ impl Rule for NoAwaitInPromiseMethods { { let property_name = member_expr.static_property_name().expect("callee is a static property"); + let await_keyword_span = Span::sized(await_expr.span.start, 5); ctx.diagnostic_with_suggestion( - no_await_in_promise_methods_diagnostic( - Span::sized(await_expr.span.start, 5), - property_name, - ), + no_await_in_promise_methods_diagnostic(await_keyword_span, property_name), |fixer| { - let await_span = Span::sized(await_expr.span.start, 5); let source = fixer.source_text(); - let after_await = &source[(await_span.end as usize)..]; + let after_await = &source[(await_keyword_span.end as usize)..]; let trailing_spaces = after_await .chars() @@ -114,7 +111,7 @@ impl Rule for NoAwaitInPromiseMethods { }) .sum(); - fixer.delete_range(await_span.expand_right(trailing_spaces)) + fixer.delete_range(await_keyword_span.expand_right(trailing_spaces)) }, ); }