Skip to content

Commit

Permalink
[RFC-3086] Add a new concat metavar expr
Browse files Browse the repository at this point in the history
  • Loading branch information
c410-f3r committed Dec 15, 2023
1 parent 740cea8 commit 5e8fac5
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 5 deletions.
27 changes: 26 additions & 1 deletion compiler/rustc_expand/src/mbe/metavar_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ use rustc_span::Span;
/// A meta-variable expression, for expansions based on properties of meta-variables.
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
pub(crate) enum MetaVarExpr {
/// Unification of two identifiers. The `bool` of each element indicates if there is a
/// preceding dollar sign.
Concat((Ident, bool), (Ident, bool)),

/// The number of repetitions of an identifier.
Count(Ident, usize),

Expand Down Expand Up @@ -41,6 +45,16 @@ impl MetaVarExpr {
check_trailing_token(&mut tts, sess)?;
let mut iter = args.trees();
let rslt = match ident.as_str() {
"concat" => {
let lhs_sign = try_eat_dollar(&mut iter);
let lhs = parse_ident(&mut iter, sess, ident.span)?;
if !try_eat_comma(&mut iter) {
return Err(sess.span_diagnostic.struct_span_err(ident.span, "expected comma"));
}
let rhs_sign = try_eat_dollar(&mut iter);
let rhs = parse_ident(&mut iter, sess, ident.span)?;
MetaVarExpr::Concat((lhs, lhs_sign), (rhs, rhs_sign))
}
"count" => parse_count(&mut iter, sess, ident.span)?,
"ignore" => {
eat_dollar(&mut iter, sess, ident.span)?;
Expand All @@ -67,7 +81,7 @@ impl MetaVarExpr {
pub(crate) fn ident(&self) -> Option<Ident> {
match *self {
MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident),
MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None,
MetaVarExpr::Concat(..) | MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None,
}
}
}
Expand Down Expand Up @@ -170,6 +184,17 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
false
}

/// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
/// iterator is not modified and the result is `false`.
fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
{
let _ = iter.next();
return true;
}
false
}

/// Expects that the next item is a dollar sign.
fn eat_dollar<'sess>(
iter: &mut RefTokenTreeCursor<'_>,
Expand Down
27 changes: 25 additions & 2 deletions compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use crate::errors::{
use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch};
use crate::mbe::{self, MetaVarExpr};
use rustc_ast::mut_visit::{self, MutVisitor};
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, PResult};
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
use rustc_span::hygiene::{LocalExpnId, Transparency};
use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
use rustc_span::Span;
use rustc_span::{Span, Symbol};

use smallvec::{smallvec, SmallVec};
use std::mem;
Expand Down Expand Up @@ -558,6 +558,29 @@ fn transcribe_metavar_expr<'a>(
span
};
match *expr {
MetaVarExpr::Concat((lhs, lhs_sign), (rhs, rhs_sign)) => {
let string = |ident: Ident, has_sign: bool| {
if !has_sign {
return ident.to_string();
}
let mrni = MacroRulesNormalizedIdent::new(ident);
if let Some(nm) = lookup_cur_matched(mrni, interp, &repeats)
&& let MatchedNonterminal(nt) = nm
&& let Nonterminal::NtIdent(nt_ident, _) = &nt.0
{
nt_ident.to_string()
} else {
ident.to_string()
}
};
let symbol_span = lhs.span.to(rhs.span);
let mut symbol_string = string(lhs, lhs_sign);
symbol_string.push_str(&string(rhs, rhs_sign));
result.push(TokenTree::Token(
Token::from_ast_ident(Ident::new(Symbol::intern(&symbol_string), symbol_span)),
Spacing::Alone,
));
}
MetaVarExpr::Count(original_ident, depth) => {
let matched = matched_from_ident(cx, original_ident, interp)?;
let count = count_repetitions(cx, depth, matched, repeats, sp)?;
Expand Down
47 changes: 47 additions & 0 deletions tests/ui/macros/rfc-3086-metavar-expr/concat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// run-pass

#![allow(dead_code, non_camel_case_types, non_upper_case_globals)]
#![feature(macro_metavar_expr)]

macro_rules! simple_ident {
( $lhs:ident, $rhs:ident ) => { ${concat($lhs, $rhs)} };
}

macro_rules! create_things {
( $lhs:ident ) => {
struct ${concat($lhs, _separated_idents_in_a_struct)} {
foo: i32,
${concat($lhs, _separated_idents_in_a_field)}: i32,
}

mod ${concat($lhs, _separated_idents_in_a_module)} {
pub const FOO: () = ();
}

fn ${concat($lhs, _separated_idents_in_a_fn)}() {}
};
}

macro_rules! without_dollar_sign_is_an_ident {
( $ident:ident ) => {
const ${concat(VAR, ident)}: i32 = 1;
const ${concat(VAR, $ident)}: i32 = 2;
};
}

fn main() {
let abcdef = 1;
let _another = simple_ident!(abc, def);

create_things!(behold);
behold_separated_idents_in_a_fn();
let _ = behold_separated_idents_in_a_module::FOO;
let _ = behold_separated_idents_in_a_struct {
foo: 1,
behold_separated_idents_in_a_field: 2,
};

without_dollar_sign_is_an_ident!(_123);
assert_eq!(VARident, 1);
assert_eq!(VAR_123, 2);
}
13 changes: 13 additions & 0 deletions tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ macro_rules! unknown_metavar {
//~| ERROR expected expression
}

macro_rules! wrong_concat_declarations {
() => {
${concat()}
//~^ ERROR expected identifier

${concat(aaaa)}
//~^ ERROR expected comma

${concat(aaaa,)}
//~^ ERROR expected identifier
};
}

fn main() {
curly__no_rhs_dollar__round!(a, b, c);
curly__no_rhs_dollar__no_round!(a);
Expand Down
22 changes: 20 additions & 2 deletions tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,24 @@ error: unrecognized meta-variable expression
LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
| ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and length

error: expected identifier
--> $DIR/syntax-errors.rs:142:11
|
LL | ${concat()}
| ^^^^^^

error: expected comma
--> $DIR/syntax-errors.rs:145:11
|
LL | ${concat(aaaa)}
| ^^^^^^

error: expected identifier
--> $DIR/syntax-errors.rs:148:11
|
LL | ${concat(aaaa,)}
| ^^^^^^

error: `count` can not be placed inside the inner-most repetition
--> $DIR/syntax-errors.rs:12:24
|
Expand Down Expand Up @@ -336,7 +354,7 @@ LL | no_curly__no_rhs_dollar__no_round!(a);
= 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 `a` in this scope
--> $DIR/syntax-errors.rs:147:37
--> $DIR/syntax-errors.rs:160:37
|
LL | no_curly__rhs_dollar__no_round!(a);
| ^ not found in this scope
Expand Down Expand Up @@ -374,6 +392,6 @@ LL | no_curly__rhs_dollar__no_round!(a);
|
= 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: aborting due to 39 previous errors
error: aborting due to 42 previous errors

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

0 comments on commit 5e8fac5

Please sign in to comment.