The state of the trailing comma


#1

So I went down the list of macros in std and tested every. single. one of them for trailing comma support in all possible locations.

Check out the test here!

By some blessing of fortune, every single “variadic” macro correctly supports trailing commas beyond a sufficient number of arguments. The majority of them forward the token list to format_args!, which handles the commas correctly. It is only a small number of fixed argument counts that fail for each macro.

I am also delighted to report that the single most glaring “trailing comma” bug (assert_eq!) has already been fixed in 1.22. (I only discovered this because I switched toolchains to test the unstable macros!)

Here’s the full list of failing test cases

assert!(true, ); //~ ERROR: unexpected end of macro invocation
assert!(true, "hello",); //~ ERROR: unexpected end of macro invocation
assert_eq!(1, 1,);  // (fixed in 1.22)
assert_ne!(1, 2,);  // (fixed in 1.22)
compile_error!("lel",); //~ ERROR: compile_error! takes 1 argument
debug_assert!(true, ); //~ ERROR: unexpected end of macro invocation
debug_assert!(true, "hello",); //~ ERROR: unexpected end of macro invocation
debug_assert_eq!(1, 1,); //  (fixed in 1.22)
debug_assert_ne!(1, 2,); //  (fixed in 1.22)
include!("dumdum.rs",); //~ ERROR: include! takes 1 argument
include_bytes!("dumdum.rs",); //~ ERROR: include_bytes! takes 1 argument
include_str!("dumdum.rs",); //~ ERROR: include_str! takes 1 argument
option_env!("PATH",); //~ ERROR: option_env! takes 1 argument
panic!("hello",); //~ ERROR: unexpected end of macro invocation
try!(Ok(()),); //~ ERROR: no rules expected the token `,`
unreachable!("hello",); //~ ERROR: unexpected end of macro invocation
writeln!(&mut stdout,); //~ ERROR: unexpected end of macro invocation

// This one is dubious since it's not "expression-like".
// That said, concat_idents! supports trailing commas...
cfg!(unix,); //~ ERROR: expected 1 cfg-pattern

// Not shown:
// * A great number of failing cases for the unstable `select!`
// * Cases that probably shouldn't succeed for `thread_local!`, but do anyways.

Notes:

  • Some macros support trailing commas even though their documented macro_rules! patterns suggest that they wouldn’t. This is because they’re actually compiler builtins, and the macros in std are just stubs for documentation purposes. (I think)

  • env! and option_env! amusingly differ in their trailing comma support

  • The fact that unimplemented!() takes format args is not only undocumented, but it also makes me sad; I’ve always wanted unimplemented!() to take a bunch of expressions and treat them as used.

  • thread_local! bizarrely supports omitting the last semicolon, similar to macro_rules!. (aside: this is above and beyond my least favorite feature of macro_rules!)


#2

Thanks for this detailed analysis!