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,
}