From 142ef4f3854341fdb67e6c6eca7d2032795c9a7c Mon Sep 17 00:00:00 2001 From: Cam McHenry Date: Wed, 28 Oct 2026 22:28:43 -0400 Subject: [PATCH] perf(unicorn): narrow no-await-in-promise-methods dispatch (#28349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Visit await expressions rather than all calls. Preserve direct array-element matching and parenthesized arguments; reduce fixture calls from 62,606 to 231. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Stack created with GitHub Stacks CLI • Give Feedback 💬 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/generated/rule_runner_impls.rs | 2 +- .../unicorn/no_await_in_promise_methods.rs | 87 +++++++++++-------- .../track_linter_timings/linter_timings.snap | 2 +- 3 files changed, 52 insertions(+), 39 deletions(-) diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index edfe993acf..f22c5f63f1 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -3328,7 +3328,7 @@ impl RuleRunner for crate::rules::unicorn::no_await_expression_member::NoAwaitEx impl RuleRunner for crate::rules::unicorn::no_await_in_promise_methods::NoAwaitInPromiseMethods { const NODE_TYPES: Option<&AstTypesBitset> = - Some(&AstTypesBitset::from_types(&[AstType::CallExpression])); + Some(&AstTypesBitset::from_types(&[AstType::AwaitExpression])); const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } 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 1607510e26..d20244df2f 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 @@ -1,10 +1,15 @@ -use oxc_ast::{AstKind, ast::Expression}; +use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; -use oxc_span::Span; +use oxc_span::{GetSpan, Span}; use oxc_str::JSStr; -use crate::{AstNode, ast_util::is_method_call, context::LintContext, rule::Rule}; +use crate::{ + AstNode, + ast_util::{is_method_call, outermost_paren_parent}, + context::LintContext, + rule::Rule, +}; fn no_await_in_promise_methods_diagnostic(span: Span, method_name: &str) -> OxcDiagnostic { OxcDiagnostic::warn(format!("Promise in `Promise.{method_name}()` should not be awaited.")) @@ -58,7 +63,19 @@ declare_oxc_lint!( impl Rule for NoAwaitInPromiseMethods { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::CallExpression(call_expr) = node.kind() else { + let AstKind::AwaitExpression(await_expr) = node.kind() else { + return; + }; + let Some(array_node) = outermost_paren_parent(node, ctx) else { + return; + }; + let AstKind::ArrayExpression(array_expr) = array_node.kind() else { + return; + }; + let Some(call_node) = outermost_paren_parent(array_node, ctx) else { + return; + }; + let AstKind::CallExpression(call_expr) = call_node.kind() else { return; }; @@ -87,41 +104,33 @@ impl Rule for NoAwaitInPromiseMethods { let Some(first_argument) = call_expr.arguments[0].as_expression() else { return; }; - let first_argument = first_argument.without_parentheses(); - let Expression::ArrayExpression(first_argument_array_expr) = first_argument else { + if first_argument.without_parentheses().span() != array_expr.span { return; - }; - - for element in &first_argument_array_expr.elements { - if let Some(element_expr) = element.as_expression() - && let Expression::AwaitExpression(await_expr) = element_expr.without_parentheses() - { - let property_name = member_expr - .static_property_name() - .and_then(JSStr::as_str) - .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(await_keyword_span, property_name), - |fixer| { - let source = fixer.source_text(); - let after_await = &source[(await_keyword_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_keyword_span.expand_right(trailing_spaces)) - }, - ); - } } + let property_name = member_expr + .static_property_name() + .and_then(JSStr::as_str) + .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(await_keyword_span, property_name), + |fixer| { + let source = fixer.source_text(); + let after_await = &source[(await_keyword_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_keyword_span.expand_right(trailing_spaces)) + }, + ); } } @@ -145,6 +154,9 @@ fn test() { "Promise.notListedMethod([await promise])", "NotPromise.all([await promise])", "Promise.all([(await promise, 0)])", + "Promise.all([...await promise])", + "Promise.all([[await promise]])", + "Promise.all([fn(await promise)])", "new Promise.all([await promise])", "globalThis.Promise.all([await promise])", r#"Promise["all"]([await promise])"#, @@ -181,6 +193,7 @@ fn test() { "Promise.all([...foo, promise1, promise2])", ), ("Promise.all([await /* comment*/ promise])", "Promise.all([/* comment*/ promise])"), + ("Promise.all(([(await promise)]))", "Promise.all(([(promise)]))"), ]; Tester::new(NoAwaitInPromiseMethods::NAME, NoAwaitInPromiseMethods::PLUGIN, pass, fail) diff --git a/tasks/track_linter_timings/linter_timings.snap b/tasks/track_linter_timings/linter_timings.snap index 1bfe401019..56194b3feb 100644 --- a/tasks/track_linter_timings/linter_timings.snap +++ b/tasks/track_linter_timings/linter_timings.snap @@ -539,7 +539,7 @@ unicorn/no-array-reduce | 62606 unicorn/no-array-reverse | 62606 unicorn/no-array-sort | 62606 unicorn/no-await-expression-member | 231 -unicorn/no-await-in-promise-methods | 62606 +unicorn/no-await-in-promise-methods | 231 unicorn/no-confusing-array-with | 62606 unicorn/no-console-spaces | 62606 unicorn/no-document-cookie | 13005