Skip to content

Commit

Permalink
More detail when expecting expression but encountering bad macro argu…
Browse files Browse the repository at this point in the history
…ment

Partially address rust-lang#71039.
  • Loading branch information
estebank committed Jul 31, 2023
1 parent b321edd commit c9266ef
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 3 deletions.
17 changes: 17 additions & 0 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,23 @@ impl Nonterminal {
NtVis(vis) => vis.span,
}
}

pub fn descr(&self) -> &'static str {
match self {
NtItem(_) => "item",
NtBlock(_) => "block",
NtStmt(_) => "statement",
NtPat(_) => "pattern",
NtExpr(_) => "expression",
NtLiteral(_) => "literal",
NtTy(_) => "type",
NtIdent(..) => "identifier",
NtLifetime(_) => "lifetime",
NtMeta(_) => "attribute",
NtPath(_) => "path",
NtVis(_) => "visibility",
}
}
}

impl PartialEq for Nonterminal {
Expand Down
43 changes: 41 additions & 2 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ use crate::parser;
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
use rustc_ast::tokenstream::AttrTokenTree;
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block,
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
Path, PathSegment, QSelf, Ty, TyKind,
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat,
PatKind, Path, PathSegment, QSelf, Ty, TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
Expand Down Expand Up @@ -2163,6 +2164,44 @@ impl<'a> Parser<'a> {
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
err.span_label(span, "expected expression");

// Walk the chain of macro expansions for the current token to point at how the original
// code was interpreted. This helps the user realize when a macro argument of one type is
// later reinterpreted as a different type, like `$x:expr` being reinterpreted as `$x:pat`
// in a subsequent macro invokation (#71039).
let mut tok = self.token.clone();
let mut labels = vec![];
let mut pos = 0;
while let TokenKind::Interpolated(node) = &tok.kind {
// FIXME: figure out how to point at `$x:pat` when `tok`
// is a `NtPat` corresponding to `$x`.
let tokens = node.tokens();
labels.push((
pos,
node.span(),
format!("this is interpreted as {} {}", node.descr(), super::token_descr(&tok)),
));
if let Some(tokens) = tokens
&& let tokens = tokens.to_attr_token_stream()
&& let tokens = tokens.0.deref()
&& let [AttrTokenTree::Token(token, _)] = &tokens[..]
{
info!(?token);
tok = token.clone();
pos += 1;
} else {
break;
}
}
for (i, span, label) in labels {
let post = if pos > 0 {
// When there are more than one macro expansions at play, clarify the order.
format!(" (in expansion #{})", pos + 1 - i)
} else {
String::new()
};
err.span_label(span, format!("{label}{post}"));
}
err
}

Expand Down
11 changes: 11 additions & 0 deletions tests/ui/macros/trace_faulty_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,14 @@ fn use_bang_macro_as_attr() {}

#[derive(Debug)] //~ ERROR `derive` may only be applied to `struct`s
fn use_derive_macro_as_attr() {}

macro_rules! test {
(let $p:pat = $e:expr) => {test!(($p,$e))};
// this should be expr
// vvv
(($p:pat, $e:pat)) => {let $p = $e;}; //~ ERROR expected expression, found `1 + 1`
}

fn foo() {
test!(let x = 1+1);
}
33 changes: 32 additions & 1 deletion tests/ui/macros/trace_faulty_macros.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ LL | my_recursive_macro!();
error: expected expression, found `A { a: a, b: 0, c: _, .. }`
--> $DIR/trace_faulty_macros.rs:16:9
|
LL | pat_macro!(A{a:a, b:0, c:_, ..});
| -------------------- this is interpreted as pattern `A { a: a, b: 0, c: _, .. }`
...
LL | $a
| ^^ expected expression
...
Expand All @@ -69,6 +72,23 @@ LL | #[derive(Debug)]
LL | fn use_derive_macro_as_attr() {}
| -------------------------------- not a `struct`, `enum` or `union`

error: expected expression, found `1 + 1`
--> $DIR/trace_faulty_macros.rs:49:37
|
LL | (let $p:pat = $e:expr) => {test!(($p,$e))};
| -- this is interpreted as pattern `1 + 1` (in expansion #2)
...
LL | (($p:pat, $e:pat)) => {let $p = $e;};
| ^^ expected expression
...
LL | test!(let x = 1+1);
| ------------------
| | |
| | this is interpreted as expression `1 + 1` (in expansion #1)
| in this macro invocation
|
= note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)

note: trace_macro
--> $DIR/trace_faulty_macros.rs:36:13
|
Expand All @@ -80,6 +100,17 @@ LL | let a = pat_macro!();
= note: expanding `pat_macro! { A { a : a, b : 0, c : _, .. } }`
= note: to `A { a: a, b: 0, c: _, .. }`

error: aborting due to 4 previous errors
note: trace_macro
--> $DIR/trace_faulty_macros.rs:53:5
|
LL | test!(let x = 1+1);
| ^^^^^^^^^^^^^^^^^^
|
= note: expanding `test! { let x = 1 + 1 }`
= note: to `test! ((x, 1 + 1))`
= note: expanding `test! { (x, 1 + 1) }`
= note: to `let x = 1 + 1 ;`

error: aborting due to 5 previous errors

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

0 comments on commit c9266ef

Please sign in to comment.