Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 30 additions & 38 deletions crates/oxc_linter/src/rules/eslint/grouped_accessor_pairs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::borrow::Cow;

use oxc_allocator::Box;
use oxc_allocator::Box as OBox;
use oxc_ast::{
AstKind,
ast::{
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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::<DefaultRuleConfig<GroupedAccessorPairs>>(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<ObjectProperty>)>>::default();
FxHashMap::<(String, bool), Vec<(usize, &OBox<ObjectProperty>)>>::default();
let properties = &obj_expr.properties;

for (idx, v) in properties.iter().enumerate() {
Expand Down Expand Up @@ -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),
Expand All @@ -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<MethodDefinition>)>,
Vec<(usize, &OBox<MethodDefinition>)>,
>::default();

for (idx, v) in method_defines.iter().enumerate() {
Expand Down Expand Up @@ -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),
Expand All @@ -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);
}
_ => {}
Expand All @@ -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<TSMethodSignature>)>>::default();
FxHashMap::<(String, bool), Vec<(usize, &OBox<TSMethodSignature>)>>::default();

for (idx, signature) in signatures.iter().enumerate() {
let TSSignature::TSMethodSignature(method_sig) = signature else {
Expand Down Expand Up @@ -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),
Expand Down
66 changes: 33 additions & 33 deletions crates/oxc_linter/src/rules/eslint/init_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -60,17 +62,20 @@ 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) {
/// bar = 1;
/// } 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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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,
Expand All @@ -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::<DefaultRuleConfig<InitDeclarations>>(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;
}
Expand Down Expand Up @@ -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(),
));
}
Expand Down
Loading