Skip to content

Commit cc686ac

Browse files
committed
refactor(linter): Update various rules to use DefaultRuleConfig pattern (#16249)
Basically just refactoring a bunch of rules to use the `DefaultRuleConfig` pattern, which conveniently also allows us to ensure that the serialization works as-expected with how the docs are rendering these config options. The only real change here, other than to some diagnostics and a handful of docs, is the fix in `eslint/no-unused-expression` to ensure the docs for the `enforceForJSX` option capitalize it correctly, which I discovered because I updated to use this pattern and that caused the tests to - correctly - fail. I ran the `just website` command and confirmed that the only docs changes from this branch vs main were the `no-unused-expression` fix and the minor docs update I did on no-bitwise.
1 parent 505ceb1 commit cc686ac

21 files changed

+254
-249
lines changed

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

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,25 @@ use oxc_macros::declare_oxc_lint;
44
use oxc_span::{CompactStr, Span};
55
use oxc_syntax::operator::BinaryOperator;
66
use schemars::JsonSchema;
7+
use serde::Deserialize;
8+
use serde_json::Value;
79

8-
use crate::{AstNode, context::LintContext, rule::Rule};
10+
use crate::{
11+
AstNode,
12+
context::LintContext,
13+
rule::{DefaultRuleConfig, Rule},
14+
};
915

1016
fn no_bitwise_diagnostic(operator: &str, span: Span) -> OxcDiagnostic {
11-
OxcDiagnostic::warn(format!("Unexpected use of {operator:?}"))
12-
.with_help("bitwise operators are not allowed, maybe you mistyped `&&` or `||`")
17+
OxcDiagnostic::warn(format!("Unexpected use of `{operator:?}`."))
18+
.with_help("bitwise operators are not allowed, maybe you mistyped `&&` or `||`?")
1319
.with_label(span)
1420
}
1521

16-
#[derive(Debug, Default, Clone)]
22+
#[derive(Debug, Default, Clone, Deserialize)]
1723
pub struct NoBitwise(Box<NoBitwiseConfig>);
1824

19-
#[derive(Debug, Default, Clone, JsonSchema)]
25+
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
2026
#[serde(rename_all = "camelCase", default)]
2127
pub struct NoBitwiseConfig {
2228
/// The `allow` option permits the given list of bitwise operators to be used
@@ -29,7 +35,7 @@ pub struct NoBitwiseConfig {
2935
/// ~[1,2,3].indexOf(1) === -1;
3036
/// ```
3137
allow: Vec<CompactStr>,
32-
/// When set to true the `int32Hint` option allows the use of bitwise OR in |0
38+
/// When set to `true` the `int32Hint` option allows the use of bitwise OR in |0
3339
/// pattern for type casting.
3440
///
3541
/// For example with `{ "int32Hint": true }` the following is permitted:
@@ -92,22 +98,10 @@ declare_oxc_lint!(
9298
);
9399

94100
impl Rule for NoBitwise {
95-
fn from_configuration(value: serde_json::Value) -> Self {
96-
let obj = value.get(0);
97-
98-
Self(Box::new(NoBitwiseConfig {
99-
allow: obj
100-
.and_then(|v| v.get("allow"))
101-
.and_then(serde_json::Value::as_array)
102-
.map(|v| {
103-
v.iter().filter_map(serde_json::Value::as_str).map(CompactStr::from).collect()
104-
})
105-
.unwrap_or_default(),
106-
int32_hint: obj
107-
.and_then(|v| v.get("int32Hint"))
108-
.and_then(serde_json::Value::as_bool)
109-
.unwrap_or_default(),
110-
}))
101+
fn from_configuration(value: Value) -> Self {
102+
serde_json::from_value::<DefaultRuleConfig<NoBitwise>>(value)
103+
.unwrap_or_default()
104+
.into_inner()
111105
}
112106

113107
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
@@ -183,7 +177,7 @@ fn test() {
183177
("a < b", None),
184178
("~[1, 2, 3].indexOf(1)", Some(json!([ { "allow": ["~"] }]))),
185179
("~1<<2 === -8", Some(json!([ { "allow": ["~", "<<"] }]))),
186-
("a|0", Some(json!([ { "int32Hint": true}]))),
180+
("a|0", Some(json!([ { "int32Hint": true }]))),
187181
("a|0", Some(json!([ { "int32Hint": false, "allow": ["|"] }]))),
188182
];
189183

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

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
AstNode,
1010
context::LintContext,
1111
fixer::{RuleFix, RuleFixer},
12-
rule::Rule,
12+
rule::{DefaultRuleConfig, Rule},
1313
};
1414

1515
fn no_console_diagnostic(span: Span, allow: &[CompactStr]) -> OxcDiagnostic {
@@ -22,7 +22,7 @@ fn no_console_diagnostic(span: Span, allow: &[CompactStr]) -> OxcDiagnostic {
2222
OxcDiagnostic::warn("Unexpected console statement.").with_label(span).with_help(only_msg)
2323
}
2424

25-
#[derive(Debug, Default, Clone)]
25+
#[derive(Debug, Default, Clone, Deserialize)]
2626
pub struct NoConsole(Box<NoConsoleConfig>);
2727

2828
#[derive(Debug, Default, Clone, Deserialize, JsonSchema)]
@@ -90,16 +90,9 @@ declare_oxc_lint!(
9090

9191
impl Rule for NoConsole {
9292
fn from_configuration(value: serde_json::Value) -> Self {
93-
Self(Box::new(NoConsoleConfig {
94-
allow: value
95-
.get(0)
96-
.and_then(|v| v.get("allow"))
97-
.and_then(serde_json::Value::as_array)
98-
.map(|v| {
99-
v.iter().filter_map(serde_json::Value::as_str).map(CompactStr::from).collect()
100-
})
101-
.unwrap_or_default(),
102-
}))
93+
serde_json::from_value::<DefaultRuleConfig<NoConsole>>(value)
94+
.unwrap_or_default()
95+
.into_inner()
10396
}
10497

10598
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
@@ -200,12 +193,12 @@ fn test() {
200193
(
201194
"console.info(foo)",
202195
Some(serde_json::json!([{ "allow": ["info"] }])),
203-
Some(serde_json::json!({ "env": { "browser": true}})),
196+
Some(serde_json::json!({ "env": { "browser": true }})),
204197
),
205198
(
206199
"console.info(foo)",
207200
Some(serde_json::json!([{ "allow": ["info"] }])),
208-
Some(serde_json::json!({ "globals": { "console": "readonly"}})),
201+
Some(serde_json::json!({ "globals": { "console": "readonly" }})),
209202
),
210203
("console.warn(foo)", Some(serde_json::json!([{ "allow": ["warn"] }])), None),
211204
("console.error(foo)", Some(serde_json::json!([{ "allow": ["error"] }])), None),
@@ -225,12 +218,26 @@ fn test() {
225218
("console.error(foo)", None, None),
226219
("console.info(foo)", None, None),
227220
("console.warn(foo)", None, None),
221+
(
222+
"console
223+
.warn(foo)",
224+
None,
225+
None,
226+
),
227+
(
228+
"console
229+
/* comment */
230+
.warn(foo);",
231+
None,
232+
None,
233+
),
234+
("console.warn(foo)", Some(serde_json::json!([{ "allow": [] }])), None),
228235
("console['log'](foo)", None, None),
229236
("console[`log`](foo)", None, None),
230237
("console['lo\\x67'](foo)", Some(serde_json::json!([{ "allow": ["lo\\x67"] }])), None),
231238
("console[`lo\\x67`](foo)", Some(serde_json::json!([{ "allow": ["lo\\x67"] }])), None),
232-
("console.log()", None, Some(serde_json::json!({ "env": { "browser": true}}))),
233-
("console.log()", None, Some(serde_json::json!({ "globals": { "console": "off"}}))),
239+
("console.log()", None, Some(serde_json::json!({ "env": { "browser": true }}))),
240+
("console.log()", None, Some(serde_json::json!({ "globals": { "console": "off" }}))),
234241
("console.log(foo)", Some(serde_json::json!([{ "allow": ["error"] }])), None),
235242
("console.error(foo)", Some(serde_json::json!([{ "allow": ["warn"] }])), None),
236243
("console.info(foo)", Some(serde_json::json!([{ "allow": ["log"] }])), None),

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

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use oxc_diagnostics::OxcDiagnostic;
44
use oxc_macros::declare_oxc_lint;
55
use oxc_span::{CompactStr, Span};
66
use schemars::JsonSchema;
7+
use serde::Deserialize;
78

89
use crate::{
910
context::LintContext,
1011
module_record::{ExportImportName, ImportImportName},
11-
rule::Rule,
12+
rule::{DefaultRuleConfig, Rule},
1213
};
1314

1415
fn no_duplicate_imports_diagnostic(
@@ -37,7 +38,7 @@ fn no_duplicate_exports_diagnostic(
3738
])
3839
}
3940

40-
#[derive(Debug, Default, Clone, JsonSchema)]
41+
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
4142
#[serde(rename_all = "camelCase", default)]
4243
pub struct NoDuplicateImports {
4344
/// When `true` this rule will also look at exports to see if there is both a re-export of a
@@ -139,17 +140,9 @@ enum ModuleType {
139140

140141
impl Rule for NoDuplicateImports {
141142
fn from_configuration(value: serde_json::Value) -> Self {
142-
let value = value.get(0);
143-
Self {
144-
include_exports: value
145-
.and_then(|v| v.get("includeExports"))
146-
.and_then(serde_json::Value::as_bool)
147-
.unwrap_or(false),
148-
allow_separate_type_imports: value
149-
.and_then(|v| v.get("allowSeparateTypeImports"))
150-
.and_then(serde_json::Value::as_bool)
151-
.unwrap_or(false),
152-
}
143+
serde_json::from_value::<DefaultRuleConfig<NoDuplicateImports>>(value)
144+
.unwrap_or_default()
145+
.into_inner()
153146
}
154147

155148
fn run_once(&self, ctx: &LintContext) {

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@ use oxc_diagnostics::OxcDiagnostic;
33
use oxc_macros::declare_oxc_lint;
44
use oxc_span::Span;
55
use schemars::JsonSchema;
6+
use serde::Deserialize;
67

7-
use crate::{AstNode, context::LintContext, rule::Rule};
8+
use crate::{
9+
AstNode,
10+
context::LintContext,
11+
rule::{DefaultRuleConfig, Rule},
12+
};
813

914
fn no_empty_diagnostic(stmt_kind: &str, span: Span) -> OxcDiagnostic {
1015
OxcDiagnostic::warn("Unexpected empty block statements")
1116
.with_help(format!("Remove this {stmt_kind} or add a comment inside it"))
1217
.with_label(span)
1318
}
1419

15-
#[derive(Debug, Default, Clone, JsonSchema)]
20+
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
1621
#[serde(rename_all = "camelCase", default)]
1722
pub struct NoEmpty {
1823
/// If set to `true`, allows an empty `catch` block without triggering the linter.
@@ -53,13 +58,7 @@ declare_oxc_lint!(
5358

5459
impl Rule for NoEmpty {
5560
fn from_configuration(value: serde_json::Value) -> Self {
56-
let obj = value.get(0);
57-
Self {
58-
allow_empty_catch: obj
59-
.and_then(|v| v.get("allowEmptyCatch"))
60-
.and_then(serde_json::Value::as_bool)
61-
.unwrap_or_default(),
62-
}
61+
serde_json::from_value::<DefaultRuleConfig<NoEmpty>>(value).unwrap_or_default().into_inner()
6362
}
6463

6564
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ use rustc_hash::FxHashSet;
88
use schemars::JsonSchema;
99
use serde::Deserialize;
1010

11-
use crate::{AstNode, context::LintContext, rule::Rule};
11+
use crate::{
12+
AstNode,
13+
context::LintContext,
14+
rule::{DefaultRuleConfig, Rule},
15+
};
1216

1317
// Use the same prefix with `oxc_regular_expression` crate
1418
fn duplicated_flag_diagnostic(span: Span) -> OxcDiagnostic {
@@ -24,7 +28,7 @@ fn invalid_unicode_flags_diagnostic(span: Span) -> OxcDiagnostic {
2428
.with_label(span)
2529
}
2630

27-
#[derive(Debug, Default, Clone)]
31+
#[derive(Debug, Default, Clone, Deserialize)]
2832
pub struct NoInvalidRegexp(Box<NoInvalidRegexpConfig>);
2933

3034
declare_oxc_lint!(
@@ -67,11 +71,9 @@ struct NoInvalidRegexpConfig {
6771

6872
impl Rule for NoInvalidRegexp {
6973
fn from_configuration(value: serde_json::Value) -> Self {
70-
value
71-
.as_array()
72-
.and_then(|arr| arr.first())
73-
.and_then(|value| serde_json::from_value(value.clone()).ok())
74-
.map_or_else(Self::default, |value| Self(Box::new(value)))
74+
serde_json::from_value::<DefaultRuleConfig<NoInvalidRegexp>>(value)
75+
.unwrap_or_default()
76+
.into_inner()
7577
}
7678

7779
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ use oxc_regular_expression::{
77
};
88
use oxc_span::Span;
99
use schemars::JsonSchema;
10+
use serde::Deserialize;
1011

11-
use crate::{AstNode, context::LintContext, rule::Rule, utils::run_on_regex_node};
12+
use crate::{
13+
AstNode,
14+
context::LintContext,
15+
rule::{DefaultRuleConfig, Rule},
16+
utils::run_on_regex_node,
17+
};
1218

1319
fn surrogate_pair_diagnostic(span: Span) -> OxcDiagnostic {
1420
OxcDiagnostic::warn("Unexpected surrogate pair in character class.").with_label(span)
@@ -30,7 +36,7 @@ fn zwj_diagnostic(span: Span) -> OxcDiagnostic {
3036
OxcDiagnostic::warn("Unexpected joined character sequence in character class.").with_label(span)
3137
}
3238

33-
#[derive(Debug, Default, Clone, JsonSchema)]
39+
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
3440
#[serde(rename_all = "camelCase", default)]
3541
pub struct NoMisleadingCharacterClass {
3642
/// When set to `true`, the rule allows any grouping of code points
@@ -144,14 +150,9 @@ impl<'ast> Visit<'ast> for CharacterSequenceCollector<'ast> {
144150

145151
impl Rule for NoMisleadingCharacterClass {
146152
fn from_configuration(value: serde_json::Value) -> Self {
147-
let allow_escape = value
148-
.get(0)
149-
.and_then(|v| v.as_object())
150-
.and_then(|v| v.get("allowEscape"))
151-
.and_then(serde_json::Value::as_bool)
152-
.unwrap_or_default();
153-
154-
Self { allow_escape }
153+
serde_json::from_value::<DefaultRuleConfig<NoMisleadingCharacterClass>>(value)
154+
.unwrap_or_default()
155+
.into_inner()
155156
}
156157

157158
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ use oxc_diagnostics::OxcDiagnostic;
33
use oxc_macros::declare_oxc_lint;
44
use oxc_span::{GetSpan, Span};
55
use schemars::JsonSchema;
6+
use serde::Deserialize;
67

7-
use crate::{AstNode, context::LintContext, rule::Rule};
8+
use crate::{
9+
AstNode,
10+
context::LintContext,
11+
rule::{DefaultRuleConfig, Rule},
12+
};
813

914
fn no_plusplus_diagnostic(span: Span, operator: UpdateOperator) -> OxcDiagnostic {
1015
let diagnostic = OxcDiagnostic::warn(format!(
@@ -23,7 +28,7 @@ fn no_plusplus_diagnostic(span: Span, operator: UpdateOperator) -> OxcDiagnostic
2328
}
2429
}
2530

26-
#[derive(Debug, Default, Clone, JsonSchema)]
31+
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
2732
#[serde(rename_all = "camelCase", default)]
2833
pub struct NoPlusplus {
2934
/// Whether to allow `++` and `--` in for loop afterthoughts.
@@ -90,13 +95,9 @@ declare_oxc_lint!(
9095

9196
impl Rule for NoPlusplus {
9297
fn from_configuration(value: serde_json::Value) -> Self {
93-
let obj = value.get(0);
94-
Self {
95-
allow_for_loop_afterthoughts: obj
96-
.and_then(|v| v.get("allowForLoopAfterthoughts"))
97-
.and_then(serde_json::Value::as_bool)
98-
.unwrap_or(false),
99-
}
98+
serde_json::from_value::<DefaultRuleConfig<NoPlusplus>>(value)
99+
.unwrap_or_default()
100+
.into_inner()
100101
}
101102

102103
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ use oxc_syntax::operator::AssignmentOperator;
1313
use schemars::JsonSchema;
1414
use serde::Deserialize;
1515

16-
use crate::{AstNode, context::LintContext, rule::Rule};
16+
use crate::{
17+
AstNode,
18+
context::LintContext,
19+
rule::{DefaultRuleConfig, Rule},
20+
};
1721

1822
fn no_self_assign_diagnostic(span: Span) -> OxcDiagnostic {
1923
OxcDiagnostic::warn("this expression is assigned to itself").with_label(span)
@@ -100,13 +104,9 @@ declare_oxc_lint!(
100104

101105
impl Rule for NoSelfAssign {
102106
fn from_configuration(value: serde_json::Value) -> Self {
103-
Self {
104-
props: value
105-
.get(0)
106-
.and_then(|v| v.get("props"))
107-
.and_then(serde_json::Value::as_bool)
108-
.unwrap_or(true),
109-
}
107+
serde_json::from_value::<DefaultRuleConfig<NoSelfAssign>>(value)
108+
.unwrap_or_default()
109+
.into_inner()
110110
}
111111

112112
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {

0 commit comments

Comments
 (0)