From f2942d242659d05585e8107694b213ceb595d8ee Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Oct 2022 11:45:00 -0400 Subject: [PATCH 01/10] Add helper function to determine if a TokenTree is a comma --- src/parse/macros/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 67f3985926e..a1f1202b2ac 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}; @@ -92,6 +92,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, From 00b90033280b685eb62e3a719de2126d1dda8a89 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 30 Sep 2022 18:16:46 -0400 Subject: [PATCH 02/10] Parse `matches!` macro The first step in being able to format the `matches!` macro is to parse it into ast nodes that we know how to rewrite. --- src/parse/macros/matches.rs | 54 +++++++++++++++++++++++++++++++++++++ src/parse/macros/mod.rs | 1 + 2 files changed, 55 insertions(+) create mode 100644 src/parse/macros/matches.rs diff --git a/src/parse/macros/matches.rs b/src/parse/macros/matches.rs new file mode 100644 index 00000000000..f2d05069d20 --- /dev/null +++ b/src/parse/macros/matches.rs @@ -0,0 +1,54 @@ +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 }) +} diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index a1f1202b2ac..7def0e07cb0 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -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) From 735efa609ec7b1a1d5bda2d64e11b25fa59b5b7f Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Oct 2022 11:56:17 -0400 Subject: [PATCH 03/10] Increase visibility of `expr::choose_separator_tactic` --- src/expr.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/expr.rs b/src/expr.rs index 91f3cc81bae..76e47ac97eb 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) From fbcc07a87cad107a27867b11f2729bad026f9c82 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Oct 2022 02:59:43 -0400 Subject: [PATCH 04/10] Better encapsulate guard rewrite logic in relation to pattern rewirte The guard rewrite rules vary based on how the pattern was rewritten. That logic was previously written in `rewrite_match_arm`, but now it's fully encapsulates in `rewrite_guard`. --- src/matches.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) 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, From 8de254056aa05baad3619d52be728dd86e28de03 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Oct 2022 11:58:06 -0400 Subject: [PATCH 05/10] Add MatchesMacroItem and imp Rewrite, Spanned, and IntoOverflowableItem These traits will make it easier to implement rewriting matches! in terms of `overflow::rewrite_with_*` methods. --- src/macros.rs | 15 +++++++++++++++ src/overflow.rs | 13 +++++++++++-- src/parse/macros/matches.rs | 15 +++++++++++++++ src/spanned.rs | 11 +++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d58f7547fef..a084fc35afb 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,8 +27,10 @@ use crate::comment::{ use crate::config::lists::*; use crate::expr::{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::MatchesMacroItem; use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; @@ -1313,6 +1315,19 @@ 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 `lazy_static!` from . /// /// # Expected syntax diff --git a/src/overflow.rs b/src/overflow.rs index af0b95430a1..b23248dfe0b 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 index f2d05069d20..42ff6a8224a 100644 --- a/src/parse/macros/matches.rs +++ b/src/parse/macros/matches.rs @@ -52,3 +52,18 @@ pub(crate) fn parse_matches(context: &RewriteContext<'_>, ts: TokenStream) -> Op }; 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/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()), + } + } +} From 9d6a512c7753a3f015519caba63cfff68f412de3 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 30 Sep 2022 18:19:57 -0400 Subject: [PATCH 06/10] Special case `matches!` macro Now that we're able to parse the TokenStream of `matches!` calls into a `Matches` struct and then convert that `Matches` struct into an iterable which implments `IntoOverflowableItem` we're able to implement `matches!` rewriting in terms of `overflow::rewrite_with_*` calls. --- src/macros.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index a084fc35afb..d02a21a1efc 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -25,12 +25,12 @@ use crate::comment::{ contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, }; use crate::config::lists::*; -use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; +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::MatchesMacroItem; +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}; @@ -232,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!" { + if let success @ Some(..) = format_matches(context, shape, ¯o_name, mac) { + return success; + } } let ParsedMacroArgs { @@ -1328,6 +1332,46 @@ impl Rewrite for MatchesMacroItem { } } +/// 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 From e563f1be9968823c2919d884570701c6b9e9cc40 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Oct 2022 12:42:18 -0400 Subject: [PATCH 07/10] Version gate `matches!` special case formatting After special casing `matches!`, the formatting more closely resembles `match` expressions. These changes cause breaking formatting changes to that need to be version gated. --- src/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d02a21a1efc..fa7ff9da182 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,7 +24,7 @@ use rustc_span::{ use crate::comment::{ contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, }; -use crate::config::lists::*; +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; @@ -232,7 +232,7 @@ fn rewrite_macro_inner( if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) { return success; } - } else if macro_name == "matches!" { + } else if macro_name == "matches!" && context.config.version() == Version::Two { if let success @ Some(..) = format_matches(context, shape, ¯o_name, mac) { return success; } From 9e37c1ea67d03d54fb22f21f895d59edea16381d Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Oct 2022 12:46:22 -0400 Subject: [PATCH 08/10] Add unit tests for `matches!` special case formatting The unit tests cover version One and Two formatting --- tests/source/macros/matches/version_one.rs | 134 ++++++++++++++++ tests/source/macros/matches/version_two.rs | 134 ++++++++++++++++ tests/target/macros/matches/version_one.rs | 172 +++++++++++++++++++++ tests/target/macros/matches/version_two.rs | 154 ++++++++++++++++++ 4 files changed, 594 insertions(+) create mode 100644 tests/source/macros/matches/version_one.rs create mode 100644 tests/source/macros/matches/version_two.rs create mode 100644 tests/target/macros/matches/version_one.rs create mode 100644 tests/target/macros/matches/version_two.rs 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/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 + } + } +} From 93130c0bf47edb0fb574dcdb56c8f2f9210a76d6 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 9 Mar 2023 09:03:13 -0500 Subject: [PATCH 09/10] Add `matches!` test case for issue 5709 --- tests/source/issue_5709.rs | 9 +++++++++ tests/target/issue_5709.rs | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/source/issue_5709.rs create mode 100644 tests/target/issue_5709.rs 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/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) + ) +} From 0c5552d613b45a993f81574c7e6fa8209299cc0b Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Oct 2022 12:48:17 -0400 Subject: [PATCH 10/10] Apply updated `matches!` formatting to rustfmt Since rustfmt uses version two formatting the self tests were failing due to the new special case handling for `matches!`. various `matches!` calls in the codebase were updated to address this issue. --- src/expr.rs | 6 +++--- src/items.rs | 5 +---- src/macros.rs | 12 +++--------- src/overflow.rs | 4 ++-- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 76e47ac97eb..31419a1b228 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1891,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 fa7ff9da182..f224d842f4d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -591,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 { @@ -1052,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 { diff --git a/src/overflow.rs b/src/overflow.rs index b23248dfe0b..bce1d54a4ef 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -141,8 +141,8 @@ impl<'a> OverflowableItem<'a> { matches!( self, OverflowableItem::Expr(..) - | OverflowableItem::MacroArg(MacroArg::Expr(..)) - | OverflowableItem::MatchesMacroItem(MatchesMacroItem::Expr(..)) + | OverflowableItem::MacroArg(MacroArg::Expr(..)) + | OverflowableItem::MatchesMacroItem(MatchesMacroItem::Expr(..)) ) }