More detail when expecting expression but encountering bad macro argument

Partially address #71039.
This commit is contained in:
Esteban Küber 2023-07-31 14:55:47 +00:00
parent 1be1e84872
commit 4e418805da
27 changed files with 200 additions and 67 deletions

View file

@ -342,7 +342,7 @@ fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
let span = span.with_hi(segments.last().unwrap().ident.span.hi());
Path { span, segments, tokens: None }
}
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt {
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &nt.0 {
token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
token::Nonterminal::NtPath(path) => (**path).clone(),
_ => return None,

View file

@ -764,7 +764,10 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
return; // Avoid visiting the span for the second time.
}
token::Interpolated(nt) => {
visit_nonterminal(Lrc::make_mut(nt), vis);
let nt = Lrc::make_mut(nt);
let (nt, sp) = (&mut nt.0, &mut nt.1);
vis.visit_span(sp);
visit_nonterminal(nt, vis);
}
_ => {}
}

View file

@ -110,7 +110,7 @@ pub fn from_token(token: &Token) -> Option<Lit> {
Ident(name, false) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
Literal(token_lit) => Some(token_lit),
Interpolated(ref nt)
if let NtExpr(expr) | NtLiteral(expr) = &**nt
if let NtExpr(expr) | NtLiteral(expr) = &nt.0
&& let ast::ExprKind::Lit(token_lit) = expr.kind =>
{
Some(token_lit)
@ -314,7 +314,7 @@ pub enum TokenKind {
/// - It prevents `Token` from implementing `Copy`.
/// It adds complexity and likely slows things down. Please don't add new
/// occurrences of this token kind!
Interpolated(Lrc<Nonterminal>),
Interpolated(Lrc<(Nonterminal, Span)>),
/// A doc comment token.
/// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
@ -421,7 +421,7 @@ pub fn from_ast_ident(ident: Ident) -> Self {
/// if they keep spans or perform edition checks.
pub fn uninterpolated_span(&self) -> Span {
match &self.kind {
Interpolated(nt) => nt.span(),
Interpolated(nt) => nt.0.use_span(),
_ => self.span,
}
}
@ -464,7 +464,7 @@ pub fn can_begin_expr(&self) -> bool {
ModSep | // global path
Lifetime(..) | // labeled loop
Pound => true, // expression attributes
Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) |
NtExpr(..) |
NtBlock(..) |
NtPath(..)),
@ -488,7 +488,7 @@ pub fn can_begin_pattern(&self) -> bool {
| DotDot | DotDotDot | DotDotEq // ranges
| Lt | BinOp(Shl) // associated path
| ModSep => true, // global path
Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) |
NtPat(..) |
NtBlock(..) |
NtPath(..)),
@ -511,7 +511,7 @@ pub fn can_begin_type(&self) -> bool {
Lifetime(..) | // lifetime bound in trait object
Lt | BinOp(Shl) | // associated path
ModSep => true, // global path
Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
Interpolated(ref nt) => matches!(&nt.0, NtTy(..) | NtPath(..)),
// For anonymous structs or unions, which only appear in specific positions
// (type of struct fields or union fields), we don't consider them as regular types
_ => false,
@ -522,7 +522,7 @@ pub fn can_begin_type(&self) -> bool {
pub fn can_begin_const_arg(&self) -> bool {
match self.kind {
OpenDelim(Delimiter::Brace) => true,
Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
Interpolated(ref nt) => matches!(&nt.0, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
_ => self.can_begin_literal_maybe_minus(),
}
}
@ -576,7 +576,7 @@ pub fn can_begin_literal_maybe_minus(&self) -> bool {
match self.uninterpolate().kind {
Literal(..) | BinOp(Minus) => true,
Ident(name, false) if name.is_bool_lit() => true,
Interpolated(ref nt) => match &**nt {
Interpolated(ref nt) => match &nt.0 {
NtLiteral(_) => true,
NtExpr(e) => match &e.kind {
ast::ExprKind::Lit(_) => true,
@ -597,9 +597,9 @@ pub fn can_begin_literal_maybe_minus(&self) -> bool {
/// otherwise returns the original token.
pub fn uninterpolate(&self) -> Cow<'_, Token> {
match &self.kind {
Interpolated(nt) => match **nt {
Interpolated(nt) => match &nt.0 {
NtIdent(ident, is_raw) => {
Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span))
Cow::Owned(Token::new(Ident(ident.name, *is_raw), ident.span))
}
NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)),
_ => Cow::Borrowed(self),
@ -614,8 +614,8 @@ pub fn ident(&self) -> Option<(Ident, /* is_raw */ bool)> {
// We avoid using `Token::uninterpolate` here because it's slow.
match &self.kind {
&Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
Interpolated(nt) => match **nt {
NtIdent(ident, is_raw) => Some((ident, is_raw)),
Interpolated(nt) => match &nt.0 {
NtIdent(ident, is_raw) => Some((*ident, *is_raw)),
_ => None,
},
_ => None,
@ -628,8 +628,8 @@ pub fn lifetime(&self) -> Option<Ident> {
// We avoid using `Token::uninterpolate` here because it's slow.
match &self.kind {
&Lifetime(name) => Some(Ident::new(name, self.span)),
Interpolated(nt) => match **nt {
NtLifetime(ident) => Some(ident),
Interpolated(nt) => match &nt.0 {
NtLifetime(ident) => Some(*ident),
_ => None,
},
_ => None,
@ -655,7 +655,7 @@ pub fn is_ident_named(&self, name: Symbol) -> bool {
/// Returns `true` if the token is an interpolated path.
fn is_path(&self) -> bool {
if let Interpolated(nt) = &self.kind
&& let NtPath(..) = **nt
&& let NtPath(..) = &nt.0
{
return true;
}
@ -668,7 +668,7 @@ fn is_path(&self) -> bool {
/// (which happens while parsing the result of macro expansion)?
pub fn is_whole_expr(&self) -> bool {
if let Interpolated(nt) = &self.kind
&& let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt
&& let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = &nt.0
{
return true;
}
@ -679,7 +679,7 @@ pub fn is_whole_expr(&self) -> bool {
/// Is the token an interpolated block (`$b:block`)?
pub fn is_whole_block(&self) -> bool {
if let Interpolated(nt) = &self.kind
&& let NtBlock(..) = **nt
&& let NtBlock(..) = &nt.0
{
return true;
}
@ -927,7 +927,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
impl Nonterminal {
pub fn span(&self) -> Span {
pub fn use_span(&self) -> Span {
match self {
NtItem(item) => item.span,
NtBlock(block) => block.span,
@ -941,6 +941,23 @@ pub fn span(&self) -> Span {
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 {

View file

@ -477,13 +477,13 @@ pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream {
fn flatten_token(token: &Token, spacing: Spacing) -> TokenTree {
match &token.kind {
token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = **nt => {
token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = nt.0 => {
TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
}
token::Interpolated(nt) => TokenTree::Delimited(
DelimSpan::from_single(token.span),
Delimiter::Invisible,
TokenStream::from_nonterminal_ast(nt).flattened(),
TokenStream::from_nonterminal_ast(&nt.0).flattened(),
),
_ => TokenTree::Token(token.clone(), spacing),
}

View file

@ -825,7 +825,7 @@ fn token_kind_to_string_ext(
}
token::Eof => "<eof>".into(),
token::Interpolated(ref nt) => self.nonterminal_to_string(nt).into(),
token::Interpolated(ref nt) => self.nonterminal_to_string(&nt.0).into(),
}
}

View file

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

View file

@ -397,7 +397,7 @@ pub(crate) enum NamedMatch {
MatchedTokenTree(rustc_ast::tokenstream::TokenTree),
// A metavar match of any type other than `tt`.
MatchedNonterminal(Lrc<Nonterminal>),
MatchedNonterminal(Lrc<(Nonterminal, rustc_span::Span)>),
}
/// Performs a token equality check, ignoring syntax context (that is, an unhygienic comparison)
@ -692,7 +692,7 @@ pub(super) fn parse_tt<'matcher, T: Tracker<'matcher>>(
Ok(nt) => nt,
};
let m = match nt {
ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new(nt)),
ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new((nt, span))),
ParseNtResult::Tt(tt) => MatchedTokenTree(tt),
};
mp.push_match(next_metavar, seq_depth, m);

View file

@ -126,7 +126,7 @@ fn expand(
Annotatable::Stmt(stmt) => token::NtStmt(stmt),
_ => unreachable!(),
};
TokenStream::token_alone(token::Interpolated(Lrc::new(nt)), DUMMY_SP)
TokenStream::token_alone(token::Interpolated(Lrc::new((nt, span))), DUMMY_SP)
} else {
item.to_tokens()
};

View file

@ -226,18 +226,23 @@ fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self {
}));
}
Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => trees
.push(TokenTree::Ident(Ident { sym: ident.name, is_raw, span: ident.span })),
Interpolated(ref nt) if let NtIdent(ident, is_raw) = &nt.0 => {
trees.push(TokenTree::Ident(Ident {
sym: ident.name,
is_raw: *is_raw,
span: ident.span,
}))
}
Interpolated(nt) => {
let stream = TokenStream::from_nonterminal_ast(&nt);
let stream = TokenStream::from_nonterminal_ast(&nt.0);
// A hack used to pass AST fragments to attribute and derive
// macros as a single nonterminal token instead of a token
// stream. Such token needs to be "unwrapped" and not
// represented as a delimited group.
// FIXME: It needs to be removed, but there are some
// compatibility issues (see #73345).
if crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.sess()) {
if crate::base::nt_pretty_printing_compatibility_hack(&nt.0, rustc.sess()) {
trees.extend(Self::from_internal((stream, rustc)));
} else {
trees.push(TokenTree::Group(Group {

View file

@ -249,7 +249,7 @@ pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerA
/// The delimiters or `=` are still put into the resulting token stream.
pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> {
let item = match &self.token.kind {
token::Interpolated(nt) => match &**nt {
token::Interpolated(nt) => match &nt.0 {
Nonterminal::NtMeta(item) => Some(item.clone().into_inner()),
_ => None,
},
@ -369,7 +369,7 @@ pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::NestedMe
/// ```
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
let nt_meta = match &self.token.kind {
token::Interpolated(nt) => match &**nt {
token::Interpolated(nt) => match &nt.0 {
token::NtMeta(e) => Some(e.clone()),
_ => None,
},

View file

@ -24,11 +24,12 @@
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;
@ -2252,6 +2253,59 @@ pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a, ErrorGua
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 invocation (#71039).
let mut tok = self.token.clone();
let mut labels = vec![];
while let TokenKind::Interpolated(node) = &tok.kind {
let tokens = node.0.tokens();
labels.push(node.clone());
if let Some(tokens) = tokens
&& let tokens = tokens.to_attr_token_stream()
&& let tokens = tokens.0.deref()
&& let [AttrTokenTree::Token(token, _)] = &tokens[..]
{
tok = token.clone();
} else {
break;
}
}
let mut iter = labels.into_iter().peekable();
let mut show_link = false;
while let Some(node) = iter.next() {
let descr = node.0.descr();
if let Some(next) = iter.peek() {
let next_descr = next.0.descr();
if next_descr != descr {
err.span_label(next.1, format!("this macro fragment matcher is {next_descr}"));
err.span_label(node.1, format!("this macro fragment matcher is {descr}"));
err.span_label(
next.0.use_span(),
format!("this is expected to be {next_descr}"),
);
err.span_label(
node.0.use_span(),
format!(
"this is interpreted as {}, but it is expected to be {}",
next_descr, descr,
),
);
show_link = true;
} else {
err.span_label(node.1, "");
}
}
}
if show_link {
err.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",
);
}
err
}

View file

@ -46,7 +46,7 @@
macro_rules! maybe_whole_expr {
($p:expr) => {
if let token::Interpolated(nt) = &$p.token.kind {
match &**nt {
match &nt.0 {
token::NtExpr(e) | token::NtLiteral(e) => {
let e = e.clone();
$p.bump();
@ -1952,7 +1952,7 @@ fn handle_missing_lit<L>(
mk_lit_char: impl FnOnce(Symbol, Span) -> L,
) -> PResult<'a, L> {
if let token::Interpolated(nt) = &self.token.kind
&& let token::NtExpr(e) | token::NtLiteral(e) = &**nt
&& let token::NtExpr(e) | token::NtLiteral(e) = &nt.0
&& matches!(e.kind, ExprKind::Err)
{
let mut err = errors::InvalidInterpolatedExpression { span: self.token.span }

View file

@ -123,7 +123,7 @@ pub(super) fn parse_item_common(
// Don't use `maybe_whole` so that we have precise control
// over when we bump the parser
if let token::Interpolated(nt) = &self.token.kind
&& let token::NtItem(item) = &**nt
&& let token::NtItem(item) = &nt.0
{
let mut item = item.clone();
self.bump();
@ -2750,7 +2750,7 @@ fn parse_self_param(&mut self) -> PResult<'a, Option<Param>> {
fn is_named_param(&self) -> bool {
let offset = match &self.token.kind {
token::Interpolated(nt) => match **nt {
token::Interpolated(nt) => match &nt.0 {
token::NtPat(..) => return self.look_ahead(1, |t| t == &token::Colon),
_ => 0,
},

View file

@ -93,7 +93,7 @@ pub enum TrailingToken {
macro_rules! maybe_whole {
($p:expr, $constructor:ident, |$x:ident| $e:expr) => {
if let token::Interpolated(nt) = &$p.token.kind {
if let token::$constructor(x) = &**nt {
if let token::$constructor(x) = &nt.0 {
let $x = x.clone();
$p.bump();
return Ok($e);
@ -110,7 +110,7 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath {
&& $self.may_recover()
&& $self.look_ahead(1, |t| t == &token::ModSep)
&& let token::Interpolated(nt) = &$self.token.kind
&& let token::NtTy(ty) = &**nt
&& let token::NtTy(ty) = &nt.0
{
let ty = ty.clone();
$self.bump();
@ -367,12 +367,14 @@ pub fn from_token(token: &Token) -> Option<Self> {
pub(super) fn token_descr(token: &Token) -> String {
let name = pprust::token_to_string(token).to_string();
let kind = TokenDescription::from_token(token).map(|kind| match kind {
TokenDescription::ReservedIdentifier => "reserved identifier",
TokenDescription::Keyword => "keyword",
TokenDescription::ReservedKeyword => "reserved keyword",
TokenDescription::DocComment => "doc comment",
});
let kind = match (TokenDescription::from_token(token), &token.kind) {
(Some(TokenDescription::ReservedIdentifier), _) => Some("reserved identifier"),
(Some(TokenDescription::Keyword), _) => Some("keyword"),
(Some(TokenDescription::ReservedKeyword), _) => Some("reserved keyword"),
(Some(TokenDescription::DocComment), _) => Some("doc comment"),
(None, TokenKind::Interpolated(node)) => Some(node.0.descr()),
(None, _) => None,
};
if let Some(kind) = kind { format!("{kind} `{name}`") } else { format!("`{name}`") }
}
@ -662,7 +664,7 @@ fn check_const_closure(&self) -> bool {
fn check_inline_const(&self, dist: usize) -> bool {
self.is_keyword_ahead(dist, &[kw::Const])
&& self.look_ahead(dist + 1, |t| match &t.kind {
token::Interpolated(nt) => matches!(**nt, token::NtBlock(..)),
token::Interpolated(nt) => matches!(&nt.0, token::NtBlock(..)),
token::OpenDelim(Delimiter::Brace) => true,
_ => false,
})

View file

@ -50,12 +50,12 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
NonterminalKind::Literal => token.can_begin_literal_maybe_minus(),
NonterminalKind::Vis => match token.kind {
// The follow-set of :vis + "priv" keyword + interpolated
token::Comma | token::Ident(..) | token::Interpolated(..) => true,
token::Comma | token::Ident(..) | token::Interpolated(_) => true,
_ => token.can_begin_type(),
},
NonterminalKind::Block => match &token.kind {
token::OpenDelim(Delimiter::Brace) => true,
token::Interpolated(nt) => match **nt {
token::Interpolated(nt) => match &nt.0 {
NtBlock(_) | NtLifetime(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true,
NtItem(_) | NtPat(_) | NtTy(_) | NtIdent(..) | NtMeta(_) | NtPath(_)
| NtVis(_) => false,
@ -64,7 +64,7 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
},
NonterminalKind::Path | NonterminalKind::Meta => match &token.kind {
token::ModSep | token::Ident(..) => true,
token::Interpolated(nt) => may_be_ident(nt),
token::Interpolated(nt) => may_be_ident(&nt.0),
_ => false,
},
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => {
@ -75,7 +75,7 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
token::BinOp(token::And) | // reference
token::BinOp(token::Minus) | // negative literal
token::AndAnd | // double reference
token::Literal(..) | // literal
token::Literal(_) | // literal
token::DotDot | // range pattern (future compat)
token::DotDotDot | // range pattern (future compat)
token::ModSep | // path
@ -83,14 +83,14 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
token::BinOp(token::Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern
token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr),
token::Interpolated(nt) => may_be_ident(nt),
token::Interpolated(nt) => may_be_ident(&nt.0),
_ => false,
}
}
NonterminalKind::Lifetime => match &token.kind {
token::Lifetime(_) => true,
token::Interpolated(nt) => {
matches!(**nt, NtLifetime(_))
matches!(&nt.0, NtLifetime(_))
}
_ => false,
},
@ -191,7 +191,7 @@ pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseN
panic!(
"Missing tokens for nt {:?} at {:?}: {:?}",
nt,
nt.span(),
nt.use_span(),
pprust::nonterminal_to_string(&nt)
);
}

View file

@ -592,7 +592,7 @@ fn parse_pat_ident_mut(&mut self, syntax_loc: Option<PatternLocation>) -> PResul
// Make sure we don't allow e.g. `let mut $p;` where `$p:pat`.
if let token::Interpolated(nt) = &self.token.kind {
if let token::NtPat(_) = **nt {
if let token::NtPat(..) = &nt.0 {
self.expected_ident_found_err().emit();
}
}

View file

@ -185,7 +185,7 @@ pub(super) fn parse_path_inner(
});
if let token::Interpolated(nt) = &self.token.kind {
if let token::NtTy(ty) = &**nt {
if let token::NtTy(ty) = &nt.0 {
if let ast::TyKind::Path(None, path) = &ty.kind {
let path = path.clone();
self.bump();

View file

@ -53,7 +53,7 @@ pub fn parse_stmt_without_recovery(
// Don't use `maybe_whole` so that we have precise control
// over when we bump the parser
if let token::Interpolated(nt) = &self.token.kind
&& let token::NtStmt(stmt) = &**nt
&& let token::NtStmt(stmt) = &nt.0
{
let mut stmt = stmt.clone();
self.bump();

View file

@ -1,6 +1,6 @@
macro_rules! get_opt {
($tgt:expr, $field:ident) => {
if $tgt.has_$field() {} //~ ERROR expected `{`, found `foo`
if $tgt.has_$field() {} //~ ERROR expected `{`, found identifier `foo`
}
}

View file

@ -1,4 +1,4 @@
error: expected `{`, found `foo`
error: expected `{`, found identifier `foo`
--> $DIR/issue-39848.rs:3:21
|
LL | if $tgt.has_$field() {}

View file

@ -1,6 +1,8 @@
error: no rules expected the token `enum E {}`
--> $DIR/nonterminal-matching.rs:19:10
|
LL | macro complex_nonterminal($nt_item: item) {
| --------------
LL | macro n(a $nt_item b) {
| --------------------- when calling this macro
...

View file

@ -9,7 +9,7 @@ pub enum TokenKind {
}
};
}
//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)`
//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found type `(String)`
//~| ERROR macro expansion ignores token `(String)` and any following
values!(STRING(1) as (String) => cfg(test),);

View file

@ -1,4 +1,4 @@
error: expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)`
error: expected one of `(`, `,`, `=`, `{`, or `}`, found type `(String)`
--> $DIR/syntax-error-recovery.rs:7:26
|
LL | $token $($inner)? = $value,

View file

@ -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 pattern `1 + 1`
}
fn foo() {
test!(let x = 1+1);
}

View file

@ -50,7 +50,7 @@ LL | my_recursive_macro!();
= note: expanding `my_recursive_macro! { }`
= note: to `my_recursive_macro! () ;`
error: expected expression, found `A { a: a, b: 0, c: _, .. }`
error: expected expression, found pattern `A { a: a, b: 0, c: _, .. }`
--> $DIR/trace_faulty_macros.rs:16:9
|
LL | $a
@ -69,6 +69,28 @@ LL | #[derive(Debug)]
LL | fn use_derive_macro_as_attr() {}
| -------------------------------- not a `struct`, `enum` or `union`
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)
note: trace_macro
--> $DIR/trace_faulty_macros.rs:36:13
|
@ -80,6 +102,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`.

View file

@ -6,9 +6,9 @@ macro_rules! generate_field_accesses {
s.$a; // OK
{ s.$b; } //~ ERROR unexpected token: `1.1`
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found literal `1.1`
{ s.$c; } //~ ERROR unexpected token: `1.1`
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found expression `1.1`
};
}

View file

@ -9,7 +9,7 @@ LL | generate_field_accesses!(1.1, 1.1, 1.1);
|
= note: this error originates in the macro `generate_field_accesses` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
error: expected one of `.`, `;`, `?`, `}`, or an operator, found literal `1.1`
--> $DIR/float-field-interpolated.rs:8:13
|
LL | { s.$b; }
@ -31,7 +31,7 @@ LL | generate_field_accesses!(1.1, 1.1, 1.1);
|
= note: this error originates in the macro `generate_field_accesses` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
error: expected one of `.`, `;`, `?`, `}`, or an operator, found expression `1.1`
--> $DIR/float-field-interpolated.rs:10:13
|
LL | { s.$c; }