Skip to content

Commit a754f7a

Browse files
feat(linter): support countVoidThis option in max-params rule (#12604)
Implements the `countVoidThis` option for the `max-params` rule Rule detail: https://eslint.org/docs/latest/rules/max-params#options ```typescript /* max-params: ["error", { max: 2, countVoidThis: false }] */ function testD(this: void, a, b) {} ```
1 parent 6af8631 commit a754f7a

File tree

2 files changed

+108
-10
lines changed

2 files changed

+108
-10
lines changed

crates/oxc_linter/src/rules/eslint/max_params.rs

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use oxc_ast::AstKind;
1+
use oxc_ast::{AstKind, ast::TSType};
22
use oxc_diagnostics::OxcDiagnostic;
33
use oxc_macros::declare_oxc_lint;
44
use oxc_span::Span;
@@ -20,6 +20,7 @@ pub struct MaxParams(Box<MaxParamsConfig>);
2020
#[derive(Debug, Clone)]
2121
pub struct MaxParamsConfig {
2222
max: usize,
23+
count_void_this: bool,
2324
}
2425

2526
impl std::ops::Deref for MaxParams {
@@ -32,7 +33,7 @@ impl std::ops::Deref for MaxParams {
3233

3334
impl Default for MaxParamsConfig {
3435
fn default() -> Self {
35-
Self { max: 3 }
36+
Self { max: 3, count_void_this: false }
3637
}
3738
}
3839

@@ -88,6 +89,15 @@ declare_oxc_lint!(
8889
///
8990
/// For example `{ "max": 4 }` would mean that having a function take four
9091
/// parameters is allowed which overrides the default of three.
92+
///
93+
/// ### countVoidThis
94+
///
95+
/// `{ "countVoidThis": boolean }`
96+
///
97+
/// This option is for counting the `this` parameter if it is of type `void`.
98+
///
99+
/// For example `{ "countVoidThis": true }` would mean that having a function
100+
/// take a `this` parameter of type `void` is counted towards the maximum number of parameters.
91101
MaxParams,
92102
eslint,
93103
style
@@ -101,15 +111,19 @@ impl Rule for MaxParams {
101111
.and_then(serde_json::Number::as_u64)
102112
.and_then(|v| usize::try_from(v).ok())
103113
{
104-
Self(Box::new(MaxParamsConfig { max }))
114+
Self(Box::new(MaxParamsConfig { max, count_void_this: false }))
105115
} else {
106116
let max = config
107117
.and_then(|config| config.get("max"))
108118
.and_then(Value::as_number)
109119
.and_then(serde_json::Number::as_u64)
110120
.map_or(3, |v| usize::try_from(v).unwrap_or(3));
121+
let count_void_this = config
122+
.and_then(|config| config.get("countVoidThis"))
123+
.and_then(Value::as_bool)
124+
.unwrap_or(false);
111125

112-
Self(Box::new(MaxParamsConfig { max }))
126+
Self(Box::new(MaxParamsConfig { max, count_void_this }))
113127
}
114128
}
115129

@@ -119,23 +133,31 @@ impl Rule for MaxParams {
119133
if !function.is_declaration() & !function.is_expression() {
120134
return;
121135
}
136+
let params = &function.params;
137+
let mut real_len = params.items.len();
138+
if let Some(this_params) = &function.this_param {
139+
let is_void_this = this_params
140+
.type_annotation
141+
.as_ref()
142+
.is_some_and(|t| matches!(t.type_annotation, TSType::TSVoidKeyword(_)));
143+
if self.count_void_this || !is_void_this {
144+
real_len += 1;
145+
}
146+
}
122147

123-
if function.params.items.len() > self.max {
148+
if real_len > self.max {
124149
if let Some(id) = &function.id {
125150
let function_name = id.name.as_str();
126151
let error_msg = format!(
127152
"Function '{}' has too many parameters ({}). Maximum allowed is {}.",
128-
function_name,
129-
function.params.items.len(),
130-
self.max
153+
function_name, real_len, self.max
131154
);
132155
let span = function.params.span;
133156
ctx.diagnostic(max_params_diagnostic(&error_msg, span));
134157
} else {
135158
let error_msg = format!(
136159
"Function has too many parameters ({}). Maximum allowed is {}.",
137-
function.params.items.len(),
138-
self.max
160+
real_len, self.max
139161
);
140162
let span = function.params.span;
141163
ctx.diagnostic(max_params_diagnostic(&error_msg, span));
@@ -168,6 +190,23 @@ fn test() {
168190
("var test = (a, b, c) => {};", Some(serde_json::json!([3]))),
169191
("var test = function test(a, b, c) {};", Some(serde_json::json!([3]))),
170192
("var test = function(a, b, c) {};", Some(serde_json::json!([{ "max": 3 }]))),
193+
(
194+
"function testD(this: void, a) {}",
195+
Some(serde_json::json!([{ "max": 2, "countVoidThis": true }])),
196+
),
197+
(
198+
"function testD(this: void, a, b) {}",
199+
Some(serde_json::json!([{ "max": 2, "countVoidThis": false }])),
200+
),
201+
("const testE = function (this: void, a) {}", Some(serde_json::json!([1]))),
202+
(
203+
"const testE = function (this: void, a) {}",
204+
Some(serde_json::json!([{ "max": 2, "countVoidThis": false }])),
205+
),
206+
(
207+
"const testE = function (this: any, a) {}",
208+
Some(serde_json::json!([{ "max": 2, "countVoidThis": true }])),
209+
),
171210
];
172211

173212
let fail = vec![
@@ -186,6 +225,28 @@ fn test() {
186225
}",
187226
Some(serde_json::json!([{ "max": 2 }])),
188227
),
228+
(
229+
"function testD(this: void, a, b) {}",
230+
Some(serde_json::json!([{ "max": 2, "countVoidThis": true }])),
231+
),
232+
(
233+
"
234+
class Foo { method(this: void, a) {} }
235+
",
236+
Some(serde_json::json!([{ "max": 1, "countVoidThis": true }])),
237+
),
238+
(
239+
"const testE = function (this: void, a) {}",
240+
Some(serde_json::json!([{ "max": 1, "countVoidThis": true }])),
241+
),
242+
(
243+
"const testE = function (this: any, a) {}",
244+
Some(serde_json::json!([{ "max": 1, "countVoidThis": true }])),
245+
),
246+
(
247+
"const testE = function (this: any, a) {}",
248+
Some(serde_json::json!([{ "max": 1, "countVoidThis": false }])),
249+
),
189250
];
190251

191252
Tester::new(MaxParams::NAME, MaxParams::PLUGIN, pass, fail).test_and_snapshot();

crates/oxc_linter/src/snapshots/eslint_max_params.snap

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,40 @@ source: crates/oxc_linter/src/tester.rs
7171
2// Just to make it longer
7272
╰────
7373
help: This rule enforces a maximum number of parameters allowed in function definitions.
74+
75+
⚠ eslint(max-params): Function 'testD' has too many parameters (3). Maximum allowed is 2.
76+
╭─[max_params.tsx:1:15]
77+
1 │ function testD(this: void, a, b) {}
78+
· ──────────────────
79+
╰────
80+
help: This rule enforces a maximum number of parameters allowed in function definitions.
81+
82+
⚠ eslint(max-params): Function has too many parameters (2). Maximum allowed is 1.
83+
╭─[max_params.tsx:2:35]
84+
1 │
85+
2 │ class Foo { method(this: void, a) {} }
86+
· ───────────────
87+
3
88+
╰────
89+
help: This rule enforces a maximum number of parameters allowed in function definitions.
90+
91+
⚠ eslint(max-params): Function has too many parameters (2). Maximum allowed is 1.
92+
╭─[max_params.tsx:1:24]
93+
1 │ const testE = function (this: void, a) {}
94+
· ───────────────
95+
╰────
96+
help: This rule enforces a maximum number of parameters allowed in function definitions.
97+
98+
⚠ eslint(max-params): Function has too many parameters (2). Maximum allowed is 1.
99+
╭─[max_params.tsx:1:24]
100+
1 │ const testE = function (this: any, a) {}
101+
· ──────────────
102+
╰────
103+
help: This rule enforces a maximum number of parameters allowed in function definitions.
104+
105+
⚠ eslint(max-params): Function has too many parameters (2). Maximum allowed is 1.
106+
╭─[max_params.tsx:1:24]
107+
1 │ const testE = function (this: any, a) {}
108+
· ──────────────
109+
╰────
110+
help: This rule enforces a maximum number of parameters allowed in function definitions.

0 commit comments

Comments
 (0)