Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linter): eslint/max-params (#2749)
Rule detail: https://eslint.org/docs/latest/rules/max-params Maybe I can add function name, I was searching for it, but I don't know how to get it. --------- Co-authored-by: j.buendia <j.buendia>
- Loading branch information
Showing
3 changed files
with
233 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
use oxc_ast::AstKind; | ||
use oxc_diagnostics::{ | ||
miette::{self, Diagnostic}, | ||
thiserror::Error, | ||
}; | ||
use oxc_macros::declare_oxc_lint; | ||
use oxc_span::{CompactStr, Span}; | ||
use serde_json::Value; | ||
|
||
use crate::{context::LintContext, rule::Rule, AstNode}; | ||
|
||
#[derive(Debug, Error, Diagnostic)] | ||
#[error("eslint(max-params): {0:?}")] | ||
#[diagnostic( | ||
severity(warning), | ||
help("This rule enforces a maximum number of parameters allowed in function definitions.") | ||
)] | ||
struct MaxParamsDiagnostic(CompactStr, #[label] pub Span); | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub struct MaxParams(Box<MaxParamsConfig>); | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct MaxParamsConfig { | ||
max: usize, | ||
} | ||
|
||
impl std::ops::Deref for MaxParams { | ||
type Target = MaxParamsConfig; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl Default for MaxParamsConfig { | ||
fn default() -> Self { | ||
Self { max: 3 } | ||
} | ||
} | ||
|
||
declare_oxc_lint!( | ||
/// ### What it does | ||
/// Enforce a maximum number of parameters in function definitions | ||
/// | ||
/// ### Why is this bad? | ||
/// Functions that take numerous parameters can be difficult to read and write because it requires the memorization of what each parameter is, its type, and the order they should appear in. As a result, many coders adhere to a convention that caps the number of parameters a function can take. | ||
/// | ||
/// ### Example | ||
/// ```javascript | ||
/// function foo (bar, baz, qux, qxx) { | ||
/// doSomething(); | ||
/// } | ||
/// ``` | ||
MaxParams, | ||
style | ||
); | ||
|
||
impl Rule for MaxParams { | ||
fn from_configuration(value: Value) -> Self { | ||
let config = value.get(0); | ||
if let Some(max) = config | ||
.and_then(Value::as_number) | ||
.and_then(serde_json::Number::as_u64) | ||
.and_then(|v| usize::try_from(v).ok()) | ||
{ | ||
Self(Box::new(MaxParamsConfig { max })) | ||
} else { | ||
let max = config | ||
.and_then(|config| config.get("max")) | ||
.and_then(Value::as_number) | ||
.and_then(serde_json::Number::as_u64) | ||
.map_or(3, |v| usize::try_from(v).unwrap_or(3)); | ||
|
||
Self(Box::new(MaxParamsConfig { max })) | ||
} | ||
} | ||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { | ||
match node.kind() { | ||
AstKind::Function(function) => { | ||
if !function.is_declaration() & !function.is_expression() { | ||
return; | ||
} | ||
|
||
if function.params.items.len() > self.max { | ||
if let Some(id) = &function.id { | ||
let function_name = id.name.as_str(); | ||
let error_msg = format!( | ||
"Function '{}' has too many parameters ({}). Maximum allowed is {}.", | ||
function_name, | ||
function.params.items.len(), | ||
self.max | ||
); | ||
let error = CompactStr::from(error_msg); | ||
let span = function.params.span; | ||
ctx.diagnostic(MaxParamsDiagnostic(error, Span::new(span.start, span.end))); | ||
} else { | ||
let error_msg = format!( | ||
"Function has too many parameters ({}). Maximum allowed is {}.", | ||
function.params.items.len(), | ||
self.max | ||
); | ||
let error = CompactStr::from(error_msg); | ||
let span = function.params.span; | ||
ctx.diagnostic(MaxParamsDiagnostic(error, Span::new(span.start, span.end))); | ||
} | ||
} | ||
} | ||
AstKind::ArrowFunctionExpression(function) => { | ||
if function.params.items.len() > self.max { | ||
let error_msg = format!( | ||
"Arrow function has too many parameters ({}). Maximum allowed is {}.", | ||
function.params.items.len(), | ||
self.max | ||
); | ||
let error = CompactStr::from(error_msg); | ||
let span = function.params.span; | ||
ctx.diagnostic(MaxParamsDiagnostic(error, Span::new(span.start, span.end))); | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
use crate::tester::Tester; | ||
|
||
let pass = vec![ | ||
("function test(d, e, f) {}", None), | ||
("var test = function(a, b, c) {};", Some(serde_json::json!([3]))), | ||
("var test = (a, b, c) => {};", Some(serde_json::json!([3]))), | ||
("var test = function test(a, b, c) {};", Some(serde_json::json!([3]))), | ||
("var test = function(a, b, c) {};", Some(serde_json::json!([{ "max": 3 }]))), | ||
]; | ||
|
||
let fail = vec![ | ||
("function test(a, b, c) {}", Some(serde_json::json!([2]))), | ||
("function test(a, b, c, d) {}", None), | ||
("var test = function(a, b, c, d) {};", Some(serde_json::json!([3]))), | ||
("var test = (a, b, c, d) => {};", Some(serde_json::json!([3]))), | ||
("(function(a, b, c, d) {});", Some(serde_json::json!([3]))), | ||
("var test = function test(a, b, c) {};", Some(serde_json::json!([1]))), | ||
("function test(a, b, c) {}", Some(serde_json::json!([{ "max": 2 }]))), | ||
("function test(a, b, c, d) {}", Some(serde_json::json!([{}]))), | ||
("function test(a) {}", Some(serde_json::json!([{ "max": 0 }]))), | ||
( | ||
"function test(a, b, c) { | ||
// Just to make it longer | ||
}", | ||
Some(serde_json::json!([{ "max": 2 }])), | ||
), | ||
]; | ||
|
||
Tester::new(MaxParams::NAME, pass, fail).test_and_snapshot(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
--- | ||
source: crates/oxc_linter/src/tester.rs | ||
expression: max_params | ||
--- | ||
⚠ eslint(max-params): "Function 'test' has too many parameters (3). Maximum allowed is 2." | ||
╭─[max_params.tsx:1:14] | ||
1 │ function test(a, b, c) {} | ||
· ───────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function 'test' has too many parameters (4). Maximum allowed is 3." | ||
╭─[max_params.tsx:1:14] | ||
1 │ function test(a, b, c, d) {} | ||
· ──────────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function has too many parameters (4). Maximum allowed is 3." | ||
╭─[max_params.tsx:1:20] | ||
1 │ var test = function(a, b, c, d) {}; | ||
· ──────────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Arrow function has too many parameters (4). Maximum allowed is 3." | ||
╭─[max_params.tsx:1:12] | ||
1 │ var test = (a, b, c, d) => {}; | ||
· ──────────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function has too many parameters (4). Maximum allowed is 3." | ||
╭─[max_params.tsx:1:10] | ||
1 │ (function(a, b, c, d) {}); | ||
· ──────────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function 'test' has too many parameters (3). Maximum allowed is 1." | ||
╭─[max_params.tsx:1:25] | ||
1 │ var test = function test(a, b, c) {}; | ||
· ───────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function 'test' has too many parameters (3). Maximum allowed is 2." | ||
╭─[max_params.tsx:1:14] | ||
1 │ function test(a, b, c) {} | ||
· ───────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function 'test' has too many parameters (4). Maximum allowed is 3." | ||
╭─[max_params.tsx:1:14] | ||
1 │ function test(a, b, c, d) {} | ||
· ──────────── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function 'test' has too many parameters (1). Maximum allowed is 0." | ||
╭─[max_params.tsx:1:14] | ||
1 │ function test(a) {} | ||
· ─── | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. | ||
|
||
⚠ eslint(max-params): "Function 'test' has too many parameters (3). Maximum allowed is 2." | ||
╭─[max_params.tsx:1:14] | ||
1 │ function test(a, b, c) { | ||
· ───────── | ||
2 │ // Just to make it longer | ||
╰──── | ||
help: This rule enforces a maximum number of parameters allowed in function definitions. |