From ccf797e274a9ba5bc2d9360bd2b0c9d1d1bebbc4 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 14 Feb 2024 15:36:06 +0100 Subject: [PATCH] Simplify macro to simply forward to `CombineOptions` trait --- crates/ruff_macros/src/combine_options.rs | 84 ++++--------------- crates/ruff_macros/src/lib.rs | 4 + crates/ruff_workspace/src/configuration.rs | 96 +++++++++++++++++----- 3 files changed, 93 insertions(+), 91 deletions(-) diff --git a/crates/ruff_macros/src/combine_options.rs b/crates/ruff_macros/src/combine_options.rs index f8886e9360760..936cf752e31e1 100644 --- a/crates/ruff_macros/src/combine_options.rs +++ b/crates/ruff_macros/src/combine_options.rs @@ -1,5 +1,5 @@ use quote::{quote, quote_spanned}; -use syn::{Data, DataStruct, DeriveInput, Field, Fields, Path, PathSegment, Type, TypePath}; +use syn::{Data, DataStruct, DeriveInput, Fields}; pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result { let DeriveInput { ident, data, .. } = input; @@ -9,15 +9,24 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result { - let output = fields + let output: Vec<_> = fields .named .iter() - .map(handle_field) - .collect::, _>>()?; + .map(|field| { + let ident = field + .ident + .as_ref() + .expect("Expected to handle named fields"); + + quote_spanned!( + ident.span() => #ident: crate::configuration::CombineOptions::combine(self.#ident, other.#ident) + ) + }) + .collect(); Ok(quote! { #[automatically_derived] - impl crate::configuration::CombinePluginOptions for #ident { + impl crate::configuration::CombineOptions for #ident { fn combine(self, other: Self) -> Self { #[allow(deprecated)] Self { @@ -35,68 +44,3 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result syn::Result { - 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, - }) if type_ident == "Option" => { - // Given `Option>`, combine the maps by merging. In TOML, a hash map is - // represented as a table, so merging the maps is the correct behavior. - if let syn::PathArguments::AngleBracketed(args) = arguments { - let inner_type_ident = args - .args - .first() - .and_then(|arg| match arg { - syn::GenericArgument::Type(Type::Path(TypePath { - path: Path { segments, .. }, - .. - })) => segments.first().map(|seg| &seg.ident), - _ => None, - }) - .ok_or_else(|| { - syn::Error::new( - ident.span(), - "Expected `Option<_>` with a single type argument.", - ) - })?; - if inner_type_ident == "HashMap" - || inner_type_ident == "BTreeMap" - || inner_type_ident == "FxHashMap" - { - return Ok(quote_spanned!( - ident.span() => #ident: match (self.#ident, other.#ident) { - (Some(mut m1), Some(m2)) => { - m1.extend(m2); - Some(m1) - }, - (None, Some(m)) | (Some(m), None) => Some(m), - (None, None) => None, - } - )); - } - } - - Ok(quote_spanned!( - ident.span() => #ident: self.#ident.or(other.#ident) - )) - } - - _ => Err(syn::Error::new( - ident.span(), - "Expected `Option<_>` as type.", - )), - }, - _ => Err(syn::Error::new(ident.span(), "Expected type.")), - } -} diff --git a/crates/ruff_macros/src/lib.rs b/crates/ruff_macros/src/lib.rs index ed9e77508f493..c5fa65d1cd9f7 100644 --- a/crates/ruff_macros/src/lib.rs +++ b/crates/ruff_macros/src/lib.rs @@ -24,6 +24,10 @@ pub fn derive_options_metadata(input: TokenStream) -> TokenStream { .into() } +/// Automatically derives a `ruff_workspace::configuration::CombineOptions` implementation for the attributed type +/// that calls `ruff_workspace::configuration::CombineOptions::combine` for each field. +/// +/// The derive macro can only be used on structs with named fields. #[proc_macro_derive(CombineOptions)] pub fn derive_combine_options(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index dac4e14387ba0..09170123c66b5 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -23,7 +23,14 @@ use ruff_linter::line_width::{IndentWidth, LineLength}; use ruff_linter::registry::RuleNamespace; use ruff_linter::registry::{Rule, RuleSet, INCOMPATIBLE_CODES}; use ruff_linter::rule_selector::{PreviewOptions, Specificity}; +use ruff_linter::rules::flake8_pytest_style::types::{ + ParametrizeNameType, ParametrizeValuesRowType, ParametrizeValuesType, +}; +use ruff_linter::rules::flake8_quotes::settings::Quote; +use ruff_linter::rules::flake8_tidy_imports::settings::Strictness; +use ruff_linter::rules::isort::settings::RelativeImportsOrder; use ruff_linter::rules::pycodestyle; +use ruff_linter::rules::pydocstyle::settings::Convention; use ruff_linter::settings::rule_table::RuleTable; use ruff_linter::settings::types::{ ExtensionMapping, FilePattern, FilePatternSet, PerFileIgnore, PerFileIgnores, PreviewMode, @@ -34,6 +41,7 @@ use ruff_linter::{ fs, warn_user_once, warn_user_once_by_id, warn_user_once_by_message, RuleSelector, RUFF_PKG_VERSION, }; +use ruff_macros::CombineOptions; use ruff_python_formatter::{ DocstringCode, DocstringCodeLineWidth, MagicTrailingComma, QuoteStyle, }; @@ -1161,7 +1169,7 @@ impl LintConfiguration { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, CombineOptions)] pub struct FormatConfiguration { pub exclude: Option>, pub preview: Option, @@ -1212,31 +1220,37 @@ impl FormatConfiguration { docstring_code_line_width: options.docstring_code_line_length, }) } - - #[must_use] - #[allow(clippy::needless_pass_by_value)] - pub fn combine(self, config: Self) -> Self { - Self { - exclude: self.exclude.or(config.exclude), - preview: self.preview.or(config.preview), - extension: self.extension.or(config.extension), - indent_style: self.indent_style.or(config.indent_style), - quote_style: self.quote_style.or(config.quote_style), - magic_trailing_comma: self.magic_trailing_comma.or(config.magic_trailing_comma), - line_ending: self.line_ending.or(config.line_ending), - docstring_code_format: self.docstring_code_format.or(config.docstring_code_format), - docstring_code_line_width: self - .docstring_code_line_width - .or(config.docstring_code_line_width), - } - } } -pub(crate) trait CombinePluginOptions { +pub(crate) trait CombineOptions { #[must_use] fn combine(self, other: Self) -> Self; } -impl CombinePluginOptions for Option { +macro_rules! or_combine_options_impl { + ($ty:ident) => { + impl CombineOptions for Option<$ty> { + #[inline] + fn combine(self, other: Self) -> Self { + self.or(other) + } + } + }; +} + +or_combine_options_impl!(bool); +or_combine_options_impl!(u8); +or_combine_options_impl!(u16); +or_combine_options_impl!(u32); +or_combine_options_impl!(u64); +or_combine_options_impl!(usize); +or_combine_options_impl!(i8); +or_combine_options_impl!(i16); +or_combine_options_impl!(i32); +or_combine_options_impl!(i64); +or_combine_options_impl!(isize); +or_combine_options_impl!(String); + +impl CombineOptions for Option { fn combine(self, other: Self) -> Self { match (self, other) { (Some(base), Some(other)) => Some(base.combine(other)), @@ -1247,6 +1261,46 @@ impl CombinePluginOptions for Option { } } +impl CombineOptions for Option> { + fn combine(self, other: Self) -> Self { + self.or(other) + } +} + +impl CombineOptions for Option> { + fn combine(self, other: Self) -> Self { + self.or(other) + } +} + +impl CombineOptions for std::collections::hash_map::HashMap +where + K: Eq + std::hash::Hash, + S: std::hash::BuildHasher, +{ + fn combine(mut self, other: Self) -> Self { + self.extend(other); + self + } +} + +or_combine_options_impl!(ParametrizeNameType); +or_combine_options_impl!(ParametrizeValuesType); +or_combine_options_impl!(ParametrizeValuesRowType); +or_combine_options_impl!(Quote); +or_combine_options_impl!(Strictness); +or_combine_options_impl!(RelativeImportsOrder); +or_combine_options_impl!(LineLength); +or_combine_options_impl!(Convention); +or_combine_options_impl!(IndentStyle); +or_combine_options_impl!(QuoteStyle); +or_combine_options_impl!(LineEnding); +or_combine_options_impl!(DocstringCodeLineWidth); +or_combine_options_impl!(ExtensionMapping); +or_combine_options_impl!(MagicTrailingComma); +or_combine_options_impl!(DocstringCode); +or_combine_options_impl!(PreviewMode); + /// Given a list of source paths, which could include glob patterns, resolve the /// matching paths. pub fn resolve_src(src: &[String], project_root: &Path) -> Result> {