diff --git a/.clippy.toml b/.clippy.toml index 9da0cf0e84dcc..c76422cd094e8 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1,4 +1,4 @@ -ignore-interior-mutability = ["oxc_linter::rule::RuleWithSeverity"] +ignore-interior-mutability = ["oxc_linter::rules::RuleEnum"] disallowed-methods = [ { path = "str::to_ascii_lowercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_lowercase` instead." }, diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index af048f17bb023..9d0c54d65e614 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -4,14 +4,13 @@ use std::{ }; use itertools::Itertools; -use rustc_hash::FxHashSet; +use rustc_hash::FxHashMap; use oxc_diagnostics::OxcDiagnostic; use oxc_span::{CompactStr, format_compact_str}; use crate::{ AllowWarnDeny, LintConfig, LintFilter, LintFilterKind, Oxlintrc, RuleCategory, RuleEnum, - RuleWithSeverity, config::{ESLintRule, LintPlugins, OxlintOverrides, OxlintRules, overrides::OxlintOverride}, rules::RULES, }; @@ -20,7 +19,7 @@ use super::Config; #[must_use = "You dropped your builder without building a Linter! Did you mean to call .build()?"] pub struct ConfigStoreBuilder { - pub(super) rules: FxHashSet, + pub(super) rules: FxHashMap, config: LintConfig, overrides: OxlintOverrides, cache: RulesCache, @@ -39,7 +38,7 @@ impl ConfigStoreBuilder { /// You can think of this as `oxlint -A all`. pub fn empty() -> Self { let config = LintConfig::default(); - let rules = FxHashSet::default(); + let rules = FxHashMap::default(); let overrides = OxlintOverrides::default(); let cache = RulesCache::new(config.plugins); @@ -54,15 +53,8 @@ impl ConfigStoreBuilder { let config = LintConfig { plugins: LintPlugins::all(), ..LintConfig::default() }; let overrides = OxlintOverrides::default(); let cache = RulesCache::new(config.plugins); - Self { - rules: RULES - .iter() - .map(|rule| RuleWithSeverity { rule: rule.clone(), severity: AllowWarnDeny::Warn }) - .collect(), - config, - overrides, - cache, - } + let rules = RULES.iter().map(|rule| (rule.clone(), AllowWarnDeny::Warn)).collect(); + Self { rules, config, overrides, cache } } /// Create a [`ConfigStoreBuilder`] from a loaded or manually built [`Oxlintrc`]. @@ -104,7 +96,7 @@ impl ConfigStoreBuilder { let config = LintConfig { plugins, settings, env, globals, path: Some(path) }; let rules = - if start_empty { FxHashSet::default() } else { Self::warn_correctness(plugins) }; + if start_empty { FxHashMap::default() } else { Self::warn_correctness(plugins) }; let cache = RulesCache::new(config.plugins); let mut builder = Self { rules, config, overrides, cache }; @@ -222,12 +214,15 @@ impl ConfigStoreBuilder { } #[cfg(test)] - pub(crate) fn with_rule(mut self, rule: RuleWithSeverity) -> Self { - self.rules.insert(rule); + pub(crate) fn with_rule(mut self, rule: RuleEnum, severity: AllowWarnDeny) -> Self { + self.rules.insert(rule, severity); self } - pub(crate) fn with_rules>(mut self, rules: R) -> Self { + pub(crate) fn with_rules>( + mut self, + rules: R, + ) -> Self { self.rules.extend(rules); self } @@ -263,12 +258,12 @@ impl ConfigStoreBuilder { }, AllowWarnDeny::Allow => match filter { LintFilterKind::Category(category) => { - self.rules.retain(|rule| rule.category() != *category); + self.rules.retain(|rule, _| rule.category() != *category); } LintFilterKind::Rule(plugin, rule) => { - self.rules.retain(|r| r.plugin_name() != plugin || r.name() != rule); + self.rules.retain(|r, _| r.plugin_name() != plugin || r.name() != rule); } - LintFilterKind::Generic(name) => self.rules.retain(|rule| rule.name() != name), + LintFilterKind::Generic(name) => self.rules.retain(|rule, _| rule.name() != name), LintFilterKind::All => self.rules.clear(), }, } @@ -287,14 +282,13 @@ impl ConfigStoreBuilder { // NOTE: we may want to warn users if they're configuring a rule that does not exist. let rules_to_configure = all_rules.iter().filter(query); for rule in rules_to_configure { - match self.rules.take(rule) { - Some(mut existing_rule) => { - existing_rule.severity = severity; - self.rules.insert(existing_rule); - } - _ => { - self.rules.insert(RuleWithSeverity::new(rule.clone(), severity)); - } + // If the rule is already in the list, just update its severity. + // Otherwise, add it to the map. + + if let Some(existing_rule) = self.rules.get_mut(rule) { + *existing_rule = severity; + } else { + self.rules.insert(rule.clone(), severity); } } } @@ -306,17 +300,20 @@ impl ConfigStoreBuilder { // to be taken out. let plugins = self.plugins(); let mut rules = if self.cache.is_stale() { - self.rules.into_iter().filter(|r| plugins.contains(r.plugin_name().into())).collect() + self.rules + .into_iter() + .filter(|(r, _)| plugins.contains(r.plugin_name().into())) + .collect() } else { self.rules.into_iter().collect::>() }; - rules.sort_unstable_by_key(|r| r.id()); + rules.sort_unstable_by_key(|(r, _)| r.id()); Ok(Config::new(rules, self.config, self.overrides)) } /// Warn for all correctness rules in the given set of plugins. - fn warn_correctness(plugins: LintPlugins) -> FxHashSet { + fn warn_correctness(plugins: LintPlugins) -> FxHashMap { RULES .iter() .filter(|rule| { @@ -325,7 +322,7 @@ impl ConfigStoreBuilder { rule.category() == RuleCategory::Correctness && plugins.contains(LintPlugins::from(rule.plugin_name())) }) - .map(|rule| RuleWithSeverity { rule: rule.clone(), severity: AllowWarnDeny::Warn }) + .map(|rule| (rule.clone(), AllowWarnDeny::Warn)) .collect() } @@ -344,13 +341,13 @@ impl ConfigStoreBuilder { let new_rules = self .rules .iter() - .sorted_by_key(|x| (x.plugin_name(), x.name())) - .map(|r: &RuleWithSeverity| ESLintRule { + .sorted_by_key(|(r, _)| (r.plugin_name(), r.name())) + .map(|(r, severity)| ESLintRule { plugin_name: r.plugin_name().to_string(), - rule_name: r.rule.name().to_string(), - severity: r.severity, + rule_name: r.name().to_string(), + severity: *severity, config: rule_name_to_rule - .get(&get_name(r.plugin_name(), r.rule.name())) + .get(&get_name(r.plugin_name(), r.name())) .and_then(|r| r.config.clone()), }) .collect(); @@ -519,10 +516,10 @@ mod test { // populated with all correctness-level ESLint rules at a "warn" severity assert!(!builder.rules.is_empty()); - for rule in &builder.rules { + for (rule, severity) in &builder.rules { assert_eq!(rule.category(), RuleCategory::Correctness); - assert_eq!(rule.severity, AllowWarnDeny::Warn); - let plugin = rule.rule.plugin_name(); + assert_eq!(*severity, AllowWarnDeny::Warn); + let plugin = rule.plugin_name(); let name = rule.name(); assert!( builder.plugins().contains(plugin.into()), @@ -551,9 +548,9 @@ mod test { assert!(!builder.rules.is_empty()); assert_eq!(initial_rule_count, rule_count_after_deny); - for rule in &builder.rules { + for (rule, severity) in &builder.rules { assert_eq!(rule.category(), RuleCategory::Correctness); - assert_eq!(rule.severity, AllowWarnDeny::Deny); + assert_eq!(*severity, AllowWarnDeny::Deny); let plugin = rule.plugin_name(); let name = rule.name(); @@ -579,12 +576,12 @@ mod test { "Changing a single rule from warn to deny should not add a new one, just modify what's already there." ); - let no_const_assign = builder + let (_, severity) = builder .rules .iter() - .find(|r| r.plugin_name() == "eslint" && r.name() == "no-const-assign") + .find(|(r, _)| r.plugin_name() == "eslint" && r.name() == "no-const-assign") .expect("Could not find eslint/no-const-assign after configuring it to 'deny'"); - assert_eq!(no_const_assign.severity, AllowWarnDeny::Deny); + assert_eq!(*severity, AllowWarnDeny::Deny); } } // turn on a rule that isn't configured yet and set it to "warn" @@ -595,15 +592,15 @@ mod test { let filter = LintFilter::new(AllowWarnDeny::Warn, filter_string).unwrap(); let builder = ConfigStoreBuilder::default(); // sanity check: not already turned on - assert!(!builder.rules.iter().any(|r| r.name() == "no-console")); + assert!(!builder.rules.iter().any(|(r, _)| r.name() == "no-console")); let builder = builder.with_filter(&filter); - let no_console = builder + let (_, severity) = builder .rules .iter() - .find(|r| r.plugin_name() == "eslint" && r.name() == "no-console") + .find(|(r, _)| r.plugin_name() == "eslint" && r.name() == "no-console") .expect("Could not find eslint/no-console after configuring it to 'warn'"); - assert_eq!(no_console.severity, AllowWarnDeny::Warn); + assert_eq!(*severity, AllowWarnDeny::Warn); } } @@ -618,11 +615,11 @@ mod test { !builder.rules.is_empty(), "warning on categories after allowing all rules should populate the rules set" ); - for rule in &builder.rules { + for (rule, severity) in &builder.rules { let plugin = rule.plugin_name(); let name = rule.name(); assert_eq!( - rule.severity, + *severity, AllowWarnDeny::Warn, "{plugin}/{name} should have a warning severity" ); @@ -656,7 +653,7 @@ mod test { desired_plugins.set(LintPlugins::TYPESCRIPT, false); let linter = ConfigStoreBuilder::default().with_plugins(desired_plugins).build().unwrap(); - for rule in linter.base.rules.iter() { + for (rule, _) in linter.base.rules.iter() { let name = rule.name(); let plugin = rule.plugin_name(); assert_ne!( @@ -727,7 +724,7 @@ mod test { ) .unwrap(); let builder = ConfigStoreBuilder::from_oxlintrc(false, oxlintrc).unwrap(); - for rule in &builder.rules { + for (rule, severity) in &builder.rules { let name = rule.name(); let plugin = rule.plugin_name(); let category = rule.category(); @@ -735,24 +732,20 @@ mod test { RuleCategory::Correctness => { if name == "no-const-assign" { assert_eq!( - rule.severity, + *severity, AllowWarnDeny::Deny, "no-const-assign should be denied", ); } else { assert_eq!( - rule.severity, + *severity, AllowWarnDeny::Warn, "{plugin}/{name} should be a warning" ); } } RuleCategory::Suspicious => { - assert_eq!( - rule.severity, - AllowWarnDeny::Deny, - "{plugin}/{name} should be denied" - ); + assert_eq!(*severity, AllowWarnDeny::Deny, "{plugin}/{name} should be denied"); } invalid => { panic!("Found rule {plugin}/{name} with an unexpected category {invalid:?}"); @@ -796,25 +789,26 @@ mod test { update_rules_config .rules() .iter() - .any(|r| r.name() == "no-debugger" && r.severity == AllowWarnDeny::Warn) + .any(|(r, severity)| r.name() == "no-debugger" && *severity == AllowWarnDeny::Warn) ); assert!( update_rules_config .rules() .iter() - .any(|r| r.name() == "no-console" && r.severity == AllowWarnDeny::Warn) + .any(|(r, severity)| r.name() == "no-console" && *severity == AllowWarnDeny::Warn) ); assert!( !update_rules_config .rules() .iter() - .any(|r| r.name() == "no-null" && r.severity == AllowWarnDeny::Allow) + .any(|(r, severity)| r.name() == "no-null" && *severity == AllowWarnDeny::Allow) ); assert!( update_rules_config .rules() .iter() - .any(|r| r.name() == "prefer-as-const" && r.severity == AllowWarnDeny::Warn) + .any(|(r, severity)| r.name() == "prefer-as-const" + && *severity == AllowWarnDeny::Warn) ); } @@ -831,7 +825,7 @@ mod test { } "#, ); - assert!(warn_all.rules().iter().all(|r| r.severity == AllowWarnDeny::Warn)); + assert!(warn_all.rules().iter().all(|(_, severity)| *severity == AllowWarnDeny::Warn)); let deny_all = config_store_from_str( r#" @@ -844,7 +838,7 @@ mod test { } "#, ); - assert!(deny_all.rules().iter().all(|r| r.severity == AllowWarnDeny::Deny)); + assert!(deny_all.rules().iter().all(|(_, severity)| *severity == AllowWarnDeny::Deny)); let allow_all = config_store_from_str( r#" @@ -857,7 +851,7 @@ mod test { } "#, ); - assert!(allow_all.rules().iter().all(|r| r.severity == AllowWarnDeny::Allow)); + assert!(allow_all.rules().iter().all(|(_, severity)| *severity == AllowWarnDeny::Allow)); assert_eq!(allow_all.number_of_rules(), 0); let allow_and_override_config = config_store_from_str( @@ -879,19 +873,20 @@ mod test { allow_and_override_config .rules() .iter() - .any(|r| r.name() == "no-var" && r.severity == AllowWarnDeny::Warn) + .any(|(r, severity)| r.name() == "no-var" && *severity == AllowWarnDeny::Warn) ); assert!( allow_and_override_config .rules() .iter() - .any(|r| r.name() == "approx-constant" && r.severity == AllowWarnDeny::Deny) + .any(|(r, severity)| r.name() == "approx-constant" + && *severity == AllowWarnDeny::Deny) ); assert!( allow_and_override_config .rules() .iter() - .any(|r| r.name() == "no-null" && r.severity == AllowWarnDeny::Deny) + .any(|(r, severity)| r.name() == "no-null" && *severity == AllowWarnDeny::Deny) ); } diff --git a/crates/oxc_linter/src/config/config_store.rs b/crates/oxc_linter/src/config/config_store.rs index 111aebc7a47f5..9c75e4c117dbc 100644 --- a/crates/oxc_linter/src/config/config_store.rs +++ b/crates/oxc_linter/src/config/config_store.rs @@ -3,16 +3,19 @@ use std::{ sync::Arc, }; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use super::{LintConfig, LintPlugins, overrides::OxlintOverrides}; -use crate::{RuleWithSeverity, rules::RULES}; +use crate::{ + AllowWarnDeny, + rules::{RULES, RuleEnum}, +}; // TODO: support `categories` et. al. in overrides. #[derive(Debug)] pub struct ResolvedLinterState { // TODO: Arc + Vec -> SyncVec? It would save a pointer dereference. - pub rules: Arc<[RuleWithSeverity]>, + pub rules: Arc<[(RuleEnum, AllowWarnDeny)]>, pub config: Arc, } @@ -33,7 +36,7 @@ pub struct Config { impl Config { pub fn new( - rules: Vec, + rules: Vec<(RuleEnum, AllowWarnDeny)>, config: LintConfig, overrides: OxlintOverrides, ) -> Self { @@ -50,7 +53,7 @@ impl Config { self.base.config.plugins } - pub fn rules(&self) -> &Arc<[RuleWithSeverity]> { + pub fn rules(&self) -> &Arc<[(RuleEnum, AllowWarnDeny)]> { &self.base.rules } @@ -89,9 +92,9 @@ impl Config { .base .rules .iter() - .filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name()))) + .filter(|(rule, _)| plugins.contains(LintPlugins::from(rule.plugin_name()))) .cloned() - .collect::>(); + .collect::>(); let all_rules = RULES .iter() @@ -153,7 +156,7 @@ impl ConfigStore { self.nested_configs.is_empty().then_some(self.base.base.rules.len()) } - pub fn rules(&self) -> &Arc<[RuleWithSeverity]> { + pub fn rules(&self) -> &Arc<[(RuleEnum, AllowWarnDeny)]> { &self.base.base.rules } @@ -193,7 +196,7 @@ mod test { use super::{ConfigStore, OxlintOverrides}; use crate::{ - AllowWarnDeny, LintPlugins, RuleEnum, RuleWithSeverity, + AllowWarnDeny, LintPlugins, RuleEnum, config::{LintConfig, OxlintEnv, OxlintGlobals, OxlintSettings, config_store::Config}, }; @@ -204,11 +207,8 @@ mod test { } #[expect(clippy::default_trait_access)] - fn no_explicit_any() -> RuleWithSeverity { - RuleWithSeverity::new( - RuleEnum::TypescriptNoExplicitAny(Default::default()), - AllowWarnDeny::Warn, - ) + fn no_explicit_any() -> (RuleEnum, AllowWarnDeny) { + (RuleEnum::TypescriptNoExplicitAny(Default::default()), AllowWarnDeny::Warn) } /// an empty ruleset is a no-op @@ -229,10 +229,7 @@ mod test { assert_eq!(rules_for_source_file.rules.len(), 1); assert_eq!(rules_for_test_file.rules.len(), 1); - assert_eq!( - rules_for_test_file.rules[0].rule.id(), - rules_for_source_file.rules[0].rule.id() - ); + assert_eq!(rules_for_test_file.rules[0].0.id(), rules_for_source_file.rules[0].0.id()); } /// adding plugins but no rules is a no-op @@ -254,10 +251,7 @@ mod test { assert_eq!(rules_for_source_file.rules.len(), 1); assert_eq!(rules_for_test_file.rules.len(), 1); - assert_eq!( - rules_for_test_file.rules[0].rule.id(), - rules_for_source_file.rules[0].rule.id() - ); + assert_eq!(rules_for_test_file.rules[0].0.id(), rules_for_source_file.rules[0].0.id()); } #[test] @@ -324,11 +318,11 @@ mod test { let app = store.resolve("App.tsx".as_ref()).rules; assert_eq!(app.len(), 1); - assert_eq!(app[0].severity, AllowWarnDeny::Warn); + assert_eq!(app[0].1, AllowWarnDeny::Warn); let src_app = store.resolve("src/App.tsx".as_ref()).rules; assert_eq!(src_app.len(), 1); - assert_eq!(src_app[0].severity, AllowWarnDeny::Deny); + assert_eq!(src_app[0].1, AllowWarnDeny::Deny); } #[test] diff --git a/crates/oxc_linter/src/config/mod.rs b/crates/oxc_linter/src/config/mod.rs index 0b0314c9d4bf8..fab4d0472f9f5 100644 --- a/crates/oxc_linter/src/config/mod.rs +++ b/crates/oxc_linter/src/config/mod.rs @@ -50,7 +50,7 @@ mod test { use std::env; use oxc_span::CompactStr; - use rustc_hash::FxHashSet; + use rustc_hash::FxHashMap; use serde::Deserialize; use super::Oxlintrc; @@ -111,10 +111,10 @@ mod test { let fixture_path: std::path::PathBuf = env::current_dir().unwrap().join("fixtures/eslint_config_vitest_replace.json"); let config = Oxlintrc::from_file(&fixture_path).unwrap(); - let mut set = FxHashSet::default(); + let mut set = FxHashMap::default(); config.rules.override_rules(&mut set, &RULES); - let rule = set.into_iter().next().unwrap(); + let (rule, _) = set.into_iter().next().unwrap(); assert_eq!(rule.name(), "no-disabled-tests"); assert_eq!(rule.plugin_name(), "jest"); } diff --git a/crates/oxc_linter/src/config/rules.rs b/crates/oxc_linter/src/config/rules.rs index 538b5aa61278b..2433d0d70b3a7 100644 --- a/crates/oxc_linter/src/config/rules.rs +++ b/crates/oxc_linter/src/config/rules.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, fmt}; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::Schema}; use serde::{ Deserialize, Serialize, Serializer, @@ -11,12 +11,12 @@ use serde::{ use oxc_diagnostics::{Error, OxcDiagnostic}; use crate::{ - AllowWarnDeny, RuleWithSeverity, + AllowWarnDeny, rules::{RULES, RuleEnum}, utils::{is_eslint_rule_adapted_to_typescript, is_jest_rule_adapted_to_vitest}, }; -type RuleSet = FxHashSet; +type RuleSet = FxHashMap; // TS type is `Record` // - type SeverityConf = 0 | 1 | 2 | "off" | "warn" | "error"; @@ -60,8 +60,8 @@ pub struct ESLintRule { impl OxlintRules { pub(crate) fn override_rules(&self, rules_for_override: &mut RuleSet, all_rules: &[RuleEnum]) { use itertools::Itertools; - let mut rules_to_replace: Vec = vec![]; - let mut rules_to_remove: Vec = vec![]; + let mut rules_to_replace: Vec<(RuleEnum, AllowWarnDeny)> = vec![]; + let mut rules_to_remove: Vec = vec![]; // Rules can have the same name but different plugin names let lookup = self.rules.iter().into_group_map_by(|r| r.rule_name.as_str()); @@ -84,16 +84,14 @@ impl OxlintRules { { let config = rule_config.config.clone().unwrap_or_default(); let rule = rule.read_json(config); - rules_to_replace.push(RuleWithSeverity::new(rule, severity)); + rules_to_replace.push((rule, severity)); } } AllowWarnDeny::Allow => { - if let Some(rule) = rules_for_override - .iter() - .find(|r| r.name() == rule_name && r.plugin_name() == plugin_name) - { - let rule = rule.clone(); - rules_to_remove.push(rule); + if let Some((rule, _)) = rules_for_override.iter().find(|(r, _)| { + r.name() == rule_name && r.plugin_name() == plugin_name + }) { + rules_to_remove.push(rule.clone()); } // If the given rule is not found in the rule list (for example, if all rules are disabled), // then look it up in the entire rules list and add it. @@ -103,7 +101,7 @@ impl OxlintRules { { let config = rule_config.config.clone().unwrap_or_default(); let rule = rule.read_json(config); - rules_to_remove.push(RuleWithSeverity::new(rule, severity)); + rules_to_remove.push(rule); } } } @@ -111,7 +109,7 @@ impl OxlintRules { _ => { let rules = rules_for_override .iter() - .filter_map(|r| { + .filter_map(|(r, _)| { if r.name() == *name { Some((r.plugin_name(), r)) } else { None } }) .collect::>(); @@ -125,10 +123,8 @@ impl OxlintRules { if rule_config.severity.is_warn_deny() { let config = rule_config.config.clone().unwrap_or_default(); if let Some(rule) = rules.get(&plugin_name) { - rules_to_replace.push(RuleWithSeverity::new( - rule.read_json(config), - rule_config.severity, - )); + rules_to_replace + .push((rule.read_json(config), rule_config.severity)); } // If the given rule is not found in the rule list (for example, if all rules are disabled), // then look it up in the entire rules list and add it. @@ -136,10 +132,8 @@ impl OxlintRules { .iter() .find(|r| r.name() == rule_name && r.plugin_name() == plugin_name) { - rules_to_replace.push(RuleWithSeverity::new( - rule.read_json(config), - rule_config.severity, - )); + rules_to_replace + .push((rule.read_json(config), rule_config.severity)); } } else if let Some(&rule) = rules.get(&plugin_name) { rules_to_remove.push(rule.clone()); @@ -152,8 +146,8 @@ impl OxlintRules { for rule in rules_to_remove { rules_for_override.remove(&rule); } - for rule in rules_to_replace { - rules_for_override.replace(rule); + for (rule, severity) in rules_to_replace { + rules_for_override.insert(rule, severity); } } } @@ -354,7 +348,7 @@ mod test { use serde_json::{Value, json}; use crate::{ - AllowWarnDeny, RuleWithSeverity, + AllowWarnDeny, rules::{RULES, RuleEnum}, }; @@ -417,9 +411,9 @@ mod test { r#override(&mut rules, &config); assert_eq!(rules.len(), 1, "{config:?}"); - let rule = rules.iter().next().unwrap(); + let (rule, severity) = rules.iter().next().unwrap(); assert_eq!(rule.name(), "no-console", "{config:?}"); - assert_eq!(rule.severity, AllowWarnDeny::Deny, "{config:?}"); + assert_eq!(severity, &AllowWarnDeny::Deny, "{config:?}"); } } @@ -440,15 +434,15 @@ mod test { 1, "eslint rules should be configurable by their typescript-eslint reimplementations: {config:?}" ); - let rule = rules.iter().next().unwrap(); + let (rule, severity) = rules.iter().next().unwrap(); assert_eq!( rule.name(), "no-console", "eslint rules should be configurable by their typescript-eslint reimplementations: {config:?}" ); assert_eq!( - rule.severity, - AllowWarnDeny::Deny, + severity, + &AllowWarnDeny::Deny, "eslint rules should be configurable by their typescript-eslint reimplementations: {config:?}" ); } @@ -456,10 +450,7 @@ mod test { #[test] fn test_override_allow() { let mut rules = RuleSet::default(); - rules.insert(RuleWithSeverity { - rule: RuleEnum::EslintNoConsole(Default::default()), - severity: AllowWarnDeny::Deny, - }); + rules.insert(RuleEnum::EslintNoConsole(Default::default()), AllowWarnDeny::Deny); r#override(&mut rules, &json!({ "eslint/no-console": "off" })); assert!(rules.is_empty()); @@ -478,23 +469,20 @@ mod test { r#override(&mut rules, config); assert_eq!(rules.len(), 1, "{config:?}"); - let rule = rules.iter().next().unwrap(); + let (rule, severity) = rules.iter().next().unwrap(); assert_eq!(rule.name(), "no-unused-vars", "{config:?}"); - assert_eq!(rule.severity, AllowWarnDeny::Deny, "{config:?}"); + assert_eq!(severity, &AllowWarnDeny::Deny, "{config:?}"); } for config in &configs { let mut rules = RuleSet::default(); - rules.insert(RuleWithSeverity { - rule: RuleEnum::EslintNoUnusedVars(Default::default()), - severity: AllowWarnDeny::Warn, - }); + rules.insert(RuleEnum::EslintNoUnusedVars(Default::default()), AllowWarnDeny::Warn); r#override(&mut rules, config); assert_eq!(rules.len(), 1, "{config:?}"); - let rule = rules.iter().next().unwrap(); + let (rule, severity) = rules.iter().next().unwrap(); assert_eq!(rule.name(), "no-unused-vars", "{config:?}"); - assert_eq!(rule.severity, AllowWarnDeny::Deny, "{config:?}"); + assert_eq!(severity, &AllowWarnDeny::Deny, "{config:?}"); } } } diff --git a/crates/oxc_linter/src/context/host.rs b/crates/oxc_linter/src/context/host.rs index c9bb43749d974..50c789797289b 100644 --- a/crates/oxc_linter/src/context/host.rs +++ b/crates/oxc_linter/src/context/host.rs @@ -5,13 +5,14 @@ use oxc_semantic::Semantic; use oxc_span::{SourceType, Span}; use crate::{ - FrameworkFlags, RuleWithSeverity, + AllowWarnDeny, FrameworkFlags, config::{LintConfig, LintPlugins}, disable_directives::{DisableDirectives, DisableDirectivesBuilder}, fixer::{FixKind, Message}, frameworks, module_record::ModuleRecord, options::LintOptions, + rules::RuleEnum, }; use super::{LintContext, plugin_name_to_prefix}; @@ -226,7 +227,7 @@ impl<'a> ContextHost<'a> { } /// Creates a new [`LintContext`] for a specific rule. - pub fn spawn(self: Rc, rule: &RuleWithSeverity) -> LintContext<'a> { + pub fn spawn(self: Rc, rule: &RuleEnum, severity: AllowWarnDeny) -> LintContext<'a> { let rule_name = rule.name(); let plugin_name = rule.plugin_name(); @@ -236,8 +237,8 @@ impl<'a> ContextHost<'a> { current_plugin_name: plugin_name, current_plugin_prefix: plugin_name_to_prefix(plugin_name), #[cfg(debug_assertions)] - current_rule_fix_capabilities: rule.rule.fix(), - severity: rule.severity.into(), + current_rule_fix_capabilities: rule.fix(), + severity: severity.into(), } } diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index 8030e83fe8d7d..5d053c5e7f272 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -38,7 +38,7 @@ pub use crate::{ module_record::ModuleRecord, options::LintOptions, options::{AllowWarnDeny, InvalidFilterKind, LintFilter, LintFilterKind}, - rule::{RuleCategory, RuleFixMeta, RuleMeta, RuleWithSeverity}, + rule::{RuleCategory, RuleFixMeta, RuleMeta}, service::{LintService, LintServiceOptions, RuntimeFileSystem}, utils::read_to_string, }; @@ -64,7 +64,6 @@ fn size_asserts() { #[derive(Debug, Clone)] pub struct Linter { - // rules: Vec, options: LintOptions, // config: Arc, config: ConfigStore, @@ -112,8 +111,8 @@ impl Linter { let rules = rules .iter() - .filter(|rule| rule.should_run(&ctx_host)) - .map(|rule| (rule, Rc::clone(&ctx_host).spawn(rule))); + .filter(|(rule, _)| rule.should_run(&ctx_host)) + .map(|(rule, severity)| (rule, Rc::clone(&ctx_host).spawn(rule, *severity))); let semantic = ctx_host.semantic(); diff --git a/crates/oxc_linter/src/rule.rs b/crates/oxc_linter/src/rule.rs index cb651f449cca3..a6f993600493f 100644 --- a/crates/oxc_linter/src/rule.rs +++ b/crates/oxc_linter/src/rule.rs @@ -1,16 +1,11 @@ -use std::{ - borrow::{Borrow, Cow}, - fmt, - hash::{Hash, Hasher}, - ops::Deref, -}; +use std::{borrow::Cow, fmt, hash::Hash}; use oxc_semantic::SymbolId; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::{ - AllowWarnDeny, AstNode, FixKind, RuleEnum, + AstNode, FixKind, context::{ContextHost, LintContext}, utils::PossibleJestNode, }; @@ -265,46 +260,6 @@ impl From for FixKind { } } -#[derive(Debug, Clone)] -pub struct RuleWithSeverity { - pub rule: RuleEnum, - pub severity: AllowWarnDeny, -} - -impl Hash for RuleWithSeverity { - fn hash(&self, state: &mut H) { - self.rule.hash(state); - } -} - -impl PartialEq for RuleWithSeverity { - fn eq(&self, other: &Self) -> bool { - self.rule == other.rule - } -} - -impl Eq for RuleWithSeverity {} - -impl Deref for RuleWithSeverity { - type Target = RuleEnum; - - fn deref(&self) -> &Self::Target { - &self.rule - } -} - -impl Borrow for RuleWithSeverity { - fn borrow(&self) -> &RuleEnum { - &self.rule - } -} - -impl RuleWithSeverity { - pub fn new(rule: RuleEnum, severity: AllowWarnDeny) -> Self { - Self { rule, severity } - } -} - #[cfg(test)] mod test { use super::RuleCategory; diff --git a/crates/oxc_linter/src/tester.rs b/crates/oxc_linter/src/tester.rs index 09001da8827bb..87d3725e6d16f 100644 --- a/crates/oxc_linter/src/tester.rs +++ b/crates/oxc_linter/src/tester.rs @@ -15,7 +15,7 @@ use serde_json::Value; use crate::{ AllowWarnDeny, ConfigStore, ConfigStoreBuilder, LintPlugins, LintService, LintServiceOptions, - Linter, Oxlintrc, RuleEnum, RuleWithSeverity, + Linter, Oxlintrc, RuleEnum, fixer::{FixKind, Fixer}, options::LintOptions, read_to_string, @@ -492,7 +492,7 @@ impl Tester { .unwrap() }) .with_plugins(self.plugins) - .with_rule(RuleWithSeverity::new(rule, AllowWarnDeny::Warn)) + .with_rule(rule, AllowWarnDeny::Warn) .build() .unwrap(), FxHashMap::default(),