From a88b7c65be73363a6e82e1109374d75eb398e8ef Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 18 Oct 2023 08:44:45 +0900 Subject: [PATCH] Options: Support `#[serde(alias = name)]` Signed-off-by: Micha Reiser --- Cargo.lock | 22 ++++++++--- Cargo.toml | 2 +- crates/ruff_dev/src/generate_options.rs | 19 ++++++++++ crates/ruff_macros/Cargo.toml | 1 + crates/ruff_macros/src/config.rs | 46 +++++++++++++---------- crates/ruff_workspace/src/options.rs | 1 + crates/ruff_workspace/src/options_base.rs | 2 + 7 files changed, 68 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f7cfecf40b3cc..d0cc166241c595 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2259,6 +2259,7 @@ dependencies = [ "proc-macro2", "quote", "ruff_python_trivia", + "serde_derive_internals 0.29.0", "syn 2.0.38", ] @@ -2624,7 +2625,7 @@ checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" dependencies = [ "proc-macro2", "quote", - "serde_derive_internals", + "serde_derive_internals 0.26.0", "syn 1.0.109", ] @@ -2664,9 +2665,9 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] @@ -2684,9 +2685,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", @@ -2704,6 +2705,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "serde_json" version = "1.0.107" diff --git a/Cargo.toml b/Cargo.toml index 5c90bd89b787f5..e4afb539722acb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/crates/ruff_dev/src/generate_options.rs b/crates/ruff_dev/src/generate_options.rs index 3e73b74f430100..047e62ee4c5b0a 100644 --- a/crates/ruff_dev/src/generate_options.rs +++ b/crates/ruff_dev/src/generate_options.rs @@ -1,6 +1,7 @@ //! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`. //! //! Used for . +use itertools::Itertools; use std::fmt::Write; use ruff_workspace::options::Options; @@ -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() { diff --git a/crates/ruff_macros/Cargo.toml b/crates/ruff_macros/Cargo.toml index 2cd64bbe54f06d..e568fe832e66a8 100644 --- a/crates/ruff_macros/Cargo.toml +++ b/crates/ruff_macros/Cargo.toml @@ -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 } diff --git a/crates/ruff_macros/src/config.rs b/crates/ruff_macros/src/config.rs index ac7851384e9b4e..1ae6c1b3f89781 100644 --- a/crates/ruff_macros/src/config.rs +++ b/crates/ruff_macros/src/config.rs @@ -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; @@ -38,25 +38,14 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result (#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)))); } } } @@ -193,6 +182,10 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result()?; 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{ @@ -200,6 +193,7 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result syn::Result { _ => Err(syn::Error::new(value.span(), "Expected literal string")), } } + +fn serde_field_metadata(field: &Field) -> syn::Result { + 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) +} diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index 6d4802edc25011..38b7062136ef66 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -361,6 +361,7 @@ pub struct Options { line-length = 120 "# )] + #[serde(alias = "line-width", alias = "max-line-length")] pub line_length: Option, /// The number of spaces a tab is equal to when enforcing long-line violations (like `E501`) diff --git a/crates/ruff_workspace/src/options_base.rs b/crates/ruff_workspace/src/options_base.rs index 8bf095598976ea..e959a3efc8a313 100644 --- a/crates/ruff_workspace/src/options_base.rs +++ b/crates/ruff_workspace/src/options_base.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::fmt::{Debug, Display, Formatter}; /// Visits [`OptionsMetadata`]. @@ -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, }