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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 14 additions & 30 deletions 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<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
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
96 changes: 75 additions & 21 deletions crates/ruff_workspace/src/configuration.rs
Expand Up @@ -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,
Expand All @@ -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,
};
Expand Down Expand Up @@ -1161,7 +1169,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 @@ -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<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 @@ -1247,6 +1261,46 @@ 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);

/// 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