mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 07:04:37 +00:00
Add proc-macro to derive CheckCodePrefix
(#1656)
IMO a derive macro is a natural way to generate new code, and it reduces the chance of merge conflicts.
This commit is contained in:
parent
2045b739a9
commit
1991d618a3
27 changed files with 530 additions and 4350 deletions
3
.github/workflows/ci.yaml
vendored
3
.github/workflows/ci.yaml
vendored
|
@ -42,9 +42,8 @@ jobs:
|
||||||
- run: cargo build --all --release
|
- run: cargo build --all --release
|
||||||
- run: ./target/release/ruff_dev generate-all
|
- run: ./target/release/ruff_dev generate-all
|
||||||
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo +nightly dev generate-all'."
|
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo +nightly dev generate-all'."
|
||||||
- run: git diff --quiet src/registry_gen.rs || echo "::error file=src/registry_gen.rs::This file is outdated. Run 'cargo +nightly dev generate-all'."
|
|
||||||
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo +nightly dev generate-all'."
|
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo +nightly dev generate-all'."
|
||||||
- run: git diff --exit-code -- README.md src/registry_gen.rs ruff.schema.json
|
- run: git diff --exit-code -- README.md ruff.schema.json
|
||||||
|
|
||||||
cargo-fmt:
|
cargo-fmt:
|
||||||
name: "cargo fmt"
|
name: "cargo fmt"
|
||||||
|
|
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -367,15 +367,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "codegen"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ff61280aed771c3070e7dcc9e050c66f1eb1e3b96431ba66f9f74641d02fc41d"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -1365,9 +1356,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oorandom"
|
name = "oorandom"
|
||||||
|
@ -1945,7 +1936,6 @@ version = "0.0.211"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap 4.0.32",
|
"clap 4.0.32",
|
||||||
"codegen",
|
|
||||||
"itertools",
|
"itertools",
|
||||||
"libcst",
|
"libcst",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
@ -1964,6 +1954,7 @@ dependencies = [
|
||||||
name = "ruff_macros"
|
name = "ruff_macros"
|
||||||
version = "0.0.211"
|
version = "0.0.211"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ruff::flake8_pytest_style::types::{
|
||||||
use ruff::flake8_quotes::settings::Quote;
|
use ruff::flake8_quotes::settings::Quote;
|
||||||
use ruff::flake8_tidy_imports::settings::Strictness;
|
use ruff::flake8_tidy_imports::settings::Strictness;
|
||||||
use ruff::pydocstyle::settings::Convention;
|
use ruff::pydocstyle::settings::Convention;
|
||||||
use ruff::registry_gen::CheckCodePrefix;
|
use ruff::registry::CheckCodePrefix;
|
||||||
use ruff::settings::options::Options;
|
use ruff::settings::options::Options;
|
||||||
use ruff::settings::pyproject::Pyproject;
|
use ruff::settings::pyproject::Pyproject;
|
||||||
use ruff::{
|
use ruff::{
|
||||||
|
@ -345,7 +345,7 @@ mod tests {
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ruff::pydocstyle::settings::Convention;
|
use ruff::pydocstyle::settings::Convention;
|
||||||
use ruff::registry_gen::CheckCodePrefix;
|
use ruff::registry::CheckCodePrefix;
|
||||||
use ruff::settings::options::Options;
|
use ruff::settings::options::Options;
|
||||||
use ruff::settings::pyproject::Pyproject;
|
use ruff::settings::pyproject::Pyproject;
|
||||||
use ruff::{flake8_quotes, pydocstyle};
|
use ruff::{flake8_quotes, pydocstyle};
|
||||||
|
|
|
@ -3,8 +3,7 @@ use std::str::FromStr;
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ruff::registry::PREFIX_REDIRECTS;
|
use ruff::registry::{CheckCodePrefix, PREFIX_REDIRECTS};
|
||||||
use ruff::registry_gen::CheckCodePrefix;
|
|
||||||
use ruff::settings::types::PatternPrefixPair;
|
use ruff::settings::types::PatternPrefixPair;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
@ -201,7 +200,7 @@ pub fn collect_per_file_ignores(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ruff::registry_gen::CheckCodePrefix;
|
use ruff::registry::CheckCodePrefix;
|
||||||
use ruff::settings::types::PatternPrefixPair;
|
use ruff::settings::types::PatternPrefixPair;
|
||||||
|
|
||||||
use crate::parser::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};
|
use crate::parser::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use ruff::registry_gen::CheckCodePrefix;
|
use ruff::registry::CheckCodePrefix;
|
||||||
|
|
||||||
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub enum Plugin {
|
pub enum Plugin {
|
||||||
|
|
|
@ -6,7 +6,6 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0.66" }
|
anyhow = { version = "1.0.66" }
|
||||||
clap = { version = "4.0.1", features = ["derive"] }
|
clap = { version = "4.0.1", features = ["derive"] }
|
||||||
codegen = { version = "0.2.0" }
|
|
||||||
itertools = { version = "0.10.5" }
|
itertools = { version = "0.10.5" }
|
||||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||||
once_cell = { version = "1.16.0" }
|
once_cell = { version = "1.16.0" }
|
||||||
|
|
|
@ -3,10 +3,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{generate_cli_help, generate_json_schema, generate_options, generate_rules_table};
|
||||||
generate_check_code_prefix, generate_cli_help, generate_json_schema, generate_options,
|
|
||||||
generate_rules_table,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
|
@ -16,9 +13,6 @@ pub struct Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(cli: &Cli) -> Result<()> {
|
pub fn main(cli: &Cli) -> Result<()> {
|
||||||
generate_check_code_prefix::main(&generate_check_code_prefix::Cli {
|
|
||||||
dry_run: cli.dry_run,
|
|
||||||
})?;
|
|
||||||
generate_json_schema::main(&generate_json_schema::Cli {
|
generate_json_schema::main(&generate_json_schema::Cli {
|
||||||
dry_run: cli.dry_run,
|
dry_run: cli.dry_run,
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -1,221 +0,0 @@
|
||||||
//! Generate the `CheckCodePrefix` enum.
|
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
|
||||||
use std::fs;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::{Command, Output, Stdio};
|
|
||||||
|
|
||||||
use anyhow::{ensure, Result};
|
|
||||||
use clap::Parser;
|
|
||||||
use codegen::{Scope, Type, Variant};
|
|
||||||
use itertools::Itertools;
|
|
||||||
use ruff::registry::{CheckCode, PREFIX_REDIRECTS};
|
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
|
|
||||||
const ALL: &str = "ALL";
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(author, version, about, long_about = None)]
|
|
||||||
pub struct Cli {
|
|
||||||
/// Write the generated source code to stdout (rather than to
|
|
||||||
/// `src/registry_gen.rs`).
|
|
||||||
#[arg(long)]
|
|
||||||
pub(crate) dry_run: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main(cli: &Cli) -> Result<()> {
|
|
||||||
// Build up a map from prefix to matching CheckCodes.
|
|
||||||
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = BTreeMap::default();
|
|
||||||
for check_code in CheckCode::iter() {
|
|
||||||
let code_str: String = check_code.as_ref().to_string();
|
|
||||||
let code_prefix_len = code_str
|
|
||||||
.chars()
|
|
||||||
.take_while(|char| char.is_alphabetic())
|
|
||||||
.count();
|
|
||||||
let code_suffix_len = code_str.len() - code_prefix_len;
|
|
||||||
for i in 0..=code_suffix_len {
|
|
||||||
let prefix = code_str[..code_prefix_len + i].to_string();
|
|
||||||
prefix_to_codes
|
|
||||||
.entry(prefix)
|
|
||||||
.or_default()
|
|
||||||
.insert(check_code.clone());
|
|
||||||
}
|
|
||||||
prefix_to_codes
|
|
||||||
.entry(ALL.to_string())
|
|
||||||
.or_default()
|
|
||||||
.insert(check_code.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any prefix aliases (e.g., "U" to "UP").
|
|
||||||
for (alias, check_code) in PREFIX_REDIRECTS.iter() {
|
|
||||||
prefix_to_codes.insert(
|
|
||||||
(*alias).to_string(),
|
|
||||||
prefix_to_codes
|
|
||||||
.get(&check_code.as_ref().to_string())
|
|
||||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {alias:?}"))
|
|
||||||
.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
|
|
||||||
// Create the `CheckCodePrefix` definition.
|
|
||||||
let mut gen = scope
|
|
||||||
.new_enum("CheckCodePrefix")
|
|
||||||
.vis("pub")
|
|
||||||
.derive("EnumString")
|
|
||||||
.derive("AsRefStr")
|
|
||||||
.derive("Debug")
|
|
||||||
.derive("PartialEq")
|
|
||||||
.derive("Eq")
|
|
||||||
.derive("PartialOrd")
|
|
||||||
.derive("Ord")
|
|
||||||
.derive("Clone")
|
|
||||||
.derive("Serialize")
|
|
||||||
.derive("Deserialize")
|
|
||||||
.derive("JsonSchema");
|
|
||||||
for prefix in prefix_to_codes.keys() {
|
|
||||||
gen = gen.push_variant(Variant::new(prefix.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the `SuffixLength` definition.
|
|
||||||
scope
|
|
||||||
.new_enum("SuffixLength")
|
|
||||||
.vis("pub")
|
|
||||||
.derive("PartialEq")
|
|
||||||
.derive("Eq")
|
|
||||||
.derive("PartialOrd")
|
|
||||||
.derive("Ord")
|
|
||||||
.push_variant(Variant::new("None"))
|
|
||||||
.push_variant(Variant::new("Zero"))
|
|
||||||
.push_variant(Variant::new("One"))
|
|
||||||
.push_variant(Variant::new("Two"))
|
|
||||||
.push_variant(Variant::new("Three"))
|
|
||||||
.push_variant(Variant::new("Four"));
|
|
||||||
|
|
||||||
// Create the `match` statement, to map from definition to relevant codes.
|
|
||||||
let mut gen = scope
|
|
||||||
.new_impl("CheckCodePrefix")
|
|
||||||
.new_fn("codes")
|
|
||||||
.arg_ref_self()
|
|
||||||
.ret(Type::new("Vec<CheckCode>"))
|
|
||||||
.vis("pub")
|
|
||||||
.line("#[allow(clippy::match_same_arms)]")
|
|
||||||
.line("match self {");
|
|
||||||
for (prefix, codes) in &prefix_to_codes {
|
|
||||||
if let Some(target) = PREFIX_REDIRECTS.get(&prefix.as_str()) {
|
|
||||||
gen = gen.line(format!(
|
|
||||||
"CheckCodePrefix::{prefix} => {{ one_time_warning!(\"{{}}{{}} {{}}\", \
|
|
||||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
|
|
||||||
`{}`\".bold()); \n vec![{}] }}",
|
|
||||||
prefix,
|
|
||||||
target.as_ref(),
|
|
||||||
codes
|
|
||||||
.iter()
|
|
||||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
|
||||||
.join(", ")
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
gen = gen.line(format!(
|
|
||||||
"CheckCodePrefix::{prefix} => vec![{}],",
|
|
||||||
codes
|
|
||||||
.iter()
|
|
||||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
|
||||||
.join(", ")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gen.line("}");
|
|
||||||
|
|
||||||
// Create the `match` statement, to map from definition to specificity.
|
|
||||||
let mut gen = scope
|
|
||||||
.new_impl("CheckCodePrefix")
|
|
||||||
.new_fn("specificity")
|
|
||||||
.arg_ref_self()
|
|
||||||
.ret(Type::new("SuffixLength"))
|
|
||||||
.vis("pub")
|
|
||||||
.line("#[allow(clippy::match_same_arms)]")
|
|
||||||
.line("match self {");
|
|
||||||
for prefix in prefix_to_codes.keys() {
|
|
||||||
let specificity = if prefix == "ALL" {
|
|
||||||
"None"
|
|
||||||
} else {
|
|
||||||
let num_numeric = prefix.chars().filter(|char| char.is_numeric()).count();
|
|
||||||
match num_numeric {
|
|
||||||
0 => "Zero",
|
|
||||||
1 => "One",
|
|
||||||
2 => "Two",
|
|
||||||
3 => "Three",
|
|
||||||
4 => "Four",
|
|
||||||
_ => panic!("Invalid prefix: {prefix}"),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
gen = gen.line(format!(
|
|
||||||
"CheckCodePrefix::{prefix} => SuffixLength::{specificity},"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
gen.line("}");
|
|
||||||
|
|
||||||
// Construct the output contents.
|
|
||||||
let mut output = String::new();
|
|
||||||
output
|
|
||||||
.push_str("//! File automatically generated by `examples/generate_check_code_prefix.rs`.");
|
|
||||||
output.push('\n');
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str("use colored::Colorize;");
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str("use schemars::JsonSchema;");
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str("use serde::{Deserialize, Serialize};");
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str("use strum_macros::{AsRefStr, EnumString};");
|
|
||||||
output.push('\n');
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str("use crate::registry::CheckCode;");
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str("use crate::one_time_warning;");
|
|
||||||
output.push('\n');
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str(&scope.to_string());
|
|
||||||
output.push('\n');
|
|
||||||
output.push('\n');
|
|
||||||
|
|
||||||
// Add the list of output categories (not generated).
|
|
||||||
output.push_str("pub const CATEGORIES: &[CheckCodePrefix] = &[");
|
|
||||||
output.push('\n');
|
|
||||||
for prefix in prefix_to_codes.keys() {
|
|
||||||
if prefix.chars().all(char::is_alphabetic)
|
|
||||||
&& !PREFIX_REDIRECTS.contains_key(&prefix.as_str())
|
|
||||||
{
|
|
||||||
output.push_str(&format!("CheckCodePrefix::{prefix},"));
|
|
||||||
output.push('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.push_str("];");
|
|
||||||
output.push('\n');
|
|
||||||
output.push('\n');
|
|
||||||
|
|
||||||
let rustfmt = Command::new("rustfmt")
|
|
||||||
.stdin(Stdio::piped())
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.spawn()?;
|
|
||||||
write!(rustfmt.stdin.as_ref().unwrap(), "{output}")?;
|
|
||||||
let Output { status, stdout, .. } = rustfmt.wait_with_output()?;
|
|
||||||
ensure!(status.success(), "rustfmt failed with {status}");
|
|
||||||
|
|
||||||
// Write the output to `src/registry_gen.rs` (or stdout).
|
|
||||||
if cli.dry_run {
|
|
||||||
println!("{}", String::from_utf8(stdout)?);
|
|
||||||
} else {
|
|
||||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
||||||
.parent()
|
|
||||||
.expect("Failed to find root directory")
|
|
||||||
.join("src/registry_gen.rs");
|
|
||||||
if fs::read(&file).map_or(true, |old| old != stdout) {
|
|
||||||
fs::write(&file, stdout)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -12,7 +12,6 @@
|
||||||
)]
|
)]
|
||||||
|
|
||||||
pub mod generate_all;
|
pub mod generate_all;
|
||||||
pub mod generate_check_code_prefix;
|
|
||||||
pub mod generate_cli_help;
|
pub mod generate_cli_help;
|
||||||
pub mod generate_json_schema;
|
pub mod generate_json_schema;
|
||||||
pub mod generate_options;
|
pub mod generate_options;
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use ruff_dev::{
|
use ruff_dev::{
|
||||||
generate_all, generate_check_code_prefix, generate_cli_help, generate_json_schema,
|
generate_all, generate_cli_help, generate_json_schema, generate_options, generate_rules_table,
|
||||||
generate_options, generate_rules_table, print_ast, print_cst, print_tokens, round_trip,
|
print_ast, print_cst, print_tokens, round_trip,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -30,8 +30,6 @@ struct Cli {
|
||||||
enum Commands {
|
enum Commands {
|
||||||
/// Run all code and documentation generation steps.
|
/// Run all code and documentation generation steps.
|
||||||
GenerateAll(generate_all::Cli),
|
GenerateAll(generate_all::Cli),
|
||||||
/// Generate the `CheckCodePrefix` enum.
|
|
||||||
GenerateCheckCodePrefix(generate_check_code_prefix::Cli),
|
|
||||||
/// Generate JSON schema for the TOML configuration file.
|
/// Generate JSON schema for the TOML configuration file.
|
||||||
GenerateJSONSchema(generate_json_schema::Cli),
|
GenerateJSONSchema(generate_json_schema::Cli),
|
||||||
/// Generate a Markdown-compatible table of supported lint rules.
|
/// Generate a Markdown-compatible table of supported lint rules.
|
||||||
|
@ -54,7 +52,6 @@ fn main() -> Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Commands::GenerateAll(args) => generate_all::main(args)?,
|
Commands::GenerateAll(args) => generate_all::main(args)?,
|
||||||
Commands::GenerateCheckCodePrefix(args) => generate_check_code_prefix::main(args)?,
|
|
||||||
Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?,
|
Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?,
|
||||||
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
|
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
|
||||||
Commands::GenerateOptions(args) => generate_options::main(args)?,
|
Commands::GenerateOptions(args) => generate_options::main(args)?,
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
once_cell = { version = "1.17.0" }
|
||||||
proc-macro2 = { version = "1.0.47" }
|
proc-macro2 = { version = "1.0.47" }
|
||||||
quote = { version = "1.0.21" }
|
quote = { version = "1.0.21" }
|
||||||
syn = { version = "1.0.103", features = ["derive", "parsing"] }
|
syn = { version = "1.0.103", features = ["derive", "parsing"] }
|
||||||
|
|
289
ruff_macros/src/check_code_prefix.rs
Normal file
289
ruff_macros/src/check_code_prefix.rs
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use proc_macro2::Span;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::punctuated::Punctuated;
|
||||||
|
use syn::token::Comma;
|
||||||
|
use syn::{DataEnum, DeriveInput, Ident, Variant};
|
||||||
|
|
||||||
|
const ALL: &str = "ALL";
|
||||||
|
|
||||||
|
/// A hash map from deprecated `CheckCodePrefix` to latest `CheckCodePrefix`.
|
||||||
|
pub static PREFIX_REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||||
|
HashMap::from_iter([
|
||||||
|
// TODO(charlie): Remove by 2023-01-01.
|
||||||
|
("U001", "UP001"),
|
||||||
|
("U003", "UP003"),
|
||||||
|
("U004", "UP004"),
|
||||||
|
("U005", "UP005"),
|
||||||
|
("U006", "UP006"),
|
||||||
|
("U007", "UP007"),
|
||||||
|
("U008", "UP008"),
|
||||||
|
("U009", "UP009"),
|
||||||
|
("U010", "UP010"),
|
||||||
|
("U011", "UP011"),
|
||||||
|
("U012", "UP012"),
|
||||||
|
("U013", "UP013"),
|
||||||
|
("U014", "UP014"),
|
||||||
|
("U015", "UP015"),
|
||||||
|
("U016", "UP016"),
|
||||||
|
("U017", "UP017"),
|
||||||
|
("U019", "UP019"),
|
||||||
|
// TODO(charlie): Remove by 2023-02-01.
|
||||||
|
("I252", "TID252"),
|
||||||
|
("M001", "RUF100"),
|
||||||
|
// TODO(charlie): Remove by 2023-02-01.
|
||||||
|
("PDV002", "PD002"),
|
||||||
|
("PDV003", "PD003"),
|
||||||
|
("PDV004", "PD004"),
|
||||||
|
("PDV007", "PD007"),
|
||||||
|
("PDV008", "PD008"),
|
||||||
|
("PDV009", "PD009"),
|
||||||
|
("PDV010", "PD010"),
|
||||||
|
("PDV011", "PD011"),
|
||||||
|
("PDV012", "PD012"),
|
||||||
|
("PDV013", "PD013"),
|
||||||
|
("PDV015", "PD015"),
|
||||||
|
("PDV901", "PD901"),
|
||||||
|
// TODO(charlie): Remove by 2023-02-01.
|
||||||
|
("R501", "RET501"),
|
||||||
|
("R502", "RET502"),
|
||||||
|
("R503", "RET503"),
|
||||||
|
("R504", "RET504"),
|
||||||
|
("R505", "RET505"),
|
||||||
|
("R506", "RET506"),
|
||||||
|
("R507", "RET507"),
|
||||||
|
("R508", "RET508"),
|
||||||
|
("IC001", "ICN001"),
|
||||||
|
("IC002", "ICN001"),
|
||||||
|
("IC003", "ICN001"),
|
||||||
|
("IC004", "ICN001"),
|
||||||
|
// TODO(charlie): Remove by 2023-01-01.
|
||||||
|
("U", "UP"),
|
||||||
|
("U0", "UP0"),
|
||||||
|
("U00", "UP00"),
|
||||||
|
("U01", "UP01"),
|
||||||
|
// TODO(charlie): Remove by 2023-02-01.
|
||||||
|
("I2", "TID2"),
|
||||||
|
("I25", "TID25"),
|
||||||
|
("M", "RUF100"),
|
||||||
|
("M0", "RUF100"),
|
||||||
|
// TODO(charlie): Remove by 2023-02-01.
|
||||||
|
("PDV", "PD"),
|
||||||
|
("PDV0", "PD0"),
|
||||||
|
("PDV01", "PD01"),
|
||||||
|
("PDV9", "PD9"),
|
||||||
|
("PDV90", "PD90"),
|
||||||
|
// TODO(charlie): Remove by 2023-02-01.
|
||||||
|
("R", "RET"),
|
||||||
|
("R5", "RET5"),
|
||||||
|
("R50", "RET50"),
|
||||||
|
// TODO(charlie): Remove by 2023-02-01.
|
||||||
|
("IC", "ICN"),
|
||||||
|
("IC0", "ICN0"),
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||||
|
let DeriveInput { ident, data, .. } = input;
|
||||||
|
let syn::Data::Enum(DataEnum { variants, .. }) = data else {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
ident.span(),
|
||||||
|
"Can only derive `CheckCodePrefix` from enums.",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let prefix_ident = Ident::new(&format!("{ident}Prefix"), ident.span());
|
||||||
|
let prefix = expand(&ident, &prefix_ident, &variants);
|
||||||
|
let expanded = quote! {
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum SuffixLength {
|
||||||
|
None,
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
Four,
|
||||||
|
}
|
||||||
|
|
||||||
|
#prefix
|
||||||
|
};
|
||||||
|
Ok(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand(
|
||||||
|
ident: &Ident,
|
||||||
|
prefix_ident: &Ident,
|
||||||
|
variants: &Punctuated<Variant, Comma>,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
// Build up a map from prefix to matching CheckCodes.
|
||||||
|
let mut prefix_to_codes: BTreeMap<Ident, BTreeSet<String>> = BTreeMap::default();
|
||||||
|
for variant in variants {
|
||||||
|
let span = variant.ident.span();
|
||||||
|
let code_str = variant.ident.to_string();
|
||||||
|
let code_prefix_len = code_str
|
||||||
|
.chars()
|
||||||
|
.take_while(|char| char.is_alphabetic())
|
||||||
|
.count();
|
||||||
|
let code_suffix_len = code_str.len() - code_prefix_len;
|
||||||
|
for i in 0..=code_suffix_len {
|
||||||
|
let prefix = code_str[..code_prefix_len + i].to_string();
|
||||||
|
prefix_to_codes
|
||||||
|
.entry(Ident::new(&prefix, span))
|
||||||
|
.or_default()
|
||||||
|
.insert(code_str.clone());
|
||||||
|
}
|
||||||
|
prefix_to_codes
|
||||||
|
.entry(Ident::new(ALL, span))
|
||||||
|
.or_default()
|
||||||
|
.insert(code_str.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any prefix aliases (e.g., "U" to "UP").
|
||||||
|
for (alias, check_code) in PREFIX_REDIRECTS.iter() {
|
||||||
|
prefix_to_codes.insert(
|
||||||
|
Ident::new(alias, Span::call_site()),
|
||||||
|
prefix_to_codes
|
||||||
|
.get(&Ident::new(check_code, Span::call_site()))
|
||||||
|
.unwrap_or_else(|| panic!("Unknown CheckCode: {alias:?}"))
|
||||||
|
.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefix_variants = prefix_to_codes.keys().map(|prefix| {
|
||||||
|
quote! {
|
||||||
|
#prefix
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let prefix_impl = generate_impls(ident, prefix_ident, &prefix_to_codes);
|
||||||
|
|
||||||
|
let prefix_redirects = PREFIX_REDIRECTS.iter().map(|(alias, check_code)| {
|
||||||
|
let code = Ident::new(check_code, Span::call_site());
|
||||||
|
quote! {
|
||||||
|
(#alias, #prefix_ident::#code)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[derive(
|
||||||
|
::strum_macros::EnumString,
|
||||||
|
::strum_macros::AsRefStr,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Clone,
|
||||||
|
::serde::Serialize,
|
||||||
|
::serde::Deserialize,
|
||||||
|
::schemars::JsonSchema,
|
||||||
|
)]
|
||||||
|
pub enum #prefix_ident {
|
||||||
|
#(#prefix_variants,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
#prefix_impl
|
||||||
|
|
||||||
|
/// A hash map from deprecated `CheckCodePrefix` to latest `CheckCodePrefix`.
|
||||||
|
pub static PREFIX_REDIRECTS: ::once_cell::sync::Lazy<::rustc_hash::FxHashMap<&'static str, #prefix_ident>> = ::once_cell::sync::Lazy::new(|| {
|
||||||
|
::rustc_hash::FxHashMap::from_iter([
|
||||||
|
#(#prefix_redirects),*
|
||||||
|
])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_impls(
|
||||||
|
ident: &Ident,
|
||||||
|
prefix_ident: &Ident,
|
||||||
|
prefix_to_codes: &BTreeMap<Ident, BTreeSet<String>>,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let codes_match_arms = prefix_to_codes.iter().map(|(prefix, codes)| {
|
||||||
|
let codes = codes.iter().map(|code| {
|
||||||
|
let code = Ident::new(code, Span::call_site());
|
||||||
|
quote! {
|
||||||
|
#ident::#code
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let prefix_str = prefix.to_string();
|
||||||
|
if let Some(target) = PREFIX_REDIRECTS.get(prefix_str.as_str()) {
|
||||||
|
quote! {
|
||||||
|
#prefix_ident::#prefix => {
|
||||||
|
crate::one_time_warning!(
|
||||||
|
"{}{} {}",
|
||||||
|
"warning".yellow().bold(),
|
||||||
|
":".bold(),
|
||||||
|
format!("`{}` has been remapped to `{}`", #prefix_str, #target).bold()
|
||||||
|
);
|
||||||
|
vec![#(#codes),*]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#prefix_ident::#prefix => vec![#(#codes),*],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let specificity_match_arms = prefix_to_codes.keys().map(|prefix| {
|
||||||
|
if *prefix == ALL {
|
||||||
|
quote! {
|
||||||
|
#prefix_ident::#prefix => SuffixLength::None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let num_numeric = prefix
|
||||||
|
.to_string()
|
||||||
|
.chars()
|
||||||
|
.filter(|char| char.is_numeric())
|
||||||
|
.count();
|
||||||
|
let suffix_len = match num_numeric {
|
||||||
|
0 => quote! { SuffixLength::Zero },
|
||||||
|
1 => quote! { SuffixLength::One },
|
||||||
|
2 => quote! { SuffixLength::Two },
|
||||||
|
3 => quote! { SuffixLength::Three },
|
||||||
|
4 => quote! { SuffixLength::Four },
|
||||||
|
_ => panic!("Invalid prefix: {prefix}"),
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
#prefix_ident::#prefix => #suffix_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let categories = prefix_to_codes.keys().map(|prefix| {
|
||||||
|
let prefix_str = prefix.to_string();
|
||||||
|
if prefix_str.chars().all(char::is_alphabetic)
|
||||||
|
&& !PREFIX_REDIRECTS.contains_key(&prefix_str.as_str())
|
||||||
|
{
|
||||||
|
quote! {
|
||||||
|
#prefix_ident::#prefix,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #prefix_ident {
|
||||||
|
pub fn codes(&self) -> Vec<#ident> {
|
||||||
|
use colored::Colorize;
|
||||||
|
|
||||||
|
#[allow(clippy::match_same_arms)]
|
||||||
|
match self {
|
||||||
|
#(#codes_match_arms)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn specificity(&self) -> SuffixLength {
|
||||||
|
#[allow(clippy::match_same_arms)]
|
||||||
|
match self {
|
||||||
|
#(#specificity_match_arms)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CATEGORIES: &[#prefix_ident] = &[#(#categories)*];
|
||||||
|
}
|
||||||
|
}
|
203
ruff_macros/src/config.rs
Normal file
203
ruff_macros/src/config.rs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
use quote::{quote, quote_spanned};
|
||||||
|
use syn::parse::{Parse, ParseStream};
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
use syn::token::Comma;
|
||||||
|
use syn::{
|
||||||
|
AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput, Field, Fields, Lit,
|
||||||
|
LitStr, Path, PathArguments, PathSegment, Token, Type, TypePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||||
|
let DeriveInput { ident, data, .. } = input;
|
||||||
|
|
||||||
|
match data {
|
||||||
|
Data::Struct(DataStruct {
|
||||||
|
fields: Fields::Named(fields),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let mut output = vec![];
|
||||||
|
|
||||||
|
for field in fields.named.iter() {
|
||||||
|
let docs: Vec<&Attribute> = field
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|attr| attr.path.is_ident("doc"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if docs.is_empty() {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
field.span(),
|
||||||
|
"Missing documentation for field",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(attr) = field.attrs.iter().find(|attr| attr.path.is_ident("option")) {
|
||||||
|
output.push(handle_option(field, attr, docs)?);
|
||||||
|
};
|
||||||
|
|
||||||
|
if field
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.any(|attr| attr.path.is_ident("option_group"))
|
||||||
|
{
|
||||||
|
output.push(handle_option_group(field)?);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
use crate::settings::options_base::{OptionEntry, OptionField, OptionGroup, ConfigurationOptions};
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl ConfigurationOptions for #ident {
|
||||||
|
fn get_available_options() -> Vec<OptionEntry> {
|
||||||
|
vec![#(#output),*]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(syn::Error::new(
|
||||||
|
ident.span(),
|
||||||
|
"Can only derive ConfigurationOptions from structs with named fields.",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For a field with type `Option<Foobar>` where `Foobar` itself is a struct
|
||||||
|
/// deriving `ConfigurationOptions`, create code that calls retrieves options
|
||||||
|
/// from that group: `Foobar::get_available_options()`
|
||||||
|
fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
|
||||||
|
let ident = field
|
||||||
|
.ident
|
||||||
|
.as_ref()
|
||||||
|
.expect("Expected to handle named fields");
|
||||||
|
|
||||||
|
match &field.ty {
|
||||||
|
Type::Path(TypePath {
|
||||||
|
path: Path { segments, .. },
|
||||||
|
..
|
||||||
|
}) => match segments.first() {
|
||||||
|
Some(PathSegment {
|
||||||
|
ident: type_ident,
|
||||||
|
arguments:
|
||||||
|
PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
|
||||||
|
..
|
||||||
|
}) if type_ident == "Option" => {
|
||||||
|
let path = &args[0];
|
||||||
|
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
|
||||||
|
|
||||||
|
Ok(quote_spanned!(
|
||||||
|
ident.span() => OptionEntry::Group(OptionGroup {
|
||||||
|
name: #kebab_name,
|
||||||
|
fields: #path::get_available_options(),
|
||||||
|
})
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => Err(syn::Error::new(
|
||||||
|
ident.span(),
|
||||||
|
"Expected `Option<_>` as type.",
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
_ => Err(syn::Error::new(ident.span(), "Expected type.")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a `doc` attribute into it a string literal.
|
||||||
|
fn parse_doc(doc: &Attribute) -> syn::Result<String> {
|
||||||
|
let doc = doc
|
||||||
|
.parse_meta()
|
||||||
|
.map_err(|e| syn::Error::new(doc.span(), e))?;
|
||||||
|
|
||||||
|
match doc {
|
||||||
|
syn::Meta::NameValue(syn::MetaNameValue {
|
||||||
|
lit: Lit::Str(lit_str),
|
||||||
|
..
|
||||||
|
}) => Ok(lit_str.value()),
|
||||||
|
_ => Err(syn::Error::new(doc.span(), "Expected doc attribute.")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse an `#[option(doc="...", default="...", value_type="...",
|
||||||
|
/// example="...")]` attribute and return data in the form of an `OptionField`.
|
||||||
|
fn handle_option(
|
||||||
|
field: &Field,
|
||||||
|
attr: &Attribute,
|
||||||
|
docs: Vec<&Attribute>,
|
||||||
|
) -> syn::Result<proc_macro2::TokenStream> {
|
||||||
|
// Convert the list of `doc` attributes into a single string.
|
||||||
|
let doc = textwrap::dedent(
|
||||||
|
&docs
|
||||||
|
.into_iter()
|
||||||
|
.map(parse_doc)
|
||||||
|
.collect::<syn::Result<Vec<_>>>()?
|
||||||
|
.join("\n"),
|
||||||
|
)
|
||||||
|
.trim_matches('\n')
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let ident = field
|
||||||
|
.ident
|
||||||
|
.as_ref()
|
||||||
|
.expect("Expected to handle named fields");
|
||||||
|
|
||||||
|
let FieldAttributes {
|
||||||
|
default,
|
||||||
|
value_type,
|
||||||
|
example,
|
||||||
|
..
|
||||||
|
} = attr.parse_args::<FieldAttributes>()?;
|
||||||
|
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
|
||||||
|
|
||||||
|
Ok(quote_spanned!(
|
||||||
|
ident.span() => OptionEntry::Field(OptionField {
|
||||||
|
name: #kebab_name,
|
||||||
|
doc: &#doc,
|
||||||
|
default: &#default,
|
||||||
|
value_type: &#value_type,
|
||||||
|
example: &#example,
|
||||||
|
})
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FieldAttributes {
|
||||||
|
default: String,
|
||||||
|
value_type: String,
|
||||||
|
example: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for FieldAttributes {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let default = _parse_key_value(input, "default")?;
|
||||||
|
input.parse::<Comma>()?;
|
||||||
|
let value_type = _parse_key_value(input, "value_type")?;
|
||||||
|
input.parse::<Comma>()?;
|
||||||
|
let example = _parse_key_value(input, "example")?;
|
||||||
|
if !input.is_empty() {
|
||||||
|
input.parse::<Comma>()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(FieldAttributes {
|
||||||
|
default,
|
||||||
|
value_type,
|
||||||
|
example: textwrap::dedent(&example).trim_matches('\n').to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_key_value(input: ParseStream, name: &str) -> syn::Result<String> {
|
||||||
|
let ident: proc_macro2::Ident = input.parse()?;
|
||||||
|
if ident != name {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
ident.span(),
|
||||||
|
format!("Expected `{name}` name"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
let value: Lit = input.parse()?;
|
||||||
|
|
||||||
|
match &value {
|
||||||
|
Lit::Str(v) => Ok(v.value()),
|
||||||
|
_ => Err(syn::Error::new(value.span(), "Expected literal string")),
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,215 +11,25 @@
|
||||||
clippy::too_many_lines
|
clippy::too_many_lines
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use quote::{quote, quote_spanned};
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
use syn::parse::{Parse, ParseStream};
|
|
||||||
use syn::spanned::Spanned;
|
mod check_code_prefix;
|
||||||
use syn::token::Comma;
|
mod config;
|
||||||
use syn::{
|
|
||||||
parse_macro_input, AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput,
|
|
||||||
Field, Fields, Lit, LitStr, Path, PathArguments, PathSegment, Token, Type, TypePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[proc_macro_derive(ConfigurationOptions, attributes(option, doc, option_group))]
|
#[proc_macro_derive(ConfigurationOptions, attributes(option, doc, option_group))]
|
||||||
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
derive_impl(input)
|
config::derive_impl(input)
|
||||||
.unwrap_or_else(syn::Error::into_compile_error)
|
.unwrap_or_else(syn::Error::into_compile_error)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
#[proc_macro_derive(CheckCodePrefix)]
|
||||||
let DeriveInput { ident, data, .. } = input;
|
pub fn derive_check_code_prefix(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
match data {
|
check_code_prefix::derive_impl(input)
|
||||||
Data::Struct(DataStruct {
|
.unwrap_or_else(syn::Error::into_compile_error)
|
||||||
fields: Fields::Named(fields),
|
.into()
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let mut output = vec![];
|
|
||||||
|
|
||||||
for field in fields.named.iter() {
|
|
||||||
let docs: Vec<&Attribute> = field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.filter(|attr| attr.path.is_ident("doc"))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if docs.is_empty() {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
field.span(),
|
|
||||||
"Missing documentation for field",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(attr) = field.attrs.iter().find(|attr| attr.path.is_ident("option")) {
|
|
||||||
output.push(handle_option(field, attr, docs)?);
|
|
||||||
};
|
|
||||||
|
|
||||||
if field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.any(|attr| attr.path.is_ident("option_group"))
|
|
||||||
{
|
|
||||||
output.push(handle_option_group(field)?);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(quote! {
|
|
||||||
use crate::settings::options_base::{OptionEntry, OptionField, OptionGroup, ConfigurationOptions};
|
|
||||||
|
|
||||||
#[automatically_derived]
|
|
||||||
impl ConfigurationOptions for #ident {
|
|
||||||
fn get_available_options() -> Vec<OptionEntry> {
|
|
||||||
vec![#(#output),*]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Err(syn::Error::new(
|
|
||||||
ident.span(),
|
|
||||||
"Can only derive ConfigurationOptions from structs with named fields.",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For a field with type `Option<Foobar>` where `Foobar` itself is a struct
|
|
||||||
/// deriving `ConfigurationOptions`, create code that calls retrieves options
|
|
||||||
/// from that group: `Foobar::get_available_options()`
|
|
||||||
fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
|
|
||||||
let ident = field
|
|
||||||
.ident
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected to handle named fields");
|
|
||||||
|
|
||||||
match &field.ty {
|
|
||||||
Type::Path(TypePath {
|
|
||||||
path: Path { segments, .. },
|
|
||||||
..
|
|
||||||
}) => match segments.first() {
|
|
||||||
Some(PathSegment {
|
|
||||||
ident: type_ident,
|
|
||||||
arguments:
|
|
||||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
|
|
||||||
..
|
|
||||||
}) if type_ident == "Option" => {
|
|
||||||
let path = &args[0];
|
|
||||||
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
|
|
||||||
|
|
||||||
Ok(quote_spanned!(
|
|
||||||
ident.span() => OptionEntry::Group(OptionGroup {
|
|
||||||
name: #kebab_name,
|
|
||||||
fields: #path::get_available_options(),
|
|
||||||
})
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => Err(syn::Error::new(
|
|
||||||
ident.span(),
|
|
||||||
"Expected `Option<_>` as type.",
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
_ => Err(syn::Error::new(ident.span(), "Expected type.")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a `doc` attribute into it a string literal.
|
|
||||||
fn parse_doc(doc: &Attribute) -> syn::Result<String> {
|
|
||||||
let doc = doc
|
|
||||||
.parse_meta()
|
|
||||||
.map_err(|e| syn::Error::new(doc.span(), e))?;
|
|
||||||
|
|
||||||
match doc {
|
|
||||||
syn::Meta::NameValue(syn::MetaNameValue {
|
|
||||||
lit: Lit::Str(lit_str),
|
|
||||||
..
|
|
||||||
}) => Ok(lit_str.value()),
|
|
||||||
_ => Err(syn::Error::new(doc.span(), "Expected doc attribute.")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse an `#[option(doc="...", default="...", value_type="...",
|
|
||||||
/// example="...")]` attribute and return data in the form of an `OptionField`.
|
|
||||||
fn handle_option(
|
|
||||||
field: &Field,
|
|
||||||
attr: &Attribute,
|
|
||||||
docs: Vec<&Attribute>,
|
|
||||||
) -> syn::Result<proc_macro2::TokenStream> {
|
|
||||||
// Convert the list of `doc` attributes into a single string.
|
|
||||||
let doc = textwrap::dedent(
|
|
||||||
&docs
|
|
||||||
.into_iter()
|
|
||||||
.map(parse_doc)
|
|
||||||
.collect::<syn::Result<Vec<_>>>()?
|
|
||||||
.join("\n"),
|
|
||||||
)
|
|
||||||
.trim_matches('\n')
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let ident = field
|
|
||||||
.ident
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected to handle named fields");
|
|
||||||
|
|
||||||
let FieldAttributes {
|
|
||||||
default,
|
|
||||||
value_type,
|
|
||||||
example,
|
|
||||||
..
|
|
||||||
} = attr.parse_args::<FieldAttributes>()?;
|
|
||||||
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
|
|
||||||
|
|
||||||
Ok(quote_spanned!(
|
|
||||||
ident.span() => OptionEntry::Field(OptionField {
|
|
||||||
name: #kebab_name,
|
|
||||||
doc: &#doc,
|
|
||||||
default: &#default,
|
|
||||||
value_type: &#value_type,
|
|
||||||
example: &#example,
|
|
||||||
})
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct FieldAttributes {
|
|
||||||
default: String,
|
|
||||||
value_type: String,
|
|
||||||
example: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for FieldAttributes {
|
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
||||||
let default = _parse_key_value(input, "default")?;
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
let value_type = _parse_key_value(input, "value_type")?;
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
let example = _parse_key_value(input, "example")?;
|
|
||||||
if !input.is_empty() {
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(FieldAttributes {
|
|
||||||
default,
|
|
||||||
value_type,
|
|
||||||
example: textwrap::dedent(&example).trim_matches('\n').to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _parse_key_value(input: ParseStream, name: &str) -> syn::Result<String> {
|
|
||||||
let ident: proc_macro2::Ident = input.parse()?;
|
|
||||||
if ident != name {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
ident.span(),
|
|
||||||
format!("Expected `{name}` name"),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
input.parse::<Token![=]>()?;
|
|
||||||
let value: Lit = input.parse()?;
|
|
||||||
|
|
||||||
match &value {
|
|
||||||
Lit::Str(v) => Ok(v.value()),
|
|
||||||
_ => Err(syn::Error::new(value.span(), "Expected literal string")),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,7 @@ use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::fs;
|
use crate::fs;
|
||||||
use crate::logging::LogLevel;
|
use crate::logging::LogLevel;
|
||||||
use crate::registry::CheckCode;
|
use crate::registry::{CheckCode, CheckCodePrefix};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
|
||||||
use crate::settings::types::{
|
use crate::settings::types::{
|
||||||
FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion, SerializationFormat,
|
FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion, SerializationFormat,
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,7 +66,6 @@ mod pylint;
|
||||||
mod python;
|
mod python;
|
||||||
mod pyupgrade;
|
mod pyupgrade;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub mod registry_gen;
|
|
||||||
pub mod resolver;
|
pub mod resolver;
|
||||||
mod ruff;
|
mod ruff;
|
||||||
mod rustpython_helpers;
|
mod rustpython_helpers;
|
||||||
|
|
|
@ -6,8 +6,7 @@ use serde::Serialize;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use crate::linter::check_path;
|
use crate::linter::check_path;
|
||||||
use crate::registry::CheckCode;
|
use crate::registry::{CheckCode, CheckCodePrefix};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
|
||||||
use crate::rustpython_helpers::tokenize;
|
use crate::rustpython_helpers::tokenize;
|
||||||
use crate::settings::configuration::Configuration;
|
use crate::settings::configuration::Configuration;
|
||||||
use crate::settings::options::Options;
|
use crate::settings::options::Options;
|
||||||
|
|
|
@ -10,8 +10,7 @@ mod tests {
|
||||||
use textwrap::dedent;
|
use textwrap::dedent;
|
||||||
|
|
||||||
use crate::linter::check_path;
|
use crate::linter::check_path;
|
||||||
use crate::registry::CheckCode;
|
use crate::registry::{CheckCode, CheckCodePrefix};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
|
||||||
use crate::settings::flags;
|
use crate::settings::flags;
|
||||||
use crate::source_code_locator::SourceCodeLocator;
|
use crate::source_code_locator::SourceCodeLocator;
|
||||||
use crate::source_code_style::SourceCodeStyleDetector;
|
use crate::source_code_style::SourceCodeStyleDetector;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ruff_macros::ConfigurationOptions;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
use crate::registry::CheckCodePrefix;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
|
|
@ -16,8 +16,7 @@ mod tests {
|
||||||
use textwrap::dedent;
|
use textwrap::dedent;
|
||||||
|
|
||||||
use crate::linter::{check_path, test_path};
|
use crate::linter::{check_path, test_path};
|
||||||
use crate::registry::CheckCode;
|
use crate::registry::{CheckCode, CheckCodePrefix};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
|
||||||
use crate::settings::flags;
|
use crate::settings::flags;
|
||||||
use crate::source_code_locator::SourceCodeLocator;
|
use crate::source_code_locator::SourceCodeLocator;
|
||||||
use crate::source_code_style::SourceCodeStyleDetector;
|
use crate::source_code_style::SourceCodeStyleDetector;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::fmt;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use ruff_macros::CheckCodePrefix;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use rustpython_ast::Cmpop;
|
use rustpython_ast::Cmpop;
|
||||||
use rustpython_parser::ast::Location;
|
use rustpython_parser::ast::Location;
|
||||||
|
@ -19,10 +20,10 @@ use crate::flake8_pytest_style::types::{
|
||||||
use crate::flake8_quotes::settings::Quote;
|
use crate::flake8_quotes::settings::Quote;
|
||||||
use crate::flake8_tidy_imports::settings::Strictness;
|
use crate::flake8_tidy_imports::settings::Strictness;
|
||||||
use crate::pyupgrade::types::Primitive;
|
use crate::pyupgrade::types::Primitive;
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
AsRefStr,
|
AsRefStr,
|
||||||
|
CheckCodePrefix,
|
||||||
EnumIter,
|
EnumIter,
|
||||||
EnumString,
|
EnumString,
|
||||||
Debug,
|
Debug,
|
||||||
|
@ -4026,82 +4027,6 @@ pub const INCOMPATIBLE_CODES: &[(CheckCode, CheckCode, &str)] = &[(
|
||||||
Consider adding `D203` to `ignore`.",
|
Consider adding `D203` to `ignore`.",
|
||||||
)];
|
)];
|
||||||
|
|
||||||
/// A hash map from deprecated `CheckCodePrefix` to latest `CheckCodePrefix`.
|
|
||||||
pub static PREFIX_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCodePrefix>> = Lazy::new(|| {
|
|
||||||
FxHashMap::from_iter([
|
|
||||||
// TODO(charlie): Remove by 2023-01-01.
|
|
||||||
("U001", CheckCodePrefix::UP001),
|
|
||||||
("U003", CheckCodePrefix::UP003),
|
|
||||||
("U004", CheckCodePrefix::UP004),
|
|
||||||
("U005", CheckCodePrefix::UP005),
|
|
||||||
("U006", CheckCodePrefix::UP006),
|
|
||||||
("U007", CheckCodePrefix::UP007),
|
|
||||||
("U008", CheckCodePrefix::UP008),
|
|
||||||
("U009", CheckCodePrefix::UP009),
|
|
||||||
("U010", CheckCodePrefix::UP010),
|
|
||||||
("U011", CheckCodePrefix::UP011),
|
|
||||||
("U012", CheckCodePrefix::UP012),
|
|
||||||
("U013", CheckCodePrefix::UP013),
|
|
||||||
("U014", CheckCodePrefix::UP014),
|
|
||||||
("U015", CheckCodePrefix::UP015),
|
|
||||||
("U016", CheckCodePrefix::UP016),
|
|
||||||
("U017", CheckCodePrefix::UP017),
|
|
||||||
("U019", CheckCodePrefix::UP019),
|
|
||||||
// TODO(charlie): Remove by 2023-02-01.
|
|
||||||
("I252", CheckCodePrefix::TID252),
|
|
||||||
("M001", CheckCodePrefix::RUF100),
|
|
||||||
// TODO(charlie): Remove by 2023-02-01.
|
|
||||||
("PDV002", CheckCodePrefix::PD002),
|
|
||||||
("PDV003", CheckCodePrefix::PD003),
|
|
||||||
("PDV004", CheckCodePrefix::PD004),
|
|
||||||
("PDV007", CheckCodePrefix::PD007),
|
|
||||||
("PDV008", CheckCodePrefix::PD008),
|
|
||||||
("PDV009", CheckCodePrefix::PD009),
|
|
||||||
("PDV010", CheckCodePrefix::PD010),
|
|
||||||
("PDV011", CheckCodePrefix::PD011),
|
|
||||||
("PDV012", CheckCodePrefix::PD012),
|
|
||||||
("PDV013", CheckCodePrefix::PD013),
|
|
||||||
("PDV015", CheckCodePrefix::PD015),
|
|
||||||
("PDV901", CheckCodePrefix::PD901),
|
|
||||||
// TODO(charlie): Remove by 2023-02-01.
|
|
||||||
("R501", CheckCodePrefix::RET501),
|
|
||||||
("R502", CheckCodePrefix::RET502),
|
|
||||||
("R503", CheckCodePrefix::RET503),
|
|
||||||
("R504", CheckCodePrefix::RET504),
|
|
||||||
("R505", CheckCodePrefix::RET505),
|
|
||||||
("R506", CheckCodePrefix::RET506),
|
|
||||||
("R507", CheckCodePrefix::RET507),
|
|
||||||
("R508", CheckCodePrefix::RET508),
|
|
||||||
("IC001", CheckCodePrefix::ICN001),
|
|
||||||
("IC002", CheckCodePrefix::ICN001),
|
|
||||||
("IC003", CheckCodePrefix::ICN001),
|
|
||||||
("IC004", CheckCodePrefix::ICN001),
|
|
||||||
// TODO(charlie): Remove by 2023-01-01.
|
|
||||||
("U", CheckCodePrefix::UP),
|
|
||||||
("U0", CheckCodePrefix::UP0),
|
|
||||||
("U00", CheckCodePrefix::UP00),
|
|
||||||
("U01", CheckCodePrefix::UP01),
|
|
||||||
// TODO(charlie): Remove by 2023-02-01.
|
|
||||||
("I2", CheckCodePrefix::TID2),
|
|
||||||
("I25", CheckCodePrefix::TID25),
|
|
||||||
("M", CheckCodePrefix::RUF100),
|
|
||||||
("M0", CheckCodePrefix::RUF100),
|
|
||||||
// TODO(charlie): Remove by 2023-02-01.
|
|
||||||
("PDV", CheckCodePrefix::PD),
|
|
||||||
("PDV0", CheckCodePrefix::PD0),
|
|
||||||
("PDV01", CheckCodePrefix::PD01),
|
|
||||||
("PDV9", CheckCodePrefix::PD9),
|
|
||||||
("PDV90", CheckCodePrefix::PD90),
|
|
||||||
// TODO(charlie): Remove by 2023-02-01.
|
|
||||||
("R", CheckCodePrefix::RET),
|
|
||||||
("R5", CheckCodePrefix::RET5),
|
|
||||||
("R50", CheckCodePrefix::RET50),
|
|
||||||
// TODO(charlie): Remove by 2023-02-01.
|
|
||||||
("IC", CheckCodePrefix::ICN),
|
|
||||||
("IC0", CheckCodePrefix::ICN0),
|
|
||||||
])
|
|
||||||
});
|
|
||||||
|
|
||||||
/// A hash map from deprecated to latest `CheckCode`.
|
/// A hash map from deprecated to latest `CheckCode`.
|
||||||
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||||
FxHashMap::from_iter([
|
FxHashMap::from_iter([
|
||||||
|
|
3797
src/registry_gen.rs
3797
src/registry_gen.rs
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,7 @@ use shellexpand;
|
||||||
use shellexpand::LookupError;
|
use shellexpand::LookupError;
|
||||||
|
|
||||||
use crate::cli::{collect_per_file_ignores, Overrides};
|
use crate::cli::{collect_per_file_ignores, Overrides};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
use crate::registry::CheckCodePrefix;
|
||||||
use crate::settings::options::Options;
|
use crate::settings::options::Options;
|
||||||
use crate::settings::pyproject::load_options;
|
use crate::settings::pyproject::load_options;
|
||||||
use crate::settings::types::{
|
use crate::settings::types::{
|
||||||
|
|
|
@ -17,8 +17,7 @@ use regex::Regex;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::cache::cache_dir;
|
use crate::cache::cache_dir;
|
||||||
use crate::registry::{CheckCode, INCOMPATIBLE_CODES};
|
use crate::registry::{CheckCode, CheckCodePrefix, SuffixLength, CATEGORIES, INCOMPATIBLE_CODES};
|
||||||
use crate::registry_gen::{CheckCodePrefix, SuffixLength, CATEGORIES};
|
|
||||||
use crate::settings::configuration::Configuration;
|
use crate::settings::configuration::Configuration;
|
||||||
use crate::settings::types::{
|
use crate::settings::types::{
|
||||||
FilePattern, PerFileIgnore, PythonVersion, SerializationFormat, Version,
|
FilePattern, PerFileIgnore, PythonVersion, SerializationFormat, Version,
|
||||||
|
@ -471,8 +470,7 @@ fn validate_enabled(enabled: FxHashSet<CheckCode>) -> FxHashSet<CheckCode> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::registry::CheckCode;
|
use crate::registry::{CheckCode, CheckCodePrefix};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
|
||||||
use crate::settings::{resolve_codes, CheckCodeSpec};
|
use crate::settings::{resolve_codes, CheckCodeSpec};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hash::FxHashMap;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
use crate::registry::CheckCodePrefix;
|
||||||
use crate::settings::types::{PythonVersion, SerializationFormat, Version};
|
use crate::settings::types::{PythonVersion, SerializationFormat, Version};
|
||||||
use crate::{
|
use crate::{
|
||||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
||||||
|
|
|
@ -131,7 +131,7 @@ mod tests {
|
||||||
|
|
||||||
use crate::flake8_quotes::settings::Quote;
|
use crate::flake8_quotes::settings::Quote;
|
||||||
use crate::flake8_tidy_imports::settings::{BannedApi, Strictness};
|
use crate::flake8_tidy_imports::settings::{BannedApi, Strictness};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
use crate::registry::CheckCodePrefix;
|
||||||
use crate::settings::pyproject::{
|
use crate::settings::pyproject::{
|
||||||
find_settings_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
find_settings_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,8 +12,7 @@ use schemars::JsonSchema;
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
use crate::fs;
|
use crate::fs;
|
||||||
use crate::registry::CheckCode;
|
use crate::registry::{CheckCode, CheckCodePrefix};
|
||||||
use crate::registry_gen::CheckCodePrefix;
|
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema,
|
Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue