From 2ab73cc2f4f7ba62f80ca753195ae8baf4e8e9af Mon Sep 17 00:00:00 2001 From: Cam McHenry Date: Wed, 28 Oct 2026 22:28:45 -0400 Subject: [PATCH] perf(linter/unicorn/no-single-promise-in-promise-methods): narrow AST dispatch (#28353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start from singleton array literals and retain enclosing call context for fixes. Preserve TypeScript wrappers and reduce fixture callbacks from 62,606 to 4,115. --- 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 +- .../no_single_promise_in_promise_methods.rs | 38 +++++++++++-------- .../track_linter_timings/linter_timings.snap | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index 8ab0344123..21349411ce 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -3470,7 +3470,7 @@ impl RuleRunner for crate::rules::unicorn::no_single_promise_in_promise_methods::NoSinglePromiseInPromiseMethods { const NODE_TYPES: Option<&AstTypesBitset> = - Some(&AstTypesBitset::from_types(&[AstType::CallExpression])); + Some(&AstTypesBitset::from_types(&[AstType::ArrayExpression])); const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } diff --git a/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs b/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs index c60ad28cd3..2b3f38d5e5 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_single_promise_in_promise_methods.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - AstKind, - ast::{CallExpression, Expression}, -}; +use oxc_ast::{AstKind, ast::CallExpression}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::NodeId; @@ -63,27 +60,31 @@ declare_oxc_lint!( impl Rule for NoSinglePromiseInPromiseMethods { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::ArrayExpression(array_expr) = node.kind() else { + return; + }; + if array_expr.elements.len() != 1 { + return; + } + let first = &array_expr.elements[0]; + if !first.is_expression() { + return; + } + let Some(node) = + ctx.nodes().ancestors(node.id()).find(|parent| !is_ignorable_kind(&parent.kind())) + else { + return; + }; let AstKind::CallExpression(call_expr) = node.kind() else { return; }; - if !is_promise_method_with_single_argument(call_expr) { return; } let Some(first_argument) = call_expr.arguments[0].as_expression() else { return; }; - let first_argument = first_argument.get_inner_expression(); - let Expression::ArrayExpression(first_argument_array_expr) = first_argument else { - return; - }; - - if first_argument_array_expr.elements.len() != 1 { - return; - } - - let first = &first_argument_array_expr.elements[0]; - if !first.is_expression() { + if first_argument.get_inner_expression().span() != array_expr.span { return; } @@ -224,6 +225,8 @@ fn test() { "Promise.race?.([promise])", "Promise.race(...[promise])", "Promise.race([promise], extraArguments)", + "Promise.race([promise, anotherPromise], [other])", + "Promise.race([promise, anotherPromise].concat([]))", "Promise.race()", "new Promise.race([promise])", // We are not checking these cases @@ -327,6 +330,9 @@ fn test() { ]; let fix = vec![ + ("Promise.race([[promise]])", "Promise.resolve([promise])", None), + ("Promise.race((([promise])))", "Promise.resolve(promise)", None), + ("Promise.race(([promise] as const))", "Promise.resolve(promise)", None), ("Promise.race([null]).then()", "Promise.resolve(null).then()", None), // Promise.all returns an array, so we preserve the array structure // when the result is used, but avoid creating an unread array when diff --git a/tasks/track_linter_timings/linter_timings.snap b/tasks/track_linter_timings/linter_timings.snap index 467fa25d2f..a3b80d482a 100644 --- a/tasks/track_linter_timings/linter_timings.snap +++ b/tasks/track_linter_timings/linter_timings.snap @@ -561,7 +561,7 @@ unicorn/no-new-buffer | 1421 unicorn/no-null | 2492 unicorn/no-object-as-default-parameter | 24806 unicorn/no-process-exit | 62606 -unicorn/no-single-promise-in-promise-methods | 62606 +unicorn/no-single-promise-in-promise-methods | 4115 unicorn/no-static-only-class | 300 unicorn/no-thenable | 92476 unicorn/no-this-assignment | 50504