diff --git a/src/expr.rs b/src/expr.rs index 91f3cc81bae..31419a1b228 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1261,7 +1261,10 @@ fn rewrite_int_lit( ) } -fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option { +pub(crate) fn choose_separator_tactic( + context: &RewriteContext<'_>, + span: Span, +) -> Option { if context.inside_macro() { if span_ends_with_comma(context, span) { Some(SeparatorTactic::Always) @@ -1888,9 +1891,9 @@ impl<'ast> RhsAssignKind<'ast> { matches!( kind, ast::ExprKind::Try(..) - | ast::ExprKind::Field(..) - | ast::ExprKind::MethodCall(..) - | ast::ExprKind::Await(_) + | ast::ExprKind::Field(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::Await(_) ) } _ => false, diff --git a/src/items.rs b/src/items.rs index 3c5293b6bf5..70fa86a33aa 100644 --- a/src/items.rs +++ b/src/items.rs @@ -3324,10 +3324,7 @@ pub(crate) fn rewrite_extern_crate( /// Returns `true` for `mod foo;`, false for `mod foo { .. }`. pub(crate) fn is_mod_decl(item: &ast::Item) -> bool { - !matches!( - item.kind, - ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _)) - ) + !matches!(item.kind, ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _))) } pub(crate) fn is_use_item(item: &ast::Item) -> bool { diff --git a/src/macros.rs b/src/macros.rs index d58f7547fef..f224d842f4d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,11 +24,13 @@ use rustc_span::{ use crate::comment::{ contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, }; -use crate::config::lists::*; -use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; +use crate::config::{lists::*, Version}; +use crate::expr::{choose_separator_tactic, rewrite_array, rewrite_assign_rhs, RhsAssignKind}; use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::matches::rewrite_guard; use crate::overflow; use crate::parse::macros::lazy_static::parse_lazy_static; +use crate::parse::macros::matches::{parse_matches, MatchesMacroItem}; use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; @@ -230,6 +232,10 @@ fn rewrite_macro_inner( if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) { return success; } + } else if macro_name == "matches!" && context.config.version() == Version::Two { + if let success @ Some(..) = format_matches(context, shape, ¯o_name, mac) { + return success; + } } let ParsedMacroArgs { @@ -585,15 +591,12 @@ impl MacroArgKind { matches!( *self, MacroArgKind::Repeat(Delimiter::Brace, _, _, _) - | MacroArgKind::Delimited(Delimiter::Brace, _) + | MacroArgKind::Delimited(Delimiter::Brace, _) ) } fn starts_with_dollar(&self) -> bool { - matches!( - *self, - MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..) - ) + matches!(*self, MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..)) } fn ends_with_space(&self) -> bool { @@ -1046,10 +1049,7 @@ fn force_space_before(tok: &TokenKind) -> bool { } fn ident_like(tok: &Token) -> bool { - matches!( - tok.kind, - TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_) - ) + matches!(tok.kind, TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_)) } fn next_space(tok: &TokenKind) -> SpaceState { @@ -1313,6 +1313,59 @@ impl MacroBranch { } } +impl Rewrite for MatchesMacroItem { + fn rewrite(&self, context: &RewriteContext<'_>, shape: crate::shape::Shape) -> Option { + match self { + Self::Expr(expr) => expr.rewrite(context, shape), + Self::Arm(pat, guard) => { + let pats_str = pat.rewrite(context, shape)?; + let guard_str = rewrite_guard(context, guard, shape, &pats_str)?; + Some(pats_str + &guard_str) + } + } + } +} + +/// Format `matches!` from +/// +/// # Expected syntax +/// +/// ```text +/// matches!(expr, pat) +/// matches!(expr, pat if expr) +/// ``` +fn format_matches( + context: &RewriteContext<'_>, + shape: Shape, + name: &str, + mac: &ast::MacCall, +) -> Option { + let span = mac.span(); + let matches = parse_matches(context, mac.args.tokens.clone())?.items(); + let force_separator_tactic = choose_separator_tactic(context, span); + match mac.args.delim { + ast::MacDelimiter::Parenthesis => overflow::rewrite_with_parens( + context, + name, + matches.iter(), + shape, + span, + shape.width, + force_separator_tactic, + ), + ast::MacDelimiter::Bracket => overflow::rewrite_with_square_brackets( + context, + name, + matches.iter(), + shape, + span, + force_separator_tactic, + None, + ), + ast::MacDelimiter::Brace => None, + } +} + /// Format `lazy_static!` from . /// /// # Expected syntax diff --git a/src/matches.rs b/src/matches.rs index 85d9c5d2b9b..1e9f057414a 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -251,15 +251,7 @@ fn rewrite_match_arm( let pats_str = arm.pat.rewrite(context, pat_shape)?; // Guard - let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces(); - let new_line_guard = pats_str.contains('\n') && !block_like_pat; - let guard_str = rewrite_guard( - context, - &arm.guard, - shape, - trimmed_last_line_width(&pats_str), - new_line_guard, - )?; + let guard_str = rewrite_guard(context, &arm.guard, shape, &pats_str)?; let lhs_str = combine_strs_with_missing_comments( context, @@ -512,8 +504,26 @@ fn rewrite_match_body( } } +pub(crate) fn rewrite_guard( + context: &RewriteContext<'_>, + guard: &Option>, + shape: Shape, + pattern_str: &str, +) -> Option { + let last_line_pattern_width = trimmed_last_line_width(pattern_str); + let block_like_pat = last_line_pattern_width <= context.config.tab_spaces(); + let new_line_guard = pattern_str.contains('\n') && !block_like_pat; + rewrite_guard_inner( + context, + guard, + shape, + last_line_pattern_width, + new_line_guard, + ) +} + // The `if ...` guard on a match arm. -fn rewrite_guard( +fn rewrite_guard_inner( context: &RewriteContext<'_>, guard: &Option>, shape: Shape, diff --git a/src/overflow.rs b/src/overflow.rs index af0b95430a1..bce1d54a4ef 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -18,6 +18,7 @@ use crate::lists::{ definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, }; use crate::macros::MacroArg; +use crate::parse::macros::matches::MatchesMacroItem; use crate::patterns::{can_be_overflowed_pat, TuplePatField}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; @@ -76,6 +77,7 @@ pub(crate) enum OverflowableItem<'a> { TuplePatField(&'a TuplePatField<'a>), Ty(&'a ast::Ty), Pat(&'a ast::Pat), + MatchesMacroItem(&'a MatchesMacroItem), } impl<'a> Rewrite for OverflowableItem<'a> { @@ -116,6 +118,7 @@ impl<'a> OverflowableItem<'a> { OverflowableItem::TuplePatField(pat) => f(*pat), OverflowableItem::Ty(ty) => f(*ty), OverflowableItem::Pat(pat) => f(*pat), + OverflowableItem::MatchesMacroItem(item) => f(*item), } } @@ -137,7 +140,9 @@ impl<'a> OverflowableItem<'a> { pub(crate) fn is_expr(&self) -> bool { matches!( self, - OverflowableItem::Expr(..) | OverflowableItem::MacroArg(MacroArg::Expr(..)) + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchesMacroItem(MatchesMacroItem::Expr(..)) ) } @@ -153,6 +158,7 @@ impl<'a> OverflowableItem<'a> { match self { OverflowableItem::Expr(expr) => Some(expr), OverflowableItem::MacroArg(MacroArg::Expr(ref expr)) => Some(expr), + OverflowableItem::MatchesMacroItem(MatchesMacroItem::Expr(expr)) => Some(expr), _ => None, } } @@ -233,7 +239,10 @@ macro_rules! impl_into_overflowable_item_for_rustfmt_types { } impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, FieldDef, Ty, Pat); -impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]); +impl_into_overflowable_item_for_rustfmt_types!( + [MacroArg, MatchesMacroItem], + [SegmentParam, TuplePatField] +); pub(crate) fn into_overflowable_list<'a, T>( iter: impl Iterator, diff --git a/src/parse/macros/matches.rs b/src/parse/macros/matches.rs new file mode 100644 index 00000000000..42ff6a8224a --- /dev/null +++ b/src/parse/macros/matches.rs @@ -0,0 +1,69 @@ +use rustc_ast::ast; +use rustc_ast::ptr::P; +use rustc_ast::token::TokenKind; +use rustc_ast::tokenstream::TokenStream; +use rustc_parse::parser::{CommaRecoveryMode, RecoverColon, RecoverComma}; +use rustc_span::symbol::kw; + +use super::is_token_tree_comma; +use crate::rewrite::RewriteContext; + +#[derive(Debug)] +pub(crate) struct Matches { + pub(crate) expr: P, + pub(crate) pat: P, + pub(crate) guard: Option>, +} + +/// Parse matches! from +pub(crate) fn parse_matches(context: &RewriteContext<'_>, ts: TokenStream) -> Option { + let mut cursor = ts.trees().peekable(); + // remove trailing commmas from the TokenStream since they lead to errors when parsing ast::Pat + // using parse_pat_allow_top_alt below since the parser isn't expecting a trailing comma. + // This is only an issue when the `ast::Pat` is not followed by a guard. In either case it's ok + // to remove the comma from the stream since we don't need it to parse into a Matches struct + let mut token_trees = vec![]; + while let Some(tt) = cursor.next() { + let is_last = cursor.peek().is_none(); + if !(is_last && is_token_tree_comma(tt)) { + token_trees.push(tt.clone()) + } + } + + let ts = token_trees.into_iter().collect(); + let mut parser = super::build_parser(context, ts); + let expr = parser.parse_expr().ok()?; + + parser.eat(&TokenKind::Comma); + + let pat = parser + .parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::EitherTupleOrPipe, + ) + .ok()?; + + let guard = if parser.eat_keyword(kw::If) { + Some(parser.parse_expr().ok()?) + } else { + None + }; + Some(Matches { expr, pat, guard }) +} + +impl Matches { + pub(crate) fn items(self) -> [MatchesMacroItem; 2] { + [ + MatchesMacroItem::Expr(self.expr), + MatchesMacroItem::Arm(self.pat, self.guard), + ] + } +} + +#[derive(Debug)] +pub(crate) enum MatchesMacroItem { + Expr(P), + Arm(P, Option>), +} diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 67f3985926e..7def0e07cb0 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -1,5 +1,5 @@ use rustc_ast::token::{Delimiter, TokenKind}; -use rustc_ast::tokenstream::TokenStream; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; @@ -13,6 +13,7 @@ use crate::rewrite::RewriteContext; pub(crate) mod asm; pub(crate) mod cfg_if; pub(crate) mod lazy_static; +pub(crate) mod matches; fn build_stream_parser<'a>(sess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { stream_to_parser(sess, tokens, MACRO_ARGUMENTS) @@ -92,6 +93,11 @@ fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { None } +/// Helper function to determine if a tokentree is a comma +pub(crate) fn is_token_tree_comma(tt: &TokenTree) -> bool { + matches!(tt, TokenTree::Token(token, _) if token.kind == TokenKind::Comma) +} + pub(crate) fn parse_macro_args( context: &RewriteContext<'_>, tokens: TokenStream, diff --git a/src/spanned.rs b/src/spanned.rs index 2136cfeae1a..23ceb61e673 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -4,6 +4,7 @@ use rustc_ast::{ast, ptr}; use rustc_span::{source_map, Span}; use crate::macros::MacroArg; +use crate::parse::macros::matches::MatchesMacroItem; use crate::utils::{mk_sp, outer_attributes}; /// Spanned returns a span including attributes, if available. @@ -197,3 +198,13 @@ impl Spanned for ast::NestedMetaItem { self.span() } } + +impl Spanned for MatchesMacroItem { + fn span(&self) -> rustc_span::Span { + match self { + Self::Expr(expr) => expr.span, + Self::Arm(pat, None) => pat.span, + Self::Arm(pat, Some(guard)) => mk_sp(pat.span.lo(), guard.span.hi()), + } + } +} diff --git a/tests/source/issue_5709.rs b/tests/source/issue_5709.rs new file mode 100644 index 00000000000..0ae790f2b76 --- /dev/null +++ b/tests/source/issue_5709.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two + +fn is_something(foo: Foo, bar: Bar) -> bool { + matches!((legacy_required_finality, signature_weight), + | (LegacyRequiredFinality::Any, Insufficient | Weak | Strict) + | (LegacyRequiredFinality::Weak, Weak | Strict) + | (LegacyRequiredFinality::Strict, Strict) + ) +} diff --git a/tests/source/macros/matches/version_one.rs b/tests/source/macros/matches/version_one.rs new file mode 100644 index 00000000000..2fa2fafbdf4 --- /dev/null +++ b/tests/source/macros/matches/version_one.rs @@ -0,0 +1,134 @@ +//rustfmt-version: One + +// To visually verify the special case handling of `matches!` we include the equivalent match expr + +// issue #4462 +fn issue_4462() { +matches!(c, + 'x' | 'c' | 'b' | 'B' | '?' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'n' + | 'N' | 'f' | 'd' | 's' | 'p' | 'P'); + +match c { + 'x' | 'c' | 'b' | 'B' | '?' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'n' + | 'N' | 'f' | 'd' | 's' | 'p' | 'P' => {} +} +} + +// issue #4885 +fn issue_4885() { +matches!( + c, + '\\' | '.' | '+' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | + '$' | '#' | '&' | '-' | '~' | '*' | '?' +); + +match c { + '\\' | '.' | '+' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | + '$' | '#' | '&' | '-' | '~' | '*' | '?' => {} +} +} + +// issue #5176 +fn issue_5176() { +matches!(self, | Self::A | Self::B); +match self { + | Self::A | Self::B => {} +} +} + +// issue #5547 +fn issue_5547() { + matches!(something.very_very_very.long.even.more.fields, Some(very_long_field_name_name_to_check) if method(very_long_field_name)); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } +} + +// other scenarios +fn other_scenarios() { + + // no guard with trailing comma + matches!(self, | Self::A | Self::B,); + match self { + | Self::A | Self::B => {} + } + + // guard with trailing comma + matches!(something.very_very_very.long.even.more.fields, Some(very_long_field_name_name_to_check) if method(very_long_field_name),); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } + + // short expr and pattern, but guard is long. + matches!(something, Some(_) if method(very_long_input_1, very_long_input_2, very_long_input_3, very_long_input_4, very_long_input_5),); + match something { + Some(_) if method(very_long_input_1, very_long_input_2, very_long_input_3, very_long_input_4, very_long_input_5) => {} + } + + // square brackets + matches![self, | Self::A | Self::B]; + match self { + | Self::A | Self::B => {} + } + // curly braces + matches!{self, | Self::A | Self::B}; + match self { + | Self::A | Self::B => {} + } +} + +// nested matches! calls +impl Mystruct { + pub(crate) fn is_expr(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) => {} + } + } + + pub(crate) fn is_expr_with_guard(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() => {} + } + } +} + +fn multi_line_struct_pattern_with_guard() { + matches!( + token, + Token::Dimension { + value, + ref unit, + .. + } if num_context.is_ok(context.parsing_mode, value) + ); + + match token { + Token::Dimension { + value, + ref unit, + .. + } if num_context.is_ok(context.parsing_mode, value) => { + // body + }, + } +} diff --git a/tests/source/macros/matches/version_two.rs b/tests/source/macros/matches/version_two.rs new file mode 100644 index 00000000000..4cd0aee7401 --- /dev/null +++ b/tests/source/macros/matches/version_two.rs @@ -0,0 +1,134 @@ +//rustfmt-version: Two + +// To visually verify the special case handling of `matches!` we include the equivalent match expr + +// issue #4462 +fn issue_4462() { +matches!(c, + 'x' | 'c' | 'b' | 'B' | '?' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'n' + | 'N' | 'f' | 'd' | 's' | 'p' | 'P'); + +match c { + 'x' | 'c' | 'b' | 'B' | '?' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'n' + | 'N' | 'f' | 'd' | 's' | 'p' | 'P' => {} +} +} + +// issue #4885 +fn issue_4885() { +matches!( + c, + '\\' | '.' | '+' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | + '$' | '#' | '&' | '-' | '~' | '*' | '?' +); + +match c { + '\\' | '.' | '+' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | + '$' | '#' | '&' | '-' | '~' | '*' | '?' => {} +} +} + +// issue #5176 +fn issue_5176() { +matches!(self, | Self::A | Self::B); +match self { + | Self::A | Self::B => {} +} +} + +// issue #5547 +fn issue_5547() { + matches!(something.very_very_very.long.even.more.fields, Some(very_long_field_name_name_to_check) if method(very_long_field_name)); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } +} + +// other scenarios +fn other_scenarios() { + + // no guard with trailing comma + matches!(self, | Self::A | Self::B,); + match self { + | Self::A | Self::B => {} + } + + // guard with trailing comma + matches!(something.very_very_very.long.even.more.fields, Some(very_long_field_name_name_to_check) if method(very_long_field_name),); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } + + // short expr and pattern, but guard is long. + matches!(something, Some(_) if method(very_long_input_1, very_long_input_2, very_long_input_3, very_long_input_4, very_long_input_5),); + match something { + Some(_) if method(very_long_input_1, very_long_input_2, very_long_input_3, very_long_input_4, very_long_input_5) => {} + } + + // square brackets + matches![self, | Self::A | Self::B]; + match self { + | Self::A | Self::B => {} + } + // curly braces + matches!{self, | Self::A | Self::B}; + match self { + | Self::A | Self::B => {} + } +} + +// nested matches! calls +impl Mystruct { + pub(crate) fn is_expr(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) => {} + } + } + + pub(crate) fn is_expr_with_guard(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() => {} + } + } +} + +fn multi_line_struct_pattern_with_guard() { + matches!( + token, + Token::Dimension { + value, + ref unit, + .. + } if num_context.is_ok(context.parsing_mode, value) + ); + + match token { + Token::Dimension { + value, + ref unit, + .. + } if num_context.is_ok(context.parsing_mode, value) => { + // body + }, + } +} diff --git a/tests/target/issue_5709.rs b/tests/target/issue_5709.rs new file mode 100644 index 00000000000..ef96af0e6b8 --- /dev/null +++ b/tests/target/issue_5709.rs @@ -0,0 +1,10 @@ +// rustfmt-version: Two + +fn is_something(foo: Foo, bar: Bar) -> bool { + matches!( + (legacy_required_finality, signature_weight), + (LegacyRequiredFinality::Any, Insufficient | Weak | Strict) + | (LegacyRequiredFinality::Weak, Weak | Strict) + | (LegacyRequiredFinality::Strict, Strict) + ) +} diff --git a/tests/target/macros/matches/version_one.rs b/tests/target/macros/matches/version_one.rs new file mode 100644 index 00000000000..b3c24eff62f --- /dev/null +++ b/tests/target/macros/matches/version_one.rs @@ -0,0 +1,172 @@ +//rustfmt-version: One + +// To visually verify the special case handling of `matches!` we include the equivalent match expr + +// issue #4462 +fn issue_4462() { + matches!( + c, + 'x' | 'c' + | 'b' + | 'B' + | '?' + | 'h' + | 'H' + | 'i' + | 'I' + | 'l' + | 'L' + | 'q' + | 'Q' + | 'n' + | 'N' + | 'f' + | 'd' + | 's' + | 'p' + | 'P' + ); + + match c { + 'x' | 'c' | 'b' | 'B' | '?' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'n' | 'N' + | 'f' | 'd' | 's' | 'p' | 'P' => {} + } +} + +// issue #4885 +fn issue_4885() { + matches!( + c, + '\\' | '.' + | '+' + | '(' + | ')' + | '|' + | '[' + | ']' + | '{' + | '}' + | '^' + | '$' + | '#' + | '&' + | '-' + | '~' + | '*' + | '?' + ); + + match c { + '\\' | '.' | '+' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' + | '-' | '~' | '*' | '?' => {} + } +} + +// issue #5176 +fn issue_5176() { + matches!(self, |Self::A| Self::B); + match self { + Self::A | Self::B => {} + } +} + +// issue #5547 +fn issue_5547() { + matches!(something.very_very_very.long.even.more.fields, Some(very_long_field_name_name_to_check) if method(very_long_field_name)); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } +} + +// other scenarios +fn other_scenarios() { + // no guard with trailing comma + matches!(self, |Self::A| Self::B,); + match self { + Self::A | Self::B => {} + } + + // guard with trailing comma + matches!(something.very_very_very.long.even.more.fields, Some(very_long_field_name_name_to_check) if method(very_long_field_name),); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } + + // short expr and pattern, but guard is long. + matches!(something, Some(_) if method(very_long_input_1, very_long_input_2, very_long_input_3, very_long_input_4, very_long_input_5),); + match something { + Some(_) + if method( + very_long_input_1, + very_long_input_2, + very_long_input_3, + very_long_input_4, + very_long_input_5, + ) => {} + } + + // square brackets + matches![self, |Self::A| Self::B]; + match self { + Self::A | Self::B => {} + } + // curly braces + matches! {self, | Self::A | Self::B}; + match self { + Self::A | Self::B => {} + } +} + +// nested matches! calls +impl Mystruct { + pub(crate) fn is_expr(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) => {} + } + } + + pub(crate) fn is_expr_with_guard(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() => {} + } + } +} + +fn multi_line_struct_pattern_with_guard() { + matches!( + token, + Token::Dimension { + value, + ref unit, + .. + } if num_context.is_ok(context.parsing_mode, value) + ); + + match token { + Token::Dimension { + value, ref unit, .. + } if num_context.is_ok(context.parsing_mode, value) => { + // body + } + } +} diff --git a/tests/target/macros/matches/version_two.rs b/tests/target/macros/matches/version_two.rs new file mode 100644 index 00000000000..3137c0f42ca --- /dev/null +++ b/tests/target/macros/matches/version_two.rs @@ -0,0 +1,154 @@ +//rustfmt-version: Two + +// To visually verify the special case handling of `matches!` we include the equivalent match expr + +// issue #4462 +fn issue_4462() { + matches!( + c, + 'x' | 'c' | 'b' | 'B' | '?' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'n' | 'N' + | 'f' | 'd' | 's' | 'p' | 'P' + ); + + match c { + 'x' | 'c' | 'b' | 'B' | '?' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'n' | 'N' + | 'f' | 'd' | 's' | 'p' | 'P' => {} + } +} + +// issue #4885 +fn issue_4885() { + matches!( + c, + '\\' | '.' | '+' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' + | '~' | '*' | '?' + ); + + match c { + '\\' | '.' | '+' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' + | '-' | '~' | '*' | '?' => {} + } +} + +// issue #5176 +fn issue_5176() { + matches!(self, Self::A | Self::B); + match self { + Self::A | Self::B => {} + } +} + +// issue #5547 +fn issue_5547() { + matches!( + something.very_very_very.long.even.more.fields, + Some(very_long_field_name_name_to_check) if method(very_long_field_name) + ); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } +} + +// other scenarios +fn other_scenarios() { + // no guard with trailing comma + matches!(self, Self::A | Self::B,); + match self { + Self::A | Self::B => {} + } + + // guard with trailing comma + matches!( + something.very_very_very.long.even.more.fields, + Some(very_long_field_name_name_to_check) if method(very_long_field_name), + ); + match something.very_very_very.long.even.more.fields { + Some(very_long_field_name_name_to_check) if method(very_long_field_name) => {} + } + + // short expr and pattern, but guard is long. + matches!( + something, + Some(_) + if method( + very_long_input_1, + very_long_input_2, + very_long_input_3, + very_long_input_4, + very_long_input_5 + ), + ); + match something { + Some(_) + if method( + very_long_input_1, + very_long_input_2, + very_long_input_3, + very_long_input_4, + very_long_input_5, + ) => {} + } + + // square brackets + matches![self, Self::A | Self::B]; + match self { + Self::A | Self::B => {} + } + // curly braces + matches! {self, | Self::A | Self::B}; + match self { + Self::A | Self::B => {} + } +} + +// nested matches! calls +impl Mystruct { + pub(crate) fn is_expr(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) => {} + } + } + + pub(crate) fn is_expr_with_guard(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() + ); + + match self { + OverflowableItem::Expr(..) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchMacroItem(MatchMacroItem::Expr(..)) + if self.condition() => {} + } + } +} + +fn multi_line_struct_pattern_with_guard() { + matches!( + token, + Token::Dimension { + value, ref unit, .. + } if num_context.is_ok(context.parsing_mode, value) + ); + + match token { + Token::Dimension { + value, ref unit, .. + } if num_context.is_ok(context.parsing_mode, value) => { + // body + } + } +}