feat(linter): default_param_last (#2756)

Rule detail: https://eslint.org/docs/latest/rules/default-param-last

---------

Co-authored-by: j.buendia <j.buendia>
Co-authored-by: Dunqing <dengqing0821@gmail.com>
This commit is contained in:
Jose 2024-03-20 03:05:51 +01:00 committed by GitHub
parent 291dc0597c
commit 1c07a9908d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 200 additions and 0 deletions

View file

@ -40,6 +40,7 @@ mod eslint {
pub mod array_callback_return;
pub mod constructor_super;
pub mod default_case_last;
pub mod default_param_last;
pub mod eqeqeq;
pub mod for_direction;
pub mod getter_return;
@ -363,6 +364,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::array_callback_return,
eslint::constructor_super,
eslint::default_case_last,
eslint::default_param_last,
eslint::eqeqeq,
eslint::for_direction,
eslint::getter_return,

View file

@ -0,0 +1,104 @@
use oxc_ast::ast::FormalParameter;
use oxc_ast::AstKind;
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint(default-param-last): Default parameters should be last")]
#[diagnostic(severity(warning), help("Enforce default parameters to be last."))]
struct DefaultParamLastDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct DefaultParamLast;
declare_oxc_lint!(
/// ### What it does
/// Enforce default parameters to be last
///
/// ### Why is this bad?
/// Putting default parameter at last allows function calls to omit optional tail arguments.
///
/// ### Example
/// ```javascript
/// // Correct: optional argument can be omitted
/// function createUser(id, isAdmin = false) {}
/// createUser("tabby")
///
/// // Incorrect: optional argument can **not** be omitted
/// function createUser(isAdmin = false, id) {}
/// createUser(undefined, "tabby")
/// ```
DefaultParamLast,
style
);
impl Rule for DefaultParamLast {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.kind() {
AstKind::Function(function) => {
if !function.is_declaration() && !function.is_expression() {
return;
}
check_params(&function.params.items, ctx);
}
AstKind::ArrowFunctionExpression(function) => check_params(&function.params.items, ctx),
_ => {}
}
}
}
fn check_params<'a>(items: &'a [FormalParameter<'a>], ctx: &LintContext<'a>) {
let mut has_seen_plain_param = false;
for param in items.iter().rev() {
if !param.pattern.kind.is_assignment_pattern() {
has_seen_plain_param = true;
continue;
}
if has_seen_plain_param && param.pattern.kind.is_assignment_pattern() {
ctx.diagnostic(DefaultParamLastDiagnostic(param.span));
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
"function f() {}",
"function f(a) {}",
"function f(a = 5) {}",
"function f(a, b) {}",
"function f(a, b = 5) {}",
"function f(a, b = 5, c = 5) {}",
"function f(a, b = 5, ...c) {}",
"const f = () => {}",
"const f = (a) => {}",
"const f = (a = 5) => {}",
"const f = function f() {}",
"const f = function f(a) {}",
"const f = function f(a = 5) {}",
];
let fail = vec![
"function f(a = 5, b) {}",
"function f(a = 5, b = 6, c) {}",
"function f (a = 5, b, c = 6, d) {}",
"function f(a = 5, b, c = 5) {}",
"const f = (a = 5, b, ...c) => {}",
"const f = function f (a, b = 5, c) {}",
"const f = (a = 5, { b }) => {}",
"const f = ({ a } = {}, b) => {}",
"const f = ({ a, b } = { a: 1, b: 2 }, c) => {}",
"const f = ([a] = [], b) => {}",
"const f = ([a, b] = [1, 2], c) => {}",
];
Tester::new(DefaultParamLast::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,94 @@
---
source: crates/oxc_linter/src/tester.rs
expression: default_param_last
---
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ function f(a = 5, b) {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:19]
1 │ function f(a = 5, b = 6, c) {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ function f(a = 5, b = 6, c) {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:23]
1 │ function f (a = 5, b, c = 6, d) {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:13]
1 │ function f (a = 5, b, c = 6, d) {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ function f(a = 5, b, c = 5) {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ const f = (a = 5, b, ...c) => {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:26]
1 │ const f = function f (a, b = 5, c) {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ const f = (a = 5, { b }) => {}
· ─────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ const f = ({ a } = {}, b) => {}
· ──────────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ const f = ({ a, b } = { a: 1, b: 2 }, c) => {}
· ─────────────────────────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ const f = ([a] = [], b) => {}
· ────────
╰────
help: Enforce default parameters to be last.
⚠ eslint(default-param-last): Default parameters should be last
╭─[default_param_last.tsx:1:12]
1 │ const f = ([a, b] = [1, 2], c) => {}
· ───────────────
╰────
help: Enforce default parameters to be last.