Skip to content

Commit 2831701

Browse files
committed
Auto merge of #114292 - estebank:issue-71039, r=b-naber
More detail when expecting expression but encountering bad macro argument On nested macro invocations where the same macro fragment changes fragment type from one to the next, point at the chain of invocations and at the macro fragment definition place, explaining that the change has occurred. Fix #71039. ``` error: expected expression, found pattern `1 + 1` --> $DIR/trace_faulty_macros.rs:49:37 | LL | (let $p:pat = $e:expr) => {test!(($p,$e))}; | ------- -- this is interpreted as expression, but it is expected to be pattern | | | this macro fragment matcher is expression ... LL | (($p:pat, $e:pat)) => {let $p = $e;}; | ------ ^^ expected expression | | | this macro fragment matcher is pattern ... LL | test!(let x = 1+1); | ------------------ | | | | | this is expected to be expression | in this macro invocation | = note: when forwarding a matched fragment to another macro-by-example, matchers in the second macro will see an opaque AST of the fragment type, not the underlying tokens = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) ```
2 parents f6dcaee + 4e41880 commit 2831701

27 files changed

+200
-67
lines changed

compiler/rustc_ast/src/attr/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ impl MetaItem {
342342
let span = span.with_hi(segments.last().unwrap().ident.span.hi());
343343
Path { span, segments, tokens: None }
344344
}
345-
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt {
345+
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &nt.0 {
346346
token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
347347
token::Nonterminal::NtPath(path) => (**path).clone(),
348348
_ => return None,

compiler/rustc_ast/src/mut_visit.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,10 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
764764
return; // Avoid visiting the span for the second time.
765765
}
766766
token::Interpolated(nt) => {
767-
visit_nonterminal(Lrc::make_mut(nt), vis);
767+
let nt = Lrc::make_mut(nt);
768+
let (nt, sp) = (&mut nt.0, &mut nt.1);
769+
vis.visit_span(sp);
770+
visit_nonterminal(nt, vis);
768771
}
769772
_ => {}
770773
}

compiler/rustc_ast/src/token.rs

