diff --git a/crates/oxc_linter/src/rules/eslint/grouped_accessor_pairs.rs b/crates/oxc_linter/src/rules/eslint/grouped_accessor_pairs.rs index 539080d5d32a3..439d05164e840 100644 --- a/crates/oxc_linter/src/rules/eslint/grouped_accessor_pairs.rs +++ b/crates/oxc_linter/src/rules/eslint/grouped_accessor_pairs.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use oxc_allocator::Box; +use oxc_allocator::Box as OBox; use oxc_ast::{ AstKind, ast::{ @@ -17,7 +17,11 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::{AstNode, context::LintContext, rule::Rule}; +use crate::{ + AstNode, + context::LintContext, + rule::{DefaultRuleConfig, Rule}, +}; fn grouped_accessor_pairs_diagnostic( getter_span: Span, @@ -36,31 +40,24 @@ fn grouped_accessor_pairs_diagnostic( #[derive(Debug, Default, PartialEq, Clone, Copy, JsonSchema, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] enum PairOrder { + /// Accessors can be in any order. This is the default. #[default] AnyOrder, + /// Getters must come before setters. GetBeforeSet, + /// Setters must come before getters. SetBeforeGet, } -impl PairOrder { - pub fn from(raw: &str) -> Self { - match raw { - "getBeforeSet" => Self::GetBeforeSet, - "setBeforeGet" => Self::SetBeforeGet, - _ => Self::AnyOrder, - } - } -} +#[derive(Debug, Default, Clone, JsonSchema, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", default)] +pub struct GroupedAccessorPairs(PairOrder, GroupedAccessorPairsConfig); #[derive(Debug, Default, Clone, JsonSchema, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] -pub struct GroupedAccessorPairs { - /// A string value to control the order of the getter/setter pairs: - /// - `"anyOrder"`: Accessors can be in any order - /// - `"getBeforeSet"`: Getters must come before setters - /// - `"setBeforeGet"`: Setters must come before getters - pair_order: PairOrder, - /// When `enforceForTSTypes` is enabled, this rule also applies to TypeScript interfaces and type aliases: +pub struct GroupedAccessorPairsConfig { + /// When `enforceForTSTypes` is enabled, this rule also applies to TypeScript interfaces + /// and type aliases. /// /// Examples of **incorrect** TypeScript code: /// ```ts @@ -189,25 +186,17 @@ declare_oxc_lint!( impl Rule for GroupedAccessorPairs { fn from_configuration(value: Value) -> Self { - Self { - pair_order: value - .get(0) - .and_then(Value::as_str) - .map(PairOrder::from) - .unwrap_or_default(), - enforce_for_ts_types: value - .get(1) - .and_then(|v| v.get("enforceForTSTypes")) - .and_then(Value::as_bool) - .unwrap_or(false), - } + serde_json::from_value::>(value) + .unwrap_or_default() + .into_inner() } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { AstKind::ObjectExpression(obj_expr) => { + let GroupedAccessorPairs(pair_order, _config) = &self; let mut prop_map = - FxHashMap::<(String, bool), Vec<(usize, &Box)>>::default(); + FxHashMap::<(String, bool), Vec<(usize, &OBox)>>::default(); let properties = &obj_expr.properties; for (idx, v) in properties.iter().enumerate() { @@ -250,7 +239,7 @@ impl Rule for GroupedAccessorPairs { get_diagnostic_access_name("setter", &key, is_computed, false, false); report( ctx, - self.pair_order, + *pair_order, (&getter_key, &setter_key), ( Span::new(getter_node.span.start, getter_node.key.span().end), @@ -262,10 +251,11 @@ impl Rule for GroupedAccessorPairs { } } AstKind::ClassBody(class_body) => { + let GroupedAccessorPairs(pair_order, _config) = &self; let method_defines = &class_body.body; let mut prop_map = FxHashMap::< (String, bool, bool, bool), - Vec<(usize, &Box)>, + Vec<(usize, &OBox)>, >::default(); for (idx, v) in method_defines.iter().enumerate() { @@ -320,7 +310,7 @@ impl Rule for GroupedAccessorPairs { ); report( ctx, - self.pair_order, + *pair_order, (&getter_key, &setter_key), ( Span::new(getter_node.span.start, getter_node.key.span().end), @@ -331,10 +321,10 @@ impl Rule for GroupedAccessorPairs { } } } - AstKind::TSInterfaceBody(interface_body) if self.enforce_for_ts_types => { + AstKind::TSInterfaceBody(interface_body) if self.1.enforce_for_ts_types => { self.check_ts_interface_body(interface_body, ctx); } - AstKind::TSTypeLiteral(type_literal) if self.enforce_for_ts_types => { + AstKind::TSTypeLiteral(type_literal) if self.1.enforce_for_ts_types => { self.check_ts_type_literal(type_literal, ctx); } _ => {} @@ -356,8 +346,10 @@ impl GroupedAccessorPairs { } fn check_ts_signatures<'a>(&self, signatures: &[TSSignature<'a>], ctx: &LintContext<'a>) { + let GroupedAccessorPairs(pair_order, _config) = &self; + let mut prop_map = - FxHashMap::<(String, bool), Vec<(usize, &Box)>>::default(); + FxHashMap::<(String, bool), Vec<(usize, &OBox)>>::default(); for (idx, signature) in signatures.iter().enumerate() { let TSSignature::TSMethodSignature(method_sig) = signature else { @@ -390,7 +382,7 @@ impl GroupedAccessorPairs { get_diagnostic_access_name("setter", &key, is_computed, false, false); report( ctx, - self.pair_order, + *pair_order, (&getter_key, &setter_key), ( Span::new(getter_node.span.start, getter_node.key.span().end), diff --git a/crates/oxc_linter/src/rules/eslint/init_declarations.rs b/crates/oxc_linter/src/rules/eslint/init_declarations.rs index 7ef838f02d1a1..44184b87b43e6 100644 --- a/crates/oxc_linter/src/rules/eslint/init_declarations.rs +++ b/crates/oxc_linter/src/rules/eslint/init_declarations.rs @@ -9,9 +9,14 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; use schemars::JsonSchema; +use serde::Deserialize; use serde_json::Value; -use crate::{AstNode, context::LintContext, rule::Rule}; +use crate::{ + AstNode, + context::LintContext, + rule::{DefaultRuleConfig, Rule}, +}; fn init_declarations_diagnostic(span: Span, mode: &Mode, identifier_name: &str) -> OxcDiagnostic { let msg = if Mode::Always == *mode { @@ -24,26 +29,23 @@ fn init_declarations_diagnostic(span: Span, mode: &Mode, identifier_name: &str) .with_label(span) } -#[derive(Debug, Default, PartialEq, Clone, JsonSchema)] -#[serde(rename_all = "lowercase")] +#[derive(Debug, Default, PartialEq, Clone, JsonSchema, Deserialize)] +#[serde(rename_all = "kebab-case")] enum Mode { + /// Requires that variables be initialized on declaration. This is the default behavior. #[default] Always, + /// Disallows initialization during declaration. Never, } -impl Mode { - pub fn from(raw: &str) -> Self { - if raw == "never" { Self::Never } else { Self::Always } - } -} +#[derive(Debug, Default, Clone, JsonSchema, Deserialize)] +#[serde(rename_all = "camelCase", default)] +pub struct InitDeclarations(Mode, InitDeclarationsConfig); -#[derive(Debug, Default, Clone, JsonSchema)] +#[derive(Debug, Default, Clone, JsonSchema, Deserialize)] #[serde(rename_all = "camelCase", default)] -pub struct InitDeclarations { - /// When set to `"always"` (default), requires that variables be initialized on declaration. - /// When set to `"never"`, disallows initialization during declaration. - mode: Mode, +pub struct InitDeclarationsConfig { /// When set to `true`, allows uninitialized variables in the init expression of `for`, `for-in`, and `for-of` loops. /// Only applies when mode is set to `"never"`. ignore_for_loop_init: bool, @@ -60,6 +62,8 @@ declare_oxc_lint!( /// For example, in the following code, foo is initialized during declaration, while bar is initialized later. /// /// ### Examples + /// + /// ```js /// var foo = 1; /// var bar; /// if (foo) { @@ -67,10 +71,11 @@ declare_oxc_lint!( /// } else { /// bar = 2; /// } + /// ``` /// /// Examples of incorrect code for the default "always" option: /// ```js - /// /*eslint init-declarations: ["error", "always"]*/ + /// /* init-declarations: ["error", "always"] */ /// function foo() { /// var bar; /// let baz; @@ -79,7 +84,7 @@ declare_oxc_lint!( /// /// Examples of incorrect code for the "never" option: /// ```js - /// /*eslint init-declarations: ["error", "never"]*/ + /// /* init-declarations: ["error", "never"] */ /// function foo() { /// var bar = 1; /// let baz = 2; @@ -89,7 +94,7 @@ declare_oxc_lint!( /// /// Examples of correct code for the default "always" option: /// ```js - /// /*eslint init-declarations: ["error", "always"]*/ + /// /* init-declarations: ["error", "always"] */ /// /// function foo() { /// var bar = 1; @@ -100,7 +105,7 @@ declare_oxc_lint!( /// /// Examples of correct code for the "never" option: /// ```js - /// /*eslint init-declarations: ["error", "never"]*/ + /// /* init-declarations: ["error", "never"] */ /// /// function foo() { /// var bar; @@ -111,7 +116,7 @@ declare_oxc_lint!( /// /// Examples of correct code for the "never", { "ignoreForLoopInit": true } options: /// ```js - /// /*eslint init-declarations: ["error", "never", { "ignoreForLoopInit": true }]*/ + /// /* init-declarations: ["error", "never", { "ignoreForLoopInit": true }] */ /// for (var i = 0; i < 1; i++) {} /// ``` InitDeclarations, @@ -122,23 +127,18 @@ declare_oxc_lint!( impl Rule for InitDeclarations { fn from_configuration(value: Value) -> Self { - let obj1 = value.get(0); - let obj2 = value.get(1); - - Self { - mode: obj1.and_then(Value::as_str).map(Mode::from).unwrap_or_default(), - ignore_for_loop_init: obj2 - .and_then(|v| v.get("ignoreForLoopInit")) - .and_then(Value::as_bool) - .unwrap_or(false), - } + serde_json::from_value::>(value) + .unwrap_or_default() + .into_inner() } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::VariableDeclaration(decl) = node.kind() { + let InitDeclarations(mode, config) = &self; let parent = ctx.nodes().parent_node(node.id()); + // support for TypeScript's declare variables - if self.mode == Mode::Always { + if mode == &Mode::Always { if decl.declare { return; } @@ -169,21 +169,21 @@ impl Rule for InitDeclarations { _ => v.init.is_some(), }; - match self.mode { + match mode { Mode::Always if !is_initialized => { ctx.diagnostic(init_declarations_diagnostic( v.span, - &self.mode, + mode, identifier.name.as_str(), )); } - Mode::Never if is_initialized && !self.ignore_for_loop_init => { + Mode::Never if is_initialized && !config.ignore_for_loop_init => { if matches!(&v.kind, VariableDeclarationKind::Const) { continue; } ctx.diagnostic(init_declarations_diagnostic( v.span, - &self.mode, + mode, identifier.name.as_str(), )); }