diff --git a/src/expr.rs b/src/expr.rs index 348cffc21ee..a85c617f9b2 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1446,7 +1446,10 @@ fn rewrite_float_lit( .max_width_error(shape.width, span) } -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) diff --git a/src/macros.rs b/src/macros.rs index 16897e57dcb..7a1e15e026a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,10 +24,12 @@ use crate::comment::{ }; use crate::config::StyleEdition; use crate::config::lists::*; -use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs}; +use crate::expr::{RhsAssignKind, choose_separator_tactic, rewrite_array, rewrite_assign_rhs}; use crate::lists::{ListFormatting, itemize_list, write_list}; +use crate::matches::rewrite_guard; use crate::overflow; use crate::parse::macros::lazy_static::parse_lazy_static; +use crate::parse::macros::matches::{MatchesMacroItem, parse_matches}; use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args}; use crate::rewrite::{ MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult, @@ -238,6 +240,12 @@ fn rewrite_macro_inner( _ => return Err(err), }, } + } else if macro_name == "matches!" + && context.config.style_edition() >= StyleEdition::Edition2027 + { + if let success @ Ok(..) = format_matches(context, shape, ¯o_name, mac) { + return success; + } } let ParsedMacroArgs { @@ -1394,6 +1402,67 @@ impl MacroBranch { } } +impl Rewrite for MatchesMacroItem { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result( + &self, + context: &RewriteContext<'_>, + shape: crate::shape::Shape, + ) -> RewriteResult { + match self { + Self::Expr(expr) => expr.rewrite_result(context, shape), + Self::Arm(pat, guard) => { + let pats_str = pat.rewrite_result(context, shape)?; + let guard_str = rewrite_guard(context, guard, shape, &pats_str)?; + Ok(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, +) -> RewriteResult { + 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 { + Delimiter::Parenthesis => overflow::rewrite_with_parens( + context, + name, + matches.iter(), + shape, + span, + shape.width, + force_separator_tactic, + ), + Delimiter::Bracket => overflow::rewrite_with_square_brackets( + context, + name, + matches.iter(), + shape, + span, + force_separator_tactic, + None, + ), + Delimiter::Brace | Delimiter::Invisible(_) => Err(RewriteError::Unknown), + } +} + /// Format `lazy_static!` from . /// /// # Expected syntax diff --git a/src/matches.rs b/src/matches.rs index 1727a9de868..4a0229e7abb 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -296,15 +296,7 @@ fn rewrite_match_arm( let pats_str = arm.pat.rewrite_result(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, @@ -568,8 +560,26 @@ fn rewrite_match_body( } } +pub(crate) fn rewrite_guard( + context: &RewriteContext<'_>, + guard: &Option>, + shape: Shape, + pattern_str: &str, +) -> RewriteResult { + 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 19f7b06f8a3..7e9d6ce407d 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -19,6 +19,7 @@ use crate::lists::{ ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; use crate::macros::MacroArg; +use crate::parse::macros::matches::MatchesMacroItem; use crate::patterns::{TuplePatField, can_be_overflowed_pat}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; @@ -85,6 +86,7 @@ pub(crate) enum OverflowableItem<'a> { Ty(&'a ast::Ty), Pat(&'a ast::Pat), PreciseCapturingArg(&'a ast::PreciseCapturingArg), + MatchesMacroItem(&'a MatchesMacroItem), } impl<'a> Rewrite for OverflowableItem<'a> { @@ -130,6 +132,7 @@ impl<'a> OverflowableItem<'a> { OverflowableItem::Ty(ty) => f(*ty), OverflowableItem::Pat(pat) => f(*pat), OverflowableItem::PreciseCapturingArg(arg) => f(*arg), + OverflowableItem::MatchesMacroItem(item) => f(*item), } } @@ -154,7 +157,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(..)) ) } @@ -170,6 +175,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, } } @@ -265,7 +271,10 @@ impl_into_overflowable_item_for_ast_node!( Pat, PreciseCapturingArg ); -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..2b6a6d5dc9d --- /dev/null +++ b/src/parse/macros/matches.rs @@ -0,0 +1,71 @@ +use rustc_ast::ast; +use rustc_ast::ptr::P; +use rustc_ast::tokenstream::TokenStream; +use rustc_parse::exp; +use rustc_parse::parser::{CommaRecoveryMode, RecoverColon, RecoverComma}; + +use super::is_token_tree_comma; +use crate::rewrite::{RewriteContext, RewriteError}; + +#[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, +) -> Result { + let mut cursor = ts.iter().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().map_err(|_| RewriteError::Unknown)?; + + let _ = parser.eat(exp!(Comma)); + + let pat = parser + .parse_pat_allow_top_guard( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::EitherTupleOrPipe, + ) + .map_err(|_| RewriteError::Unknown)?; + + let guard = if parser.eat_keyword(exp!(If)) { + Some(parser.parse_expr().map_err(|_| RewriteError::Unknown)?) + } else { + None + }; + Ok(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 8a956faf03b..e279c85a592 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -1,5 +1,5 @@ use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; -use rustc_ast::tokenstream::TokenStream; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::{ForceCollect, Parser, Recovery}; @@ -13,6 +13,7 @@ pub(crate) mod asm; pub(crate) mod cfg_if; pub(crate) mod cfg_match; pub(crate) mod lazy_static; +pub(crate) mod matches; fn build_stream_parser<'a>(psess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { Parser::new(psess, tokens, MACRO_ARGUMENTS).recovery(Recovery::Forbidden) @@ -96,6 +97,11 @@ fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { } } +/// 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 507647566d4..3d084c89f7d 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -4,6 +4,7 @@ use rustc_ast::{ast, ptr}; use rustc_span::{Span, source_map}; use crate::macros::MacroArg; +use crate::parse::macros::matches::MatchesMacroItem; use crate::patterns::RangeOperand; use crate::utils::{mk_sp, outer_attributes}; @@ -211,3 +212,13 @@ impl<'a, T> Spanned for RangeOperand<'a, T> { 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..9624718d5c0 --- /dev/null +++ b/tests/source/issue_5709.rs @@ -0,0 +1,9 @@ +// rustfmt-style_edition: 2027 + +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/issue_6650.rs b/tests/source/issue_6650.rs new file mode 100644 index 00000000000..72f29749aef --- /dev/null +++ b/tests/source/issue_6650.rs @@ -0,0 +1,5 @@ +// rustfmt-style_edition: 2027 + +fn main() { + matches!(stmt, ast::Stmt{kind: ast::StmtKind::MacCall(box ast::MacCallStmt {style : ast::MacStmtStyle::Braces,..}),..}) +} diff --git a/tests/source/macros/matches/style_edition_2027.rs b/tests/source/macros/matches/style_edition_2027.rs new file mode 100644 index 00000000000..d0e2d8bfa38 --- /dev/null +++ b/tests/source/macros/matches/style_edition_2027.rs @@ -0,0 +1,134 @@ +//rustfmt-style_edition: 2027 + +// 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_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/target/issue_5709.rs b/tests/target/issue_5709.rs new file mode 100644 index 00000000000..dde3558fa02 --- /dev/null +++ b/tests/target/issue_5709.rs @@ -0,0 +1,10 @@ +// rustfmt-style_edition: 2027 + +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/issue_6650.rs b/tests/target/issue_6650.rs new file mode 100644 index 00000000000..b05ed62ccee --- /dev/null +++ b/tests/target/issue_6650.rs @@ -0,0 +1,15 @@ +// rustfmt-style_edition: 2027 + +fn main() { + matches!( + stmt, + ast::Stmt { + kind: + ast::StmtKind::MacCall(box ast::MacCallStmt { + style: ast::MacStmtStyle::Braces, + .. + }), + .. + } + ) +} diff --git a/tests/target/macros/matches/style_edition_2027.rs b/tests/target/macros/matches/style_edition_2027.rs new file mode 100644 index 00000000000..c478e9bb242 --- /dev/null +++ b/tests/target/macros/matches/style_edition_2027.rs @@ -0,0 +1,149 @@ +//rustfmt-style_edition: 2027 + +// 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_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 + } + } +}