From a6f70253ab65afae9cf4be530579243010bd47d2 Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Sat, 10 Sep 2022 16:19:36 +0400 Subject: [PATCH 1/6] Add unstable format_brace_macros option --- Configurations.md | 22 +- src/config/mod.rs | 5 +- src/macros.rs | 56 +++-- src/overflow.rs | 24 ++ src/parse/macros/mod.rs | 74 +++--- .../configs/format_brace_macros/false.rs | 210 +++++++++++++++++ .../configs/format_brace_macros/true.rs | 210 +++++++++++++++++ tests/source/extern.rs | 1 + tests/source/issue-2917/packed_simd.rs | 1 + tests/source/issue-3302.rs | 1 + tests/source/macros.rs | 1 + .../configs/format_brace_macros/false.rs | 213 +++++++++++++++++ .../configs/format_brace_macros/true.rs | 217 ++++++++++++++++++ tests/target/extern.rs | 1 + tests/target/issue-2917/packed_simd.rs | 1 + tests/target/issue-3302.rs | 1 + tests/target/macros.rs | 1 + 17 files changed, 985 insertions(+), 54 deletions(-) create mode 100644 tests/source/configs/format_brace_macros/false.rs create mode 100644 tests/source/configs/format_brace_macros/true.rs create mode 100644 tests/target/configs/format_brace_macros/false.rs create mode 100644 tests/target/configs/format_brace_macros/true.rs diff --git a/Configurations.md b/Configurations.md index e066553dc63..bcbbdf157f5 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1098,7 +1098,7 @@ See also [`format_macro_bodies`](#format_macro_bodies). ## `format_macro_bodies` -Format the bodies of macros. +Format the bodies of declarative macro definitions. - **Default value**: `true` - **Possible values**: `true`, `false` @@ -1128,6 +1128,26 @@ macro_rules! foo { See also [`format_macro_matchers`](#format_macro_matchers). +## `format_brace_macros` + +Format the contents of fn-like macro invocations that use brace delimiters. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No + +#### `true` (default): + +```rust +foo! { "bar" } +``` + +#### `false`: + +```rust +foo! {"bar"} +``` + ## `skip_macro_invocations` Skip formatting the bodies of macro invocations with the following names. diff --git a/src/config/mod.rs b/src/config/mod.rs index 14f27f3f8b6..d71eb94ea3a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -71,7 +71,9 @@ create_config! { format_strings: bool, false, false, "Format string literals where necessary"; format_macro_matchers: bool, false, false, "Format the metavariable matching patterns in macros"; - format_macro_bodies: bool, true, false, "Format the bodies of macros"; + format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions"; + format_brace_macros: bool, true, false, + "Format the contents of fn-like macro invocations that use brace delimiters"; skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, "Skip formatting the bodies of macros invoked with the following names."; hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, @@ -628,6 +630,7 @@ normalize_doc_attributes = false format_strings = false format_macro_matchers = false format_macro_bodies = true +format_brace_macros = true skip_macro_invocations = [] hex_literal_case = "Preserve" empty_item_single_line = true diff --git a/src/macros.rs b/src/macros.rs index 1bcb7774c94..7ad7d37ce66 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -209,7 +209,7 @@ fn rewrite_macro_inner( original_style }; - let ts = mac.args.inner_tokens(); + let mut ts = mac.args.inner_tokens(); let has_comment = contains_comment(context.snippet(mac.span())); if ts.is_empty() && !has_comment { return match style { @@ -232,23 +232,37 @@ fn rewrite_macro_inner( } } + if Delimiter::Brace == style && context.config.format_brace_macros() { + if let ast::MacArgs::Delimited(span, ..) = *mac.args { + ts = TokenStream::new(vec![TokenTree::Delimited(span, Delimiter::Brace, ts)]); + } + } + let ParsedMacroArgs { args: arg_vec, vec_with_semi, trailing_comma, - } = match parse_macro_args(context, ts, style, is_forced_bracket) { - Some(args) => args, - None => { - return return_macro_parse_failure_fallback( - context, - shape.indent, - position, - mac.span(), - ); + } = if Delimiter::Brace == style && !context.config.format_brace_macros() { + ParsedMacroArgs::default() + } else { + match parse_macro_args(context, ts, is_forced_bracket) { + Some(args) => args, + None => { + if Delimiter::Brace == style { + ParsedMacroArgs::default() + } else { + return return_macro_parse_failure_fallback( + context, + shape.indent, + position, + mac.span(), + ); + } + } } }; - if !arg_vec.is_empty() && arg_vec.iter().all(MacroArg::is_item) { + if !arg_vec.is_empty() && arg_vec.iter().all(MacroArg::is_item) && Delimiter::Brace != style { return rewrite_macro_with_items( context, &arg_vec, @@ -325,10 +339,24 @@ fn rewrite_macro_inner( } } Delimiter::Brace => { + let snippet = if arg_vec.is_empty() { + None + } else { + overflow::rewrite_undelimited( + context, + ¯o_name, + arg_vec.iter(), + shape, + mac.span(), + context.config.fn_call_width(), + None, + ) + } + .unwrap_or(context.snippet(mac.span()).into()); + // For macro invocations with braces, always put a space between - // the `macro_name!` and `{ /* macro_body */ }` but skip modifying - // anything in between the braces (for now). - let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); + // the `macro_name!` and `{ /* macro_body */ }`. + let snippet = snippet.trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)), None => Some(format!("{} {}", macro_name, snippet)), diff --git a/src/overflow.rs b/src/overflow.rs index 6bf8cd0c70b..4bebdfa1cf3 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -268,6 +268,30 @@ pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>( .rewrite(shape) } +pub(crate) fn rewrite_undelimited<'a, T: 'a + IntoOverflowableItem<'a>>( + context: &'a RewriteContext<'_>, + ident: &'a str, + items: impl Iterator, + shape: Shape, + span: Span, + item_max_width: usize, + force_separator_tactic: Option, +) -> Option { + Context::new( + context, + items, + ident, + shape, + span, + "", + "", + item_max_width, + force_separator_tactic, + None, + ) + .rewrite(shape) +} + pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>( context: &'a RewriteContext<'_>, ident: &'a str, diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 67f3985926e..35290cfe6ad 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{Delimiter, TokenKind}; +use rustc_ast::token::TokenKind; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser}; @@ -69,6 +69,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { None } +#[derive(Default)] pub(crate) struct ParsedMacroArgs { pub(crate) vec_with_semi: bool, pub(crate) trailing_comma: bool, @@ -95,7 +96,6 @@ fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { pub(crate) fn parse_macro_args( context: &RewriteContext<'_>, tokens: TokenStream, - style: Delimiter, forced_bracket: bool, ) -> Option { let mut parser = build_parser(context, tokens); @@ -103,51 +103,49 @@ pub(crate) fn parse_macro_args( let mut vec_with_semi = false; let mut trailing_comma = false; - if Delimiter::Brace != style { - loop { - if let Some(arg) = check_keyword(&mut parser) { - args.push(arg); - } else if let Some(arg) = parse_macro_arg(&mut parser) { - args.push(arg); - } else { - return None; - } + loop { + if let Some(arg) = check_keyword(&mut parser) { + args.push(arg); + } else if let Some(arg) = parse_macro_arg(&mut parser) { + args.push(arg); + } else { + return None; + } - match parser.token.kind { - TokenKind::Eof => break, - TokenKind::Comma => (), - TokenKind::Semi => { - // Try to parse `vec![expr; expr]` - if forced_bracket { - parser.bump(); - if parser.token.kind != TokenKind::Eof { - match parse_macro_arg(&mut parser) { - Some(arg) => { - args.push(arg); - parser.bump(); - if parser.token.kind == TokenKind::Eof && args.len() == 2 { - vec_with_semi = true; - break; - } - } - None => { - return None; + match parser.token.kind { + TokenKind::Eof => break, + TokenKind::Comma => (), + TokenKind::Semi => { + // Try to parse `vec![expr; expr]` + if forced_bracket { + parser.bump(); + if parser.token.kind != TokenKind::Eof { + match parse_macro_arg(&mut parser) { + Some(arg) => { + args.push(arg); + parser.bump(); + if parser.token.kind == TokenKind::Eof && args.len() == 2 { + vec_with_semi = true; + break; } } + None => { + return None; + } } } - return None; } - _ if args.last().map_or(false, MacroArg::is_item) => continue, - _ => return None, + return None; } + _ if args.last().map_or(false, MacroArg::is_item) => continue, + _ => return None, + } - parser.bump(); + parser.bump(); - if parser.token.kind == TokenKind::Eof { - trailing_comma = true; - break; - } + if parser.token.kind == TokenKind::Eof { + trailing_comma = true; + break; } } diff --git a/tests/source/configs/format_brace_macros/false.rs b/tests/source/configs/format_brace_macros/false.rs new file mode 100644 index 00000000000..d66c4c79541 --- /dev/null +++ b/tests/source/configs/format_brace_macros/false.rs @@ -0,0 +1,210 @@ +// rustfmt-format_brace_macros: false + +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn foo() { + f! {r#" + test + "#}; +} + +x! {()} +x! {#} +x! {ident} + +macro_rules! moo1 { + () => { + bar! { +" +" + } + }; +} + +macro_rules! moo2 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo4 { + () => { + bar! { +" + foo + bar +baz" + } + }; +} + +macro_rules! impl_from_vector { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $source:ident) => { + impl From<$source> for $id { + #[inline] + fn from(source: $source) -> Self { + fn static_assert_same_number_of_lanes() + where + T: crate::sealed::Simd, + U: crate::sealed::Simd, + { + } + use llvm::simd_cast; + static_assert_same_number_of_lanes::<$id, $source>(); + Simd(unsafe { simd_cast(source.0) }) + } + } + + // FIXME: `Into::into` is not inline, but due to + // the blanket impl in `std`, which is not + // marked `default`, we cannot override it here with + // specialization. + /* + impl Into<$id> for $source { + #[inline] + fn into(self) -> $id { + unsafe { simd_cast(self) } + } + } + */ + + test_if! { + $test_tt: + interpolate_idents! { + mod [$id _from_ $source] { + use super::*; + #[test] + fn from() { + assert_eq!($id::lanes(), $source::lanes()); + let source: $source = Default::default(); + let vec: $id = Default::default(); + + let e = $id::from(source); + assert_eq!(e, vec); + + let e: $id = source.into(); + assert_eq!(e, vec); + } + } + } + } + }; +} + +// https://github.com/rust-lang/rustfmt/issues/3434 +html! {
+Hello +
} + +// https://github.com/rust-lang/rustfmt/issues/3445 +wrapper! { +use std:: collections::HashMap; +pub fn the (a: sdf ) +{ + ( ) +} +} + +// https://github.com/rust-lang/rustfmt/issues/5254 +widget! { + /// A frame around content + /// + /// This widget provides a simple abstraction: drawing a frame around its + /// contents. + #[autoimpl(Deref, DerefMut on self.inner)] + #[autoimpl(class_traits where W: trait on self.inner)] + #[derive(Clone, Debug, Default)] + #[handler(msg = ::Msg)] + #[widget{ + layout = frame(self.inner, kas::theme::FrameStyle::Frame); + }] + pub struct Frame { + #[widget_core] + core: CoreData, + #[widget] + pub inner: W, + } + + // NOTE: `impl Self` is not valid Rust syntax, but rustfmt handles it fine + impl Self { + /// Construct a frame + #[inline] + pub fn new(inner: W) -> Self { + Frame { + core: Default::default(), + inner, + } + } + } +} + +// https://users.rust-lang.org/t/rustfmt-skips-macro-arguments/74807 +macro_rules! identity { + ($x:expr) => { + $x + }; +} + +fn main() { + foo("first very very long argument", "second very very long argument"); + identity! { foo("first very very long argument", "second very very long argument") }; +} + +#[tokio::main] +fn main() { + // This gets formatted as expected + foo( + "first very very long argument", + "second very very long argument", + ); + tokio::select! ( + _ = futures::future::ready(true) => { + // This is left unchanged + foo("first very very long argument", "second very very long argument") + } + ) +} + +// https://github.com/rust-lang/rustfmt/issues/4611 +// Note that this is not currently formatted. +tokio::select! { + result = reader => { + match result { + Ok(v) => { + eprintln!( + "message: {}", + v + ); + }, + Err(_) => { + eprintln!( + "error: {}", + e + ); + }, + } + }, + _ = writer => { + // Comment + eprintln!( + "completed: {}", + some_other_field + ); + } +} diff --git a/tests/source/configs/format_brace_macros/true.rs b/tests/source/configs/format_brace_macros/true.rs new file mode 100644 index 00000000000..8b5223eeddf --- /dev/null +++ b/tests/source/configs/format_brace_macros/true.rs @@ -0,0 +1,210 @@ +// rustfmt-format_brace_macros: true + +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn foo() { + f! {r#" + test + "#}; +} + +x! {()} +x! {#} +x! {ident} + +macro_rules! moo1 { + () => { + bar! { +" +" + } + }; +} + +macro_rules! moo2 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo4 { + () => { + bar! { +" + foo + bar +baz" + } + }; +} + +macro_rules! impl_from_vector { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $source:ident) => { + impl From<$source> for $id { + #[inline] + fn from(source: $source) -> Self { + fn static_assert_same_number_of_lanes() + where + T: crate::sealed::Simd, + U: crate::sealed::Simd, + { + } + use llvm::simd_cast; + static_assert_same_number_of_lanes::<$id, $source>(); + Simd(unsafe { simd_cast(source.0) }) + } + } + + // FIXME: `Into::into` is not inline, but due to + // the blanket impl in `std`, which is not + // marked `default`, we cannot override it here with + // specialization. + /* + impl Into<$id> for $source { + #[inline] + fn into(self) -> $id { + unsafe { simd_cast(self) } + } + } + */ + + test_if! { + $test_tt: + interpolate_idents! { + mod [$id _from_ $source] { + use super::*; + #[test] + fn from() { + assert_eq!($id::lanes(), $source::lanes()); + let source: $source = Default::default(); + let vec: $id = Default::default(); + + let e = $id::from(source); + assert_eq!(e, vec); + + let e: $id = source.into(); + assert_eq!(e, vec); + } + } + } + } + }; +} + +// https://github.com/rust-lang/rustfmt/issues/3434 +html! {
+Hello +
} + +// https://github.com/rust-lang/rustfmt/issues/3445 +wrapper! { +use std:: collections::HashMap; +pub fn the (a: sdf ) +{ + ( ) +} +} + +// https://github.com/rust-lang/rustfmt/issues/5254 +widget! { + /// A frame around content + /// + /// This widget provides a simple abstraction: drawing a frame around its + /// contents. + #[autoimpl(Deref, DerefMut on self.inner)] + #[autoimpl(class_traits where W: trait on self.inner)] + #[derive(Clone, Debug, Default)] + #[handler(msg = ::Msg)] + #[widget{ + layout = frame(self.inner, kas::theme::FrameStyle::Frame); + }] + pub struct Frame { + #[widget_core] + core: CoreData, + #[widget] + pub inner: W, + } + + // NOTE: `impl Self` is not valid Rust syntax, but rustfmt handles it fine + impl Self { + /// Construct a frame + #[inline] + pub fn new(inner: W) -> Self { + Frame { + core: Default::default(), + inner, + } + } + } +} + +// https://users.rust-lang.org/t/rustfmt-skips-macro-arguments/74807 +macro_rules! identity { + ($x:expr) => { + $x + }; +} + +fn main() { + foo("first very very long argument", "second very very long argument"); + identity! { foo("first very very long argument", "second very very long argument") }; +} + +#[tokio::main] +fn main() { + // This gets formatted as expected + foo( + "first very very long argument", + "second very very long argument", + ); + tokio::select! ( + _ = futures::future::ready(true) => { + // This is left unchanged + foo("first very very long argument", "second very very long argument") + } + ) +} + +// https://github.com/rust-lang/rustfmt/issues/4611 +// Note that this is not currently formatted. +tokio::select! { + result = reader => { + match result { + Ok(v) => { + eprintln!( + "message: {}", + v + ); + }, + Err(_) => { + eprintln!( + "error: {}", + e + ); + }, + } + }, + _ = writer => { + // Comment + eprintln!( + "completed: {}", + some_other_field + ); + } +} diff --git a/tests/source/extern.rs b/tests/source/extern.rs index f51ba6e98c9..301a053bc18 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -1,4 +1,5 @@ // rustfmt-normalize_comments: true +// rustfmt-format_brace_macros: false extern crate foo ; extern crate foo as bar ; diff --git a/tests/source/issue-2917/packed_simd.rs b/tests/source/issue-2917/packed_simd.rs index afa9e67c8ab..06ef2586e39 100644 --- a/tests/source/issue-2917/packed_simd.rs +++ b/tests/source/issue-2917/packed_simd.rs @@ -1,4 +1,5 @@ // rustfmt-wrap_comments: true +// rustfmt-format_brace_macros: false //! Implements `From` and `Into` for vector types. macro_rules! impl_from_vector { diff --git a/tests/source/issue-3302.rs b/tests/source/issue-3302.rs index c037584fd71..d61a9f80205 100644 --- a/tests/source/issue-3302.rs +++ b/tests/source/issue-3302.rs @@ -1,4 +1,5 @@ // rustfmt-version: Two +// rustfmt-format_brace_macros: false macro_rules! moo1 { () => { diff --git a/tests/source/macros.rs b/tests/source/macros.rs index 3b286579ca8..22b7d8b093e 100644 --- a/tests/source/macros.rs +++ b/tests/source/macros.rs @@ -1,5 +1,6 @@ // rustfmt-normalize_comments: true // rustfmt-format_macro_matchers: true +// rustfmt-format_brace_macros: false itemmacro!(this, is.now() .formatted(yay)); itemmacro!(really, long.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb() .is.formatted()); diff --git a/tests/target/configs/format_brace_macros/false.rs b/tests/target/configs/format_brace_macros/false.rs new file mode 100644 index 00000000000..2e2c4a6b3e0 --- /dev/null +++ b/tests/target/configs/format_brace_macros/false.rs @@ -0,0 +1,213 @@ +// rustfmt-format_brace_macros: false + +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn foo() { + f! {r#" + test + "#}; +} + +x! {()} +x! {#} +x! {ident} + +macro_rules! moo1 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo2 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo4 { + () => { + bar! { + " + foo + bar +baz" + } + }; +} + +macro_rules! impl_from_vector { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $source:ident) => { + impl From<$source> for $id { + #[inline] + fn from(source: $source) -> Self { + fn static_assert_same_number_of_lanes() + where + T: crate::sealed::Simd, + U: crate::sealed::Simd, + { + } + use llvm::simd_cast; + static_assert_same_number_of_lanes::<$id, $source>(); + Simd(unsafe { simd_cast(source.0) }) + } + } + + // FIXME: `Into::into` is not inline, but due to + // the blanket impl in `std`, which is not + // marked `default`, we cannot override it here with + // specialization. + /* + impl Into<$id> for $source { + #[inline] + fn into(self) -> $id { + unsafe { simd_cast(self) } + } + } + */ + + test_if! { + $test_tt: + interpolate_idents! { + mod [$id _from_ $source] { + use super::*; + #[test] + fn from() { + assert_eq!($id::lanes(), $source::lanes()); + let source: $source = Default::default(); + let vec: $id = Default::default(); + + let e = $id::from(source); + assert_eq!(e, vec); + + let e: $id = source.into(); + assert_eq!(e, vec); + } + } + } + } + }; +} + +// https://github.com/rust-lang/rustfmt/issues/3434 +html! {
+Hello +
} + +// https://github.com/rust-lang/rustfmt/issues/3445 +wrapper! { +use std:: collections::HashMap; +pub fn the (a: sdf ) +{ + ( ) +} +} + +// https://github.com/rust-lang/rustfmt/issues/5254 +widget! { + /// A frame around content + /// + /// This widget provides a simple abstraction: drawing a frame around its + /// contents. + #[autoimpl(Deref, DerefMut on self.inner)] + #[autoimpl(class_traits where W: trait on self.inner)] + #[derive(Clone, Debug, Default)] + #[handler(msg = ::Msg)] + #[widget{ + layout = frame(self.inner, kas::theme::FrameStyle::Frame); + }] + pub struct Frame { + #[widget_core] + core: CoreData, + #[widget] + pub inner: W, + } + + // NOTE: `impl Self` is not valid Rust syntax, but rustfmt handles it fine + impl Self { + /// Construct a frame + #[inline] + pub fn new(inner: W) -> Self { + Frame { + core: Default::default(), + inner, + } + } + } +} + +// https://users.rust-lang.org/t/rustfmt-skips-macro-arguments/74807 +macro_rules! identity { + ($x:expr) => { + $x + }; +} + +fn main() { + foo( + "first very very long argument", + "second very very long argument", + ); + identity! { foo("first very very long argument", "second very very long argument") }; +} + +#[tokio::main] +fn main() { + // This gets formatted as expected + foo( + "first very very long argument", + "second very very long argument", + ); + tokio::select! ( + _ = futures::future::ready(true) => { + // This is left unchanged + foo("first very very long argument", "second very very long argument") + } + ) +} + +// https://github.com/rust-lang/rustfmt/issues/4611 +// Note that this is not currently formatted. +tokio::select! { + result = reader => { + match result { + Ok(v) => { + eprintln!( + "message: {}", + v + ); + }, + Err(_) => { + eprintln!( + "error: {}", + e + ); + }, + } + }, + _ = writer => { + // Comment + eprintln!( + "completed: {}", + some_other_field + ); + } +} diff --git a/tests/target/configs/format_brace_macros/true.rs b/tests/target/configs/format_brace_macros/true.rs new file mode 100644 index 00000000000..74ef70f91e6 --- /dev/null +++ b/tests/target/configs/format_brace_macros/true.rs @@ -0,0 +1,217 @@ +// rustfmt-format_brace_macros: true + +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn foo() { + f! { + r#" + test + "# + }; +} + +x! { () } +x! {#} +x! { ident } + +macro_rules! moo1 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo2 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo4 { + () => { + bar! { + " + foo + bar +baz" + } + }; +} + +macro_rules! impl_from_vector { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $source:ident) => { + impl From<$source> for $id { + #[inline] + fn from(source: $source) -> Self { + fn static_assert_same_number_of_lanes() + where + T: crate::sealed::Simd, + U: crate::sealed::Simd, + { + } + use llvm::simd_cast; + static_assert_same_number_of_lanes::<$id, $source>(); + Simd(unsafe { simd_cast(source.0) }) + } + } + + // FIXME: `Into::into` is not inline, but due to + // the blanket impl in `std`, which is not + // marked `default`, we cannot override it here with + // specialization. + /* + impl Into<$id> for $source { + #[inline] + fn into(self) -> $id { + unsafe { simd_cast(self) } + } + } + */ + + test_if! { + $test_tt: interpolate_idents! { + mod [$id _from_ $source] { + use super::*; + #[test] + fn from() { + assert_eq!($id::lanes(), $source::lanes()); + let source: $source = Default::default(); + let vec: $id = Default::default(); + + let e = $id::from(source); + assert_eq!(e, vec); + + let e: $id = source.into(); + assert_eq!(e, vec); + } + } + } + } + }; +} + +// https://github.com/rust-lang/rustfmt/issues/3434 +html! {
+Hello +
} + +// https://github.com/rust-lang/rustfmt/issues/3445 +wrapper! { + use std::collections::HashMap; + pub fn the(a: sdf) { + () + } +} + +// https://github.com/rust-lang/rustfmt/issues/5254 +widget! { + /// A frame around content + /// + /// This widget provides a simple abstraction: drawing a frame around its + /// contents. + #[autoimpl(Deref, DerefMut on self.inner)] + #[autoimpl(class_traits where W: trait on self.inner)] + #[derive(Clone, Debug, Default)] + #[handler(msg = ::Msg)] + #[widget{ + layout = frame(self.inner, kas::theme::FrameStyle::Frame); + }] + pub struct Frame { + #[widget_core] + core: CoreData, + #[widget] + pub inner: W, + } + + // NOTE: `impl Self` is not valid Rust syntax, but rustfmt handles it fine + impl Self { + /// Construct a frame + #[inline] + pub fn new(inner: W) -> Self { + Frame { + core: Default::default(), + inner, + } + } + } +} + +// https://users.rust-lang.org/t/rustfmt-skips-macro-arguments/74807 +macro_rules! identity { + ($x:expr) => { + $x + }; +} + +fn main() { + foo( + "first very very long argument", + "second very very long argument", + ); + identity! { + foo( + "first very very long argument", + "second very very long argument", + ) + }; +} + +#[tokio::main] +fn main() { + // This gets formatted as expected + foo( + "first very very long argument", + "second very very long argument", + ); + tokio::select! ( + _ = futures::future::ready(true) => { + // This is left unchanged + foo("first very very long argument", "second very very long argument") + } + ) +} + +// https://github.com/rust-lang/rustfmt/issues/4611 +// Note that this is not currently formatted. +tokio::select! { + result = reader => { + match result { + Ok(v) => { + eprintln!( + "message: {}", + v + ); + }, + Err(_) => { + eprintln!( + "error: {}", + e + ); + }, + } + }, + _ = writer => { + // Comment + eprintln!( + "completed: {}", + some_other_field + ); + } +} diff --git a/tests/target/extern.rs b/tests/target/extern.rs index d1741360cfd..a927684e0f0 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -1,4 +1,5 @@ // rustfmt-normalize_comments: true +// rustfmt-format_brace_macros: false extern crate foo; extern crate foo as bar; diff --git a/tests/target/issue-2917/packed_simd.rs b/tests/target/issue-2917/packed_simd.rs index 274614f83e2..cccfdf2e083 100644 --- a/tests/target/issue-2917/packed_simd.rs +++ b/tests/target/issue-2917/packed_simd.rs @@ -1,4 +1,5 @@ // rustfmt-wrap_comments: true +// rustfmt-format_brace_macros: false //! Implements `From` and `Into` for vector types. macro_rules! impl_from_vector { diff --git a/tests/target/issue-3302.rs b/tests/target/issue-3302.rs index 146cb983819..3cb6169cc1a 100644 --- a/tests/target/issue-3302.rs +++ b/tests/target/issue-3302.rs @@ -1,4 +1,5 @@ // rustfmt-version: Two +// rustfmt-format_brace_macros: false macro_rules! moo1 { () => { diff --git a/tests/target/macros.rs b/tests/target/macros.rs index e930b5037d9..85e9886a4df 100644 --- a/tests/target/macros.rs +++ b/tests/target/macros.rs @@ -1,5 +1,6 @@ // rustfmt-normalize_comments: true // rustfmt-format_macro_matchers: true +// rustfmt-format_brace_macros: false itemmacro!(this, is.now().formatted(yay)); itemmacro!( From 8ca09390dac81cc08a940b50fc0d1e2416aabfb0 Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:35:13 +0400 Subject: [PATCH 2/6] Update for latest master https://github.com/rust-lang/rust/pull/104559 --- src/macros.rs | 8 +++++--- tests/source/configs/format_brace_macros/true.rs | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 0aa9242e2b4..1de2f6d2b18 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -233,9 +233,11 @@ fn rewrite_macro_inner( } if Delimiter::Brace == style && context.config.format_brace_macros() { - if let ast::MacArgs::Delimited(span, ..) = *mac.args { - ts = TokenStream::new(vec![TokenTree::Delimited(span, Delimiter::Brace, ts)]); - } + ts = TokenStream::new(vec![TokenTree::Delimited( + mac.args.dspan, + Delimiter::Brace, + ts, + )]); } let ParsedMacroArgs { diff --git a/tests/source/configs/format_brace_macros/true.rs b/tests/source/configs/format_brace_macros/true.rs index 8b5223eeddf..757e8abff8d 100644 --- a/tests/source/configs/format_brace_macros/true.rs +++ b/tests/source/configs/format_brace_macros/true.rs @@ -85,8 +85,7 @@ macro_rules! impl_from_vector { */ test_if! { - $test_tt: - interpolate_idents! { + $test_tt: interpolate_idents! { mod [$id _from_ $source] { use super::*; #[test] From 1e148fb58ccdfd977e8b8b41564a0fc6d0b0a99e Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:09:21 +0400 Subject: [PATCH 3/6] Default format_brace_macros option to off https://github.com/rust-lang/rustfmt/pull/5538#issuecomment-1272367684 --- Configurations.md | 2 +- src/config/mod.rs | 4 ++-- tests/source/extern.rs | 1 - tests/source/issue-2917/packed_simd.rs | 1 - tests/source/issue-3302.rs | 1 - tests/source/macros.rs | 1 - tests/target/extern.rs | 1 - tests/target/issue-2917/packed_simd.rs | 1 - tests/target/issue-3302.rs | 1 - tests/target/macros.rs | 1 - 10 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Configurations.md b/Configurations.md index 8bfebacb411..8e8c010c687 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1132,7 +1132,7 @@ See also [`format_macro_matchers`](#format_macro_matchers). Format the contents of fn-like macro invocations that use brace delimiters. -- **Default value**: `true` +- **Default value**: `false` - **Possible values**: `true`, `false` - **Stable**: No diff --git a/src/config/mod.rs b/src/config/mod.rs index 523ef974b8d..5719ed8fd47 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -75,7 +75,7 @@ create_config! { format_macro_matchers: bool, false, false, "Format the metavariable matching patterns in macros"; format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions"; - format_brace_macros: bool, true, false, + format_brace_macros: bool, false, false, "Format the contents of fn-like macro invocations that use brace delimiters"; skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, "Skip formatting the bodies of macros invoked with the following names."; @@ -634,7 +634,7 @@ normalize_doc_attributes = false format_strings = false format_macro_matchers = false format_macro_bodies = true -format_brace_macros = true +format_brace_macros = false skip_macro_invocations = [] hex_literal_case = "Preserve" empty_item_single_line = true diff --git a/tests/source/extern.rs b/tests/source/extern.rs index 301a053bc18..f51ba6e98c9 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -1,5 +1,4 @@ // rustfmt-normalize_comments: true -// rustfmt-format_brace_macros: false extern crate foo ; extern crate foo as bar ; diff --git a/tests/source/issue-2917/packed_simd.rs b/tests/source/issue-2917/packed_simd.rs index 06ef2586e39..afa9e67c8ab 100644 --- a/tests/source/issue-2917/packed_simd.rs +++ b/tests/source/issue-2917/packed_simd.rs @@ -1,5 +1,4 @@ // rustfmt-wrap_comments: true -// rustfmt-format_brace_macros: false //! Implements `From` and `Into` for vector types. macro_rules! impl_from_vector { diff --git a/tests/source/issue-3302.rs b/tests/source/issue-3302.rs index d61a9f80205..c037584fd71 100644 --- a/tests/source/issue-3302.rs +++ b/tests/source/issue-3302.rs @@ -1,5 +1,4 @@ // rustfmt-version: Two -// rustfmt-format_brace_macros: false macro_rules! moo1 { () => { diff --git a/tests/source/macros.rs b/tests/source/macros.rs index 22b7d8b093e..3b286579ca8 100644 --- a/tests/source/macros.rs +++ b/tests/source/macros.rs @@ -1,6 +1,5 @@ // rustfmt-normalize_comments: true // rustfmt-format_macro_matchers: true -// rustfmt-format_brace_macros: false itemmacro!(this, is.now() .formatted(yay)); itemmacro!(really, long.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb() .is.formatted()); diff --git a/tests/target/extern.rs b/tests/target/extern.rs index a927684e0f0..d1741360cfd 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -1,5 +1,4 @@ // rustfmt-normalize_comments: true -// rustfmt-format_brace_macros: false extern crate foo; extern crate foo as bar; diff --git a/tests/target/issue-2917/packed_simd.rs b/tests/target/issue-2917/packed_simd.rs index cccfdf2e083..274614f83e2 100644 --- a/tests/target/issue-2917/packed_simd.rs +++ b/tests/target/issue-2917/packed_simd.rs @@ -1,5 +1,4 @@ // rustfmt-wrap_comments: true -// rustfmt-format_brace_macros: false //! Implements `From` and `Into` for vector types. macro_rules! impl_from_vector { diff --git a/tests/target/issue-3302.rs b/tests/target/issue-3302.rs index 3cb6169cc1a..146cb983819 100644 --- a/tests/target/issue-3302.rs +++ b/tests/target/issue-3302.rs @@ -1,5 +1,4 @@ // rustfmt-version: Two -// rustfmt-format_brace_macros: false macro_rules! moo1 { () => { diff --git a/tests/target/macros.rs b/tests/target/macros.rs index 33cf23d1cef..7b4574349df 100644 --- a/tests/target/macros.rs +++ b/tests/target/macros.rs @@ -1,6 +1,5 @@ // rustfmt-normalize_comments: true // rustfmt-format_macro_matchers: true -// rustfmt-format_brace_macros: false itemmacro!(this, is.now().formatted(yay)); itemmacro!( From a5e146365a13de9417803f9d64b1362f480533dc Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:12:11 +0400 Subject: [PATCH 4/6] Fix Configurations.md --- Configurations.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Configurations.md b/Configurations.md index 8e8c010c687..dd83cd61d94 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1136,16 +1136,16 @@ Format the contents of fn-like macro invocations that use brace delimiters. - **Possible values**: `true`, `false` - **Stable**: No -#### `true` (default): +#### `false` (default): ```rust -foo! { "bar" } +foo! {"bar"} ``` -#### `false`: +#### `true`: ```rust -foo! {"bar"} +foo! { "bar" } ``` ## `skip_macro_invocations` From d11223f230430eec92a7a4a51e55b061ec8092db Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:58:23 +0400 Subject: [PATCH 5/6] Only format if TokenStream does not change --- src/macros.rs | 21 ++++++++++++++++--- .../configs/format_brace_macros/true.rs | 5 +++++ .../configs/format_brace_macros/true.rs | 8 +++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 1de2f6d2b18..0725cf88310 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -247,7 +247,7 @@ fn rewrite_macro_inner( } = if Delimiter::Brace == style && !context.config.format_brace_macros() { ParsedMacroArgs::default() } else { - match parse_macro_args(context, ts, is_forced_bracket) { + match parse_macro_args(context, ts.clone(), is_forced_bracket) { Some(args) => args, None => { if Delimiter::Brace == style { @@ -341,7 +341,7 @@ fn rewrite_macro_inner( } } Delimiter::Brace => { - let snippet = if arg_vec.is_empty() { + let snippet = if arg_vec.is_empty() || !context.config.format_brace_macros() { None } else { overflow::rewrite_undelimited( @@ -356,9 +356,24 @@ fn rewrite_macro_inner( } .unwrap_or(context.snippet(mac.span()).into()); + let mut snippet = snippet.trim_start_matches(|c| c != '{'); + + // Only format if the rewritten TokenStream is the same as the original TokenStream. + if context.config.format_brace_macros() { + let rewritten_ts = rustc_parse::parse_stream_from_source_str( + rustc_span::FileName::anon_source_code(""), + snippet.to_string(), + &rustc_session::parse::ParseSess::with_silent_emitter(None), + None, + ); + + if !ts.eq_unspanned(&rewritten_ts) { + snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); + } + } + // For macro invocations with braces, always put a space between // the `macro_name!` and `{ /* macro_body */ }`. - let snippet = snippet.trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { Some(macro_body) => Some(format!("{macro_name} {macro_body}")), None => Some(format!("{macro_name} {snippet}")), diff --git a/tests/source/configs/format_brace_macros/true.rs b/tests/source/configs/format_brace_macros/true.rs index 757e8abff8d..e5286390b81 100644 --- a/tests/source/configs/format_brace_macros/true.rs +++ b/tests/source/configs/format_brace_macros/true.rs @@ -165,6 +165,11 @@ fn main() { identity! { foo("first very very long argument", "second very very long argument") }; } +fn main() { + foo("first very very long argument", "second very very long argument"); + identity! { foo("first very very long argument", "second very very long argument", ) }; +} + #[tokio::main] fn main() { // This gets formatted as expected diff --git a/tests/target/configs/format_brace_macros/true.rs b/tests/target/configs/format_brace_macros/true.rs index 74ef70f91e6..137f4ecf7d4 100644 --- a/tests/target/configs/format_brace_macros/true.rs +++ b/tests/target/configs/format_brace_macros/true.rs @@ -160,6 +160,14 @@ macro_rules! identity { }; } +fn main() { + foo( + "first very very long argument", + "second very very long argument", + ); + identity! { foo("first very very long argument", "second very very long argument") }; +} + fn main() { foo( "first very very long argument", From 9a532044980d55ab28c58b55a9fd3f9ed808726c Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Sat, 27 Jan 2024 08:02:06 -0800 Subject: [PATCH 6/6] Update for latest master --- src/macros.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index a3ec3e4a21f..74eeb689e95 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -13,7 +13,7 @@ use std::collections::HashMap; use std::panic::{catch_unwind, AssertUnwindSafe}; use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{DelimSpacing, RefTokenTreeCursor, Spacing, TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{ @@ -235,6 +235,7 @@ fn rewrite_macro_inner( if Delimiter::Brace == style && context.config.format_brace_macros() { ts = TokenStream::new(vec![TokenTree::Delimited( mac.args.dspan, + DelimSpacing::new(Spacing::Alone, Spacing::Alone), Delimiter::Brace, ts, )]);