rustfmt icon indicating copy to clipboard operation
rustfmt copied to clipboard

Separate function parameter from its attributes by a newline

Open Veetaha opened this issue 1 year ago • 5 comments

Context

Hi, I'm the author of the crate bon. It exposes a proc macro that generates a builder for a function. Every function parameter can be configured with additional #[builder(...)] options.

Problem

For example, this function declares 4 parameters, two of which are optional (they have default values). As for me, the way rustfmt formatted this code doesn't look nice:

#[bon::builder]
fn example(
    #[builder(default = 1)] foo: u32,
    bar: u32,
    #[builder(default = 3)] baz: u32,
    fizz: u32,
) {
}

The attribute on the function's parameter was formatted on the same line with the parameter itself, even though the signature of the function already takes up multiple lines. It is harder to read the signature this way because names of function parameters aren't aligned

I'd expect the following formatting for this example of code instead:

#[bon::builder]
fn example(
    #[builder(default = 1)]
    foo: u32,
    bar: u32,
    #[builder(default = 3)]
    baz: u32,
    fizz: u32,
) {
}

Veetaha avatar Aug 12 '24 16:08 Veetaha

Maybe we need to extend inline_attribute_width (https://github.com/rust-lang/rustfmt/issues/3343) to also apply to function parameters?

ytmimi avatar Aug 12 '24 17:08 ytmimi

Maybe we need to extend inline_attribute_width (https://github.com/rust-lang/rustfmt/issues/3343) to also apply to function parameters?

It definitely makes sense, but could it just be the default? I mean that the attribute isn't placed on the same line in function arguments?

Veetaha avatar Aug 28 '24 15:08 Veetaha

rustfmt can't change default formatting because of it's stability guarantee. Breaking changes can only be made if they're gated.

ytmimi avatar Aug 28 '24 15:08 ytmimi

I see, well, at least the default can be changed across editions, but having a config override in the meantime is reasonable

Veetaha avatar Aug 28 '24 15:08 Veetaha

Any change to leverage inline_attribute_width would need to be gated since the default value for the option is 0, which would force the wrapping behavior that you're looking for.

ytmimi avatar Aug 28 '24 15:08 ytmimi

inline_attribute_width might not be enough because you might want to allow inlined attributes only if all attributes have been inline for the function.

For example when one parameter has a single attribute and another has two, the formatting can look like this:

fn format_example(
    #[allow(unused)] first: u32,
    #[allow(non_snake_case)]
    #[allow(unused)]
    SECOND: u32,
) -> String {
    "Hello, world!".to_string()
}

Which is easy to misparse on the first read.

Also I think the option to never have two inlined attributes on the same line might be nice, to avoid having:

fn example(#[allow(unused)] first: u32, #[allow(unused)] second: u32)

sjoubert49 avatar Jul 15 '25 15:07 sjoubert49