Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion compiler/rustc_expand/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,15 @@ pub(crate) struct MissingFragmentSpecifier {
code = ":spec",
applicability = "maybe-incorrect"
)]
pub add_span: Span,
pub add_span: Option<Span>,
pub valid: &'static str,
#[suggestion(
"use `:` instead of `;`",
style = "verbose",
code = ":",
applicability = "maybe-incorrect"
)]
pub semi_span: Option<Span>,
}

#[derive(Diagnostic)]
Expand Down
43 changes: 39 additions & 4 deletions compiler/rustc_expand/src/mbe/quoted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ fn parse(
let mut missing_fragment_specifier = |span| {
sess.dcx().emit_err(errors::MissingFragmentSpecifier {
span,
add_span: span.shrink_to_hi(),
add_span: Some(span.shrink_to_hi()),
valid: VALID_FRAGMENT_NAMES_MSG,
semi_span: None,
});

// Fall back to a `TokenTree` since that will match anything if we continue expanding.
Expand Down Expand Up @@ -144,9 +145,43 @@ fn parse(
});
result.push(TokenTree::MetaVarDecl { span, name: ident, kind });
} else {
// Whether it's none or some other tree, it doesn't belong to
// the current meta variable, returning the original span.
missing_fragment_specifier(start_sp);
// Check for typo: `;` instead of `:` before a valid fragment specifier.
let typo_recovery = {
let mut clone = iter.clone();
if let Some(tokenstream::TokenTree::Token(semi_token, _)) = clone.next()
&& semi_token.kind == token::Semi
&& let Some(tokenstream::TokenTree::Token(frag_token, _)) = clone.next()
&& let Some((fragment, _)) = frag_token.ident()
{
let span = frag_token.span.with_lo(start_sp.lo());
let edition = || {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you move this closure upwards and reuse it for both branches?

// FIXME(#85708) - once we properly decode a foreign
// crate's `SyntaxContext::root`, then we can replace
// this with just `span.edition()`. A
// `SyntaxContext::root()` from the current crate will
// have the edition of the current crate, and a
// `SyntaxContext::root()` from a foreign crate will
// have the edition of that crate (which we manually
// retrieve via the `edition` parameter).
if !span.from_expansion() { edition } else { span.edition() }
};
NonterminalKind::from_symbol(fragment.name, edition)
.map(|kind| (semi_token.span, span, kind))
} else {
None
}
};
if let Some((semi_span, span, kind)) = typo_recovery {
sess.dcx().emit_err(errors::MissingFragmentSpecifier {
span: start_sp,
add_span: None,
valid: VALID_FRAGMENT_NAMES_MSG,
semi_span: Some(semi_span),
});
result.push(TokenTree::MetaVarDecl { span, name: ident, kind });
} else {
missing_fragment_specifier(start_sp);
}
}
}
result
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/macros/macro-semicolon-for-colon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Ensure that using `;` instead of `:` in a macro fragment specifier
// produces a helpful suggestion.

macro_rules! m {
($x;ty) => {}; //~ ERROR missing fragment specifier
}

fn main() {}
16 changes: 16 additions & 0 deletions tests/ui/macros/macro-semicolon-for-colon.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: missing fragment specifier
--> $DIR/macro-semicolon-for-colon.rs:5:6
|
LL | ($x;ty) => {};
| ^^
|
= note: fragment specifiers must be provided
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Give that we already check that a valid fragment specifier is provided, do you think it's better to use a separate independent diagnostics than MissingFragmentSpecifier?
We probably only need the typo help here.

= help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
help: use `:` instead of `;`
|
LL - ($x;ty) => {};
LL + ($x:ty) => {};
|

error: aborting due to 1 previous error

Loading