ruff/crates/ruff_dev/src/generate_rules_table.rs
2023-05-11 18:00:00 -04:00

113 lines
3.8 KiB
Rust

//! Generate a Markdown-compatible table of supported lint rules.
use itertools::Itertools;
use strum::IntoEnumIterator;
use ruff::registry::{Linter, Rule, RuleNamespace, UpstreamCategory};
use ruff::settings::options::Options;
use ruff_diagnostics::AutofixKind;
const FIX_SYMBOL: &str = "🛠";
fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>, linter: &Linter) {
table_out.push_str("| Code | Name | Message | Fix |");
table_out.push('\n');
table_out.push_str("| ---- | ---- | ------- | --- |");
table_out.push('\n');
for rule in rules {
let fix_token = match rule.autofixable() {
AutofixKind::None => "",
AutofixKind::Always | AutofixKind::Sometimes => FIX_SYMBOL,
};
let rule_name = rule.as_ref();
#[allow(clippy::or_fun_call)]
table_out.push_str(&format!(
"| {0}{1} {{ #{0}{1} }} | {2} | {3} | {4} |",
linter.common_prefix(),
linter.code_for_rule(rule).unwrap(),
rule.explanation()
.is_some()
.then_some(format_args!("[{rule_name}](rules/{rule_name}.md)"))
.unwrap_or(format_args!("{rule_name}")),
rule.message_formats()[0],
fix_token
));
table_out.push('\n');
}
table_out.push('\n');
}
pub(crate) fn generate() -> String {
// Generate the table string.
let mut table_out = format!("The {FIX_SYMBOL} emoji indicates that a rule is automatically fixable by the `--fix` command-line option.\n\n");
for linter in Linter::iter() {
let codes_csv: String = match linter.common_prefix() {
"" => linter
.upstream_categories()
.unwrap()
.iter()
.map(|UpstreamCategory(prefix, ..)| prefix.short_code())
.join(", "),
prefix => prefix.to_string(),
};
table_out.push_str(&format!("### {} ({codes_csv})", linter.name()));
table_out.push('\n');
table_out.push('\n');
if let Some(url) = linter.url() {
let host = url
.trim_start_matches("https://")
.split('/')
.next()
.unwrap();
table_out.push_str(&format!(
"For more, see [{}]({}) on {}.",
linter.name(),
url,
match host {
"pypi.org" => "PyPI",
"github.com" => "GitHub",
host => panic!(
"unexpected host in URL of {}, expected pypi.org or github.com but found \
{host}",
linter.name()
),
}
));
table_out.push('\n');
table_out.push('\n');
}
if Options::metadata()
.iter()
.any(|(name, _)| name == &linter.name())
{
table_out.push_str(&format!(
"For related settings, see [{}](settings.md#{}).",
linter.name(),
linter.name(),
));
table_out.push('\n');
table_out.push('\n');
}
if let Some(categories) = linter.upstream_categories() {
for UpstreamCategory(prefix, name) in categories {
table_out.push_str(&format!(
"#### {name} ({}{})",
linter.common_prefix(),
prefix.short_code()
));
table_out.push('\n');
table_out.push('\n');
generate_table(&mut table_out, prefix, &linter);
}
} else {
generate_table(&mut table_out, &linter, &linter);
}
}
table_out
}