Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge maps when combining options #9927

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 14 additions & 30 deletions crates/ruff_macros/src/combine_options.rs
Original file line number Diff line number Diff line change
@@ -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<proc_macro2::TokenStream> {
let DeriveInput { ident, data, .. } = input;
Expand All @@ -9,15 +9,24 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
fields: Fields::Named(fields),
..
}) => {
let output = fields
let output: Vec<_> = fields
.named
.iter()
.map(handle_field)
.collect::<Result<Vec<_>, _>>()?;
.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 {
Expand All @@ -35,28 +44,3 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
)),
}
}

fn handle_field(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, ..
}) if type_ident == "Option" => Ok(quote_spanned!(
ident.span() => #ident: self.#ident.or(other.#ident)
)),
_ => Err(syn::Error::new(
ident.span(),
"Expected `Option<_>` or `Vec<_>` as type.",
)),
},
_ => Err(syn::Error::new(ident.span(), "Expected type.")),
}
}
4 changes: 4 additions & 0 deletions crates/ruff_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
98 changes: 77 additions & 21 deletions crates/ruff_workspace/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ 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::isort::ImportSection;
use ruff_linter::rules::pycodestyle;
use ruff_linter::rules::pydocstyle::settings::Convention;
use ruff_linter::settings::fix_safety_table::FixSafetyTable;
use ruff_linter::settings::rule_table::RuleTable;
use ruff_linter::settings::types::{
Expand All @@ -35,6 +43,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,
};
Expand Down Expand Up @@ -1151,7 +1160,7 @@ impl LintConfiguration {
}
}

#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, CombineOptions)]
pub struct FormatConfiguration {
pub exclude: Option<Vec<FilePattern>>,
pub preview: Option<PreviewMode>,
Expand Down Expand Up @@ -1202,31 +1211,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<T: CombinePluginOptions> CombinePluginOptions for Option<T> {
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<T: CombineOptions> CombineOptions for Option<T> {
fn combine(self, other: Self) -> Self {
match (self, other) {
(Some(base), Some(other)) => Some(base.combine(other)),
Expand All @@ -1237,6 +1252,47 @@ impl<T: CombinePluginOptions> CombinePluginOptions for Option<T> {
}
}

impl<T> CombineOptions for Option<Vec<T>> {
fn combine(self, other: Self) -> Self {
self.or(other)
}
}

impl<T, S> CombineOptions for Option<std::collections::hash_set::HashSet<T, S>> {
fn combine(self, other: Self) -> Self {
self.or(other)
}
}

impl<K, V, S> CombineOptions for std::collections::hash_map::HashMap<K, V, S>
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);
or_combine_options_impl!(ImportSection);

/// 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<Vec<PathBuf>> {
Expand Down
Loading