diff --git a/crates/ruff/src/rules/flake8_annotations/settings.rs b/crates/ruff/src/rules/flake8_annotations/settings.rs index 8b7e3c0f6e029..a0c3ba5115e66 100644 --- a/crates/ruff/src/rules/flake8_annotations/settings.rs +++ b/crates/ruff/src/rules/flake8_annotations/settings.rs @@ -2,10 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::CacheKey; -use ruff_macros::ConfigurationOptions; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_bandit/settings.rs b/crates/ruff/src/rules/flake8_bandit/settings.rs index 0334721a31872..76f363edc4645 100644 --- a/crates/ruff/src/rules/flake8_bandit/settings.rs +++ b/crates/ruff/src/rules/flake8_bandit/settings.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; fn default_tmp_dirs() -> Vec { ["/tmp", "/var/tmp", "/dev/shm"] @@ -10,7 +10,9 @@ fn default_tmp_dirs() -> Vec { .to_vec() } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_bugbear/settings.rs b/crates/ruff/src/rules/flake8_bugbear/settings.rs index 95e00f92d3481..4de75f4d782b4 100644 --- a/crates/ruff/src/rules/flake8_bugbear/settings.rs +++ b/crates/ruff/src/rules/flake8_bugbear/settings.rs @@ -2,9 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_builtins/settings.rs b/crates/ruff/src/rules/flake8_builtins/settings.rs index a9c6228f26623..2a512626b9d90 100644 --- a/crates/ruff/src/rules/flake8_builtins/settings.rs +++ b/crates/ruff/src/rules/flake8_builtins/settings.rs @@ -2,9 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_comprehensions/settings.rs b/crates/ruff/src/rules/flake8_comprehensions/settings.rs index 630f473de286b..bc063f1922193 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/settings.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/settings.rs @@ -2,9 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_errmsg/settings.rs b/crates/ruff/src/rules/flake8_errmsg/settings.rs index efa3bd93458ed..b3ea044529465 100644 --- a/crates/ruff/src/rules/flake8_errmsg/settings.rs +++ b/crates/ruff/src/rules/flake8_errmsg/settings.rs @@ -2,9 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_gettext/settings.rs b/crates/ruff/src/rules/flake8_gettext/settings.rs index 58058fc69263e..a16d61e15c881 100644 --- a/crates/ruff/src/rules/flake8_gettext/settings.rs +++ b/crates/ruff/src/rules/flake8_gettext/settings.rs @@ -1,8 +1,10 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_implicit_str_concat/settings.rs b/crates/ruff/src/rules/flake8_implicit_str_concat/settings.rs index 3a9a18bb1ec03..4a14f15396d19 100644 --- a/crates/ruff/src/rules/flake8_implicit_str_concat/settings.rs +++ b/crates/ruff/src/rules/flake8_implicit_str_concat/settings.rs @@ -2,9 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_import_conventions/settings.rs b/crates/ruff/src/rules/flake8_import_conventions/settings.rs index a3e45fd654ea4..95a0fb7e2e168 100644 --- a/crates/ruff/src/rules/flake8_import_conventions/settings.rs +++ b/crates/ruff/src/rules/flake8_import_conventions/settings.rs @@ -3,7 +3,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[ ("altair", "alt"), @@ -20,7 +20,9 @@ const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[ ("pyarrow", "pa"), ]; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_pytest_style/settings.rs b/crates/ruff/src/rules/flake8_pytest_style/settings.rs index 887c9fba74228..e70545c6d4f9a 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/settings.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/settings.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; use super::types; @@ -20,7 +20,9 @@ fn default_broad_exceptions() -> Vec { .to_vec() } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_quotes/settings.rs b/crates/ruff/src/rules/flake8_quotes/settings.rs index c64108a00501e..121501065e90b 100644 --- a/crates/ruff/src/rules/flake8_quotes/settings.rs +++ b/crates/ruff/src/rules/flake8_quotes/settings.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] @@ -20,7 +20,9 @@ impl Default for Quote { } } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_self/settings.rs b/crates/ruff/src/rules/flake8_self/settings.rs index c03fab8235712..728b64c97c286 100644 --- a/crates/ruff/src/rules/flake8_self/settings.rs +++ b/crates/ruff/src/rules/flake8_self/settings.rs @@ -2,13 +2,15 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; // By default, ignore the `namedtuple` methods and attributes, which are underscore-prefixed to // prevent conflicts with field names. const IGNORE_NAMES: [&str; 5] = ["_make", "_asdict", "_replace", "_fields", "_field_defaults"]; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_tidy_imports/options.rs b/crates/ruff/src/rules/flake8_tidy_imports/options.rs index 37dcc87b82331..f180974a49776 100644 --- a/crates/ruff/src/rules/flake8_tidy_imports/options.rs +++ b/crates/ruff/src/rules/flake8_tidy_imports/options.rs @@ -3,13 +3,15 @@ use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; -use ruff_macros::ConfigurationOptions; +use ruff_macros::{CombineOptions, ConfigurationOptions}; use super::banned_api::ApiBan; use super::relative_imports::Strictness; use super::Settings; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_type_checking/settings.rs b/crates/ruff/src/rules/flake8_type_checking/settings.rs index a400334b31f1e..69cd913dae913 100644 --- a/crates/ruff/src/rules/flake8_type_checking/settings.rs +++ b/crates/ruff/src/rules/flake8_type_checking/settings.rs @@ -2,9 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/flake8_unused_arguments/settings.rs b/crates/ruff/src/rules/flake8_unused_arguments/settings.rs index 8500616aeb4d3..f8853ddab47be 100644 --- a/crates/ruff/src/rules/flake8_unused_arguments/settings.rs +++ b/crates/ruff/src/rules/flake8_unused_arguments/settings.rs @@ -2,9 +2,11 @@ use serde::{Deserialize, Serialize}; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/isort/settings.rs b/crates/ruff/src/rules/isort/settings.rs index 35a3a8a417cda..262829b73d425 100644 --- a/crates/ruff/src/rules/isort/settings.rs +++ b/crates/ruff/src/rules/isort/settings.rs @@ -7,7 +7,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; use crate::rules::isort::categorize::KnownModules; use crate::rules::isort::ImportType; @@ -33,7 +33,9 @@ impl Default for RelativeImportsOrder { } } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/mccabe/settings.rs b/crates/ruff/src/rules/mccabe/settings.rs index 5eeef9e84aef6..8f53f535e2e6b 100644 --- a/crates/ruff/src/rules/mccabe/settings.rs +++ b/crates/ruff/src/rules/mccabe/settings.rs @@ -1,9 +1,11 @@ //! Settings for the `mccabe` plugin. -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/pep8_naming/settings.rs b/crates/ruff/src/rules/pep8_naming/settings.rs index 96fc9926bac8e..ffc77699fc0d8 100644 --- a/crates/ruff/src/rules/pep8_naming/settings.rs +++ b/crates/ruff/src/rules/pep8_naming/settings.rs @@ -1,6 +1,6 @@ //! Settings for the `pep8-naming` plugin. -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; use serde::{Deserialize, Serialize}; const IGNORE_NAMES: [&str; 12] = [ @@ -18,7 +18,9 @@ const IGNORE_NAMES: [&str; 12] = [ "maxDiff", ]; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/rules/pycodestyle/settings.rs b/crates/ruff/src/rules/pycodestyle/settings.rs index dacb7b4cfe25f..06204f29bba3c 100644 --- a/crates/ruff/src/rules/pycodestyle/settings.rs +++ b/crates/ruff/src/rules/pycodestyle/settings.rs @@ -1,9 +1,11 @@ //! Settings for the `pycodestyle` plugin. -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde(deny_unknown_fields, rename_all = "kebab-case", rename = "Pycodestyle")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Options { diff --git a/crates/ruff/src/rules/pydocstyle/settings.rs b/crates/ruff/src/rules/pydocstyle/settings.rs index 3094418cda9f8..e3ce5bd399f8f 100644 --- a/crates/ruff/src/rules/pydocstyle/settings.rs +++ b/crates/ruff/src/rules/pydocstyle/settings.rs @@ -1,7 +1,7 @@ //! Settings for the `pydocstyle` plugin. use crate::registry::Rule; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; @@ -68,7 +68,9 @@ impl Convention { } } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde(deny_unknown_fields, rename_all = "kebab-case", rename = "Pydocstyle")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Options { diff --git a/crates/ruff/src/rules/pylint/settings.rs b/crates/ruff/src/rules/pylint/settings.rs index 61815ffb03709..2525d2f564ea2 100644 --- a/crates/ruff/src/rules/pylint/settings.rs +++ b/crates/ruff/src/rules/pylint/settings.rs @@ -1,7 +1,7 @@ //! Settings for the `pylint` plugin. use anyhow::anyhow; -use ruff_macros::{CacheKey, ConfigurationOptions}; +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; use rustpython_parser::ast::Constant; use serde::{Deserialize, Serialize}; @@ -35,7 +35,9 @@ impl TryFrom<&Constant> for ConstantType { } } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)] +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] #[serde( deny_unknown_fields, rename_all = "kebab-case", diff --git a/crates/ruff/src/settings/configuration.rs b/crates/ruff/src/settings/configuration.rs index 5e6318e5cb0a5..612122cefc5aa 100644 --- a/crates/ruff/src/settings/configuration.rs +++ b/crates/ruff/src/settings/configuration.rs @@ -268,33 +268,53 @@ impl Configuration { task_tags: self.task_tags.or(config.task_tags), typing_modules: self.typing_modules.or(config.typing_modules), // Plugins - flake8_annotations: self.flake8_annotations.or(config.flake8_annotations), - flake8_bandit: self.flake8_bandit.or(config.flake8_bandit), - flake8_bugbear: self.flake8_bugbear.or(config.flake8_bugbear), - flake8_builtins: self.flake8_builtins.or(config.flake8_builtins), - flake8_comprehensions: self.flake8_comprehensions.or(config.flake8_comprehensions), - flake8_errmsg: self.flake8_errmsg.or(config.flake8_errmsg), - flake8_gettext: self.flake8_gettext.or(config.flake8_gettext), + flake8_annotations: self.flake8_annotations.combine(config.flake8_annotations), + flake8_bandit: self.flake8_bandit.combine(config.flake8_bandit), + flake8_bugbear: self.flake8_bugbear.combine(config.flake8_bugbear), + flake8_builtins: self.flake8_builtins.combine(config.flake8_builtins), + flake8_comprehensions: self + .flake8_comprehensions + .combine(config.flake8_comprehensions), + flake8_errmsg: self.flake8_errmsg.combine(config.flake8_errmsg), + flake8_gettext: self.flake8_gettext.combine(config.flake8_gettext), flake8_implicit_str_concat: self .flake8_implicit_str_concat - .or(config.flake8_implicit_str_concat), + .combine(config.flake8_implicit_str_concat), flake8_import_conventions: self .flake8_import_conventions - .or(config.flake8_import_conventions), - flake8_pytest_style: self.flake8_pytest_style.or(config.flake8_pytest_style), - flake8_quotes: self.flake8_quotes.or(config.flake8_quotes), - flake8_self: self.flake8_self.or(config.flake8_self), - flake8_tidy_imports: self.flake8_tidy_imports.or(config.flake8_tidy_imports), - flake8_type_checking: self.flake8_type_checking.or(config.flake8_type_checking), + .combine(config.flake8_import_conventions), + flake8_pytest_style: self.flake8_pytest_style.combine(config.flake8_pytest_style), + flake8_quotes: self.flake8_quotes.combine(config.flake8_quotes), + flake8_self: self.flake8_self.combine(config.flake8_self), + flake8_tidy_imports: self.flake8_tidy_imports.combine(config.flake8_tidy_imports), + flake8_type_checking: self + .flake8_type_checking + .combine(config.flake8_type_checking), flake8_unused_arguments: self .flake8_unused_arguments - .or(config.flake8_unused_arguments), - isort: self.isort.or(config.isort), - mccabe: self.mccabe.or(config.mccabe), - pep8_naming: self.pep8_naming.or(config.pep8_naming), - pycodestyle: self.pycodestyle.or(config.pycodestyle), - pydocstyle: self.pydocstyle.or(config.pydocstyle), - pylint: self.pylint.or(config.pylint), + .combine(config.flake8_unused_arguments), + isort: self.isort.combine(config.isort), + mccabe: self.mccabe.combine(config.mccabe), + pep8_naming: self.pep8_naming.combine(config.pep8_naming), + pycodestyle: self.pycodestyle.combine(config.pycodestyle), + pydocstyle: self.pydocstyle.combine(config.pydocstyle), + pylint: self.pylint.combine(config.pylint), + } + } +} + +pub trait CombinePluginOptions { + #[must_use] + fn combine(self, other: Self) -> Self; +} + +impl CombinePluginOptions for Option { + fn combine(self, other: Self) -> Self { + match (self, other) { + (Some(base), Some(other)) => Some(base.combine(other)), + (Some(base), None) => Some(base), + (None, Some(other)) => Some(other), + (None, None) => None, } } } diff --git a/crates/ruff_macros/src/combine_options.rs b/crates/ruff_macros/src/combine_options.rs new file mode 100644 index 0000000000000..8f7e381093e65 --- /dev/null +++ b/crates/ruff_macros/src/combine_options.rs @@ -0,0 +1,62 @@ +use quote::{quote, quote_spanned}; +use syn::{Data, DataStruct, DeriveInput, Field, Fields, Path, PathSegment, Type, TypePath}; + +pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result { + let DeriveInput { ident, data, .. } = input; + + match data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => { + let output = fields + .named + .iter() + .map(handle_field) + .collect::, _>>()?; + + Ok(quote! { + use crate::settings::configuration::CombinePluginOptions; + + impl CombinePluginOptions for #ident { + fn combine(self, other: Self) -> Self { + Self { + #( + #output + ),* + } + } + } + }) + } + _ => Err(syn::Error::new( + ident.span(), + "Can only derive CombineOptions from structs with named fields.", + )), + } +} + +fn handle_field(field: &Field) -> 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, .. + }) 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.")), + } +} diff --git a/crates/ruff_macros/src/lib.rs b/crates/ruff_macros/src/lib.rs index 8234653419d3b..3c108bf2bc392 100644 --- a/crates/ruff_macros/src/lib.rs +++ b/crates/ruff_macros/src/lib.rs @@ -5,6 +5,7 @@ use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput, ItemFn, ItemStruct}; mod cache_key; +mod combine_options; mod config; mod derive_message_formats; mod map_codes; @@ -22,6 +23,15 @@ pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream .into() } +#[proc_macro_derive(CombineOptions)] +pub fn derive_combine_options(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + combine_options::derive_impl(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + #[proc_macro_derive(CacheKey)] pub fn cache_key(input: TokenStream) -> TokenStream { let item = parse_macro_input!(input as DeriveInput);