Skip to content

Commit

Permalink
Options: Support #[serde(alias = name)]
Browse files Browse the repository at this point in the history
Signed-off-by: Micha Reiser <micha@reiser.io>
  • Loading branch information
MichaReiser committed Oct 17, 2023
1 parent 366d2be commit a88b7c6
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 25 deletions.
22 changes: 17 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ quote = { version = "1.0.23" }
regex = { version = "1.10.2" }
rustc-hash = { version = "1.1.0" }
schemars = { version = "0.8.15" }
serde = { version = "1.0.152", features = ["derive"] }
serde = { version = "1.0.189", features = ["derive"] }
serde_json = { version = "1.0.107" }
shellexpand = { version = "3.0.0" }
similar = { version = "2.3.0", features = ["inline"] }
Expand Down
19 changes: 19 additions & 0 deletions crates/ruff_dev/src/generate_options.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`.
//!
//! Used for <https://docs.astral.sh/ruff/settings/>.
use itertools::Itertools;
use std::fmt::Write;

use ruff_workspace::options::Options;
Expand Down Expand Up @@ -107,6 +108,24 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set:
output.push('\n');
output.push_str(&format!("**Type**: `{}`\n", field.value_type));
output.push('\n');

if !field.aliases.is_empty() {
let title = if field.aliases.len() == 1 {
"Alias"
} else {
"Aliases"
};
output.push_str(&format!(
"**{title}**: {}\n",
field
.aliases
.iter()
.map(|alias| format!("`{alias}`"))
.join(", ")
));
output.push('\n');
}

output.push_str(&format!(
"**Example usage**:\n\n```toml\n[tool.ruff{}]\n{}\n```\n",
if let Some(set_name) = parent_set.name() {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ doctest = false

[dependencies]
ruff_python_trivia = { path = "../ruff_python_trivia" }
serde_derive_internals = "0.29.0"

proc-macro2 = { workspace = true }
quote = { workspace = true }
Expand Down
46 changes: 27 additions & 19 deletions crates/ruff_macros/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use proc_macro2::TokenTree;
use quote::{quote, quote_spanned};
use serde_derive_internals::Ctxt;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::token::Comma;
use syn::{
AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput, ExprLit, Field,
Fields, Lit, LitStr, Meta, Path, PathArguments, PathSegment, Token, Type, TypePath,
Fields, Lit, LitStr, Path, PathArguments, PathSegment, Token, Type, TypePath,
};

use ruff_python_trivia::textwrap::dedent;
Expand Down Expand Up @@ -38,25 +38,14 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
.any(|attr| attr.path().is_ident("option_group"))
{
output.push(handle_option_group(field)?);
} else if let Some(serde) = field
.attrs
.iter()
.find(|attr| attr.path().is_ident("serde"))
{
} else if let Type::Path(ty) = &field.ty {
let serde_field = serde_field_metadata(field)?;

// If a field has the `serde(flatten)` attribute, flatten the options into the parent
// by calling `Type::record` instead of `visitor.visit_set`
if let (Type::Path(ty), Meta::List(list)) = (&field.ty, &serde.meta) {
for token in list.tokens.clone() {
if let TokenTree::Ident(ident) = token {
if ident == "flatten" {
let ty_name = ty.path.require_ident()?;
output.push(quote_spanned!(
ident.span() => (#ty_name::record(visit))
));
break;
}
}
}
if serde_field.flatten() {
let ty_name = ty.path.require_ident()?;
output.push(quote_spanned!(ident.span() => (#ty_name::record(visit))));
}
}
}
Expand Down Expand Up @@ -193,13 +182,18 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::To
} = attr.parse_args::<FieldAttributes>()?;
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());

let serde_field = serde_field_metadata(field)?;
let attributed_aliases = serde_field.aliases();
let aliases = quote!(BTreeSet::from_iter([#(#attributed_aliases),*]));

Ok(quote_spanned!(
ident.span() => {
visit.record_field(#kebab_name, crate::options_base::OptionField{
doc: &#doc,
default: &#default,
value_type: &#value_type,
example: &#example,
aliases: #aliases
})
}
))
Expand Down Expand Up @@ -248,3 +242,17 @@ fn _parse_key_value(input: ParseStream, name: &str) -> syn::Result<String> {
_ => Err(syn::Error::new(value.span(), "Expected literal string")),
}
}

fn serde_field_metadata(field: &Field) -> syn::Result<serde_derive_internals::attr::Field> {
let context = Ctxt::new();
let field = serde_derive_internals::attr::Field::from_ast(
&context,
0,
&field,
None,
&serde_derive_internals::attr::Default::None,
);
context.check()?;

Ok(field)
}
1 change: 1 addition & 0 deletions crates/ruff_workspace/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ pub struct Options {
line-length = 120
"#
)]
#[serde(alias = "line-width", alias = "max-line-length")]
pub line_length: Option<LineLength>,

/// The number of spaces a tab is equal to when enforcing long-line violations (like `E501`)
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_workspace/src/options_base.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::BTreeSet;
use std::fmt::{Debug, Display, Formatter};

/// Visits [`OptionsMetadata`].
Expand Down Expand Up @@ -307,6 +308,7 @@ pub struct OptionField {
pub doc: &'static str,
pub default: &'static str,
pub value_type: &'static str,
pub aliases: BTreeSet<&'static str>,
pub example: &'static str,
}

Expand Down

0 comments on commit a88b7c6

Please sign in to comment.