Skip to content

Commit c9266ef

Browse files
committed
More detail when expecting expression but encountering bad macro argument
Partially address #71039.
1 parent b321edd commit c9266ef

File tree

4 files changed

+101
-3
lines changed

4 files changed

+101
-3
lines changed

compiler/rustc_ast/src/token.rs

+17
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,23 @@ impl Nonterminal {
911911
NtVis(vis) => vis.span,
912912
}
913913
}
914+
915+
pub fn descr(&self) -> &'static str {
916+
match self {
917+
NtItem(_) => "item",
918+
NtBlock(_) => "block",
919+
NtStmt(_) => "statement",
920+
NtPat(_) => "pattern",
921+
NtExpr(_) => "expression",
922+
NtLiteral(_) => "literal",
923+
NtTy(_) => "type",
924+
NtIdent(..) => "identifier",
925+
NtLifetime(_) => "lifetime",
926+
NtMeta(_) => "attribute",
927+
NtPath(_) => "path",
928+
NtVis(_) => "visibility",
929+
}
930+
}
914931
}
915932

916933
impl PartialEq for Nonterminal {

compiler/rustc_parse/src/parser/diagnostics.rs

+41-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ use crate::parser;
2323
use rustc_ast as ast;
2424
use rustc_ast::ptr::P;
2525
use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
26+
use rustc_ast::tokenstream::AttrTokenTree;
2627
use rustc_ast::util::parser::AssocOp;
2728
use rustc_ast::{
2829
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block,
29-
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
30-
Path, PathSegment, QSelf, Ty, TyKind,
30+
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat,
31+
PatKind, Path, PathSegment, QSelf, Ty, TyKind,
3132
};
3233
use rustc_ast_pretty::pprust;
3334
use rustc_data_structures::fx::FxHashSet;
@@ -2163,6 +2164,44 @@ impl<'a> Parser<'a> {
21632164
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
21642165
}
21652166
err.span_label(span, "expected expression");
2167+
2168+
// Walk the chain of macro expansions for the current token to point at how the original
2169+
// code was interpreted. This helps the user realize when a macro argument of one type is
2170+
// later reinterpreted as a different type, like `$x:expr` being reinterpreted as `$x:pat`
2171+
// in a subsequent macro invokation (#71039).
2172+
let mut tok = self.token.clone();
2173+
let mut labels = vec![];
2174+
let mut pos = 0;
2175+
while let TokenKind::Interpolated(node) = &tok.kind {
2176+
// FIXME: figure out how to point at `$x:pat` when `tok`
2177+
// is a `NtPat` corresponding to `$x`.
2178+
let tokens = node.tokens();
2179+
labels.push((
2180+
pos,
2181+
node.span(),
2182+
format!("this is interpreted as {} {}", node.descr(), super::token_descr(&tok)),
2183+
));
2184+
if let Some(tokens) = tokens
2185+
&& let tokens = tokens.to_attr_token_stream()
2186+
&& let tokens = tokens.0.deref()
2187+
&& let [AttrTokenTree::Token(token, _)] = &tokens[..]
2188+
{
2189+
info!(?token);
2190+
tok = token.clone();
2191+
pos += 1;
2192+
} else {
2193+
break;
2194+
}
2195+
}
2196+
for (i, span, label) in labels {
2197+
let post = if pos > 0 {
2198+
// When there are more than one macro expansions at play, clarify the order.
2199+
format!(" (in expansion #{})", pos + 1 - i)
2200+
} else {
2201+
String::new()
2202+
};
2203+
err.span_label(span, format!("{label}{post}"));
2204+
}
21662205
err
21672206
}
21682207

tests/ui/macros/trace_faulty_macros.rs

+11
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,14 @@ fn use_bang_macro_as_attr() {}
4141

4242
#[derive(Debug)] //~ ERROR `derive` may only be applied to `struct`s
4343
fn use_derive_macro_as_attr() {}
44+
45+
macro_rules! test {
46+
(let $p:pat = $e:expr) => {test!(($p,$e))};
47+
// this should be expr
48+
// vvv
49+
(($p:pat, $e:pat)) => {let $p = $e;}; //~ ERROR expected expression, found `1 + 1`
50+
}
51+
52+
fn foo() {
53+
test!(let x = 1+1);
54+
}

tests/ui/macros/trace_faulty_macros.stderr

+32-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ LL | my_recursive_macro!();
5353
error: expected expression, found `A { a: a, b: 0, c: _, .. }`
5454
--> $DIR/trace_faulty_macros.rs:16:9
5555
|
56+
LL | pat_macro!(A{a:a, b:0, c:_, ..});
57+
| -------------------- this is interpreted as pattern `A { a: a, b: 0, c: _, .. }`
58+
...
5659
LL | $a
5760
| ^^ expected expression
5861
...
@@ -69,6 +72,23 @@ LL | #[derive(Debug)]
6972
LL | fn use_derive_macro_as_attr() {}
7073
| -------------------------------- not a `struct`, `enum` or `union`
7174

75+
error: expected expression, found `1 + 1`
76+
--> $DIR/trace_faulty_macros.rs:49:37
77+
|
78+
LL | (let $p:pat = $e:expr) => {test!(($p,$e))};
79+
| -- this is interpreted as pattern `1 + 1` (in expansion #2)
80+
...
81+
LL | (($p:pat, $e:pat)) => {let $p = $e;};
82+
| ^^ expected expression
83+
...
84+
LL | test!(let x = 1+1);
85+
| ------------------
86+
| | |
87+
| | this is interpreted as expression `1 + 1` (in expansion #1)
88+
| in this macro invocation
89+
|
90+
= note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
91+
7292
note: trace_macro
7393
--> $DIR/trace_faulty_macros.rs:36:13
7494
|
@@ -80,6 +100,17 @@ LL | let a = pat_macro!();
80100
= note: expanding `pat_macro! { A { a : a, b : 0, c : _, .. } }`
81101
= note: to `A { a: a, b: 0, c: _, .. }`
82102

83-
error: aborting due to 4 previous errors
103+
note: trace_macro
104+
--> $DIR/trace_faulty_macros.rs:53:5
105+
|
106+
LL | test!(let x = 1+1);
107+
| ^^^^^^^^^^^^^^^^^^
108+
|
109+
= note: expanding `test! { let x = 1 + 1 }`
110+
= note: to `test! ((x, 1 + 1))`
111+
= note: expanding `test! { (x, 1 + 1) }`
112+
= note: to `let x = 1 + 1 ;`
113+
114+
error: aborting due to 5 previous errors
84115

85116
For more information about this error, try `rustc --explain E0774`.

0 commit comments

Comments
 (0)