fix(linter): Don't mark binding rest elements as unused in TS function overloads (#5470)

- Fixes https://github.com/oxc-project/oxc/issues/5406

This implements a fix for the `BindingRestElement` symbol, which is
currently unhandled and gets automatically marked as unused. If we
happen to find that it is a child of declaration, then we will
automatically allow the binding rest element.

The code for this was based on what we currently do in
`is_allowed_param_because_of_method`:
5187f384cb/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs (L258)

I opted not to refactor this to re-use the same code though, as I think
the duplication is still incidental and the implementations could
diverge in the future.
This commit is contained in:
Cam McHenry 2024-09-06 03:30:28 -04:00 committed by GitHub
parent fce549e648
commit ff88c1fc07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 60 additions and 0 deletions

View file

@ -294,4 +294,19 @@ impl NoUnusedVars {
_ => false,
}
}
/// Returns `true` if this binding rest element should be allowed (i.e. not
/// reported). Currently, this handles the case where a rest element is part
/// of a TS function declaration.
pub(super) fn is_allowed_binding_rest_element(symbol: &Symbol) -> bool {
for parent in symbol.iter_parents() {
// If this is a binding rest element that is part of a TS function parameter,
// for example: `function foo(...messages: string[]) {}`, then we will allow it.
if let AstKind::Function(f) = parent.kind() {
return f.is_typescript_syntax();
}
}
false
}
}

View file

@ -297,6 +297,12 @@ impl NoUnusedVars {
}
ctx.diagnostic(diagnostic::param(symbol));
}
AstKind::BindingRestElement(_) => {
if NoUnusedVars::is_allowed_binding_rest_element(symbol) {
return;
}
ctx.diagnostic(diagnostic::declared(symbol));
}
AstKind::Class(_) | AstKind::Function(_) => {
if self.is_allowed_class_or_function(symbol) {
return;

View file

@ -495,12 +495,31 @@ fn test_functions() {
",
"const foo = () => function bar() { }\nfoo()",
"module.exports.foo = () => function bar() { }",
// https://github.com/oxc-project/oxc/issues/5406
"
export function log(message: string, ...interpolations: unknown[]): void;
export function log(message: string, ...interpolations: unknown[]): void {
console.log(message, interpolations);
}
",
"declare function func(strings: any, ...values: any[]): object"
];
let fail = vec![
"function foo() {}",
"function foo() { foo() }",
"const foo = () => { function bar() { } }\nfoo()",
"
export function log(message: string, ...interpolations: unknown[]): void;
export function log(message: string, ...interpolations: unknown[]): void {
console.log(message);
}
",
"
export function log(...messages: unknown[]): void {
return;
}
",
];
let fix = vec![

View file

@ -25,3 +25,23 @@ source: crates/oxc_linter/src/tester.rs
2 │ foo()
╰────
help: Consider removing this declaration.
⚠ eslint(no-unused-vars): Variable 'interpolations' is declared but never used.
╭─[no_unused_vars.tsx:3:46]
2 │ export function log(message: string, ...interpolations: unknown[]): void;
3 │ export function log(message: string, ...interpolations: unknown[]): void {
· ──────────────┬─────────────
· ╰── 'interpolations' is declared here
4 │ console.log(message);
╰────
help: Consider removing this declaration.
⚠ eslint(no-unused-vars): Variable 'messages' is declared but never used.
╭─[no_unused_vars.tsx:2:29]
1 │
2 │ export function log(...messages: unknown[]): void {
· ───────────┬──────────
· ╰── 'messages' is declared here
3 │ return;
╰────
help: Consider removing this declaration.