From 857ecc00d637c01007ca8f90e3dd603b2b65b4fe Mon Sep 17 00:00:00 2001 From: "j.buendia" Date: Mon, 18 Mar 2024 19:32:58 +0100 Subject: [PATCH 1/4] feat(linter): add default_param_last --- crates/oxc_linter/src/rules.rs | 2 + .../src/rules/eslint/default_param_last.rs | 118 ++++++++++++++++++ .../src/snapshots/default_param_last.snap | 94 ++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 crates/oxc_linter/src/rules/eslint/default_param_last.rs create mode 100644 crates/oxc_linter/src/snapshots/default_param_last.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 0df8b78d8267..9912fbbc8fcb 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -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; @@ -358,6 +359,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, diff --git a/crates/oxc_linter/src/rules/eslint/default_param_last.rs b/crates/oxc_linter/src/rules/eslint/default_param_last.rs new file mode 100644 index 000000000000..c773ce1b5e98 --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/default_param_last.rs @@ -0,0 +1,118 @@ +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; + } + let mut has_seen_plain_param = false; + for i in (0..function.params.items.len()).rev() { + let param = &function.params.items[i]; + 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(Span::new( + param.span.start, + param.span.end, + ))); + } + } + } + AstKind::ArrowFunctionExpression(function) => { + let mut has_seen_plain_param = false; + for i in (0..function.params.items.len()).rev() { + let param = &function.params.items[i]; + 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(Span::new( + param.span.start, + param.span.end, + ))); + } + } + } + _ => {} + } + } +} + +#[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(); +} diff --git a/crates/oxc_linter/src/snapshots/default_param_last.snap b/crates/oxc_linter/src/snapshots/default_param_last.snap new file mode 100644 index 000000000000..3274e5596381 --- /dev/null +++ b/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] + 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. From 8fa018e24f33fe3a4efce2c1b589f292e4555774 Mon Sep 17 00:00:00 2001 From: "j.buendia" Date: Tue, 19 Mar 2024 10:16:04 +0100 Subject: [PATCH 2/4] improve loop --- crates/oxc_linter/src/rules/eslint/default_param_last.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/default_param_last.rs b/crates/oxc_linter/src/rules/eslint/default_param_last.rs index c773ce1b5e98..13d220680e5d 100644 --- a/crates/oxc_linter/src/rules/eslint/default_param_last.rs +++ b/crates/oxc_linter/src/rules/eslint/default_param_last.rs @@ -45,8 +45,7 @@ impl Rule for DefaultParamLast { return; } let mut has_seen_plain_param = false; - for i in (0..function.params.items.len()).rev() { - let param = &function.params.items[i]; + for param in function.params.items.iter().rev() { if !param.pattern.kind.is_assignment_pattern() { has_seen_plain_param = true; continue; @@ -61,8 +60,7 @@ impl Rule for DefaultParamLast { } AstKind::ArrowFunctionExpression(function) => { let mut has_seen_plain_param = false; - for i in (0..function.params.items.len()).rev() { - let param = &function.params.items[i]; + for param in function.params.items.iter().rev() { if !param.pattern.kind.is_assignment_pattern() { has_seen_plain_param = true; continue; From 963cae553e959bfb2046d8256534978f6a617c72 Mon Sep 17 00:00:00 2001 From: "j.buendia" Date: Tue, 19 Mar 2024 18:45:24 +0100 Subject: [PATCH 3/4] refactor to default_param_last.rs --- .../src/rules/eslint/default_param_last.rs | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/default_param_last.rs b/crates/oxc_linter/src/rules/eslint/default_param_last.rs index 13d220680e5d..3690016d2b09 100644 --- a/crates/oxc_linter/src/rules/eslint/default_param_last.rs +++ b/crates/oxc_linter/src/rules/eslint/default_param_last.rs @@ -1,3 +1,4 @@ +use oxc_ast::ast::FormalParameter; use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, @@ -44,40 +45,27 @@ impl Rule for DefaultParamLast { if !function.is_declaration() & !function.is_expression() { return; } - let mut has_seen_plain_param = false; - for param in function.params.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(Span::new( - param.span.start, - param.span.end, - ))); - } - } - } - AstKind::ArrowFunctionExpression(function) => { - let mut has_seen_plain_param = false; - for param in function.params.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(Span::new( - param.span.start, - param.span.end, - ))); - } - } + 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; From 538ed4b5337ba292ab1233cc396a85c542a421bd Mon Sep 17 00:00:00 2001 From: Dunqing Date: Wed, 20 Mar 2024 09:46:25 +0800 Subject: [PATCH 4/4] Update crates/oxc_linter/src/rules/eslint/default_param_last.rs --- crates/oxc_linter/src/rules/eslint/default_param_last.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/eslint/default_param_last.rs b/crates/oxc_linter/src/rules/eslint/default_param_last.rs index 3690016d2b09..529e7d2f67b1 100644 --- a/crates/oxc_linter/src/rules/eslint/default_param_last.rs +++ b/crates/oxc_linter/src/rules/eslint/default_param_last.rs @@ -42,7 +42,7 @@ 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() { + if !function.is_declaration() && !function.is_expression() { return; } check_params(&function.params.items, ctx);