diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 74cc1ec17e6f0..024624cd3bb87 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -938,7 +938,7 @@ pub enum PatKind { Never, /// A guard pattern (e.g., `x if guard(x)`). - Guard(Box, Box), + Guard(Box, Box), /// Parentheses in patterns used for grouping (i.e., `(PAT)`). Paren(Box), @@ -1346,7 +1346,7 @@ pub struct Arm { /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`. pub pat: Box, /// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`. - pub guard: Option>, + pub guard: Option>, /// Match arm body. Omitted if the pattern is a never pattern. pub body: Option>, pub span: Span, @@ -3954,6 +3954,18 @@ impl ConstBlockItem { pub const IDENT: Ident = Ident { name: kw::Underscore, span: DUMMY_SP }; } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub struct Guard { + pub cond: Expr, + pub span_with_leading_if: Span, +} + +impl Guard { + pub fn span(&self) -> Span { + self.cond.span + } +} + // Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum ItemKind { diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f82513094aa1f..62ec063585171 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -94,6 +94,7 @@ pub enum MetaVarKind { }, Path, Vis, + Guard, TT, } @@ -114,6 +115,7 @@ impl fmt::Display for MetaVarKind { MetaVarKind::Meta { .. } => sym::meta, MetaVarKind::Path => sym::path, MetaVarKind::Vis => sym::vis, + MetaVarKind::Guard => sym::guard, MetaVarKind::TT => sym::tt, }; write!(f, "{sym}") @@ -1124,6 +1126,7 @@ pub enum NonterminalKind { Meta, Path, Vis, + Guard, TT, } @@ -1161,6 +1164,7 @@ impl NonterminalKind { sym::meta => NonterminalKind::Meta, sym::path => NonterminalKind::Path, sym::vis => NonterminalKind::Vis, + sym::guard => NonterminalKind::Guard, sym::tt => NonterminalKind::TT, _ => return None, }) @@ -1182,6 +1186,7 @@ impl NonterminalKind { NonterminalKind::Meta => sym::meta, NonterminalKind::Path => sym::path, NonterminalKind::Vis => sym::vis, + NonterminalKind::Guard => sym::guard, NonterminalKind::TT => sym::tt, } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index bdf290f9a9e63..6aa8d5f38ad24 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -442,6 +442,7 @@ macro_rules! common_visitor_and_walkers { FormatArguments, FormatPlaceholder, GenericParamKind, + Guard, Impl, ImplPolarity, Inline, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 305df40651969..35a74a63b8e4c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -639,7 +639,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> { let pat = self.lower_pat(&arm.pat); - let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond)); + let guard = arm.guard.as_ref().map(|guard| self.lower_expr(&guard.cond)); let hir_id = self.next_id(); let span = self.lower_span(arm.span); self.lower_attrs(hir_id, &arm.attrs, arm.span, Target::Arm); @@ -662,7 +662,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else if let Some(body) = &arm.body { self.dcx().emit_err(NeverPatternWithBody { span: body.span }); } else if let Some(g) = &arm.guard { - self.dcx().emit_err(NeverPatternWithGuard { span: g.span }); + self.dcx().emit_err(NeverPatternWithGuard { span: g.span() }); } // We add a fake `loop {}` arm body so that it typecks to `!`. The mir lowering of never diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index c1c13977e1037..3aadf09597ee2 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -133,8 +133,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } - PatKind::Guard(inner, cond) => { - break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond)); + PatKind::Guard(inner, guard) => { + break hir::PatKind::Guard( + self.lower_pat(inner), + self.lower_expr(&guard.cond), + ); } PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 4ba5dc541342a..1f07ee71c420a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1889,12 +1889,12 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); } } - PatKind::Guard(subpat, condition) => { + PatKind::Guard(subpat, guard) => { self.popen(); self.print_pat(subpat); self.space(); self.word_space("if"); - self.print_expr(condition, FixupContext::default()); + self.print_expr(&guard.cond, FixupContext::default()); self.pclose(); } PatKind::Slice(elts) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 9b4ff2b63bd45..f6db4cdef7224 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -880,9 +880,9 @@ impl<'a> State<'a> { self.print_outer_attributes(&arm.attrs); self.print_pat(&arm.pat); self.space(); - if let Some(e) = &arm.guard { + if let Some(guard) = &arm.guard { self.word_space("if"); - self.print_expr(e, FixupContext::default()); + self.print_expr(&guard.cond, FixupContext::default()); self.space(); } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 7ff49e040f6f3..82d941a289e07 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -724,7 +724,7 @@ pub fn compile_declarative_macro( let args = p.parse_token_tree(); check_args_parens(sess, sym::attr, &args); let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition); - check_emission(check_lhs(sess, node_id, &args)); + check_emission(check_lhs(sess, features, node_id, &args)); if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") { return dummy_syn_ext(guar); } @@ -773,7 +773,7 @@ pub fn compile_declarative_macro( }; let lhs_tt = p.parse_token_tree(); let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition); - check_emission(check_lhs(sess, node_id, &lhs_tt)); + check_emission(check_lhs(sess, features, node_id, &lhs_tt)); if let Err(e) = p.expect(exp!(FatArrow)) { return dummy_syn_ext(e.emit()); } @@ -870,21 +870,27 @@ fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(), } } -fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> { - let e1 = check_lhs_nt_follows(sess, node_id, lhs); +fn check_lhs( + sess: &Session, + features: &Features, + node_id: NodeId, + lhs: &mbe::TokenTree, +) -> Result<(), ErrorGuaranteed> { + let e1 = check_lhs_nt_follows(sess, features, node_id, lhs); let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs)); e1.and(e2) } fn check_lhs_nt_follows( sess: &Session, + features: &Features, node_id: NodeId, lhs: &mbe::TokenTree, ) -> Result<(), ErrorGuaranteed> { // lhs is going to be like TokenTree::Delimited(...), where the // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens. if let mbe::TokenTree::Delimited(.., delimited) = lhs { - check_matcher(sess, node_id, &delimited.tts) + check_matcher(sess, features, node_id, &delimited.tts) } else { let msg = "invalid macro matcher; matchers must be contained in balanced delimiters"; Err(sess.dcx().span_err(lhs.span(), msg)) @@ -989,12 +995,13 @@ fn check_rhs(sess: &Session, rhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed fn check_matcher( sess: &Session, + features: &Features, node_id: NodeId, matcher: &[mbe::TokenTree], ) -> Result<(), ErrorGuaranteed> { let first_sets = FirstSets::new(matcher); let empty_suffix = TokenSet::empty(); - check_matcher_core(sess, node_id, &first_sets, matcher, &empty_suffix)?; + check_matcher_core(sess, features, node_id, &first_sets, matcher, &empty_suffix)?; Ok(()) } @@ -1331,6 +1338,7 @@ impl<'tt> TokenSet<'tt> { // see `FirstSets::new`. fn check_matcher_core<'tt>( sess: &Session, + features: &Features, node_id: NodeId, first_sets: &FirstSets<'tt>, matcher: &'tt [mbe::TokenTree], @@ -1369,6 +1377,17 @@ fn check_matcher_core<'tt>( | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl { .. } | TokenTree::MetaVarExpr(..) => { + if let TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } = token + && !features.macro_guard_matcher() + { + feature_err( + sess, + sym::macro_guard_matcher, + token.span(), + "`guard` fragments in macro are unstable", + ) + .emit(); + } if token_can_be_followed_by_any(token) { // don't need to track tokens that work with any, last.replace_with_irrelevant(); @@ -1385,7 +1404,7 @@ fn check_matcher_core<'tt>( d.delim.as_close_token_kind(), span.close, )); - check_matcher_core(sess, node_id, first_sets, &d.tts, &my_suffix)?; + check_matcher_core(sess, features, node_id, first_sets, &d.tts, &my_suffix)?; // don't track non NT tokens last.replace_with_irrelevant(); @@ -1417,7 +1436,14 @@ fn check_matcher_core<'tt>( // At this point, `suffix_first` is built, and // `my_suffix` is some TokenSet that we can use // for checking the interior of `seq_rep`. - let next = check_matcher_core(sess, node_id, first_sets, &seq_rep.tts, my_suffix)?; + let next = check_matcher_core( + sess, + features, + node_id, + first_sets, + &seq_rep.tts, + my_suffix, + )?; if next.maybe_empty { last.add_all(&next); } else { @@ -1609,7 +1635,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { } } NonterminalKind::Pat(PatParam { .. }) => { - const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; + const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`if let`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { FatArrow | Comma | Eq | Or => IsInFollow::Yes, @@ -1618,11 +1644,12 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { } _ => IsInFollow::No(TOKENS), }, + TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } => IsInFollow::Yes, _ => IsInFollow::No(TOKENS), } } NonterminalKind::Pat(PatWithOr) => { - const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"]; + const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`if let`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { FatArrow | Comma | Eq => IsInFollow::Yes, @@ -1631,6 +1658,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { } _ => IsInFollow::No(TOKENS), }, + TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } => IsInFollow::Yes, + _ => IsInFollow::No(TOKENS), + } + } + NonterminalKind::Guard => { + const TOKENS: &[&str] = &["`=>`", "`,`", "`{`"]; + match tok { + TokenTree::Token(token) => match token.kind { + FatArrow | Comma | OpenBrace => IsInFollow::Yes, + _ => IsInFollow::No(TOKENS), + }, _ => IsInFollow::No(TOKENS), } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 09f006c3de578..dcf2cd1fa36af 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -12,7 +12,8 @@ use rustc_parse::parser::ParseNtResult; use rustc_session::parse::ParseSess; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::{ - Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans, + BytePos, Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, kw, sym, + with_metavar_spans, }; use smallvec::{SmallVec, smallvec}; @@ -556,6 +557,19 @@ fn transcribe_pnr<'tx>( ParseNtResult::Vis(vis) => { mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis)) } + ParseNtResult::Guard(guard) => { + // FIXME(macro_guard_matcher): + // Perhaps it would be better to treat the leading `if` as part of `ast::Guard` during parsing? + // Currently they are separate, but in macros we match and emit the leading `if` for `:guard` matchers, which creates some inconsistency. + + let leading_if_span = + guard.span_with_leading_if.with_hi(guard.span_with_leading_if.lo() + BytePos(2)); + let mut ts = + TokenStream::token_alone(token::Ident(kw::If, IdentIsRaw::No), leading_if_span); + ts.push_stream(TokenStream::from_ast(&guard.cond)); + + mk_delimited(guard.span_with_leading_if, MetaVarKind::Guard, ts) + } }; tscx.result.push(tt); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 39e886227d946..b5db4e20d0d37 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -565,6 +565,8 @@ declare_features! ( (unstable, macro_attr, "1.91.0", Some(143547)), /// Allow `macro_rules!` derive rules (unstable, macro_derive, "1.91.0", Some(143549)), + /// Allow `$x:guard` matcher in macros + (unstable, macro_guard_matcher, "CURRENT_RUSTC_VERSION", Some(153104)), /// Give access to additional metadata about declarative macro meta-variables. (unstable, macro_metavar_expr, "1.61.0", Some(83527)), /// Provides a way to concatenate identifiers using metavariable expressions. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index adfc68f4bb22a..79f370bb828c3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,8 +15,8 @@ use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, - FnRetTy, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param, RangeLimits, - StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, + FnRetTy, Guard, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param, + RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, }; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; @@ -2762,7 +2762,7 @@ impl<'a> Parser<'a> { /// Parses a `let $pat = $expr` pseudo-expression. fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, Box> { - let recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { + let recovered: Recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { let err = errors::ExpectedExpressionFoundLet { span: self.token.span, reason: errors::ForbiddenLetReason::OtherForbidden, @@ -3458,20 +3458,54 @@ impl<'a> Parser<'a> { }) } - fn parse_match_arm_guard(&mut self) -> PResult<'a, Option>> { + pub(crate) fn eat_metavar_guard(&mut self) -> Option> { + self.eat_metavar_seq_with_matcher( + |mv_kind| matches!(mv_kind, MetaVarKind::Guard), + |this| this.parse_match_arm_guard(), + ) + .flatten() + } + + fn parse_match_arm_guard(&mut self) -> PResult<'a, Option>> { + if let Some(guard) = self.eat_metavar_guard() { + return Ok(Some(guard)); + } + if !self.eat_keyword(exp!(If)) { // No match arm guard present. return Ok(None); } + self.expect_match_arm_guard_cond(ForceCollect::No).map(Some) + } - let mut cond = self.parse_match_guard_condition()?; + pub(crate) fn expect_match_arm_guard( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Box> { + if let Some(guard) = self.eat_metavar_guard() { + return Ok(guard); + } + + self.expect_keyword(exp!(If))?; + self.expect_match_arm_guard_cond(force_collect) + } + + fn expect_match_arm_guard_cond( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Box> { + let leading_if_span = self.prev_token.span; + + let mut cond = self.parse_match_guard_condition(force_collect)?; + let cond_span = cond.span; CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); - Ok(Some(cond)) + let guard = Guard { cond: *cond, span_with_leading_if: leading_if_span.to(cond_span) }; + Ok(Box::new(guard)) } - fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option>)> { + fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option>)> { if self.token == token::OpenParen { let left = self.token.span; let pat = self.parse_pat_no_top_guard( @@ -3487,10 +3521,10 @@ impl<'a> Parser<'a> { // FIXME(guard_patterns): convert this to a normal guard instead let span = pat.span; let ast::PatKind::Paren(subpat) = pat.kind else { unreachable!() }; - let ast::PatKind::Guard(_, mut cond) = subpat.kind else { unreachable!() }; - self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); + let ast::PatKind::Guard(_, mut guard) = subpat.kind else { unreachable!() }; + self.psess.gated_spans.ungate_last(sym::guard_patterns, guard.span()); let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed); - checker.visit_expr(&mut cond); + checker.visit_expr(&mut guard.cond); let right = self.prev_token.span; self.dcx().emit_err(errors::ParenthesesInMatchPat { @@ -3498,14 +3532,10 @@ impl<'a> Parser<'a> { sugg: errors::ParenthesesInMatchPatSugg { left, right }, }); - Ok(( - self.mk_pat(span, ast::PatKind::Wild), - (if let Some(guar) = checker.found_incorrect_let_chain { - Some(self.mk_expr_err(cond.span, guar)) - } else { - Some(cond) - }), - )) + if let Some(guar) = checker.found_incorrect_let_chain { + guard.cond = *self.mk_expr_err(guard.span(), guar); + } + Ok((self.mk_pat(span, ast::PatKind::Wild), Some(guard))) } else { Ok((pat, self.parse_match_arm_guard()?)) } @@ -3521,33 +3551,47 @@ impl<'a> Parser<'a> { } } - fn parse_match_guard_condition(&mut self) -> PResult<'a, Box> { + fn parse_match_guard_condition( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Box> { let attrs = self.parse_outer_attributes()?; - match self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs) { - Ok((expr, _)) => Ok(expr), - Err(mut err) => { - if self.prev_token == token::OpenBrace { - let sugg_sp = self.prev_token.span.shrink_to_lo(); - // Consume everything within the braces, let's avoid further parse - // errors. - self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); - let msg = "you might have meant to start a match arm after the match guard"; - if self.eat(exp!(CloseBrace)) { - let applicability = if self.token != token::FatArrow { - // We have high confidence that we indeed didn't have a struct - // literal in the match guard, but rather we had some operation - // that ended in a path, immediately followed by a block that was - // meant to be the match arm. - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - err.span_suggestion_verbose(sugg_sp, msg, "=> ", applicability); + let expr = self.collect_tokens( + None, + AttrWrapper::empty(), + force_collect, + |this, _empty_attrs| { + match this + .parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs) + { + Ok((expr, _)) => Ok((expr, Trailing::No, UsePreAttrPos::No)), + Err(mut err) => { + if this.prev_token == token::OpenBrace { + let sugg_sp = this.prev_token.span.shrink_to_lo(); + // Consume everything within the braces, let's avoid further parse + // errors. + this.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); + let msg = + "you might have meant to start a match arm after the match guard"; + if this.eat(exp!(CloseBrace)) { + let applicability = if this.token != token::FatArrow { + // We have high confidence that we indeed didn't have a struct + // literal in the match guard, but rather we had some operation + // that ended in a path, immediately followed by a block that was + // meant to be the match arm. + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + err.span_suggestion_verbose(sugg_sp, msg, "=> ", applicability); + } + } + Err(err) } } - Err(err) - } - } + }, + )?; + Ok(expr) } pub(crate) fn is_builtin(&self) -> bool { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 36ca8a7d61339..8c1c3c7025f5e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1788,4 +1788,5 @@ pub enum ParseNtResult { Meta(Box), Path(Box), Vis(Box), + Guard(Box), } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 706b454835fab..ddf1b10e5235d 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -31,7 +31,8 @@ impl<'a> Parser<'a> { MetaVarKind::Item | MetaVarKind::Block - | MetaVarKind::Vis => false, + | MetaVarKind::Vis + | MetaVarKind::Guard => false, MetaVarKind::Ident | MetaVarKind::Lifetime @@ -86,7 +87,8 @@ impl<'a> Parser<'a> { | MetaVarKind::Ty { .. } | MetaVarKind::Meta { .. } | MetaVarKind::Path - | MetaVarKind::Vis => false, + | MetaVarKind::Vis + | MetaVarKind::Guard => false, MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => { unreachable!() } @@ -103,6 +105,7 @@ impl<'a> Parser<'a> { token::Lifetime(..) | token::NtLifetime(..) => true, _ => false, }, + NonterminalKind::Guard => token.is_keyword(kw::If), NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => { token.kind.close_delim().is_none() } @@ -196,6 +199,9 @@ impl<'a> Parser<'a> { })) } } + NonterminalKind::Guard => { + Ok(ParseNtResult::Guard(self.expect_match_arm_guard(ForceCollect::Yes)?)) + } } } } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7ee7781f6be0a..a21d19b6d3a20 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -6,8 +6,9 @@ use rustc_ast::token::{self, IdentIsRaw, MetaVarKind, Token}; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ - self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability, - Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind, + self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, Guard, LocalKind, MacCall, + Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, + StmtKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; @@ -110,11 +111,19 @@ impl<'a> Parser<'a> { let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; if self.eat_keyword(exp!(If)) { - let cond = self.parse_expr()?; + let guard = if let Some(guard) = self.eat_metavar_guard() { + guard + } else { + let leading_if_span = self.prev_token.span; + let cond = self.parse_expr()?; + let cond_span = cond.span; + Box::new(Guard { cond: *cond, span_with_leading_if: leading_if_span.to(cond_span) }) + }; + // Feature-gate guard patterns - self.psess.gated_spans.gate(sym::guard_patterns, cond.span); - let span = pat.span.to(cond.span); - Ok(self.mk_pat(span, PatKind::Guard(Box::new(pat), cond))) + self.psess.gated_spans.gate(sym::guard_patterns, guard.span()); + let span = pat.span.to(guard.span()); + Ok(self.mk_pat(span, PatKind::Guard(Box::new(pat), guard))) } else { Ok(pat) } @@ -601,17 +610,18 @@ impl<'a> Parser<'a> { } Some(guard) => { // Are parentheses required around the old guard? - let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd; + let wrap_guard = + guard.cond.precedence() <= ExprPrecedence::LAnd; err.subdiagnostic( UnexpectedExpressionInPatternSugg::UpdateGuard { ident_span, guard_lo: if wrap_guard { - Some(guard.span.shrink_to_lo()) + Some(guard.span().shrink_to_lo()) } else { None }, - guard_hi: guard.span.shrink_to_hi(), + guard_hi: guard.span().shrink_to_hi(), guard_hi_paren: if wrap_guard { ")" } else { "" }, ident, expr, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 453fe9d7a8e0e..d5cb3aefec0f9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4054,7 +4054,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_arm(&mut self, arm: &'ast Arm) { self.with_rib(ValueNS, RibKind::Normal, |this| { this.resolve_pattern_top(&arm.pat, PatternSource::Match); - visit_opt!(this, visit_expr, &arm.guard); + visit_opt!(this, visit_expr, arm.guard.as_ref().map(|g| &g.cond)); visit_opt!(this, visit_expr, &arm.body); }); } @@ -4196,7 +4196,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let subpat_bindings = bindings.pop().unwrap().1; self.with_rib(ValueNS, RibKind::Normal, |this| { *this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone(); - this.resolve_expr(guard, None); + this.resolve_expr(&guard.cond, None); }); // Propagate the subpattern's bindings upwards. // FIXME(guard_patterns): For `if let` guards, we'll also need to get the diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 257ac3f51c2c1..96bda60c363ef 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1027,6 +1027,7 @@ symbols! { global_registration, globs, gt, + guard, guard_patterns, half_open_range_patterns, half_open_range_patterns_in_slices, @@ -1190,6 +1191,7 @@ symbols! { macro_derive, macro_escape, macro_export, + macro_guard_matcher, macro_lifetime_matcher, macro_literal_matcher, macro_metavar_expr, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 50cfb0ed89dec..9a463d1a9d71e 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -280,7 +280,7 @@ pub fn eq_arm(l: &Arm, r: &Arm) -> bool { l.is_placeholder == r.is_placeholder && eq_pat(&l.pat, &r.pat) && eq_expr_opt(l.body.as_deref(), r.body.as_deref()) - && eq_expr_opt(l.guard.as_deref(), r.guard.as_deref()) + && eq_expr_opt(l.guard.as_deref().map(|g| &g.cond), r.guard.as_deref().map(|g| &g.cond)) && over(&l.attrs, &r.attrs, eq_attr) } diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index 4741abbe46583..50c0db8ac06e5 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -571,7 +571,7 @@ fn rewrite_match_body( // The `if ...` guard on a match arm. fn rewrite_guard( context: &RewriteContext<'_>, - guard: &Option>, + guard: &Option>, shape: Shape, // The amount of space used up on this line for the pattern in // the arm (excludes offset). @@ -586,7 +586,7 @@ fn rewrite_guard( .and_then(|s| s.sub_width_opt(5)); if !multiline_pattern { if let Some(cond_shape) = cond_shape { - if let Ok(cond_str) = guard.rewrite_result(context, cond_shape) { + if let Ok(cond_str) = guard.cond.rewrite_result(context, cond_shape) { if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { return Ok(format!(" if {cond_str}")); } @@ -597,9 +597,9 @@ fn rewrite_guard( // Not enough space to put the guard after the pattern, try a newline. // 3 = `if `, 5 = ` => {` let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config) - .offset_left(3, guard.span)? - .sub_width(5, guard.span)?; - let cond_str = guard.rewrite_result(context, cond_shape)?; + .offset_left(3, guard.span())? + .sub_width(5, guard.span())?; + let cond_str = guard.cond.rewrite_result(context, cond_shape)?; Ok(format!( "{}if {}", cond_shape.indent.to_string_with_newline(context.config), diff --git a/tests/ui/feature-gates/feature-gate-macro-guard-matcher.rs b/tests/ui/feature-gates/feature-gate-macro-guard-matcher.rs new file mode 100644 index 0000000000000..662161f19fac7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-macro-guard-matcher.rs @@ -0,0 +1,5 @@ +fn main() { + macro_rules! m { + ($x:guard) => {}; //~ ERROR `guard` fragments in macro are unstable + } +} diff --git a/tests/ui/feature-gates/feature-gate-macro-guard-matcher.stderr b/tests/ui/feature-gates/feature-gate-macro-guard-matcher.stderr new file mode 100644 index 0000000000000..0977f944f74fd --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-macro-guard-matcher.stderr @@ -0,0 +1,13 @@ +error[E0658]: `guard` fragments in macro are unstable + --> $DIR/feature-gate-macro-guard-matcher.rs:3:10 + | +LL | ($x:guard) => {}; + | ^^^^^^^^ + | + = note: see issue #153104 for more information + = help: add `#![feature(macro_guard_matcher)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/macros/macro-follow-rpass.rs b/tests/ui/macros/macro-follow-rpass.rs index 8551b1887708e..d941423656287 100644 --- a/tests/ui/macros/macro-follow-rpass.rs +++ b/tests/ui/macros/macro-follow-rpass.rs @@ -3,13 +3,14 @@ #![allow(unused_macros)] // Check the macro follow sets (see corresponding cfail test). -// FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), Ident(in)} +// FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), MetaVarDecl(Guard), Ident(in)} macro_rules! follow_pat { ($p:pat =>) => {}; ($p:pat ,) => {}; ($p:pat =) => {}; ($p:pat |) => {}; ($p:pat if) => {}; + ($p:pat if let) => {}; ($p:pat in) => {}; } // FOLLOW(expr) = {FatArrow, Comma, Semicolon} diff --git a/tests/ui/macros/macro-follow.rs b/tests/ui/macros/macro-follow.rs index 923c9bd6cedc9..874bad6a74316 100644 --- a/tests/ui/macros/macro-follow.rs +++ b/tests/ui/macros/macro-follow.rs @@ -2,9 +2,10 @@ // // Check the macro follow sets (see corresponding rpass test). +#![feature(macro_guard_matcher)] #![allow(unused_macros)] -// FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), Ident(in)} +// FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), MetaVarDecl(Guard), Ident(in)} macro_rules! follow_pat { ($p:pat ()) => {}; //~ERROR `$p:pat` is followed by `(` ($p:pat []) => {}; //~ERROR `$p:pat` is followed by `[` @@ -47,6 +48,7 @@ macro_rules! follow_expr { ($e:expr $t:tt) => {}; //~ERROR `$e:expr` is followed by `$t:tt` ($e:expr $i:item) => {}; //~ERROR `$e:expr` is followed by `$i:item` ($e:expr $m:meta) => {}; //~ERROR `$e:expr` is followed by `$m:meta` + ($e:expr $g:guard) => {}; //~ERROR `$e:expr` is followed by `$g:guard` } // FOLLOW(ty) = {OpenDelim(Brace), Comma, FatArrow, Colon, Eq, Gt, Semi, Or, // Ident(as), Ident(where), OpenDelim(Bracket), Nonterminal(Block)} @@ -66,6 +68,7 @@ macro_rules! follow_ty { ($t:ty $r:tt) => {}; //~ERROR `$t:ty` is followed by `$r:tt` ($t:ty $i:item) => {}; //~ERROR `$t:ty` is followed by `$i:item` ($t:ty $m:meta) => {}; //~ERROR `$t:ty` is followed by `$m:meta` + ($t:ty $g:guard) => {}; //~ERROR `$t:ty` is followed by `$g:guard` } // FOLLOW(stmt) = FOLLOW(expr) macro_rules! follow_stmt { @@ -90,6 +93,7 @@ macro_rules! follow_stmt { ($s:stmt $t:tt) => {}; //~ERROR `$s:stmt` is followed by `$t:tt` ($s:stmt $i:item) => {}; //~ERROR `$s:stmt` is followed by `$i:item` ($s:stmt $m:meta) => {}; //~ERROR `$s:stmt` is followed by `$m:meta` + ($s:stmt $g:guard) => {}; //~ERROR `$s:stmt` is followed by `$g:guard` } // FOLLOW(path) = FOLLOW(ty) macro_rules! follow_path { @@ -108,6 +112,7 @@ macro_rules! follow_path { ($p:path $t:tt) => {}; //~ERROR `$p:path` is followed by `$t:tt` ($p:path $i:item) => {}; //~ERROR `$p:path` is followed by `$i:item` ($p:path $m:meta) => {}; //~ERROR `$p:path` is followed by `$m:meta` + ($p:path $g:guard) => {}; //~ERROR `$p:path` is followed by `$g:guard` } // FOLLOW(block) = any token // FOLLOW(ident) = any token diff --git a/tests/ui/macros/macro-follow.stderr b/tests/ui/macros/macro-follow.stderr index 92491dc26d12b..78d167added73 100644 --- a/tests/ui/macros/macro-follow.stderr +++ b/tests/ui/macros/macro-follow.stderr @@ -1,141 +1,141 @@ error: `$p:pat` is followed by `(`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:9:13 + --> $DIR/macro-follow.rs:10:13 | LL | ($p:pat ()) => {}; | ^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `[`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:10:13 + --> $DIR/macro-follow.rs:11:13 | LL | ($p:pat []) => {}; | ^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `{`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:11:13 + --> $DIR/macro-follow.rs:12:13 | LL | ($p:pat {}) => {}; | ^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `:`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:12:13 + --> $DIR/macro-follow.rs:13:13 | LL | ($p:pat :) => {}; | ^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `>`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:13:13 + --> $DIR/macro-follow.rs:14:13 | LL | ($p:pat >) => {}; | ^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `+`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:14:13 + --> $DIR/macro-follow.rs:15:13 | LL | ($p:pat +) => {}; | ^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `ident`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:15:13 + --> $DIR/macro-follow.rs:16:13 | LL | ($p:pat ident) => {}; | ^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$q:pat`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:16:13 + --> $DIR/macro-follow.rs:17:13 | LL | ($p:pat $q:pat) => {}; | ^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$e:expr`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:17:13 + --> $DIR/macro-follow.rs:18:13 | LL | ($p:pat $e:expr) => {}; | ^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$t:ty`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:18:13 + --> $DIR/macro-follow.rs:19:13 | LL | ($p:pat $t:ty) => {}; | ^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$s:stmt`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:19:13 + --> $DIR/macro-follow.rs:20:13 | LL | ($p:pat $s:stmt) => {}; | ^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$q:path`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:20:13 + --> $DIR/macro-follow.rs:21:13 | LL | ($p:pat $q:path) => {}; | ^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$b:block`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:21:13 + --> $DIR/macro-follow.rs:22:13 | LL | ($p:pat $b:block) => {}; | ^^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$i:ident`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:22:13 + --> $DIR/macro-follow.rs:23:13 | LL | ($p:pat $i:ident) => {}; | ^^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$t:tt`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:23:13 + --> $DIR/macro-follow.rs:24:13 | LL | ($p:pat $t:tt) => {}; | ^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$i:item`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:24:13 + --> $DIR/macro-follow.rs:25:13 | LL | ($p:pat $i:item) => {}; | ^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$p:pat` is followed by `$m:meta`, which is not allowed for `pat` fragments - --> $DIR/macro-follow.rs:25:13 + --> $DIR/macro-follow.rs:26:13 | LL | ($p:pat $m:meta) => {}; | ^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$e:expr` is followed by `(`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:29:14 + --> $DIR/macro-follow.rs:30:14 | LL | ($e:expr ()) => {}; | ^ not allowed after `expr` fragments @@ -143,7 +143,7 @@ LL | ($e:expr ()) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `[`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:30:14 + --> $DIR/macro-follow.rs:31:14 | LL | ($e:expr []) => {}; | ^ not allowed after `expr` fragments @@ -151,7 +151,7 @@ LL | ($e:expr []) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `{`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:31:14 + --> $DIR/macro-follow.rs:32:14 | LL | ($e:expr {}) => {}; | ^ not allowed after `expr` fragments @@ -159,7 +159,7 @@ LL | ($e:expr {}) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `=`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:32:14 + --> $DIR/macro-follow.rs:33:14 | LL | ($e:expr =) => {}; | ^ not allowed after `expr` fragments @@ -167,7 +167,7 @@ LL | ($e:expr =) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `|`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:33:14 + --> $DIR/macro-follow.rs:34:14 | LL | ($e:expr |) => {}; | ^ not allowed after `expr` fragments @@ -175,7 +175,7 @@ LL | ($e:expr |) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `:`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:34:14 + --> $DIR/macro-follow.rs:35:14 | LL | ($e:expr :) => {}; | ^ not allowed after `expr` fragments @@ -183,7 +183,7 @@ LL | ($e:expr :) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `>`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:35:14 + --> $DIR/macro-follow.rs:36:14 | LL | ($e:expr >) => {}; | ^ not allowed after `expr` fragments @@ -191,7 +191,7 @@ LL | ($e:expr >) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `+`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:36:14 + --> $DIR/macro-follow.rs:37:14 | LL | ($e:expr +) => {}; | ^ not allowed after `expr` fragments @@ -199,7 +199,7 @@ LL | ($e:expr +) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `ident`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:37:14 + --> $DIR/macro-follow.rs:38:14 | LL | ($e:expr ident) => {}; | ^^^^^ not allowed after `expr` fragments @@ -207,7 +207,7 @@ LL | ($e:expr ident) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `if`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:38:14 + --> $DIR/macro-follow.rs:39:14 | LL | ($e:expr if) => {}; | ^^ not allowed after `expr` fragments @@ -215,7 +215,7 @@ LL | ($e:expr if) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `in`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:39:14 + --> $DIR/macro-follow.rs:40:14 | LL | ($e:expr in) => {}; | ^^ not allowed after `expr` fragments @@ -223,7 +223,7 @@ LL | ($e:expr in) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$p:pat`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:40:14 + --> $DIR/macro-follow.rs:41:14 | LL | ($e:expr $p:pat) => {}; | ^^^^^^ not allowed after `expr` fragments @@ -231,7 +231,7 @@ LL | ($e:expr $p:pat) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$f:expr`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:41:14 + --> $DIR/macro-follow.rs:42:14 | LL | ($e:expr $f:expr) => {}; | ^^^^^^^ not allowed after `expr` fragments @@ -239,7 +239,7 @@ LL | ($e:expr $f:expr) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$t:ty`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:42:14 + --> $DIR/macro-follow.rs:43:14 | LL | ($e:expr $t:ty) => {}; | ^^^^^ not allowed after `expr` fragments @@ -247,7 +247,7 @@ LL | ($e:expr $t:ty) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$s:stmt`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:43:14 + --> $DIR/macro-follow.rs:44:14 | LL | ($e:expr $s:stmt) => {}; | ^^^^^^^ not allowed after `expr` fragments @@ -255,7 +255,7 @@ LL | ($e:expr $s:stmt) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$p:path`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:44:14 + --> $DIR/macro-follow.rs:45:14 | LL | ($e:expr $p:path) => {}; | ^^^^^^^ not allowed after `expr` fragments @@ -263,7 +263,7 @@ LL | ($e:expr $p:path) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$b:block`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:45:14 + --> $DIR/macro-follow.rs:46:14 | LL | ($e:expr $b:block) => {}; | ^^^^^^^^ not allowed after `expr` fragments @@ -271,7 +271,7 @@ LL | ($e:expr $b:block) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$i:ident`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:46:14 + --> $DIR/macro-follow.rs:47:14 | LL | ($e:expr $i:ident) => {}; | ^^^^^^^^ not allowed after `expr` fragments @@ -279,7 +279,7 @@ LL | ($e:expr $i:ident) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$t:tt`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:47:14 + --> $DIR/macro-follow.rs:48:14 | LL | ($e:expr $t:tt) => {}; | ^^^^^ not allowed after `expr` fragments @@ -287,7 +287,7 @@ LL | ($e:expr $t:tt) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$i:item`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:48:14 + --> $DIR/macro-follow.rs:49:14 | LL | ($e:expr $i:item) => {}; | ^^^^^^^ not allowed after `expr` fragments @@ -295,15 +295,23 @@ LL | ($e:expr $i:item) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$e:expr` is followed by `$m:meta`, which is not allowed for `expr` fragments - --> $DIR/macro-follow.rs:49:14 + --> $DIR/macro-follow.rs:50:14 | LL | ($e:expr $m:meta) => {}; | ^^^^^^^ not allowed after `expr` fragments | = note: allowed there are: `=>`, `,` or `;` +error: `$e:expr` is followed by `$g:guard`, which is not allowed for `expr` fragments + --> $DIR/macro-follow.rs:51:14 + | +LL | ($e:expr $g:guard) => {}; + | ^^^^^^^^ not allowed after `expr` fragments + | + = note: allowed there are: `=>`, `,` or `;` + error: `$t:ty` is followed by `(`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:54:12 + --> $DIR/macro-follow.rs:56:12 | LL | ($t:ty ()) => {}; | ^ not allowed after `ty` fragments @@ -311,7 +319,7 @@ LL | ($t:ty ()) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `+`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:56:12 + --> $DIR/macro-follow.rs:58:12 | LL | ($t:ty +) => {}; | ^ not allowed after `ty` fragments @@ -319,7 +327,7 @@ LL | ($t:ty +) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `ident`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:57:12 + --> $DIR/macro-follow.rs:59:12 | LL | ($t:ty ident) => {}; | ^^^^^ not allowed after `ty` fragments @@ -327,7 +335,7 @@ LL | ($t:ty ident) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `if`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:58:12 + --> $DIR/macro-follow.rs:60:12 | LL | ($t:ty if) => {}; | ^^ not allowed after `ty` fragments @@ -335,7 +343,7 @@ LL | ($t:ty if) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$p:pat`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:59:12 + --> $DIR/macro-follow.rs:61:12 | LL | ($t:ty $p:pat) => {}; | ^^^^^^ not allowed after `ty` fragments @@ -343,7 +351,7 @@ LL | ($t:ty $p:pat) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$e:expr`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:60:12 + --> $DIR/macro-follow.rs:62:12 | LL | ($t:ty $e:expr) => {}; | ^^^^^^^ not allowed after `ty` fragments @@ -351,7 +359,7 @@ LL | ($t:ty $e:expr) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$r:ty`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:61:12 + --> $DIR/macro-follow.rs:63:12 | LL | ($t:ty $r:ty) => {}; | ^^^^^ not allowed after `ty` fragments @@ -359,7 +367,7 @@ LL | ($t:ty $r:ty) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$s:stmt`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:62:12 + --> $DIR/macro-follow.rs:64:12 | LL | ($t:ty $s:stmt) => {}; | ^^^^^^^ not allowed after `ty` fragments @@ -367,7 +375,7 @@ LL | ($t:ty $s:stmt) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$p:path`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:63:12 + --> $DIR/macro-follow.rs:65:12 | LL | ($t:ty $p:path) => {}; | ^^^^^^^ not allowed after `ty` fragments @@ -375,7 +383,7 @@ LL | ($t:ty $p:path) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$i:ident`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:65:12 + --> $DIR/macro-follow.rs:67:12 | LL | ($t:ty $i:ident) => {}; | ^^^^^^^^ not allowed after `ty` fragments @@ -383,7 +391,7 @@ LL | ($t:ty $i:ident) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$r:tt`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:66:12 + --> $DIR/macro-follow.rs:68:12 | LL | ($t:ty $r:tt) => {}; | ^^^^^ not allowed after `ty` fragments @@ -391,7 +399,7 @@ LL | ($t:ty $r:tt) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$i:item`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:67:12 + --> $DIR/macro-follow.rs:69:12 | LL | ($t:ty $i:item) => {}; | ^^^^^^^ not allowed after `ty` fragments @@ -399,15 +407,23 @@ LL | ($t:ty $i:item) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$t:ty` is followed by `$m:meta`, which is not allowed for `ty` fragments - --> $DIR/macro-follow.rs:68:12 + --> $DIR/macro-follow.rs:70:12 | LL | ($t:ty $m:meta) => {}; | ^^^^^^^ not allowed after `ty` fragments | = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` +error: `$t:ty` is followed by `$g:guard`, which is not allowed for `ty` fragments + --> $DIR/macro-follow.rs:71:12 + | +LL | ($t:ty $g:guard) => {}; + | ^^^^^^^^ not allowed after `ty` fragments + | + = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` + error: `$s:stmt` is followed by `(`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:72:14 + --> $DIR/macro-follow.rs:75:14 | LL | ($s:stmt ()) => {}; | ^ not allowed after `stmt` fragments @@ -415,7 +431,7 @@ LL | ($s:stmt ()) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `[`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:73:14 + --> $DIR/macro-follow.rs:76:14 | LL | ($s:stmt []) => {}; | ^ not allowed after `stmt` fragments @@ -423,7 +439,7 @@ LL | ($s:stmt []) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `{`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:74:14 + --> $DIR/macro-follow.rs:77:14 | LL | ($s:stmt {}) => {}; | ^ not allowed after `stmt` fragments @@ -431,7 +447,7 @@ LL | ($s:stmt {}) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `=`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:75:14 + --> $DIR/macro-follow.rs:78:14 | LL | ($s:stmt =) => {}; | ^ not allowed after `stmt` fragments @@ -439,7 +455,7 @@ LL | ($s:stmt =) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `|`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:76:14 + --> $DIR/macro-follow.rs:79:14 | LL | ($s:stmt |) => {}; | ^ not allowed after `stmt` fragments @@ -447,7 +463,7 @@ LL | ($s:stmt |) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `:`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:77:14 + --> $DIR/macro-follow.rs:80:14 | LL | ($s:stmt :) => {}; | ^ not allowed after `stmt` fragments @@ -455,7 +471,7 @@ LL | ($s:stmt :) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `>`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:78:14 + --> $DIR/macro-follow.rs:81:14 | LL | ($s:stmt >) => {}; | ^ not allowed after `stmt` fragments @@ -463,7 +479,7 @@ LL | ($s:stmt >) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `+`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:79:14 + --> $DIR/macro-follow.rs:82:14 | LL | ($s:stmt +) => {}; | ^ not allowed after `stmt` fragments @@ -471,7 +487,7 @@ LL | ($s:stmt +) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `ident`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:80:14 + --> $DIR/macro-follow.rs:83:14 | LL | ($s:stmt ident) => {}; | ^^^^^ not allowed after `stmt` fragments @@ -479,7 +495,7 @@ LL | ($s:stmt ident) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `if`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:81:14 + --> $DIR/macro-follow.rs:84:14 | LL | ($s:stmt if) => {}; | ^^ not allowed after `stmt` fragments @@ -487,7 +503,7 @@ LL | ($s:stmt if) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `in`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:82:14 + --> $DIR/macro-follow.rs:85:14 | LL | ($s:stmt in) => {}; | ^^ not allowed after `stmt` fragments @@ -495,7 +511,7 @@ LL | ($s:stmt in) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$p:pat`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:83:14 + --> $DIR/macro-follow.rs:86:14 | LL | ($s:stmt $p:pat) => {}; | ^^^^^^ not allowed after `stmt` fragments @@ -503,7 +519,7 @@ LL | ($s:stmt $p:pat) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$e:expr`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:84:14 + --> $DIR/macro-follow.rs:87:14 | LL | ($s:stmt $e:expr) => {}; | ^^^^^^^ not allowed after `stmt` fragments @@ -511,7 +527,7 @@ LL | ($s:stmt $e:expr) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$t:ty`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:85:14 + --> $DIR/macro-follow.rs:88:14 | LL | ($s:stmt $t:ty) => {}; | ^^^^^ not allowed after `stmt` fragments @@ -519,7 +535,7 @@ LL | ($s:stmt $t:ty) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$t:stmt`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:86:14 + --> $DIR/macro-follow.rs:89:14 | LL | ($s:stmt $t:stmt) => {}; | ^^^^^^^ not allowed after `stmt` fragments @@ -527,7 +543,7 @@ LL | ($s:stmt $t:stmt) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$p:path`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:87:14 + --> $DIR/macro-follow.rs:90:14 | LL | ($s:stmt $p:path) => {}; | ^^^^^^^ not allowed after `stmt` fragments @@ -535,7 +551,7 @@ LL | ($s:stmt $p:path) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$b:block`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:88:14 + --> $DIR/macro-follow.rs:91:14 | LL | ($s:stmt $b:block) => {}; | ^^^^^^^^ not allowed after `stmt` fragments @@ -543,7 +559,7 @@ LL | ($s:stmt $b:block) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$i:ident`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:89:14 + --> $DIR/macro-follow.rs:92:14 | LL | ($s:stmt $i:ident) => {}; | ^^^^^^^^ not allowed after `stmt` fragments @@ -551,7 +567,7 @@ LL | ($s:stmt $i:ident) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$t:tt`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:90:14 + --> $DIR/macro-follow.rs:93:14 | LL | ($s:stmt $t:tt) => {}; | ^^^^^ not allowed after `stmt` fragments @@ -559,7 +575,7 @@ LL | ($s:stmt $t:tt) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$i:item`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:91:14 + --> $DIR/macro-follow.rs:94:14 | LL | ($s:stmt $i:item) => {}; | ^^^^^^^ not allowed after `stmt` fragments @@ -567,23 +583,31 @@ LL | ($s:stmt $i:item) => {}; = note: allowed there are: `=>`, `,` or `;` error: `$s:stmt` is followed by `$m:meta`, which is not allowed for `stmt` fragments - --> $DIR/macro-follow.rs:92:14 + --> $DIR/macro-follow.rs:95:14 | LL | ($s:stmt $m:meta) => {}; | ^^^^^^^ not allowed after `stmt` fragments | = note: allowed there are: `=>`, `,` or `;` -error: `$p:path` is followed by `(`, which is not allowed for `path` fragments +error: `$s:stmt` is followed by `$g:guard`, which is not allowed for `stmt` fragments --> $DIR/macro-follow.rs:96:14 | +LL | ($s:stmt $g:guard) => {}; + | ^^^^^^^^ not allowed after `stmt` fragments + | + = note: allowed there are: `=>`, `,` or `;` + +error: `$p:path` is followed by `(`, which is not allowed for `path` fragments + --> $DIR/macro-follow.rs:100:14 + | LL | ($p:path ()) => {}; | ^ not allowed after `path` fragments | = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `+`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:98:14 + --> $DIR/macro-follow.rs:102:14 | LL | ($p:path +) => {}; | ^ not allowed after `path` fragments @@ -591,7 +615,7 @@ LL | ($p:path +) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `ident`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:99:14 + --> $DIR/macro-follow.rs:103:14 | LL | ($p:path ident) => {}; | ^^^^^ not allowed after `path` fragments @@ -599,7 +623,7 @@ LL | ($p:path ident) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `if`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:100:14 + --> $DIR/macro-follow.rs:104:14 | LL | ($p:path if) => {}; | ^^ not allowed after `path` fragments @@ -607,7 +631,7 @@ LL | ($p:path if) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$q:pat`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:101:14 + --> $DIR/macro-follow.rs:105:14 | LL | ($p:path $q:pat) => {}; | ^^^^^^ not allowed after `path` fragments @@ -615,7 +639,7 @@ LL | ($p:path $q:pat) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$e:expr`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:102:14 + --> $DIR/macro-follow.rs:106:14 | LL | ($p:path $e:expr) => {}; | ^^^^^^^ not allowed after `path` fragments @@ -623,7 +647,7 @@ LL | ($p:path $e:expr) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$t:ty`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:103:14 + --> $DIR/macro-follow.rs:107:14 | LL | ($p:path $t:ty) => {}; | ^^^^^ not allowed after `path` fragments @@ -631,7 +655,7 @@ LL | ($p:path $t:ty) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$s:stmt`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:104:14 + --> $DIR/macro-follow.rs:108:14 | LL | ($p:path $s:stmt) => {}; | ^^^^^^^ not allowed after `path` fragments @@ -639,7 +663,7 @@ LL | ($p:path $s:stmt) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$q:path`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:105:14 + --> $DIR/macro-follow.rs:109:14 | LL | ($p:path $q:path) => {}; | ^^^^^^^ not allowed after `path` fragments @@ -647,7 +671,7 @@ LL | ($p:path $q:path) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$i:ident`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:107:14 + --> $DIR/macro-follow.rs:111:14 | LL | ($p:path $i:ident) => {}; | ^^^^^^^^ not allowed after `path` fragments @@ -655,7 +679,7 @@ LL | ($p:path $i:ident) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$t:tt`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:108:14 + --> $DIR/macro-follow.rs:112:14 | LL | ($p:path $t:tt) => {}; | ^^^^^ not allowed after `path` fragments @@ -663,7 +687,7 @@ LL | ($p:path $t:tt) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$i:item`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:109:14 + --> $DIR/macro-follow.rs:113:14 | LL | ($p:path $i:item) => {}; | ^^^^^^^ not allowed after `path` fragments @@ -671,12 +695,20 @@ LL | ($p:path $i:item) => {}; = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` error: `$p:path` is followed by `$m:meta`, which is not allowed for `path` fragments - --> $DIR/macro-follow.rs:110:14 + --> $DIR/macro-follow.rs:114:14 | LL | ($p:path $m:meta) => {}; | ^^^^^^^ not allowed after `path` fragments | = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` -error: aborting due to 85 previous errors +error: `$p:path` is followed by `$g:guard`, which is not allowed for `path` fragments + --> $DIR/macro-follow.rs:115:14 + | +LL | ($p:path $g:guard) => {}; + | ^^^^^^^^ not allowed after `path` fragments + | + = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` + +error: aborting due to 89 previous errors diff --git a/tests/ui/macros/macro-guard-matcher.rs b/tests/ui/macros/macro-guard-matcher.rs new file mode 100644 index 0000000000000..81a4412686de3 --- /dev/null +++ b/tests/ui/macros/macro-guard-matcher.rs @@ -0,0 +1,17 @@ +#![feature(macro_guard_matcher)] + +fn main() { + macro_rules! m { + ($x:guard) => {}; + } + + // Accepts + m!(if true); + m!(if let Some(x) = Some(1)); + m!(if let Some(x) = Some(1) && x == 1); + m!(if let Some(x) = Some(Some(1)) && let Some(1) = x); + m!(if let Some(x) = Some(Some(1)) && let Some(y) = x && y == 1); + + // Rejects + m!(let Some(x) = Some(1)); //~ERROR no rules expected keyword `let` +} diff --git a/tests/ui/macros/macro-guard-matcher.stderr b/tests/ui/macros/macro-guard-matcher.stderr new file mode 100644 index 0000000000000..eddb0de9c4c54 --- /dev/null +++ b/tests/ui/macros/macro-guard-matcher.stderr @@ -0,0 +1,17 @@ +error: no rules expected keyword `let` + --> $DIR/macro-guard-matcher.rs:16:8 + | +LL | macro_rules! m { + | -------------- when calling this macro +... +LL | m!(let Some(x) = Some(1)); + | ^^^ no rules expected this token in macro call + | +note: while trying to match meta-variable `$x:guard` + --> $DIR/macro-guard-matcher.rs:5:10 + | +LL | ($x:guard) => {}; + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/macros/macro-input-future-proofing.stderr b/tests/ui/macros/macro-input-future-proofing.stderr index 11960db987435..a12cc9f9b416d 100644 --- a/tests/ui/macros/macro-input-future-proofing.stderr +++ b/tests/ui/macros/macro-input-future-proofing.stderr @@ -20,7 +20,7 @@ error: `$pa:pat` is followed by `>`, which is not allowed for `pat` fragments LL | ($pa:pat >) => (); | ^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$pa:pat` is followed by `$pb:pat`, which is not allowed for `pat` fragments --> $DIR/macro-input-future-proofing.rs:14:14 @@ -28,7 +28,7 @@ error: `$pa:pat` is followed by `$pb:pat`, which is not allowed for `pat` fragme LL | ($pa:pat $pb:pat $ty:ty ,) => (); | ^^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$pb:pat` is followed by `$ty:ty`, which is not allowed for `pat` fragments --> $DIR/macro-input-future-proofing.rs:14:22 @@ -36,7 +36,7 @@ error: `$pb:pat` is followed by `$ty:ty`, which is not allowed for `pat` fragmen LL | ($pa:pat $pb:pat $ty:ty ,) => (); | ^^^^^^ not allowed after `pat` fragments | - = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `|`, `if`, `if let` or `in` error: `$ty:ty` is followed by `-`, which is not allowed for `ty` fragments --> $DIR/macro-input-future-proofing.rs:17:17 diff --git a/tests/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr b/tests/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr index a06487be3d601..9179fbc31961c 100644 --- a/tests/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr +++ b/tests/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr @@ -6,7 +6,7 @@ LL | macro_rules! foo { ($x:pat | $y:pat) => {} } | | | help: try a `pat_param` fragment specifier instead: `$x:pat_param` | - = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `if`, `if let` or `in` error: `$x:pat` is followed by `|`, which is not allowed for `pat` fragments --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:4:32 @@ -16,7 +16,7 @@ LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } | | | help: try a `pat_param` fragment specifier instead: `$x:pat_param` | - = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `if`, `if let` or `in` error: `$pat:pat` may be followed by `|`, which is not allowed for `pat` fragments --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:7:36 @@ -26,7 +26,7 @@ LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { | | | help: try a `pat_param` fragment specifier instead: `$pat:pat_param` | - = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `if`, `if let` or `in` error: aborting due to 3 previous errors diff --git a/tests/ui/macros/macro-pat2021-pattern-followed-by-or.stderr b/tests/ui/macros/macro-pat2021-pattern-followed-by-or.stderr index c3754dde080a3..af76e3f095f15 100644 --- a/tests/ui/macros/macro-pat2021-pattern-followed-by-or.stderr +++ b/tests/ui/macros/macro-pat2021-pattern-followed-by-or.stderr @@ -6,7 +6,7 @@ LL | macro_rules! foo { ($x:pat | $y:pat) => {} } | | | help: try a `pat_param` fragment specifier instead: `$x:pat_param` | - = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `if`, `if let` or `in` error: `$x:pat` is followed by `|`, which is not allowed for `pat` fragments --> $DIR/macro-pat2021-pattern-followed-by-or.rs:7:28 @@ -16,7 +16,7 @@ LL | macro_rules! ogg { ($x:pat | $y:pat_param) => {} } | | | help: try a `pat_param` fragment specifier instead: `$x:pat_param` | - = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `if`, `if let` or `in` error: `$pat:pat` may be followed by `|`, which is not allowed for `pat` fragments --> $DIR/macro-pat2021-pattern-followed-by-or.rs:9:35 @@ -26,7 +26,7 @@ LL | ( $expr:expr , $( $( $pat:pat)|+ => $expr_arm:pat),+ ) => { | | | help: try a `pat_param` fragment specifier instead: `$pat:pat_param` | - = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + = note: allowed there are: `=>`, `,`, `=`, `if`, `if let` or `in` error: aborting due to 3 previous errors diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 3f331b0dd5f14..af2ba7a809ad0 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -14,6 +14,7 @@ #![feature(trait_alias)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![feature(macro_guard_matcher)] #![deny(unused_macros)] // These macros force the use of AST pretty-printing by converting the input to @@ -27,6 +28,7 @@ macro_rules! path { ($path:path) => { stringify!($path) }; } macro_rules! stmt { ($stmt:stmt) => { stringify!($stmt) }; } macro_rules! ty { ($ty:ty) => { stringify!($ty) }; } macro_rules! vis { ($vis:vis) => { stringify!($vis) }; } +macro_rules! guard { ($guard:guard) => { stringify!($guard) }; } macro_rules! c1 { ($frag:ident, [$($tt:tt)*], $s:literal) => { @@ -792,6 +794,21 @@ fn test_vis() { // Attributes are not allowed on visibilities. } +#[test] +fn test_guard() { + c1!(guard, [ if true ], "if true"); + c1!(guard, [ if let Some(x) = Some(1) ], "if let Some(x) = Some(1)"); + c1!(guard, [ if let Some(x) = Some(1) && x == 1 ], "if let Some(x) = Some(1) && x == 1"); + c1!(guard, + [ if let Some(x) = Some(Some(1)) && let Some(1) = x ], + "if let Some(x) = Some(Some(1)) && let Some(1) = x" + ); + c1!(guard, + [ if let Some(x) = Some(Some(1)) && let Some(y) = x && y == 1 ], + "if let Some(x) = Some(Some(1)) && let Some(y) = x && y == 1" + ); +} + macro_rules! p { ([$($tt:tt)*], $s:literal) => { assert_eq!(stringify!($($tt)*), $s);