+35-18
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl Lit {
110110
Ident(name, false) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
111111
Literal(token_lit) => Some(token_lit),
112112
Interpolated(ref nt)
113-
if let NtExpr(expr) | NtLiteral(expr) = &**nt
113+
if let NtExpr(expr) | NtLiteral(expr) = &nt.0
114114
&& let ast::ExprKind::Lit(token_lit) = expr.kind =>
115115
{
116116
Some(token_lit)
@@ -314,7 +314,7 @@ pub enum TokenKind {
314314
/// - It prevents `Token` from implementing `Copy`.
315315
/// It adds complexity and likely slows things down. Please don't add new
316316
/// occurrences of this token kind!
317-
Interpolated(Lrc<Nonterminal>),
317+
Interpolated(Lrc<(Nonterminal, Span)>),
318318

319319
/// A doc comment token.
320320
/// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
@@ -422,7 +422,7 @@ impl Token {
422422
/// if they keep spans or perform edition checks.
423423
pub fn uninterpolated_span(&self) -> Span {
424424
match &self.kind {
425-
Interpolated(nt) => nt.span(),
425+
Interpolated(nt) => nt.0.use_span(),
426426
_ => self.span,
427427
}
428428
}
@@ -465,7 +465,7 @@ impl Token {
465465
ModSep | // global path
466466
Lifetime(..) | // labeled loop
467467
Pound => true, // expression attributes
468-
Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
468+
Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) |
469469
NtExpr(..) |
470470
NtBlock(..) |
471471
NtPath(..)),
@@ -489,7 +489,7 @@ impl Token {
489489
| DotDot | DotDotDot | DotDotEq // ranges
490490
| Lt | BinOp(Shl) // associated path
491491
| ModSep => true, // global path
492-
Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
492+
Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) |
493493
NtPat(..) |
494494
NtBlock(..) |
495495
NtPath(..)),
@@ -512,7 +512,7 @@ impl Token {
512512
Lifetime(..) | // lifetime bound in trait object
513513
Lt | BinOp(Shl) | // associated path
514514
ModSep => true, // global path
515-
Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
515+
Interpolated(ref nt) => matches!(&nt.0, NtTy(..) | NtPath(..)),
516516
// For anonymous structs or unions, which only appear in specific positions
517517
// (type of struct fields or union fields), we don't consider them as regular types
518518
_ => false,
@@ -523,7 +523,7 @@ impl Token {
523523
pub fn can_begin_const_arg(&self) -> bool {
524524
match self.kind {
525525
OpenDelim(Delimiter::Brace) => true,
526-
Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
526+
Interpolated(ref nt) => matches!(&nt.0, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
527527
_ => self.can_begin_literal_maybe_minus(),
528528
}
529529
}
@@ -577,7 +577,7 @@ impl Token {
577577
match self.uninterpolate().kind {
578578
Literal(..) | BinOp(Minus) => true,
579579
Ident(name, false) if name.is_bool_lit() => true,
580-
Interpolated(ref nt) => match &**nt {
580+
Interpolated(ref nt) => match &nt.0 {
581581
NtLiteral(_) => true,
582582
NtExpr(e) => match &e.kind {
583583
ast::ExprKind::Lit(_) => true,
@@ -598,9 +598,9 @@ impl Token {
598598
/// otherwise returns the original token.
599599
pub fn uninterpolate(&self) -> Cow<'_, Token> {
600600
match &self.kind {
601-
Interpolated(nt) => match **nt {
601+
Interpolated(nt) => match &nt.0 {
602602
NtIdent(ident, is_raw) => {
603-
Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span))
603+
Cow::Owned(Token::new(Ident(ident.name, *is_raw), ident.span))
604604
}
605605
NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)),
606606
_ => Cow::Borrowed(self),
@@ -615,8 +615,8 @@ impl Token {
615615
// We avoid using `Token::uninterpolate` here because it's slow.
616616
match &self.kind {
617617
&Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
618-
Interpolated(nt) => match **nt {
619-
NtIdent(ident, is_raw) => Some((ident, is_raw)),
618+
Interpolated(nt) => match &nt.0 {
619+
NtIdent(ident, is_raw) => Some((*ident, *is_raw)),
620620
_ => None,
621621
},
622622
_ => None,
@@ -629,8 +629,8 @@ impl Token {
629629
// We avoid using `Token::uninterpolate` here because it's slow.
630630
match &self.kind {
631631
&Lifetime(name) => Some(Ident::new(name, self.span)),
632-
Interpolated(nt) => match **nt {
633-
NtLifetime(ident) => Some(ident),
632+
Interpolated(nt) => match &nt.0 {
633+
NtLifetime(ident) => Some(*ident),
634634
_ => None,
635635
},
636636
_ => None,
@@ -656,7 +656,7 @@ impl Token {
656656
/// Returns `true` if the token is an interpolated path.
657657
fn is_path(&self) -> bool {
658658
if let Interpolated(nt) = &self.kind
659-
&& let NtPath(..) = **nt
659+
&& let NtPath(..) = &nt.0
660660
{
661661
return true;
662662
}
@@ -669,7 +669,7 @@ impl Token {
669669
/// (which happens while parsing the result of macro expansion)?
670670
pub fn is_whole_expr(&self) -> bool {
671671
if let Interpolated(nt) = &self.kind
672-
&& let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt
672+
&& let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = &nt.0
673673
{
674674
return true;
675675
}
@@ -680,7 +680,7 @@ impl Token {
680680
/// Is the token an interpolated block (`$b:block`)?
681681
pub fn is_whole_block(&self) -> bool {
682682
if let Interpolated(nt) = &self.kind
683-
&& let NtBlock(..) = **nt
683+
&& let NtBlock(..) = &nt.0
684684
{
685685
return true;
686686
}
@@ -928,7 +928,7 @@ impl fmt::Display for NonterminalKind {
928928
}
929929

930930
impl Nonterminal {
931-
pub fn span(&self) -> Span {
931+
pub fn use_span(&self) -> Span {
932932
match self {
933933
NtItem(item) => item.span,
934934
NtBlock(block) => block.span,
@@ -942,6 +942,23 @@ impl Nonterminal {
942942
NtVis(vis) => vis.span,
943943
}
944944
}
945+
946+
pub fn descr(&self) -> &'static str {
947+
match self {
948+
NtItem(..) => "item",
949+
NtBlock(..) => "block",
950+
NtStmt(..) => "statement",
951+
NtPat(..) => "pattern",
952+
NtExpr(..) => "expression",
953+
NtLiteral(..) => "literal",
954+
NtTy(..) => "type",
955+
NtIdent(..) => "identifier",
956+
NtLifetime(..) => "lifetime",
957+
NtMeta(..) => "attribute",
958+
NtPath(..) => "path",
959+
NtVis(..) => "visibility",
960+
}
961+
}
945962
}
946963

947964
impl PartialEq for Nonterminal {

compiler/rustc_ast/src/tokenstream.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -477,13 +477,13 @@ impl TokenStream {
477477

478478
fn flatten_token(token: &Token, spacing: Spacing) -> TokenTree {
479479
match &token.kind {
480-
token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = **nt => {
480+
token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = nt.0 => {
481481
TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
482482
}
483483
token::Interpolated(nt) => TokenTree::Delimited(
484484
DelimSpan::from_single(token.span),
485485
Delimiter::Invisible,
486-
TokenStream::from_nonterminal_ast(nt).flattened(),
486+
TokenStream::from_nonterminal_ast(&nt.0).flattened(),
487487
),
488488
_ => TokenTree::Token(token.clone(), spacing),
489489
}

compiler/rustc_ast_pretty/src/pprust/state.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
825825
}
826826
token::Eof => "<eof>".into(),
827827

828-
token::Interpolated(ref nt) => self.nonterminal_to_string(nt).into(),
828+
token::Interpolated(ref nt) => self.nonterminal_to_string(&nt.0).into(),
829829
}
830830
}
831831

compiler/rustc_expand/src/mbe/diagnostics.rs

+6
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ pub(super) fn failed_to_match_macro<'cx>(
6767
&& (matches!(expected_token.kind, TokenKind::Interpolated(_))
6868
|| matches!(token.kind, TokenKind::Interpolated(_)))
6969
{
70+
if let TokenKind::Interpolated(node) = &expected_token.kind {
71+
err.span_label(node.1, "");
72+
}
73+
if let TokenKind::Interpolated(node) = &token.kind {
74+
err.span_label(node.1, "");
75+
}
7076
err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens");
7177
err.note("see <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> for more information");
7278

compiler/rustc_expand/src/mbe/macro_parser.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ pub(crate) enum NamedMatch {
397397
MatchedTokenTree(rustc_ast::tokenstream::TokenTree),
398398

399399
// A metavar match of any type other than `tt`.
400-
MatchedNonterminal(Lrc<Nonterminal>),
400+
MatchedNonterminal(Lrc<(Nonterminal, rustc_span::Span)>),
401401
}
402402

403403
/// Performs a token equality check, ignoring syntax context (that is, an unhygienic comparison)
@@ -692,7 +692,7 @@ impl TtParser {
692692
Ok(nt) => nt,
693693
};
694694
let m = match nt {
695-
ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new(nt)),
695+
ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new((nt, span))),
696696
ParseNtResult::Tt(tt) => MatchedTokenTree(tt),
697697
};
698698
mp.push_match(next_metavar, seq_depth, m);

compiler/rustc_expand/src/proc_macro.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl MultiItemModifier for DeriveProcMacro {
126126
Annotatable::Stmt(stmt) => token::NtStmt(stmt),
127127
_ => unreachable!(),
128128
};
129-
TokenStream::token_alone(token::Interpolated(Lrc::new(nt)), DUMMY_SP)
129+
TokenStream::token_alone(token::Interpolated(Lrc::new((nt, span))), DUMMY_SP)
130130
} else {
131131
item.to_tokens()
132132
};

compiler/rustc_expand/src/proc_macro_server.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -226,18 +226,23 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
226226
}));
227227
}
228228

229-
Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => trees
230-
.push(TokenTree::Ident(Ident { sym: ident.name, is_raw, span: ident.span })),
229+
Interpolated(ref nt) if let NtIdent(ident, is_raw) = &nt.0 => {
230+
trees.push(TokenTree::Ident(Ident {
231+
sym: ident.name,
232+
is_raw: *is_raw,
233+
span: ident.span,
234+
}))
235+
}
231236

232237
Interpolated(nt) => {
233-
let stream = TokenStream::from_nonterminal_ast(&nt);
238+
let stream = TokenStream::from_nonterminal_ast(&nt.0);
234239
// A hack used to pass AST fragments to attribute and derive
235240
// macros as a single nonterminal token instead of a token
236241
// stream. Such token needs to be "unwrapped" and not
237242
// represented as a delimited group.
238243
// FIXME: It needs to be removed, but there are some
239244
// compatibility issues (see #73345).
240-
if crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.sess()) {
245+
if crate::base::nt_pretty_printing_compatibility_hack(&nt.0, rustc.sess()) {
241246
trees.extend(Self::from_internal((stream, rustc)));
242247
} else {
243248
trees.push(TokenTree::Group(Group {

compiler/rustc_parse/src/parser/attr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ impl<'a> Parser<'a> {
249249
/// The delimiters or `=` are still put into the resulting token stream.
250250
pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> {
251251
let item = match &self.token.kind {
252-
token::Interpolated(nt) => match &**nt {
252+
token::Interpolated(nt) => match &nt.0 {
253253
Nonterminal::NtMeta(item) => Some(item.clone().into_inner()),
254254
_ => None,
255255
},
@@ -369,7 +369,7 @@ impl<'a> Parser<'a> {
369369
/// ```
370370
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
371371
let nt_meta = match &self.token.kind {
372-
token::Interpolated(nt) => match &**nt {
372+
token::Interpolated(nt) => match &nt.0 {
373373
token::NtMeta(e) => Some(e.clone()),
374374
_ => None,
375375
},

compiler/rustc_parse/src/parser/diagnostics.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ use crate::parser;
2424
use rustc_ast as ast;
2525
use rustc_ast::ptr::P;
2626
use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
27+
use rustc_ast::tokenstream::AttrTokenTree;
2728
use rustc_ast::util::parser::AssocOp;
2829
use rustc_ast::{
2930
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block,
30-
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
31-
Path, PathSegment, QSelf, Ty, TyKind,
31+
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat,
32+
PatKind, Path, PathSegment, QSelf, Ty, TyKind,
3233
};
3334
use rustc_ast_pretty::pprust;
3435
use rustc_data_structures::fx::FxHashSet;
@@ -2252,6 +2253,59 @@ impl<'a> Parser<'a> {
22522253
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
22532254
}
22542255
err.span_label(span, "expected expression");
2256+
2257+
// Walk the chain of macro expansions for the current token to point at how the original
2258+
// code was interpreted. This helps the user realize when a macro argument of one type is
2259+
// later reinterpreted as a different type, like `$x:expr` being reinterpreted as `$x:pat`
2260+
// in a subsequent macro invocation (#71039).
2261+
let mut tok = self.token.clone();
2262+
let mut labels = vec![];
2263+
while let TokenKind::Interpolated(node) = &tok.kind {
2264+
let tokens = node.0.tokens();
2265+
labels.push(node.clone());
2266+
if let Some(tokens) = tokens
2267+
&& let tokens = tokens.to_attr_token_stream()
2268+
&& let tokens = tokens.0.deref()
2269+
&& let [AttrTokenTree::Token(token, _)] = &tokens[..]
2270+
{
2271+
tok = token.clone();
2272+
} else {
2273+
break;
2274+
}
2275+
}
2276+
let mut iter = labels.into_iter().peekable();
2277+
let mut show_link = false;
2278+
while let Some(node) = iter.next() {
2279+
let descr = node.0.descr();
2280+
if let Some(next) = iter.peek() {
2281+
let next_descr = next.0.descr();
2282+
if next_descr != descr {
2283+
err.span_label(next.1, format!("this macro fragment matcher is {next_descr}"));
2284+
err.span_label(node.1, format!("this macro fragment matcher is {descr}"));
2285+
err.span_label(
2286+
next.0.use_span(),
2287+
format!("this is expected to be {next_descr}"),
2288+
);
2289+
err.span_label(
2290+
node.0.use_span(),
2291+
format!(
2292+
"this is interpreted as {}, but it is expected to be {}",
2293+
next_descr, descr,
2294+
),
2295+
);
2296+
show_link = true;
2297+
} else {
2298+
err.span_label(node.1, "");
2299+
}
2300+
}
2301+
}
2302+
if show_link {
2303+
err.note(
2304+
"when forwarding a matched fragment to another macro-by-example, matchers in the \
2305+
second macro will see an opaque AST of the fragment type, not the underlying \
2306+
tokens",
2307+
);
2308+
}
22552309
err
22562310
}
22572311

compiler/rustc_parse/src/parser/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use thin_vec::{thin_vec, ThinVec};
4646
macro_rules! maybe_whole_expr {
4747
($p:expr) => {
4848
if let token::Interpolated(nt) = &$p.token.kind {
49-
match &**nt {
49+
match &nt.0 {
5050
token::NtExpr(e) | token::NtLiteral(e) => {
5151
let e = e.clone();
5252
$p.bump();
@@ -1952,7 +1952,7 @@ impl<'a> Parser<'a> {
19521952
mk_lit_char: impl FnOnce(Symbol, Span) -> L,
19531953
) -> PResult<'a, L> {
19541954
if let token::Interpolated(nt) = &self.token.kind
1955-
&& let token::NtExpr(e) | token::NtLiteral(e) = &**nt
1955+
&& let token::NtExpr(e) | token::NtLiteral(e) = &nt.0
19561956
&& matches!(e.kind, ExprKind::Err)
19571957
{
19581958
let mut err = errors::InvalidInterpolatedExpression { span: self.token.span }

0 commit comments

Comments
 (0)