From 2d37b7b166f57756230d1c52ef92a44452c3434d Mon Sep 17 00:00:00 2001 From: ddmoney420 Date: Thu, 29 Jan 2026 09:04:41 -0700 Subject: [PATCH 1/2] fix(linter): `no-useless-constructor` correctly handles argument transformation The rule was incorrectly flagging constructors that transform arguments before passing them to `super()`, such as `super(...args.map(x => x))`. The `is_spread_arguments` function now only returns true when the spread argument is specifically the `arguments` identifier, not any arbitrary spread expression. Fixes #17469 Co-Authored-By: Claude Opus 4.5 --- .../rules/eslint/no_useless_constructor.rs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs b/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs index f98a13e168df2..15806ca95a4b0 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs @@ -222,8 +222,19 @@ fn is_only_simple_params(params: &FormalParameters) -> bool { && params.items.iter().all(|param| param.initializer.is_none()) } +/// Check if the super call is just spreading the `arguments` object, i.e., `super(...arguments)`. +/// +/// Returns `true` only if there is exactly one spread argument that is the `arguments` identifier. +/// This ensures we don't flag constructors that transform arguments before passing to super, +/// such as `super(...args.map(x => x))`. fn is_spread_arguments(super_args: &[Argument<'_>]) -> bool { - super_args.len() == 1 && super_args[0].is_spread() + if super_args.len() != 1 { + return false; + } + let Argument::SpreadElement(spread) = &super_args[0] else { + return false; + }; + matches!(&spread.argument, Expression::Identifier(ident) if ident.name == "arguments") } fn is_passing_through<'a>( @@ -339,6 +350,13 @@ fn test() { // } // } // ", + // Argument transformation: constructor is not useless when arguments are processed/transformed + // https://github.com/oxc-project/oxc/issues/17469 + "class A extends B { constructor(...args) { super(...args.map(x => x)); } }", + "class A extends B { constructor(...args) { super(...args.filter(x => x)); } }", + "class A extends B { constructor(...args) { super(...args.slice(1)); } }", + "class A extends B { constructor(...args) { super(...transform(args)); } }", + "class A extends B { constructor(...args) { super(...[...args, extra]); } }", ]; let pass_typescript = vec![ From 8f7e40f57076cebf69f752a697ef77eb19ebdada Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Thu, 29 Jan 2026 16:46:29 +0000 Subject: [PATCH 2/2] ui --- .../src/rules/eslint/no_useless_constructor.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs b/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs index 15806ca95a4b0..0e80b9c5fa77a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_constructor.rs @@ -228,13 +228,14 @@ fn is_only_simple_params(params: &FormalParameters) -> bool { /// This ensures we don't flag constructors that transform arguments before passing to super, /// such as `super(...args.map(x => x))`. fn is_spread_arguments(super_args: &[Argument<'_>]) -> bool { - if super_args.len() != 1 { - return false; + if super_args.len() == 1 + && let Argument::SpreadElement(spread) = &super_args[0] + && spread.argument.is_specific_id("arguments") + { + return true; } - let Argument::SpreadElement(spread) = &super_args[0] else { - return false; - }; - matches!(&spread.argument, Expression::Identifier(ident) if ident.name == "arguments") + + false } fn is_passing_through<'a>(