Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Special case matches! macro #5554

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
11 changes: 7 additions & 4 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,10 @@ fn rewrite_int_lit(
)
}

fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option<SeparatorTactic> {
pub(crate) fn choose_separator_tactic(
context: &RewriteContext<'_>,
span: Span,
) -> Option<SeparatorTactic> {
if context.inside_macro() {
if span_ends_with_comma(context, span) {
Some(SeparatorTactic::Always)
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 1 addition & 4 deletions src/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
75 changes: 64 additions & 11 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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, &macro_name, mac) {
return success;
}
Comment on lines +235 to +238
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@calebcartwright per your comment #5554 (comment), I just wanted to let you know that the changes are version gated.

I don't think there's a rush to get this in, and we can definitely hold off until style_edition lands to gate this around the 2024 style edition.

}

let ParsedMacroArgs {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1313,6 +1313,59 @@ impl MacroBranch {
}
}

impl Rewrite for MatchesMacroItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: crate::shape::Shape) -> Option<String> {
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 <https://doc.rust-lang.org/std/macro.matches.html>
///
/// # Expected syntax
///
/// ```text
/// matches!(expr, pat)
/// matches!(expr, pat if expr)
/// ```
fn format_matches(
context: &RewriteContext<'_>,
shape: Shape,
name: &str,
mac: &ast::MacCall,
) -> Option<String> {
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 <https://crates.io/crates/lazy_static>.
///
/// # Expected syntax
Expand Down
30 changes: 20 additions & 10 deletions src/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -512,8 +504,26 @@ fn rewrite_match_body(
}
}

pub(crate) fn rewrite_guard(
context: &RewriteContext<'_>,
guard: &Option<ptr::P<ast::Expr>>,
shape: Shape,
pattern_str: &str,
) -> Option<String> {
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<ptr::P<ast::Expr>>,
shape: Shape,
Expand Down
13 changes: 11 additions & 2 deletions src/overflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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),
}
}

Expand All @@ -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(..))
)
}

Expand All @@ -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,
}
}
Expand Down Expand Up @@ -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<Item = &'a T>,
Expand Down
69 changes: 69 additions & 0 deletions src/parse/macros/matches.rs
Original file line number Diff line number Diff line change
@@ -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<ast::Expr>,
pub(crate) pat: P<ast::Pat>,
pub(crate) guard: Option<P<ast::Expr>>,
}

/// Parse matches! from <https://doc.rust-lang.org/std/macro.matches.html>
pub(crate) fn parse_matches(context: &RewriteContext<'_>, ts: TokenStream) -> Option<Matches> {
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<ast::Expr>),
Arm(P<ast::Pat>, Option<P<ast::Expr>>),
}
8 changes: 7 additions & 1 deletion src/parse/macros/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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)
Expand Down Expand Up @@ -92,6 +93,11 @@ fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
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,
Expand Down
11 changes: 11 additions & 0 deletions src/spanned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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()),
}
}
}
9 changes: 9 additions & 0 deletions tests/source/issue_5709.rs
Original file line number Diff line number Diff line change
@@ -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)
)
}
Loading