Skip to content

Conversation

@giammirove
Copy link

Fixes #6629

Problem

The following code:

macro_rules! reproduce { (type Fail = $ty:ty; arr = $($arr:expr),*) => { ( vec![$($arr),+] ) }; } fn main() { reproduce!(type Fail = char; arr = 1); }

is formatted as:

macro_rules! reproduce { (type Fail = $ty:ty; arr = $($arr:expr),*) => { ( vec![$($arr),+] ) }; } fn main() { reproduce!(type Fail = char;, arr = 1); } 

Notice the , right after ; in the macro invocation.
Making the formatted code's syntax wrong.

Solution

When formatting a list of commented items into a list (write_list in src/lists.rs), do not add the comma if the item's entry ends with ;.

Changes

  • Modified write_list in src/lists.rs to avoid adding , if the argument ends with ;.
  • Added regression test case issue-6629.rs to prevent future regressions
@giammirove
Copy link
Author

I found an edge case for which my fix would break the code.

The following code:

macro_rules! reproduce { (type Fail = $ty:ty; arr = $($arr:expr),+) => { ( vec![$($arr),+] ) }; ( $expr:expr, $($arr:item),+) => { 1 }; } reproduce!(type Fail = char; arr = 1); 

is rewritten as:

reproduce!(type Fail = char;, arr = 1); 

which is not valid code.

The problem is related to mixed argument types (items and non items).
The code responsible for parsing the macro arguments is parse_macro_args in src/parse/macros/mod.rs. parse_macro_args interpret type Fail = char; as an item, while arr = 1 as an array of expr.
While parse_macro_args parses the arguments, it does not detect any ;, since ; is part of the item (items are self-terminating).
Therefore, the arguments are later treated as vec![1,2,3,4], and write_list will append , after every argument, breaking the code.
My previous fix was to avoid appending , after ;. Such a fix would break other valid code, see:

reproduce!(23, type Fail = char;, type Fail = char;); 

is rewritten as:

reproduce!(23, type Fail = char; type Fail = char;); 

which is not valid code, since the macro accepts arguments like ($($arr:item),+).

The problem is therefore related to the presence of both item and non item arguments.
Currently, no implementation accounts for this case scenario in rewrite_macro_inner in src/macros.rs.
The current solution uses a conservative approach, where mixed macro arguments are not formatted.

@giammirove giammirove changed the title fix: do not add commas after ';' in macro args (#6629) fix: don’t format mixed macro args (items + non-items) (#6629) Aug 17, 2025
Items in macro input are self-terminating (their own `;`/`}` closes them), so treating mixed argument lists as comma lists leads rustfmt to invent a comma after an item: ``` reproduce!(type Fail = char; arr = 1); // became reproduce!(type Fail = char;, arr = 1); // invalid ``` `parse_macro_args` correctly parses `type Fail = char;` as an item and `arr = 1` as exprs, but later `rewrite_macro_inner`/`write_list` assumes a comma-separated list and appends a comma after the item. Fix: be conservative, if a macro invocation contains at least one `$item` and at least one non-`$item`, do not format it. Keep existing behavior for all-items (use items path) and no-items (use list formatting). This avoids inventing commas after items without breaking valid cases like: ``` reproduce!(23, type Fail = char;, type Fail = char;); ```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

2 participants