Rollup merge of #94368 - c410-f3r:metaaaaaaaaaaaaaaaaaaaaaaaaaaa, r=petrochenkov
[1/2] Implement macro meta-variable expressions See https://github.com/rust-lang/rust/pull/93545#issuecomment-1050963295 The logic behind `length`, `index` and `count` was removed but the parsing code is still present, i.e., everything is simply ignored like `ignored`. r? ``@petrochenkov``
This commit is contained in:
commit
634a6b0d25
@ -23,7 +23,7 @@ pub enum LitError {
|
|||||||
|
|
||||||
impl LitKind {
|
impl LitKind {
|
||||||
/// Converts literal token into a semantic literal.
|
/// Converts literal token into a semantic literal.
|
||||||
fn from_lit_token(lit: token::Lit) -> Result<LitKind, LitError> {
|
pub fn from_lit_token(lit: token::Lit) -> Result<LitKind, LitError> {
|
||||||
let token::Lit { kind, symbol, suffix } = lit;
|
let token::Lit { kind, symbol, suffix } = lit;
|
||||||
if suffix.is_some() && !kind.may_have_suffix() {
|
if suffix.is_some() && !kind.may_have_suffix() {
|
||||||
return Err(LitError::InvalidSuffix);
|
return Err(LitError::InvalidSuffix);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#![feature(crate_visibility_modifier)]
|
#![feature(crate_visibility_modifier)]
|
||||||
#![feature(decl_macro)]
|
#![feature(decl_macro)]
|
||||||
#![feature(if_let_guard)]
|
#![feature(if_let_guard)]
|
||||||
|
#![feature(let_chains)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
#![feature(proc_macro_diagnostic)]
|
#![feature(proc_macro_diagnostic)]
|
||||||
#![feature(proc_macro_internals)]
|
#![feature(proc_macro_internals)]
|
||||||
|
@ -6,17 +6,17 @@
|
|||||||
crate mod macro_check;
|
crate mod macro_check;
|
||||||
crate mod macro_parser;
|
crate mod macro_parser;
|
||||||
crate mod macro_rules;
|
crate mod macro_rules;
|
||||||
|
crate mod metavar_expr;
|
||||||
crate mod quoted;
|
crate mod quoted;
|
||||||
crate mod transcribe;
|
crate mod transcribe;
|
||||||
|
|
||||||
|
use metavar_expr::MetaVarExpr;
|
||||||
use rustc_ast::token::{self, NonterminalKind, Token, TokenKind};
|
use rustc_ast::token::{self, NonterminalKind, Token, TokenKind};
|
||||||
use rustc_ast::tokenstream::DelimSpan;
|
use rustc_ast::tokenstream::DelimSpan;
|
||||||
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use rustc_data_structures::sync::Lrc;
|
|
||||||
|
|
||||||
/// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note
|
/// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note
|
||||||
/// that the delimiter itself might be `NoDelim`.
|
/// that the delimiter itself might be `NoDelim`.
|
||||||
#[derive(Clone, PartialEq, Encodable, Decodable, Debug)]
|
#[derive(Clone, PartialEq, Encodable, Decodable, Debug)]
|
||||||
@ -73,8 +73,8 @@ enum KleeneOp {
|
|||||||
ZeroOrOne,
|
ZeroOrOne,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)`
|
/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, `$(...)`,
|
||||||
/// are "first-class" token trees. Useful for parsing macros.
|
/// and `${...}` are "first-class" token trees. Useful for parsing macros.
|
||||||
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
|
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
|
||||||
enum TokenTree {
|
enum TokenTree {
|
||||||
Token(Token),
|
Token(Token),
|
||||||
@ -85,6 +85,8 @@ enum TokenTree {
|
|||||||
MetaVar(Span, Ident),
|
MetaVar(Span, Ident),
|
||||||
/// e.g., `$var:expr`. This is only used in the left hand side of MBE macros.
|
/// e.g., `$var:expr`. This is only used in the left hand side of MBE macros.
|
||||||
MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),
|
MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),
|
||||||
|
/// A meta-variable expression inside `${...}`
|
||||||
|
MetaVarExpr(DelimSpan, MetaVarExpr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenTree {
|
impl TokenTree {
|
||||||
@ -139,7 +141,9 @@ impl TokenTree {
|
|||||||
TokenTree::Token(Token { span, .. })
|
TokenTree::Token(Token { span, .. })
|
||||||
| TokenTree::MetaVar(span, _)
|
| TokenTree::MetaVar(span, _)
|
||||||
| TokenTree::MetaVarDecl(span, _, _) => span,
|
| TokenTree::MetaVarDecl(span, _, _) => span,
|
||||||
TokenTree::Delimited(span, _) | TokenTree::Sequence(span, _) => span.entire(),
|
TokenTree::Delimited(span, _)
|
||||||
|
| TokenTree::MetaVarExpr(span, _)
|
||||||
|
| TokenTree::Sequence(span, _) => span.entire(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,6 +278,8 @@ fn check_binders(
|
|||||||
binders.insert(name, BinderInfo { span, ops: ops.into() });
|
binders.insert(name, BinderInfo { span, ops: ops.into() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// `MetaVarExpr` can not appear in the LHS of a macro arm
|
||||||
|
TokenTree::MetaVarExpr(..) => {}
|
||||||
TokenTree::Delimited(_, ref del) => {
|
TokenTree::Delimited(_, ref del) => {
|
||||||
for tt in &del.tts {
|
for tt in &del.tts {
|
||||||
check_binders(sess, node_id, tt, macros, binders, ops, valid);
|
check_binders(sess, node_id, tt, macros, binders, ops, valid);
|
||||||
@ -335,6 +337,8 @@ fn check_occurrences(
|
|||||||
let name = MacroRulesNormalizedIdent::new(name);
|
let name = MacroRulesNormalizedIdent::new(name);
|
||||||
check_ops_is_prefix(sess, node_id, macros, binders, ops, span, name);
|
check_ops_is_prefix(sess, node_id, macros, binders, ops, span, name);
|
||||||
}
|
}
|
||||||
|
// FIXME(c410-f3r) Check token (https://github.com/rust-lang/rust/issues/93902)
|
||||||
|
TokenTree::MetaVarExpr(..) => {}
|
||||||
TokenTree::Delimited(_, ref del) => {
|
TokenTree::Delimited(_, ref del) => {
|
||||||
check_nested_occurrences(sess, node_id, &del.tts, macros, binders, ops, valid);
|
check_nested_occurrences(sess, node_id, &del.tts, macros, binders, ops, valid);
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ struct MatcherPos<'root, 'tt> {
|
|||||||
|
|
||||||
// This type is used a lot. Make sure it doesn't unintentionally get bigger.
|
// This type is used a lot. Make sure it doesn't unintentionally get bigger.
|
||||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||||
rustc_data_structures::static_assert_size!(MatcherPos<'_, '_>, 192);
|
rustc_data_structures::static_assert_size!(MatcherPos<'_, '_>, 240);
|
||||||
|
|
||||||
impl<'root, 'tt> MatcherPos<'root, 'tt> {
|
impl<'root, 'tt> MatcherPos<'root, 'tt> {
|
||||||
/// Generates the top-level matcher position in which the "dot" is before the first token of
|
/// Generates the top-level matcher position in which the "dot" is before the first token of
|
||||||
@ -321,10 +321,13 @@ pub(super) fn count_names(ms: &[TokenTree]) -> usize {
|
|||||||
ms.iter().fold(0, |count, elt| {
|
ms.iter().fold(0, |count, elt| {
|
||||||
count
|
count
|
||||||
+ match *elt {
|
+ match *elt {
|
||||||
TokenTree::Sequence(_, ref seq) => seq.num_captures,
|
|
||||||
TokenTree::Delimited(_, ref delim) => count_names(&delim.tts),
|
TokenTree::Delimited(_, ref delim) => count_names(&delim.tts),
|
||||||
TokenTree::MetaVar(..) => 0,
|
TokenTree::MetaVar(..) => 0,
|
||||||
TokenTree::MetaVarDecl(..) => 1,
|
TokenTree::MetaVarDecl(..) => 1,
|
||||||
|
// FIXME(c410-f3r) MetaVarExpr should be handled instead of being ignored
|
||||||
|
// https://github.com/rust-lang/rust/issues/9390
|
||||||
|
TokenTree::MetaVarExpr(..) => 0,
|
||||||
|
TokenTree::Sequence(_, ref seq) => seq.num_captures,
|
||||||
TokenTree::Token(..) => 0,
|
TokenTree::Token(..) => 0,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -436,7 +439,9 @@ fn nameize<I: Iterator<Item = NamedMatch>>(
|
|||||||
}
|
}
|
||||||
Occupied(..) => return Err((sp, format!("duplicated bind name: {}", bind_name))),
|
Occupied(..) => return Err((sp, format!("duplicated bind name: {}", bind_name))),
|
||||||
},
|
},
|
||||||
TokenTree::MetaVar(..) | TokenTree::Token(..) => (),
|
// FIXME(c410-f3r) MetaVar and MetaVarExpr should be handled instead of being ignored
|
||||||
|
// https://github.com/rust-lang/rust/issues/9390
|
||||||
|
TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) | TokenTree::Token(..) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -650,7 +655,7 @@ fn inner_parse_loop<'root, 'tt>(
|
|||||||
// rules. NOTE that this is not necessarily an error unless _all_ items in
|
// rules. NOTE that this is not necessarily an error unless _all_ items in
|
||||||
// `cur_items` end up doing this. There may still be some other matchers that do
|
// `cur_items` end up doing this. There may still be some other matchers that do
|
||||||
// end up working out.
|
// end up working out.
|
||||||
TokenTree::Token(..) | TokenTree::MetaVar(..) => {}
|
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -580,7 +580,10 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
|
|||||||
use mbe::TokenTree;
|
use mbe::TokenTree;
|
||||||
for tt in tts {
|
for tt in tts {
|
||||||
match *tt {
|
match *tt {
|
||||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (),
|
TokenTree::Token(..)
|
||||||
|
| TokenTree::MetaVar(..)
|
||||||
|
| TokenTree::MetaVarDecl(..)
|
||||||
|
| TokenTree::MetaVarExpr(..) => (),
|
||||||
TokenTree::Delimited(_, ref del) => {
|
TokenTree::Delimited(_, ref del) => {
|
||||||
if !check_lhs_no_empty_seq(sess, &del.tts) {
|
if !check_lhs_no_empty_seq(sess, &del.tts) {
|
||||||
return false;
|
return false;
|
||||||
@ -669,7 +672,10 @@ impl FirstSets {
|
|||||||
let mut first = TokenSet::empty();
|
let mut first = TokenSet::empty();
|
||||||
for tt in tts.iter().rev() {
|
for tt in tts.iter().rev() {
|
||||||
match *tt {
|
match *tt {
|
||||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
|
TokenTree::Token(..)
|
||||||
|
| TokenTree::MetaVar(..)
|
||||||
|
| TokenTree::MetaVarDecl(..)
|
||||||
|
| TokenTree::MetaVarExpr(..) => {
|
||||||
first.replace_with(tt.clone());
|
first.replace_with(tt.clone());
|
||||||
}
|
}
|
||||||
TokenTree::Delimited(span, ref delimited) => {
|
TokenTree::Delimited(span, ref delimited) => {
|
||||||
@ -731,7 +737,10 @@ impl FirstSets {
|
|||||||
for tt in tts.iter() {
|
for tt in tts.iter() {
|
||||||
assert!(first.maybe_empty);
|
assert!(first.maybe_empty);
|
||||||
match *tt {
|
match *tt {
|
||||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
|
TokenTree::Token(..)
|
||||||
|
| TokenTree::MetaVar(..)
|
||||||
|
| TokenTree::MetaVarDecl(..)
|
||||||
|
| TokenTree::MetaVarExpr(..) => {
|
||||||
first.add_one(tt.clone());
|
first.add_one(tt.clone());
|
||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
@ -907,7 +916,10 @@ fn check_matcher_core(
|
|||||||
// First, update `last` so that it corresponds to the set
|
// First, update `last` so that it corresponds to the set
|
||||||
// of NT tokens that might end the sequence `... token`.
|
// of NT tokens that might end the sequence `... token`.
|
||||||
match *token {
|
match *token {
|
||||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
|
TokenTree::Token(..)
|
||||||
|
| TokenTree::MetaVar(..)
|
||||||
|
| TokenTree::MetaVarDecl(..)
|
||||||
|
| TokenTree::MetaVarExpr(..) => {
|
||||||
if token_can_be_followed_by_any(token) {
|
if token_can_be_followed_by_any(token) {
|
||||||
// don't need to track tokens that work with any,
|
// don't need to track tokens that work with any,
|
||||||
last.replace_with_irrelevant();
|
last.replace_with_irrelevant();
|
||||||
|
157
compiler/rustc_expand/src/mbe/metavar_expr.rs
Normal file
157
compiler/rustc_expand/src/mbe/metavar_expr.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
use rustc_ast::token;
|
||||||
|
use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree};
|
||||||
|
use rustc_ast::{LitIntType, LitKind};
|
||||||
|
use rustc_ast_pretty::pprust;
|
||||||
|
use rustc_errors::{Applicability, PResult};
|
||||||
|
use rustc_session::parse::ParseSess;
|
||||||
|
use rustc_span::symbol::Ident;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
/// A meta-variable expression, for expansions based on properties of meta-variables.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
|
||||||
|
crate enum MetaVarExpr {
|
||||||
|
/// The number of repetitions of an identifier, optionally limited to a number
|
||||||
|
/// of outer-most repetition depths. If the depth limit is `None` then the depth is unlimited.
|
||||||
|
Count(Ident, Option<usize>),
|
||||||
|
|
||||||
|
/// Ignore a meta-variable for repetition without expansion.
|
||||||
|
Ignore(Ident),
|
||||||
|
|
||||||
|
/// The index of the repetition at a particular depth, where 0 is the inner-most
|
||||||
|
/// repetition. The `usize` is the depth.
|
||||||
|
Index(usize),
|
||||||
|
|
||||||
|
/// The length of the repetition at a particular depth, where 0 is the inner-most
|
||||||
|
/// repetition. The `usize` is the depth.
|
||||||
|
Length(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MetaVarExpr {
|
||||||
|
/// Attempt to parse a meta-variable expression from a token stream.
|
||||||
|
crate fn parse<'sess>(
|
||||||
|
input: &TokenStream,
|
||||||
|
outer_span: Span,
|
||||||
|
sess: &'sess ParseSess,
|
||||||
|
) -> PResult<'sess, MetaVarExpr> {
|
||||||
|
let mut tts = input.trees();
|
||||||
|
let ident = parse_ident(&mut tts, sess, outer_span)?;
|
||||||
|
let Some(TokenTree::Delimited(_, token::Paren, args)) = tts.next() else {
|
||||||
|
let msg = "meta-variable expression parameter must be wrapped in parentheses";
|
||||||
|
return Err(sess.span_diagnostic.struct_span_err(ident.span, msg));
|
||||||
|
};
|
||||||
|
check_trailing_token(&mut tts, sess)?;
|
||||||
|
let mut iter = args.trees();
|
||||||
|
let rslt = match &*ident.as_str() {
|
||||||
|
"count" => parse_count(&mut iter, sess, ident.span)?,
|
||||||
|
"ignore" => MetaVarExpr::Ignore(parse_ident(&mut iter, sess, ident.span)?),
|
||||||
|
"index" => MetaVarExpr::Index(parse_depth(&mut iter, sess, ident.span)?),
|
||||||
|
"length" => MetaVarExpr::Length(parse_depth(&mut iter, sess, ident.span)?),
|
||||||
|
_ => {
|
||||||
|
let err_msg = "unrecognized meta-variable expression";
|
||||||
|
let mut err = sess.span_diagnostic.struct_span_err(ident.span, err_msg);
|
||||||
|
err.span_suggestion(
|
||||||
|
ident.span,
|
||||||
|
"supported expressions are count, ignore, index and length",
|
||||||
|
String::new(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
check_trailing_token(&mut iter, sess)?;
|
||||||
|
Ok(rslt)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn ident(&self) -> Option<&Ident> {
|
||||||
|
match self {
|
||||||
|
MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(&ident),
|
||||||
|
MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
|
||||||
|
fn check_trailing_token<'sess>(iter: &mut Cursor, sess: &'sess ParseSess) -> PResult<'sess, ()> {
|
||||||
|
if let Some(tt) = iter.next() {
|
||||||
|
let mut diag = sess.span_diagnostic.struct_span_err(
|
||||||
|
tt.span(),
|
||||||
|
&format!("unexpected token: {}", pprust::tt_to_string(&tt)),
|
||||||
|
);
|
||||||
|
diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens");
|
||||||
|
Err(diag)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a meta-variable `count` expression: `count(ident[, depth])`
|
||||||
|
fn parse_count<'sess>(
|
||||||
|
iter: &mut Cursor,
|
||||||
|
sess: &'sess ParseSess,
|
||||||
|
span: Span,
|
||||||
|
) -> PResult<'sess, MetaVarExpr> {
|
||||||
|
let ident = parse_ident(iter, sess, span)?;
|
||||||
|
let depth = if try_eat_comma(iter) { Some(parse_depth(iter, sess, span)?) } else { None };
|
||||||
|
Ok(MetaVarExpr::Count(ident, depth))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the depth used by index(depth) and length(depth).
|
||||||
|
fn parse_depth<'sess>(
|
||||||
|
iter: &mut Cursor,
|
||||||
|
sess: &'sess ParseSess,
|
||||||
|
span: Span,
|
||||||
|
) -> PResult<'sess, usize> {
|
||||||
|
let Some(tt) = iter.next() else { return Ok(0) };
|
||||||
|
let TokenTree::Token(token::Token {
|
||||||
|
kind: token::TokenKind::Literal(lit), ..
|
||||||
|
}) = tt else {
|
||||||
|
return Err(sess.span_diagnostic.struct_span_err(
|
||||||
|
span,
|
||||||
|
"meta-variable expression depth must be a literal"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
if let Ok(lit_kind) = LitKind::from_lit_token(lit)
|
||||||
|
&& let LitKind::Int(n_u128, LitIntType::Unsuffixed) = lit_kind
|
||||||
|
&& let Ok(n_usize) = usize::try_from(n_u128)
|
||||||
|
{
|
||||||
|
Ok(n_usize)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
|
||||||
|
Err(sess.span_diagnostic.struct_span_err(span, msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses an generic ident
|
||||||
|
fn parse_ident<'sess>(
|
||||||
|
iter: &mut Cursor,
|
||||||
|
sess: &'sess ParseSess,
|
||||||
|
span: Span,
|
||||||
|
) -> PResult<'sess, Ident> {
|
||||||
|
let err_fn = |msg| sess.span_diagnostic.struct_span_err(span, msg);
|
||||||
|
if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt {
|
||||||
|
if let Some((elem, false)) = token.ident() {
|
||||||
|
return Ok(elem);
|
||||||
|
}
|
||||||
|
let token_str = pprust::token_to_string(&token);
|
||||||
|
let mut err = err_fn(&format!("expected identifier, found `{}`", &token_str));
|
||||||
|
err.span_suggestion(
|
||||||
|
token.span,
|
||||||
|
&format!("try removing `{}`", &token_str),
|
||||||
|
String::new(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Err(err_fn("expected identifier"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
|
||||||
|
/// iterator is not modified and the result is `false`.
|
||||||
|
fn try_eat_comma(iter: &mut Cursor) -> bool {
|
||||||
|
if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. })) = iter.look_ahead(0) {
|
||||||
|
let _ = iter.next();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
use crate::mbe::macro_parser;
|
use crate::mbe::macro_parser;
|
||||||
use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree};
|
use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree};
|
||||||
|
|
||||||
use rustc_ast::token::{self, Token};
|
use rustc_ast::token::{self, Token};
|
||||||
use rustc_ast::tokenstream;
|
use rustc_ast::tokenstream;
|
||||||
use rustc_ast::{NodeId, DUMMY_NODE_ID};
|
use rustc_ast::{NodeId, DUMMY_NODE_ID};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_feature::Features;
|
use rustc_feature::Features;
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::{feature_err, ParseSess};
|
||||||
use rustc_span::symbol::{kw, Ident};
|
use rustc_span::symbol::{kw, sym, Ident};
|
||||||
|
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::{Span, SyntaxContext};
|
use rustc_span::{Span, SyntaxContext};
|
||||||
@ -25,22 +25,22 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
|
|||||||
/// # Parameters
|
/// # Parameters
|
||||||
///
|
///
|
||||||
/// - `input`: a token stream to read from, the contents of which we are parsing.
|
/// - `input`: a token stream to read from, the contents of which we are parsing.
|
||||||
/// - `expect_matchers`: `parse` can be used to parse either the "patterns" or the "body" of a
|
/// - `parsing_patterns`: `parse` can be used to parse either the "patterns" or the "body" of a
|
||||||
/// macro. Both take roughly the same form _except_ that in a pattern, metavars are declared with
|
/// macro. Both take roughly the same form _except_ that:
|
||||||
/// their "matcher" type. For example `$var:expr` or `$id:ident`. In this example, `expr` and
|
/// - In a pattern, metavars are declared with their "matcher" type. For example `$var:expr` or
|
||||||
/// `ident` are "matchers". They are not present in the body of a macro rule -- just in the
|
/// `$id:ident`. In this example, `expr` and `ident` are "matchers". They are not present in the
|
||||||
/// pattern, so we pass a parameter to indicate whether to expect them or not.
|
/// body of a macro rule -- just in the pattern.
|
||||||
|
/// - Metavariable expressions are only valid in the "body", not the "pattern".
|
||||||
/// - `sess`: the parsing session. Any errors will be emitted to this session.
|
/// - `sess`: the parsing session. Any errors will be emitted to this session.
|
||||||
/// - `node_id`: the NodeId of the macro we are parsing.
|
/// - `node_id`: the NodeId of the macro we are parsing.
|
||||||
/// - `features`: language features so we can do feature gating.
|
/// - `features`: language features so we can do feature gating.
|
||||||
/// - `edition`: the edition of the crate defining the macro
|
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`.
|
/// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`.
|
||||||
pub(super) fn parse(
|
pub(super) fn parse(
|
||||||
input: tokenstream::TokenStream,
|
input: tokenstream::TokenStream,
|
||||||
expect_matchers: bool,
|
parsing_patterns: bool,
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
features: &Features,
|
features: &Features,
|
||||||
@ -55,9 +55,9 @@ pub(super) fn parse(
|
|||||||
while let Some(tree) = trees.next() {
|
while let Some(tree) = trees.next() {
|
||||||
// Given the parsed tree, if there is a metavar and we are expecting matchers, actually
|
// Given the parsed tree, if there is a metavar and we are expecting matchers, actually
|
||||||
// parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
|
// parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
|
||||||
let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features, edition);
|
let tree = parse_tree(tree, &mut trees, parsing_patterns, sess, node_id, features, edition);
|
||||||
match tree {
|
match tree {
|
||||||
TokenTree::MetaVar(start_sp, ident) if expect_matchers => {
|
TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
|
||||||
let span = match trees.next() {
|
let span = match trees.next() {
|
||||||
Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => {
|
Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => {
|
||||||
match trees.next() {
|
match trees.next() {
|
||||||
@ -118,6 +118,14 @@ pub(super) fn parse(
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asks for the `macro_metavar_expr` feature if it is not already declared
|
||||||
|
fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &ParseSess, span: Span) {
|
||||||
|
if !features.macro_metavar_expr {
|
||||||
|
let msg = "meta-variable expressions are unstable";
|
||||||
|
feature_err(&sess, sym::macro_metavar_expr, span, msg).emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Takes a `tokenstream::TokenTree` and returns a `self::TokenTree`. Specifically, this takes a
|
/// Takes a `tokenstream::TokenTree` and returns a `self::TokenTree`. Specifically, this takes a
|
||||||
/// generic `TokenTree`, such as is used in the rest of the compiler, and returns a `TokenTree`
|
/// generic `TokenTree`, such as is used in the rest of the compiler, and returns a `TokenTree`
|
||||||
/// for use in parsing a macro.
|
/// for use in parsing a macro.
|
||||||
@ -129,14 +137,13 @@ pub(super) fn parse(
|
|||||||
/// - `tree`: the tree we wish to convert.
|
/// - `tree`: the tree we wish to convert.
|
||||||
/// - `outer_trees`: an iterator over trees. We may need to read more tokens from it in order to finish
|
/// - `outer_trees`: an iterator over trees. We may need to read more tokens from it in order to finish
|
||||||
/// converting `tree`
|
/// converting `tree`
|
||||||
/// - `expect_matchers`: same as for `parse` (see above).
|
/// - `parsing_patterns`: same as [parse].
|
||||||
/// - `sess`: the parsing session. Any errors will be emitted to this session.
|
/// - `sess`: the parsing session. Any errors will be emitted to this session.
|
||||||
/// - `features`: language features so we can do feature gating.
|
/// - `features`: language features so we can do feature gating.
|
||||||
/// - `edition` - the edition of the crate defining the macro
|
|
||||||
fn parse_tree(
|
fn parse_tree(
|
||||||
tree: tokenstream::TokenTree,
|
tree: tokenstream::TokenTree,
|
||||||
outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>,
|
outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>,
|
||||||
expect_matchers: bool,
|
parsing_patterns: bool,
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
features: &Features,
|
features: &Features,
|
||||||
@ -158,24 +165,57 @@ fn parse_tree(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match next {
|
match next {
|
||||||
// `tree` is followed by a delimited set of token trees. This indicates the beginning
|
// `tree` is followed by a delimited set of token trees.
|
||||||
// of a repetition sequence in the macro (e.g. `$(pat)*`).
|
Some(tokenstream::TokenTree::Delimited(delim_span, delim, tts)) => {
|
||||||
Some(tokenstream::TokenTree::Delimited(span, delim, tts)) => {
|
if parsing_patterns {
|
||||||
// Must have `(` not `{` or `[`
|
if delim != token::Paren {
|
||||||
if delim != token::Paren {
|
span_dollar_dollar_or_metavar_in_the_lhs_err(
|
||||||
let tok = pprust::token_kind_to_string(&token::OpenDelim(delim));
|
sess,
|
||||||
let msg = format!("expected `(`, found `{}`", tok);
|
&Token { kind: token::OpenDelim(delim), span: delim_span.entire() },
|
||||||
sess.span_diagnostic.span_err(span.entire(), &msg);
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match delim {
|
||||||
|
token::Brace => {
|
||||||
|
// The delimiter is `{`. This indicates the beginning
|
||||||
|
// of a meta-variable expression (e.g. `${count(ident)}`).
|
||||||
|
// Try to parse the meta-variable expression.
|
||||||
|
match MetaVarExpr::parse(&tts, delim_span.entire(), sess) {
|
||||||
|
Err(mut err) => {
|
||||||
|
err.emit();
|
||||||
|
// Returns early the same read `$` to avoid spanning
|
||||||
|
// unrelated diagnostics that could be performed afterwards
|
||||||
|
return TokenTree::token(token::Dollar, span);
|
||||||
|
}
|
||||||
|
Ok(elem) => {
|
||||||
|
maybe_emit_macro_metavar_expr_feature(
|
||||||
|
features,
|
||||||
|
sess,
|
||||||
|
delim_span.entire(),
|
||||||
|
);
|
||||||
|
return TokenTree::MetaVarExpr(delim_span, elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token::Paren => {}
|
||||||
|
_ => {
|
||||||
|
let tok = pprust::token_kind_to_string(&token::OpenDelim(delim));
|
||||||
|
let msg = format!("expected `(` or `{{`, found `{}`", tok);
|
||||||
|
sess.span_diagnostic.span_err(delim_span.entire(), &msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Parse the contents of the sequence itself
|
// If we didn't find a metavar expression above, then we must have a
|
||||||
let sequence = parse(tts, expect_matchers, sess, node_id, features, edition);
|
// repetition sequence in the macro (e.g. `$(pat)*`). Parse the
|
||||||
|
// contents of the sequence itself
|
||||||
|
let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition);
|
||||||
// Get the Kleene operator and optional separator
|
// Get the Kleene operator and optional separator
|
||||||
let (separator, kleene) =
|
let (separator, kleene) =
|
||||||
parse_sep_and_kleene_op(&mut trees, span.entire(), sess);
|
parse_sep_and_kleene_op(&mut trees, delim_span.entire(), sess);
|
||||||
// Count the number of captured "names" (i.e., named metavars)
|
// Count the number of captured "names" (i.e., named metavars)
|
||||||
let name_captures = macro_parser::count_names(&sequence);
|
let name_captures = macro_parser::count_names(&sequence);
|
||||||
TokenTree::Sequence(
|
TokenTree::Sequence(
|
||||||
span,
|
delim_span,
|
||||||
Lrc::new(SequenceRepetition {
|
Lrc::new(SequenceRepetition {
|
||||||
tts: sequence,
|
tts: sequence,
|
||||||
separator,
|
separator,
|
||||||
@ -197,7 +237,20 @@ fn parse_tree(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `tree` is followed by a random token. This is an error.
|
// `tree` is followed by another `$`. This is an escaped `$`.
|
||||||
|
Some(tokenstream::TokenTree::Token(Token { kind: token::Dollar, span })) => {
|
||||||
|
if parsing_patterns {
|
||||||
|
span_dollar_dollar_or_metavar_in_the_lhs_err(
|
||||||
|
sess,
|
||||||
|
&Token { kind: token::Dollar, span },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
maybe_emit_macro_metavar_expr_feature(features, sess, span);
|
||||||
|
}
|
||||||
|
TokenTree::token(token::Dollar, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `tree` is followed by some other token. This is an error.
|
||||||
Some(tokenstream::TokenTree::Token(token)) => {
|
Some(tokenstream::TokenTree::Token(token)) => {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"expected identifier, found `{}`",
|
"expected identifier, found `{}`",
|
||||||
@ -221,7 +274,7 @@ fn parse_tree(
|
|||||||
span,
|
span,
|
||||||
Lrc::new(Delimited {
|
Lrc::new(Delimited {
|
||||||
delim,
|
delim,
|
||||||
tts: parse(tts, expect_matchers, sess, node_id, features, edition),
|
tts: parse(tts, parsing_patterns, sess, node_id, features, edition),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -309,3 +362,15 @@ fn parse_sep_and_kleene_op(
|
|||||||
// Return a dummy
|
// Return a dummy
|
||||||
(None, KleeneToken::new(KleeneOp::ZeroOrMore, span))
|
(None, KleeneToken::new(KleeneOp::ZeroOrMore, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `$$` or a meta-variable is the lhs of a macro but shouldn't.
|
||||||
|
//
|
||||||
|
// For example, `macro_rules! foo { ( ${length()} ) => {} }`
|
||||||
|
fn span_dollar_dollar_or_metavar_in_the_lhs_err<'sess>(sess: &'sess ParseSess, token: &Token) {
|
||||||
|
sess.span_diagnostic
|
||||||
|
.span_err(token.span, &format!("unexpected token: {}", pprust::token_to_string(token)));
|
||||||
|
sess.span_diagnostic.span_note_without_error(
|
||||||
|
token.span,
|
||||||
|
"`$$` and meta-variable expressions are not allowed inside macro parameter definitions",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -255,6 +255,11 @@ pub(super) fn transcribe<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace meta-variable expressions with the result of their expansion.
|
||||||
|
mbe::TokenTree::MetaVarExpr(sp, expr) => {
|
||||||
|
transcribe_metavar_expr(cx, expr, interp, &repeats, &mut result, &sp)?;
|
||||||
|
}
|
||||||
|
|
||||||
// If we are entering a new delimiter, we push its contents to the `stack` to be
|
// If we are entering a new delimiter, we push its contents to the `stack` to be
|
||||||
// processed, and we push all of the currently produced results to the `result_stack`.
|
// processed, and we push all of the currently produced results to the `result_stack`.
|
||||||
// We will produce all of the results of the inside of the `Delimited` and then we will
|
// We will produce all of the results of the inside of the `Delimited` and then we will
|
||||||
@ -391,6 +396,28 @@ fn lockstep_iter_size(
|
|||||||
_ => LockstepIterSize::Unconstrained,
|
_ => LockstepIterSize::Unconstrained,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TokenTree::MetaVarExpr(_, ref expr) => {
|
||||||
|
let default_rslt = LockstepIterSize::Unconstrained;
|
||||||
|
let Some(ident) = expr.ident() else { return default_rslt; };
|
||||||
|
let name = MacroRulesNormalizedIdent::new(ident.clone());
|
||||||
|
match lookup_cur_matched(name, interpolations, repeats) {
|
||||||
|
Some(MatchedSeq(ref ads)) => {
|
||||||
|
default_rslt.with(LockstepIterSize::Constraint(ads.len(), name))
|
||||||
|
}
|
||||||
|
_ => default_rslt,
|
||||||
|
}
|
||||||
|
}
|
||||||
TokenTree::Token(..) => LockstepIterSize::Unconstrained,
|
TokenTree::Token(..) => LockstepIterSize::Unconstrained,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transcribe_metavar_expr<'a>(
|
||||||
|
_cx: &ExtCtxt<'a>,
|
||||||
|
_expr: mbe::MetaVarExpr,
|
||||||
|
_interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
|
||||||
|
_repeats: &[(usize, usize)],
|
||||||
|
_result: &mut Vec<TreeAndSpacing>,
|
||||||
|
_sp: &DelimSpan,
|
||||||
|
) -> PResult<'a, ()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -430,6 +430,8 @@ declare_features! (
|
|||||||
(active, link_cfg, "1.14.0", Some(37406), None),
|
(active, link_cfg, "1.14.0", Some(37406), None),
|
||||||
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
|
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
|
||||||
(active, lint_reasons, "1.31.0", Some(54503), None),
|
(active, lint_reasons, "1.31.0", Some(54503), None),
|
||||||
|
/// Give access to additional metadata about declarative macro meta-variables.
|
||||||
|
(active, macro_metavar_expr, "1.61.0", Some(83527), None),
|
||||||
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
|
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
|
||||||
(active, marker_trait_attr, "1.30.0", Some(29864), None),
|
(active, marker_trait_attr, "1.30.0", Some(29864), None),
|
||||||
/// A minimal, sound subset of specialization intended to be used by the
|
/// A minimal, sound subset of specialization intended to be used by the
|
||||||
|
@ -846,6 +846,7 @@ symbols! {
|
|||||||
macro_export,
|
macro_export,
|
||||||
macro_lifetime_matcher,
|
macro_lifetime_matcher,
|
||||||
macro_literal_matcher,
|
macro_literal_matcher,
|
||||||
|
macro_metavar_expr,
|
||||||
macro_reexport,
|
macro_reexport,
|
||||||
macro_use,
|
macro_use,
|
||||||
macro_vis_matcher,
|
macro_vis_matcher,
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(macro_metavar_expr)]
|
||||||
|
|
||||||
|
macro_rules! nested {
|
||||||
|
( $a:ident ) => {
|
||||||
|
macro_rules! $a {
|
||||||
|
( $$( $b:ident ),* ) => {
|
||||||
|
$$(
|
||||||
|
macro_rules! $b {
|
||||||
|
( $$$$( $c:ident ),* ) => {
|
||||||
|
$$$$(
|
||||||
|
fn $c() -> &'static str { stringify!($c) }
|
||||||
|
),*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
nested!(a);
|
||||||
|
a!(b);
|
||||||
|
b!(c);
|
||||||
|
assert_eq!(c(), "c");
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(macro_metavar_expr)]
|
||||||
|
|
||||||
|
macro_rules! ignore {
|
||||||
|
( $( $i:ident ),* ) => {{
|
||||||
|
let array: [i32; 0] = [$( ${ignore(i)} )*];
|
||||||
|
array
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(ignore!(a, b, c), []);
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
macro_rules! count {
|
||||||
|
( $( $e:stmt ),* ) => {
|
||||||
|
${ count(e) }
|
||||||
|
//~^ ERROR meta-variable expressions are unstable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
error[E0658]: meta-variable expressions are unstable
|
||||||
|
--> $DIR/required-feature.rs:3:10
|
||||||
|
|
|
||||||
|
LL | ${ count(e) }
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #83527 <https://github.com/rust-lang/rust/issues/83527> for more information
|
||||||
|
= help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
148
src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs
Normal file
148
src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#![feature(macro_metavar_expr)]
|
||||||
|
|
||||||
|
// `curly` = Right hand side curly brackets
|
||||||
|
// `no_rhs_dollar` = No dollar sign at the right hand side meta variable "function"
|
||||||
|
// `round` = Left hand side round brackets
|
||||||
|
|
||||||
|
macro_rules! curly__no_rhs_dollar__round {
|
||||||
|
( $( $i:ident ),* ) => { ${ count(i) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! curly__no_rhs_dollar__no_round {
|
||||||
|
( $i:ident ) => { ${ count(i) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! curly__rhs_dollar__round {
|
||||||
|
( $( $i:ident ),* ) => { ${ count($i) } };
|
||||||
|
//~^ ERROR expected identifier, found `$`
|
||||||
|
//~| ERROR expected expression, found `$`
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! curly__rhs_dollar__no_round {
|
||||||
|
( $i:ident ) => { ${ count($i) } };
|
||||||
|
//~^ ERROR expected identifier, found `$`
|
||||||
|
//~| ERROR expected expression, found `$`
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! no_curly__no_rhs_dollar__round {
|
||||||
|
( $( $i:ident ),* ) => { count(i) };
|
||||||
|
//~^ ERROR cannot find function `count` in this scope
|
||||||
|
//~| ERROR cannot find value `i` in this scope
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! no_curly__no_rhs_dollar__no_round {
|
||||||
|
( $i:ident ) => { count(i) };
|
||||||
|
//~^ ERROR cannot find function `count` in this scope
|
||||||
|
//~| ERROR cannot find value `i` in this scope
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! no_curly__rhs_dollar__round {
|
||||||
|
( $( $i:ident ),* ) => { count($i) };
|
||||||
|
//~^ ERROR variable 'i' is still repeating at this depth
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! no_curly__rhs_dollar__no_round {
|
||||||
|
( $i:ident ) => { count($i) };
|
||||||
|
//~^ ERROR cannot find function `count` in this scope
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other scenarios
|
||||||
|
|
||||||
|
macro_rules! dollar_dollar_in_the_lhs {
|
||||||
|
( $$ $a:ident ) => {
|
||||||
|
//~^ ERROR unexpected token: $
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! extra_garbage_after_metavar {
|
||||||
|
( $( $i:ident ),* ) => {
|
||||||
|
${count() a b c}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
//~| ERROR expected expression, found `$`
|
||||||
|
${count(i a b c)}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
${count(i, 1 a b c)}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
${count(i) a b c}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
|
||||||
|
${ignore(i) a b c}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
${ignore(i a b c)}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
|
||||||
|
${index() a b c}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
${index(1 a b c)}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
|
||||||
|
${index() a b c}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
${index(1 a b c)}
|
||||||
|
//~^ ERROR unexpected token: a
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const IDX: usize = 1;
|
||||||
|
macro_rules! metavar_depth_is_not_literal {
|
||||||
|
( $( $i:ident ),* ) => { ${ index(IDX) } };
|
||||||
|
//~^ ERROR meta-variable expression depth must be a literal
|
||||||
|
//~| ERROR expected expression, found `$`
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! metavar_in_the_lhs {
|
||||||
|
( ${ length() } ) => {
|
||||||
|
//~^ ERROR unexpected token: {
|
||||||
|
//~| ERROR expected one of: `*`, `+`, or `?`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! metavar_token_without_ident {
|
||||||
|
( $( $i:ident ),* ) => { ${ ignore() } };
|
||||||
|
//~^ ERROR expected identifier
|
||||||
|
//~| ERROR expected expression, found `$`
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! metavar_with_literal_suffix {
|
||||||
|
( $( $i:ident ),* ) => { ${ index(1u32) } };
|
||||||
|
//~^ ERROR only unsuffixes integer literals are supported in meta-variable expressions
|
||||||
|
//~| ERROR expected expression, found `$`
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! metavar_without_parens {
|
||||||
|
( $( $i:ident ),* ) => { ${ count{i} } };
|
||||||
|
//~^ ERROR meta-variable expression parameter must be wrapped in parentheses
|
||||||
|
//~| ERROR expected expression, found `$`
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! open_brackets_without_tokens {
|
||||||
|
( $( $i:ident ),* ) => { ${ {} } };
|
||||||
|
//~^ ERROR expected expression, found `$`
|
||||||
|
//~| ERROR expected identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! unknown_metavar {
|
||||||
|
( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
|
||||||
|
//~^ ERROR unrecognized meta-variable expression
|
||||||
|
//~| ERROR expected expression
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
curly__no_rhs_dollar__round!(a, b, c);
|
||||||
|
curly__no_rhs_dollar__no_round!(a);
|
||||||
|
curly__rhs_dollar__round!(a, b, c);
|
||||||
|
curly__rhs_dollar__no_round!(a);
|
||||||
|
no_curly__no_rhs_dollar__round!(a, b, c);
|
||||||
|
no_curly__no_rhs_dollar__no_round!(a);
|
||||||
|
no_curly__rhs_dollar__round!(a, b, c);
|
||||||
|
no_curly__rhs_dollar__no_round!(a);
|
||||||
|
//~^ ERROR cannot find value `a` in this scope
|
||||||
|
|
||||||
|
extra_garbage_after_metavar!(a);
|
||||||
|
unknown_metavar!(a);
|
||||||
|
metavar_without_parens!(a);
|
||||||
|
metavar_token_without_ident!(a);
|
||||||
|
metavar_depth_is_not_literal!(a);
|
||||||
|
metavar_with_literal_suffix!(a);
|
||||||
|
open_brackets_without_tokens!(a)
|
||||||
|
}
|
367
src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr
Normal file
367
src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
error: expected identifier, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:16:33
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ count($i) } };
|
||||||
|
| ^^^^^ - help: try removing `$`
|
||||||
|
|
||||||
|
error: expected identifier, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:22:26
|
||||||
|
|
|
||||||
|
LL | ( $i:ident ) => { ${ count($i) } };
|
||||||
|
| ^^^^^ - help: try removing `$`
|
||||||
|
|
||||||
|
error: unexpected token: $
|
||||||
|
--> $DIR/syntax-errors.rs:52:8
|
||||||
|
|
|
||||||
|
LL | ( $$ $a:ident ) => {
|
||||||
|
| ^
|
||||||
|
|
||||||
|
note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions
|
||||||
|
--> $DIR/syntax-errors.rs:52:8
|
||||||
|
|
|
||||||
|
LL | ( $$ $a:ident ) => {
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:59:19
|
||||||
|
|
|
||||||
|
LL | ${count() a b c}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:59:19
|
||||||
|
|
|
||||||
|
LL | ${count() a b c}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:62:19
|
||||||
|
|
|
||||||
|
LL | ${count(i a b c)}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:62:19
|
||||||
|
|
|
||||||
|
LL | ${count(i a b c)}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:64:22
|
||||||
|
|
|
||||||
|
LL | ${count(i, 1 a b c)}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:64:22
|
||||||
|
|
|
||||||
|
LL | ${count(i, 1 a b c)}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:66:20
|
||||||
|
|
|
||||||
|
LL | ${count(i) a b c}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:66:20
|
||||||
|
|
|
||||||
|
LL | ${count(i) a b c}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:69:21
|
||||||
|
|
|
||||||
|
LL | ${ignore(i) a b c}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:69:21
|
||||||
|
|
|
||||||
|
LL | ${ignore(i) a b c}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:71:20
|
||||||
|
|
|
||||||
|
LL | ${ignore(i a b c)}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:71:20
|
||||||
|
|
|
||||||
|
LL | ${ignore(i a b c)}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:74:19
|
||||||
|
|
|
||||||
|
LL | ${index() a b c}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:74:19
|
||||||
|
|
|
||||||
|
LL | ${index() a b c}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:76:19
|
||||||
|
|
|
||||||
|
LL | ${index(1 a b c)}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:76:19
|
||||||
|
|
|
||||||
|
LL | ${index(1 a b c)}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:79:19
|
||||||
|
|
|
||||||
|
LL | ${index() a b c}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:79:19
|
||||||
|
|
|
||||||
|
LL | ${index() a b c}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: unexpected token: a
|
||||||
|
--> $DIR/syntax-errors.rs:81:19
|
||||||
|
|
|
||||||
|
LL | ${index(1 a b c)}
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: meta-variable expression must not have trailing tokens
|
||||||
|
--> $DIR/syntax-errors.rs:81:19
|
||||||
|
|
|
||||||
|
LL | ${index(1 a b c)}
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: meta-variable expression depth must be a literal
|
||||||
|
--> $DIR/syntax-errors.rs:88:33
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ index(IDX) } };
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error: unexpected token: {
|
||||||
|
--> $DIR/syntax-errors.rs:94:8
|
||||||
|
|
|
||||||
|
LL | ( ${ length() } ) => {
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions
|
||||||
|
--> $DIR/syntax-errors.rs:94:8
|
||||||
|
|
|
||||||
|
LL | ( ${ length() } ) => {
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: expected one of: `*`, `+`, or `?`
|
||||||
|
--> $DIR/syntax-errors.rs:94:8
|
||||||
|
|
|
||||||
|
LL | ( ${ length() } ) => {
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: expected identifier
|
||||||
|
--> $DIR/syntax-errors.rs:101:33
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ ignore() } };
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
|
error: only unsuffixes integer literals are supported in meta-variable expressions
|
||||||
|
--> $DIR/syntax-errors.rs:107:33
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ index(1u32) } };
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error: meta-variable expression parameter must be wrapped in parentheses
|
||||||
|
--> $DIR/syntax-errors.rs:113:33
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ count{i} } };
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error: expected identifier
|
||||||
|
--> $DIR/syntax-errors.rs:119:31
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ {} } };
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
|
error: unrecognized meta-variable expression
|
||||||
|
--> $DIR/syntax-errors.rs:125:33
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
|
||||||
|
| ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and length
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:16:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ count($i) } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | curly__rhs_dollar__round!(a, b, c);
|
||||||
|
| ---------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `curly__rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:22:23
|
||||||
|
|
|
||||||
|
LL | ( $i:ident ) => { ${ count($i) } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | curly__rhs_dollar__no_round!(a);
|
||||||
|
| ------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: variable 'i' is still repeating at this depth
|
||||||
|
--> $DIR/syntax-errors.rs:40:36
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { count($i) };
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:59:9
|
||||||
|
|
|
||||||
|
LL | ${count() a b c}
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | extra_garbage_after_metavar!(a);
|
||||||
|
| ------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `extra_garbage_after_metavar` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:125:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | unknown_metavar!(a);
|
||||||
|
| ------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `unknown_metavar` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:113:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ count{i} } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | metavar_without_parens!(a);
|
||||||
|
| -------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `metavar_without_parens` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:101:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ ignore() } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | metavar_token_without_ident!(a);
|
||||||
|
| ------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `metavar_token_without_ident` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:88:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ index(IDX) } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | metavar_depth_is_not_literal!(a);
|
||||||
|
| -------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `metavar_depth_is_not_literal` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:107:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ index(1u32) } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | metavar_with_literal_suffix!(a);
|
||||||
|
| ------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `metavar_with_literal_suffix` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found `$`
|
||||||
|
--> $DIR/syntax-errors.rs:119:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { ${ {} } };
|
||||||
|
| ^ expected expression
|
||||||
|
...
|
||||||
|
LL | open_brackets_without_tokens!(a)
|
||||||
|
| -------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `open_brackets_without_tokens` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `count` in this scope
|
||||||
|
--> $DIR/syntax-errors.rs:28:30
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { count(i) };
|
||||||
|
| ^^^^^ not found in this scope
|
||||||
|
...
|
||||||
|
LL | no_curly__no_rhs_dollar__round!(a, b, c);
|
||||||
|
| ---------------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `no_curly__no_rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `i` in this scope
|
||||||
|
--> $DIR/syntax-errors.rs:28:36
|
||||||
|
|
|
||||||
|
LL | ( $( $i:ident ),* ) => { count(i) };
|
||||||
|
| ^ not found in this scope
|
||||||
|
...
|
||||||
|
LL | no_curly__no_rhs_dollar__round!(a, b, c);
|
||||||
|
| ---------------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `no_curly__no_rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `count` in this scope
|
||||||
|
--> $DIR/syntax-errors.rs:34:23
|
||||||
|
|
|
||||||
|
LL | ( $i:ident ) => { count(i) };
|
||||||
|
| ^^^^^ not found in this scope
|
||||||
|
...
|
||||||
|
LL | no_curly__no_rhs_dollar__no_round!(a);
|
||||||
|
| ------------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `i` in this scope
|
||||||
|
--> $DIR/syntax-errors.rs:34:29
|
||||||
|
|
|
||||||
|
LL | ( $i:ident ) => { count(i) };
|
||||||
|
| ^ not found in this scope
|
||||||
|
...
|
||||||
|
LL | no_curly__no_rhs_dollar__no_round!(a);
|
||||||
|
| ------------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `count` in this scope
|
||||||
|
--> $DIR/syntax-errors.rs:45:23
|
||||||
|
|
|
||||||
|
LL | ( $i:ident ) => { count($i) };
|
||||||
|
| ^^^^^ not found in this scope
|
||||||
|
...
|
||||||
|
LL | no_curly__rhs_dollar__no_round!(a);
|
||||||
|
| ---------------------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `no_curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `a` in this scope
|
||||||
|
--> $DIR/syntax-errors.rs:138:37
|
||||||
|
|
|
||||||
|
LL | no_curly__rhs_dollar__no_round!(a);
|
||||||
|
| ^ not found in this scope
|
||||||
|
|
||||||
|
error: aborting due to 37 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0425`.
|
Loading…
x
Reference in New Issue
Block a user