refactor: Generate Linter -> RuleSelector mapping via macro

To enable ruff_dev to automatically generate the rule Markdown tables in
the README the ruff library contained the following function:

    Linter::codes() -> Vec<RuleSelector>

which was slightly changed to `fn prefixes(&self) -> Prefixes` in
9dc66b5a65 to enable ruff_dev to split
up the Markdown tables for linters that have multiple prefixes
(pycodestyle has E & W, Pylint has PLC, PLE, PLR & PLW).

The definition of this method was however largely redundant with the
#[prefix] macro attributes in the Linter enum, which are used to
derive the Linter::parse_code function, used by the --explain command.

This commit removes the redundant Linter::prefixes by introducing a
same-named method with a different signature to the RuleNamespace trait:

     fn prefixes(&self) -> &'static [&'static str];

As well as implementing IntoIterator<Rule> for &Linter. We extend the
extisting RuleNamespace proc macro to automatically derive both
implementations from the Linter enum definition.

To support the previously mentioned Markdown table splitting we
introduce a very simple hand-written method to the Linter impl:

    fn categories(&self) -> Option<&'static [LinterCategory]>;
This commit is contained in:
Martin Fischer 2023-01-21 07:19:10 +01:00 committed by Charlie Marsh
parent c3dd1b0e3c
commit 4758ee6ac4
6 changed files with 77 additions and 87 deletions

View file

@ -2,7 +2,7 @@
use anyhow::Result;
use clap::Args;
use ruff::registry::{Linter, Prefixes, RuleSelector};
use ruff::registry::{Linter, LinterCategory, Rule, RuleNamespace};
use strum::IntoEnumIterator;
use crate::utils::replace_readme_section;
@ -20,12 +20,12 @@ pub struct Cli {
pub(crate) dry_run: bool,
}
fn generate_table(table_out: &mut String, selector: &RuleSelector) {
fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>) {
table_out.push_str("| Code | Name | Message | Fix |");
table_out.push('\n');
table_out.push_str("| ---- | ---- | ------- | --- |");
table_out.push('\n');
for rule in selector {
for rule in rules {
let fix_token = match rule.autofixable() {
None => "",
Some(_) => "🛠",
@ -48,8 +48,7 @@ pub fn main(cli: &Cli) -> Result<()> {
let mut table_out = String::new();
let mut toc_out = String::new();
for linter in Linter::iter() {
let prefixes = linter.prefixes();
let codes_csv: String = prefixes.as_list(", ");
let codes_csv: String = linter.prefixes().join(", ");
table_out.push_str(&format!("### {} ({codes_csv})", linter.name()));
table_out.push('\n');
table_out.push('\n');
@ -86,15 +85,14 @@ pub fn main(cli: &Cli) -> Result<()> {
table_out.push('\n');
}
match prefixes {
Prefixes::Single(prefix) => generate_table(&mut table_out, &prefix),
Prefixes::Multiple(entries) => {
for (selector, category) in entries {
table_out.push_str(&format!("#### {category} ({})", selector.as_ref()));
table_out.push('\n');
generate_table(&mut table_out, &selector);
}
if let Some(categories) = linter.categories() {
for LinterCategory(prefix, name, selector) in categories {
table_out.push_str(&format!("#### {name} ({prefix})"));
table_out.push('\n');
generate_table(&mut table_out, selector);
}
} else {
generate_table(&mut table_out, &linter);
}
}