Detect attempts to expand a macro to a match arm again

Because a macro invocation can expand to a never pattern, we can't rule
out a `arm!(),` arm at parse time. Instead we detect that case at
expansion time, if the macro tries to output a pattern followed by `=>`.
This commit is contained in:
Nadrieril 2023-11-27 00:50:51 +01:00
parent 80bdcbf50a
commit 0bfebc6105
8 changed files with 27 additions and 29 deletions

View file

@ -71,6 +71,8 @@ expand_macro_const_stability =
.label = invalid const stability attribute .label = invalid const stability attribute
.label2 = const stability attribute affects this macro .label2 = const stability attribute affects this macro
expand_macro_expands_to_match_arm = macros cannot expand to match arms
expand_malformed_feature_attribute = expand_malformed_feature_attribute =
malformed `feature` attribute input malformed `feature` attribute input
.expected = expected just one word .expected = expected just one word

View file

@ -304,6 +304,8 @@ pub(crate) struct IncompleteParse<'a> {
pub label_span: Span, pub label_span: Span,
pub macro_path: &'a ast::Path, pub macro_path: &'a ast::Path,
pub kind_name: &'a str, pub kind_name: &'a str,
#[note(expand_macro_expands_to_match_arm)]
pub expands_to_match_arm: Option<()>,
#[suggestion( #[suggestion(
expand_suggestion_add_semi, expand_suggestion_add_semi,

View file

@ -955,12 +955,15 @@ pub fn ensure_complete_parse<'a>(
_ => None, _ => None,
}; };
let expands_to_match_arm = kind_name == "pattern" && parser.token == token::FatArrow;
parser.sess.emit_err(IncompleteParse { parser.sess.emit_err(IncompleteParse {
span: def_site_span, span: def_site_span,
token, token,
label_span: span, label_span: span,
macro_path, macro_path,
kind_name, kind_name,
expands_to_match_arm: expands_to_match_arm.then_some(()),
add_semicolon, add_semicolon,
}); });
} }

View file

@ -456,8 +456,6 @@ parse_macro_expands_to_adt_field = macros cannot expand to {$adt_ty} fields
parse_macro_expands_to_enum_variant = macros cannot expand to enum variants parse_macro_expands_to_enum_variant = macros cannot expand to enum variants
parse_macro_expands_to_match_arm = macros cannot expand to match arms
parse_macro_invocation_visibility = can't qualify macro invocation with `pub` parse_macro_invocation_visibility = can't qualify macro invocation with `pub`
.suggestion = remove the visibility .suggestion = remove the visibility
.help = try adjusting the macro to put `{$vis}` inside the invocation .help = try adjusting the macro to put `{$vis}` inside the invocation

View file

@ -2823,7 +2823,6 @@ impl<'a> Parser<'a> {
pub(crate) fn maybe_recover_unexpected_comma( pub(crate) fn maybe_recover_unexpected_comma(
&mut self, &mut self,
lo: Span, lo: Span,
is_mac_invoc: bool,
rt: CommaRecoveryMode, rt: CommaRecoveryMode,
) -> PResult<'a, ()> { ) -> PResult<'a, ()> {
if self.token != token::Comma { if self.token != token::Comma {
@ -2844,28 +2843,24 @@ impl<'a> Parser<'a> {
let seq_span = lo.to(self.prev_token.span); let seq_span = lo.to(self.prev_token.span);
let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
if is_mac_invoc { err.multipart_suggestion(
err.note(fluent::parse_macro_expands_to_match_arm); format!(
} else { "try adding parentheses to match on a tuple{}",
err.multipart_suggestion( if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
format!( ),
"try adding parentheses to match on a tuple{}", vec![
if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, (seq_span.shrink_to_lo(), "(".to_string()),
), (seq_span.shrink_to_hi(), ")".to_string()),
vec![ ],
(seq_span.shrink_to_lo(), "(".to_string()), Applicability::MachineApplicable,
(seq_span.shrink_to_hi(), ")".to_string()), );
], if let CommaRecoveryMode::EitherTupleOrPipe = rt {
err.span_suggestion(
seq_span,
"...or a vertical bar to match on multiple alternatives",
seq_snippet.replace(',', " |"),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
if let CommaRecoveryMode::EitherTupleOrPipe = rt {
err.span_suggestion(
seq_span,
"...or a vertical bar to match on multiple alternatives",
seq_snippet.replace(',', " |"),
Applicability::MachineApplicable,
);
}
} }
} }
Err(err) Err(err)

View file

@ -155,11 +155,7 @@ impl<'a> Parser<'a> {
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
if rc == RecoverComma::Yes && !first_pat.could_be_never_pattern() { if rc == RecoverComma::Yes && !first_pat.could_be_never_pattern() {
self.maybe_recover_unexpected_comma( self.maybe_recover_unexpected_comma(first_pat.span, rt)?;
first_pat.span,
matches!(first_pat.kind, PatKind::MacCall(_)),
rt,
)?;
} }
// If the next token is not a `|`, // If the next token is not a `|`,
@ -201,7 +197,7 @@ impl<'a> Parser<'a> {
err err
})?; })?;
if rc == RecoverComma::Yes && !pat.could_be_never_pattern() { if rc == RecoverComma::Yes && !pat.could_be_never_pattern() {
self.maybe_recover_unexpected_comma(pat.span, false, rt)?; self.maybe_recover_unexpected_comma(pat.span, rt)?;
} }
pats.push(pat); pats.push(pat);
} }

View file

@ -3,6 +3,7 @@ macro_rules! arm {
$pattern => $block $pattern => $block
//~^ ERROR macro expansion ignores token `=>` and any following //~^ ERROR macro expansion ignores token `=>` and any following
//~| NOTE the usage of `arm!` is likely invalid in pattern context //~| NOTE the usage of `arm!` is likely invalid in pattern context
//~| NOTE macros cannot expand to match arms
}; };
} }

View file

@ -8,6 +8,7 @@ LL | arm!(None => {}),
| ---------------- caused by the macro expansion here | ---------------- caused by the macro expansion here
| |
= note: the usage of `arm!` is likely invalid in pattern context = note: the usage of `arm!` is likely invalid in pattern context
= note: macros cannot expand to match arms
error: aborting due to 1 previous error error: aborting due to 1 previous error