diff --git a/Configurations.md b/Configurations.md index 78545c6187a..a7e54358a00 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1138,6 +1138,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**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No + +#### `false` (default): + +```rust +foo! {"bar"} +``` + +#### `true`: + +```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 9484b2e5829..2631dc9abf3 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -75,6 +75,8 @@ 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, 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."; hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, @@ -643,6 +645,7 @@ normalize_doc_attributes = false format_strings = false format_macro_matchers = false format_macro_bodies = true +format_brace_macros = false skip_macro_invocations = [] hex_literal_case = "Preserve" empty_item_single_line = true diff --git a/src/macros.rs b/src/macros.rs index ce7d2ce47e0..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::{ @@ -209,7 +209,7 @@ fn rewrite_macro_inner( original_style }; - let ts = mac.args.tokens.clone(); + let mut ts = mac.args.tokens.clone(); let has_comment = contains_comment(context.snippet(mac.span())); if ts.is_empty() && !has_comment { return match style { @@ -232,23 +232,40 @@ 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, + )]); + } + 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.clone(), 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 +342,39 @@ fn rewrite_macro_inner( } } Delimiter::Brace => { + let snippet = if arg_vec.is_empty() || !context.config.format_brace_macros() { + 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()); + + 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 */ }` 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 */ }`. 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 c44f3788d97..e62bf7c38a1 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -280,6 +280,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 2dd2622174f..c680bcd9175 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..e5286390b81 --- /dev/null +++ b/tests/source/configs/format_brace_macros/true.rs @@ -0,0 +1,214 @@ +// 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") }; +} + +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/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..137f4ecf7d4 --- /dev/null +++ b/tests/target/configs/format_brace_macros/true.rs @@ -0,0 +1,225 @@ +// 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") }; +} + +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 + ); + } +}