diff --git a/crates/oxc_cli/src/lint/command.rs b/crates/oxc_cli/src/lint/command.rs index 30fc087f7c8b4..ef071897369fc 100644 --- a/crates/oxc_cli/src/lint/command.rs +++ b/crates/oxc_cli/src/lint/command.rs @@ -19,7 +19,7 @@ The default category is -D correctness.") Arg::new("path") .value_name("PATH") .num_args(1..) - .required(true) + .required_unless_present("rules") .value_parser(ValueParser::path_buf()) .help("File or Directory paths to scan. Directories are scanned recursively.") ) @@ -82,4 +82,8 @@ The default category is -D correctness.") .required(false) .help("This option allows you to specify a warning threshold, which can be used to force oxc_lint to exit with an error status if there are too many warning-level rule violations in your project.") ) + .arg( + Arg::new("rules") + .long("rules") + .required(false).action(ArgAction::SetTrue).help("This option allows you to list all the rules that are currently registered.")) } diff --git a/crates/oxc_cli/src/lint/mod.rs b/crates/oxc_cli/src/lint/mod.rs index ee77e93bc1d6f..b23add5bfdf6b 100644 --- a/crates/oxc_cli/src/lint/mod.rs +++ b/crates/oxc_cli/src/lint/mod.rs @@ -8,11 +8,13 @@ use clap::ArgMatches; pub use self::{command::lint_command, runner::LintRunner}; #[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] pub struct LintOptions { pub paths: Vec, /// Allow / Deny rules in order. [("allow" / "deny", rule name)] /// Defaults to [("deny", "correctness")] pub rules: Vec<(AllowWarnDeny, String)>, + pub list_rules: bool, pub fix: bool, pub quiet: bool, pub ignore_path: PathBuf, @@ -40,9 +42,11 @@ impl From<&'static str> for AllowWarnDeny { impl<'a> From<&'a ArgMatches> for LintOptions { fn from(matches: &'a ArgMatches) -> Self { + let list_rules = matches.get_flag("rules"); + Self { paths: matches.get_many("path").map_or_else( - || vec![PathBuf::from(".")], + || if list_rules { vec![] } else { vec![PathBuf::from(".")] }, |paths| paths.into_iter().cloned().collect(), ), rules: Self::get_rules(matches), @@ -57,6 +61,7 @@ impl<'a> From<&'a ArgMatches> for LintOptions { .map(|patterns| patterns.into_iter().cloned().collect()) .unwrap_or_default(), max_warnings: matches.get_one("max-warnings").copied(), + list_rules, } } } @@ -179,4 +184,11 @@ mod test { get_lint_options("lint --ignore-pattern ./test --ignore-pattern bar.js foo.js"); assert_eq!(options.ignore_pattern, vec![String::from("./test"), String::from("bar.js")]); } + + #[test] + fn list_rules_true() { + let options = get_lint_options("lint --rules"); + assert!(options.paths.is_empty()); + assert!(options.list_rules); + } } diff --git a/crates/oxc_cli/src/lint/runner.rs b/crates/oxc_cli/src/lint/runner.rs index 7d5a0f90a5450..5a1ff1389621e 100644 --- a/crates/oxc_cli/src/lint/runner.rs +++ b/crates/oxc_cli/src/lint/runner.rs @@ -20,7 +20,7 @@ use oxc_diagnostics::{ use oxc_linter::{Fixer, Linter, RuleCategory, RuleEnum, RULES}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use super::{AllowWarnDeny, LintOptions}; use crate::{CliRunResult, Walk}; @@ -43,6 +43,22 @@ impl LintRunner { Self { options, linter: Arc::new(linter) } } + fn print_rules() { + let rules_by_category = RULES.iter().fold(FxHashMap::default(), |mut map, rule| { + map.entry(rule.category()).or_insert_with(Vec::new).push(rule); + map + }); + + let mut stdout = BufWriter::new(std::io::stdout()); + for (category, rules) in rules_by_category { + writeln!(stdout, "{} ({}):", category, rules.len()).unwrap(); + for rule in rules { + writeln!(stdout, " • {}", rule.name()).unwrap(); + } + } + writeln!(stdout, "Total: {}", RULES.len()).unwrap(); + } + fn derive_rules(options: &LintOptions) -> Vec { let mut rules: FxHashSet = FxHashSet::default(); @@ -96,6 +112,10 @@ impl LintRunner { pub fn run(&self) -> CliRunResult { let now = std::time::Instant::now(); + if self.options.list_rules { + Self::print_rules(); + } + let number_of_files = Arc::new(AtomicUsize::new(0)); let (tx_error, rx_error) = mpsc::channel::<(PathBuf, Vec)>(); diff --git a/crates/oxc_linter/src/rule.rs b/crates/oxc_linter/src/rule.rs index 71e0aa6fa700c..92946f21e7502 100644 --- a/crates/oxc_linter/src/rule.rs +++ b/crates/oxc_linter/src/rule.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::fmt::{Debug, Display}; use oxc_semantic::Symbol; @@ -28,7 +28,7 @@ pub trait RuleMeta { } /// Rule categories defined by rust-clippy -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum RuleCategory { /// Code that is outright wrong or useless Correctness, @@ -53,3 +53,13 @@ impl RuleCategory { } } } + +impl Display for RuleCategory { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Correctness => write!(f, "Correctness"), + Self::Restriction => write!(f, "Restriction"), + Self::Nursery => write!(f, "Nursery"), + } + } +} diff --git a/tasks/coverage/babel b/tasks/coverage/babel index 6925ac555c62c..18493749b57cb 160000 --- a/tasks/coverage/babel +++ b/tasks/coverage/babel @@ -1 +1 @@ -Subproject commit 6925ac555c62c67886b86e38c0c6a7dca60b3cb7 +Subproject commit 18493749b57cbf01a0612f47f6a29a2e4464d900 diff --git a/tasks/coverage/test262 b/tasks/coverage/test262 index 9704d7f22f634..4a6439e4a7aba 160000 --- a/tasks/coverage/test262 +++ b/tasks/coverage/test262 @@ -1 +1 @@ -Subproject commit 9704d7f22f6342d6c4753ab9a8d62d6725de8c4e +Subproject commit 4a6439e4a7aba77e305afc30977b02a34ad11b33 diff --git a/tasks/coverage/typescript b/tasks/coverage/typescript index 7f292bf2a19aa..430c5be783928 160000 --- a/tasks/coverage/typescript +++ b/tasks/coverage/typescript @@ -1 +1 @@ -Subproject commit 7f292bf2a19aa14ed69a55e646111af9533d8f1c +Subproject commit 430c5be783928c71e1b16ef267fad26d2998d7ab