Skip to content

Commit

Permalink
feat(linter): default_param_last (#2756)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
JoSeBu1 and Dunqing committed Mar 20, 2024
1 parent 291dc05 commit 1c07a99
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
104 changes: 104 additions & 0 deletions crates/oxc_linter/src/rules/eslint/default_param_last.rs
@@ -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();
}
94 changes: 94 additions & 0 deletions crates/oxc_linter/src/snapshots/default_param_last.snap
@@ -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]
1function 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]
1function 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]
1function 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]
1function 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]
1function 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]
1function 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]
1const 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]
1const 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]
1const 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]
1const 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]
1const 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]
1const 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]
1const f = ([a, b] = [1, 2], c) => {}
· ───────────────
╰────
help: Enforce default parameters to be last.

0 comments on commit 1c07a99

Please sign in to comment.