mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter): implement eslint/prefer-rest-params (#8155)
implement: https://eslint.org/docs/latest/rules/prefer-rest-params
This commit is contained in:
parent
1171e00a3c
commit
65796c47a1
3 changed files with 155 additions and 0 deletions
|
|
@ -141,6 +141,7 @@ mod eslint {
|
|||
pub mod prefer_exponentiation_operator;
|
||||
pub mod prefer_numeric_literals;
|
||||
pub mod prefer_object_has_own;
|
||||
pub mod prefer_rest_params;
|
||||
pub mod prefer_spread;
|
||||
pub mod radix;
|
||||
pub mod require_await;
|
||||
|
|
@ -630,6 +631,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
eslint::no_var,
|
||||
eslint::no_void,
|
||||
eslint::no_with,
|
||||
eslint::prefer_rest_params,
|
||||
eslint::prefer_exponentiation_operator,
|
||||
eslint::prefer_numeric_literals,
|
||||
eslint::prefer_object_has_own,
|
||||
|
|
|
|||
126
crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs
Normal file
126
crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
use oxc_ast::AstKind;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
fn prefer_rest_params_diagnostic(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Use the rest parameters instead of 'arguments'.").with_label(span)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PreferRestParams;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// Disallows the use of the `arguments` object and instead enforces the use of rest parameters.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// The `arguments` object does not have methods from `Array.prototype`, making it inconvenient for array-like operations.
|
||||
/// Using rest parameters provides a more intuitive and efficient way to handle variadic arguments.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// Examples of **incorrect** code for this rule:
|
||||
/// ```javascript
|
||||
/// function foo() {
|
||||
/// console.log(arguments);
|
||||
/// }
|
||||
///
|
||||
/// function foo(action) {
|
||||
/// var args = Array.prototype.slice.call(arguments, 1);
|
||||
/// action.apply(null, args);
|
||||
/// }
|
||||
///
|
||||
/// function foo(action) {
|
||||
/// var args = [].slice.call(arguments, 1);
|
||||
/// action.apply(null, args);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Examples of **correct** code for this rule:
|
||||
/// ```javascript
|
||||
/// function foo(...args) {
|
||||
/// console.log(args);
|
||||
/// }
|
||||
///
|
||||
/// function foo(action, ...args) {
|
||||
/// action.apply(null, args); // Or use `action(...args)` (related to `prefer-spread` rule).
|
||||
/// }
|
||||
///
|
||||
/// // Note: Implicit `arguments` can be shadowed.
|
||||
/// function foo(arguments) {
|
||||
/// console.log(arguments); // This refers to the first argument.
|
||||
/// }
|
||||
/// function foo() {
|
||||
/// var arguments = 0;
|
||||
/// console.log(arguments); // This is a local variable.
|
||||
/// }
|
||||
/// ```
|
||||
PreferRestParams,
|
||||
style,
|
||||
);
|
||||
|
||||
impl Rule for PreferRestParams {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if let AstKind::IdentifierReference(identifier) = node.kind() {
|
||||
if identifier.name != "arguments"
|
||||
|| !is_inside_of_function(node, ctx)
|
||||
|| is_not_normal_member_access(node, ctx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let binding = ctx.scopes().find_binding(node.scope_id(), "arguments");
|
||||
if binding.is_none() {
|
||||
ctx.diagnostic(prefer_rest_params_diagnostic(node.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_inside_of_function(node: &AstNode, ctx: &LintContext) -> bool {
|
||||
let mut current = node;
|
||||
while let Some(parent) = ctx.nodes().parent_node(current.id()) {
|
||||
if matches!(parent.kind(), AstKind::Function(_)) {
|
||||
return true;
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_not_normal_member_access(identifier: &AstNode, ctx: &LintContext) -> bool {
|
||||
let parent = ctx.nodes().parent_node(identifier.id());
|
||||
if let Some(parent) = parent {
|
||||
if let AstKind::MemberExpression(member) = parent.kind() {
|
||||
return member.object().span() == identifier.span() && !member.is_computed();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
"arguments;",
|
||||
"function foo(arguments) { arguments; }",
|
||||
"function foo() { var arguments; arguments; }",
|
||||
"var foo = () => arguments;",
|
||||
"function foo(...args) { args; }",
|
||||
"function foo() { arguments.length; }",
|
||||
"function foo() { arguments.callee; }",
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
"function foo() { arguments; }",
|
||||
"function foo() { arguments[0]; }",
|
||||
"function foo() { arguments[1]; }",
|
||||
"function foo() { arguments[Symbol.iterator]; }",
|
||||
];
|
||||
|
||||
Tester::new(PreferRestParams::NAME, PreferRestParams::CATEGORY, pass, fail).test_and_snapshot();
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
⚠ eslint(prefer-rest-params): Use the rest parameters instead of 'arguments'.
|
||||
╭─[prefer_rest_params.tsx:1:18]
|
||||
1 │ function foo() { arguments; }
|
||||
· ─────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(prefer-rest-params): Use the rest parameters instead of 'arguments'.
|
||||
╭─[prefer_rest_params.tsx:1:18]
|
||||
1 │ function foo() { arguments[0]; }
|
||||
· ─────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(prefer-rest-params): Use the rest parameters instead of 'arguments'.
|
||||
╭─[prefer_rest_params.tsx:1:18]
|
||||
1 │ function foo() { arguments[1]; }
|
||||
· ─────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(prefer-rest-params): Use the rest parameters instead of 'arguments'.
|
||||
╭─[prefer_rest_params.tsx:1:18]
|
||||
1 │ function foo() { arguments[Symbol.iterator]; }
|
||||
· ─────────
|
||||
╰────
|
||||
Loading…
Reference in a new issue