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
17 changed files with 900 additions and 44 deletions
|
@ -23,7 +23,7 @@ pub enum LitError {
|
|||
|
||||
impl LitKind {
|
||||
/// 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;
|
||||
if suffix.is_some() && !kind.may_have_suffix() {
|
||||
return Err(LitError::InvalidSuffix);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(let_else)]
|
||||
#![feature(proc_macro_diagnostic)]
|
||||
#![feature(proc_macro_internals)]
|
||||
|
|
|
@ -6,17 +6,17 @@
|
|||
crate mod macro_check;
|
||||
crate mod macro_parser;
|
||||
crate mod macro_rules;
|
||||
crate mod metavar_expr;
|
||||
crate mod quoted;
|
||||
crate mod transcribe;
|
||||
|
||||
use metavar_expr::MetaVarExpr;
|
||||
use rustc_ast::token::{self, NonterminalKind, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::DelimSpan;
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_span::symbol::Ident;
|
||||
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
|
||||
/// that the delimiter itself might be `NoDelim`.
|
||||
#[derive(Clone, PartialEq, Encodable, Decodable, Debug)]
|
||||
|
@ -73,8 +73,8 @@ enum KleeneOp {
|
|||
ZeroOrOne,
|
||||
}
|
||||
|
||||
/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)`
|
||||
/// are "first-class" token trees. Useful for parsing macros.
|
||||
/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, `$(...)`,
|
||||
/// and `${...}` are "first-class" token trees. Useful for parsing macros.
|
||||
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
|
||||
enum TokenTree {
|
||||
Token(Token),
|
||||
|
@ -85,6 +85,8 @@ enum TokenTree {
|
|||
MetaVar(Span, Ident),
|
||||
/// e.g., `$var:expr`. This is only used in the left hand side of MBE macros.
|
||||
MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),
|
||||
/// A meta-variable expression inside `${...}`
|
||||
MetaVarExpr(DelimSpan, MetaVarExpr),
|
||||
}
|
||||
|
||||
impl TokenTree {
|
||||
|
@ -139,7 +141,9 @@ impl TokenTree {
|
|||
TokenTree::Token(Token { span, .. })
|
||||
| TokenTree::MetaVar(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() });
|
||||
}
|
||||
}
|
||||
// `MetaVarExpr` can not appear in the LHS of a macro arm
|
||||
TokenTree::MetaVarExpr(..) => {}
|
||||
TokenTree::Delimited(_, ref del) => {
|
||||
for tt in &del.tts {
|
||||
check_binders(sess, node_id, tt, macros, binders, ops, valid);
|
||||
|
@ -335,6 +337,8 @@ fn check_occurrences(
|
|||
let name = MacroRulesNormalizedIdent::new(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) => {
|
||||
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.
|
||||
#[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> {
|
||||
/// 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| {
|
||||
count
|
||||
+ match *elt {
|
||||
TokenTree::Sequence(_, ref seq) => seq.num_captures,
|
||||
TokenTree::Delimited(_, ref delim) => count_names(&delim.tts),
|
||||
TokenTree::MetaVar(..) => 0,
|
||||
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,
|
||||
}
|
||||
})
|
||||
|
@ -436,7 +439,9 @@ fn nameize<I: Iterator<Item = NamedMatch>>(
|
|||
}
|
||||
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(())
|
||||
|
@ -650,7 +655,7 @@ fn inner_parse_loop<'root, 'tt>(
|
|||
// 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
|
||||
// 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;
|
||||
for tt in tts {
|
||||
match *tt {
|
||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (),
|
||||
TokenTree::Token(..)
|
||||
| TokenTree::MetaVar(..)
|
||||
| TokenTree::MetaVarDecl(..)
|
||||
| TokenTree::MetaVarExpr(..) => (),
|
||||
TokenTree::Delimited(_, ref del) => {
|
||||
if !check_lhs_no_empty_seq(sess, &del.tts) {
|
||||
return false;
|
||||
|
@ -669,7 +672,10 @@ impl FirstSets {
|
|||
let mut first = TokenSet::empty();
|
||||
for tt in tts.iter().rev() {
|
||||
match *tt {
|
||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
|
||||
TokenTree::Token(..)
|
||||
| TokenTree::MetaVar(..)
|
||||
| TokenTree::MetaVarDecl(..)
|
||||
| TokenTree::MetaVarExpr(..) => {
|
||||
first.replace_with(tt.clone());
|
||||
}
|
||||
TokenTree::Delimited(span, ref delimited) => {
|
||||
|
@ -731,7 +737,10 @@ impl FirstSets {
|
|||
for tt in tts.iter() {
|
||||
assert!(first.maybe_empty);
|
||||
match *tt {
|
||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
|
||||
TokenTree::Token(..)
|
||||
| TokenTree::MetaVar(..)
|
||||
| TokenTree::MetaVarDecl(..)
|
||||
| TokenTree::MetaVarExpr(..) => {
|
||||
first.add_one(tt.clone());
|
||||
return first;
|
||||
}
|
||||
|
@ -907,7 +916,10 @@ fn check_matcher_core(
|
|||
// First, update `last` so that it corresponds to the set
|
||||
// of NT tokens that might end the sequence `... 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) {
|
||||
// don't need to track tokens that work with any,
|
||||
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::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree};
|
||||
use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree};
|
||||
|
||||
use rustc_ast::token::{self, Token};
|
||||
use rustc_ast::tokenstream;
|
||||
use rustc_ast::{NodeId, DUMMY_NODE_ID};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_feature::Features;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::symbol::{kw, Ident};
|
||||
use rustc_session::parse::{feature_err, ParseSess};
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
|
@ -25,22 +25,22 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
|
|||
/// # Parameters
|
||||
///
|
||||
/// - `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
|
||||
/// macro. Both take roughly the same form _except_ that in a pattern, metavars are declared with
|
||||
/// their "matcher" type. For example `$var:expr` or `$id:ident`. In this example, `expr` and
|
||||
/// `ident` are "matchers". They are not present in the body of a macro rule -- just in the
|
||||
/// pattern, so we pass a parameter to indicate whether to expect them or not.
|
||||
/// - `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 their "matcher" type. For example `$var:expr` or
|
||||
/// `$id:ident`. In this example, `expr` and `ident` are "matchers". They are not present in the
|
||||
/// 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.
|
||||
/// - `node_id`: the NodeId of the macro we are parsing.
|
||||
/// - `features`: language features so we can do feature gating.
|
||||
/// - `edition`: the edition of the crate defining the macro
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`.
|
||||
pub(super) fn parse(
|
||||
input: tokenstream::TokenStream,
|
||||
expect_matchers: bool,
|
||||
parsing_patterns: bool,
|
||||
sess: &ParseSess,
|
||||
node_id: NodeId,
|
||||
features: &Features,
|
||||
|
@ -55,9 +55,9 @@ pub(super) fn parse(
|
|||
while let Some(tree) = trees.next() {
|
||||
// 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`).
|
||||
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 {
|
||||
TokenTree::MetaVar(start_sp, ident) if expect_matchers => {
|
||||
TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
|
||||
let span = match trees.next() {
|
||||
Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => {
|
||||
match trees.next() {
|
||||
|
@ -118,6 +118,14 @@ pub(super) fn parse(
|
|||
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
|
||||
/// generic `TokenTree`, such as is used in the rest of the compiler, and returns a `TokenTree`
|
||||
/// for use in parsing a macro.
|
||||
|
@ -129,14 +137,13 @@ pub(super) fn parse(
|
|||
/// - `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
|
||||
/// 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.
|
||||
/// - `features`: language features so we can do feature gating.
|
||||
/// - `edition` - the edition of the crate defining the macro
|
||||
fn parse_tree(
|
||||
tree: tokenstream::TokenTree,
|
||||
outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>,
|
||||
expect_matchers: bool,
|
||||
parsing_patterns: bool,
|
||||
sess: &ParseSess,
|
||||
node_id: NodeId,
|
||||
features: &Features,
|
||||
|
@ -158,24 +165,57 @@ fn parse_tree(
|
|||
}
|
||||
|
||||
match next {
|
||||
// `tree` is followed by a delimited set of token trees. This indicates the beginning
|
||||
// of a repetition sequence in the macro (e.g. `$(pat)*`).
|
||||
Some(tokenstream::TokenTree::Delimited(span, delim, tts)) => {
|
||||
// Must have `(` not `{` or `[`
|
||||
if delim != token::Paren {
|
||||
let tok = pprust::token_kind_to_string(&token::OpenDelim(delim));
|
||||
let msg = format!("expected `(`, found `{}`", tok);
|
||||
sess.span_diagnostic.span_err(span.entire(), &msg);
|
||||
// `tree` is followed by a delimited set of token trees.
|
||||
Some(tokenstream::TokenTree::Delimited(delim_span, delim, tts)) => {
|
||||
if parsing_patterns {
|
||||
if delim != token::Paren {
|
||||
span_dollar_dollar_or_metavar_in_the_lhs_err(
|
||||
sess,
|
||||
&Token { kind: token::OpenDelim(delim), span: delim_span.entire() },
|
||||
);
|
||||
}
|
||||
} 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
|
||||
let sequence = parse(tts, expect_matchers, sess, node_id, features, edition);
|
||||
// If we didn't find a metavar expression above, then we must have a
|
||||
// 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
|
||||
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)
|
||||
let name_captures = macro_parser::count_names(&sequence);
|
||||
TokenTree::Sequence(
|
||||
span,
|
||||
delim_span,
|
||||
Lrc::new(SequenceRepetition {
|
||||
tts: sequence,
|
||||
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)) => {
|
||||
let msg = format!(
|
||||
"expected identifier, found `{}`",
|
||||
|
@ -221,7 +274,7 @@ fn parse_tree(
|
|||
span,
|
||||
Lrc::new(Delimited {
|
||||
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
|
||||
(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
|
||||
// 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
|
||||
|
@ -391,6 +396,28 @@ fn lockstep_iter_size(
|
|||
_ => 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,
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
|
||||
(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.
|
||||
(active, marker_trait_attr, "1.30.0", Some(29864), None),
|
||||
/// A minimal, sound subset of specialization intended to be used by the
|
||||
|
|
|
@ -846,6 +846,7 @@ symbols! {
|
|||
macro_export,
|
||||
macro_lifetime_matcher,
|
||||
macro_literal_matcher,
|
||||
macro_metavar_expr,
|
||||
macro_reexport,
|
||||
macro_use,
|
||||
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…
Add table
Reference in a new issue