Skip to content

Commit 5e8fac5

Browse files
committed
[RFC-3086] Add a new concat metavar expr
1 parent 740cea8 commit 5e8fac5

File tree

5 files changed

+131
-5
lines changed

5 files changed

+131
-5
lines changed

compiler/rustc_expand/src/mbe/metavar_expr.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ use rustc_span::Span;
1010
/// A meta-variable expression, for expansions based on properties of meta-variables.
1111
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
1212
pub(crate) enum MetaVarExpr {
13+
/// Unification of two identifiers. The `bool` of each element indicates if there is a
14+
/// preceding dollar sign.
15+
Concat((Ident, bool), (Ident, bool)),
16+
1317
/// The number of repetitions of an identifier.
1418
Count(Ident, usize),
1519

@@ -41,6 +45,16 @@ impl MetaVarExpr {
4145
check_trailing_token(&mut tts, sess)?;
4246
let mut iter = args.trees();
4347
let rslt = match ident.as_str() {
48+
"concat" => {
49+
let lhs_sign = try_eat_dollar(&mut iter);
50+
let lhs = parse_ident(&mut iter, sess, ident.span)?;
51+
if !try_eat_comma(&mut iter) {
52+
return Err(sess.span_diagnostic.struct_span_err(ident.span, "expected comma"));
53+
}
54+
let rhs_sign = try_eat_dollar(&mut iter);
55+
let rhs = parse_ident(&mut iter, sess, ident.span)?;
56+
MetaVarExpr::Concat((lhs, lhs_sign), (rhs, rhs_sign))
57+
}
4458
"count" => parse_count(&mut iter, sess, ident.span)?,
4559
"ignore" => {
4660
eat_dollar(&mut iter, sess, ident.span)?;
@@ -67,7 +81,7 @@ impl MetaVarExpr {
6781
pub(crate) fn ident(&self) -> Option<Ident> {
6882
match *self {
6983
MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident),
70-
MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None,
84+
MetaVarExpr::Concat(..) | MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None,
7185
}
7286
}
7387
}
@@ -170,6 +184,17 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
170184
false
171185
}
172186

187+
/// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
188+
/// iterator is not modified and the result is `false`.
189+
fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
190+
if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
191+
{
192+
let _ = iter.next();
193+
return true;
194+
}
195+
false
196+
}
197+
173198
/// Expects that the next item is a dollar sign.
174199
fn eat_dollar<'sess>(
175200
iter: &mut RefTokenTreeCursor<'_>,

compiler/rustc_expand/src/mbe/transcribe.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ use crate::errors::{
66
use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch};
77
use crate::mbe::{self, MetaVarExpr};
88
use rustc_ast::mut_visit::{self, MutVisitor};
9-
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
9+
use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind};
1010
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
1111
use rustc_data_structures::fx::FxHashMap;
1212
use rustc_errors::{pluralize, PResult};
1313
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
1414
use rustc_span::hygiene::{LocalExpnId, Transparency};
1515
use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
16-
use rustc_span::Span;
16+
use rustc_span::{Span, Symbol};
1717

1818
use smallvec::{smallvec, SmallVec};
1919
use std::mem;
@@ -558,6 +558,29 @@ fn transcribe_metavar_expr<'a>(
558558
span
559559
};
560560
match *expr {
561+
MetaVarExpr::Concat((lhs, lhs_sign), (rhs, rhs_sign)) => {
562+
let string = |ident: Ident, has_sign: bool| {
563+
if !has_sign {
564+
return ident.to_string();
565+
}
566+
let mrni = MacroRulesNormalizedIdent::new(ident);
567+
if let Some(nm) = lookup_cur_matched(mrni, interp, &repeats)
568+
&& let MatchedNonterminal(nt) = nm
569+
&& let Nonterminal::NtIdent(nt_ident, _) = &nt.0
570+
{
571+
nt_ident.to_string()
572+
} else {
573+
ident.to_string()
574+
}
575+
};
576+
let symbol_span = lhs.span.to(rhs.span);
577+
let mut symbol_string = string(lhs, lhs_sign);
578+
symbol_string.push_str(&string(rhs, rhs_sign));
579+
result.push(TokenTree::Token(
580+
Token::from_ast_ident(Ident::new(Symbol::intern(&symbol_string), symbol_span)),
581+
Spacing::Alone,
582+
));
583+
}
561584
MetaVarExpr::Count(original_ident, depth) => {
562585
let matched = matched_from_ident(cx, original_ident, interp)?;
563586
let count = count_repetitions(cx, depth, matched, repeats, sp)?;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// run-pass
2+
3+
#![allow(dead_code, non_camel_case_types, non_upper_case_globals)]
4+
#![feature(macro_metavar_expr)]
5+
6+
macro_rules! simple_ident {
7+
( $lhs:ident, $rhs:ident ) => { ${concat($lhs, $rhs)} };
8+
}
9+
10+
macro_rules! create_things {
11+
( $lhs:ident ) => {
12+
struct ${concat($lhs, _separated_idents_in_a_struct)} {
13+
foo: i32,
14+
${concat($lhs, _separated_idents_in_a_field)}: i32,
15+
}
16+
17+
mod ${concat($lhs, _separated_idents_in_a_module)} {
18+
pub const FOO: () = ();
19+
}
20+
21+
fn ${concat($lhs, _separated_idents_in_a_fn)}() {}
22+
};
23+
}
24+
25+
macro_rules! without_dollar_sign_is_an_ident {
26+
( $ident:ident ) => {
27+
const ${concat(VAR, ident)}: i32 = 1;
28+
const ${concat(VAR, $ident)}: i32 = 2;
29+
};
30+
}
31+
32+
fn main() {
33+
let abcdef = 1;
34+
let _another = simple_ident!(abc, def);
35+
36+
create_things!(behold);
37+
behold_separated_idents_in_a_fn();
38+
let _ = behold_separated_idents_in_a_module::FOO;
39+
let _ = behold_separated_idents_in_a_struct {
40+
foo: 1,
41+
behold_separated_idents_in_a_field: 2,
42+
};
43+
44+
without_dollar_sign_is_an_ident!(_123);
45+
assert_eq!(VARident, 1);
46+
assert_eq!(VAR_123, 2);
47+
}

tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs

+13
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,19 @@ macro_rules! unknown_metavar {
137137
//~| ERROR expected expression
138138
}
139139

140+
macro_rules! wrong_concat_declarations {
141+
() => {
142+
${concat()}
143+
//~^ ERROR expected identifier
144+
145+
${concat(aaaa)}
146+
//~^ ERROR expected comma
147+
148+
${concat(aaaa,)}
149+
//~^ ERROR expected identifier
150+
};
151+
}
152+
140153
fn main() {
141154
curly__no_rhs_dollar__round!(a, b, c);
142155
curly__no_rhs_dollar__no_round!(a);

tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr

+20-2
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,24 @@ error: unrecognized meta-variable expression
196196
LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
197197
| ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and length
198198

199+
error: expected identifier
200+
--> $DIR/syntax-errors.rs:142:11
201+
|
202+
LL | ${concat()}
203+
| ^^^^^^
204+
205+
error: expected comma
206+
--> $DIR/syntax-errors.rs:145:11
207+
|
208+
LL | ${concat(aaaa)}
209+
| ^^^^^^
210+
211+
error: expected identifier
212+
--> $DIR/syntax-errors.rs:148:11
213+
|
214+
LL | ${concat(aaaa,)}
215+
| ^^^^^^
216+
199217
error: `count` can not be placed inside the inner-most repetition
200218
--> $DIR/syntax-errors.rs:12:24
201219
|
@@ -336,7 +354,7 @@ LL | no_curly__no_rhs_dollar__no_round!(a);
336354
= 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)
337355

338356
error[E0425]: cannot find value `a` in this scope
339-
--> $DIR/syntax-errors.rs:147:37
357+
--> $DIR/syntax-errors.rs:160:37
340358
|
341359
LL | no_curly__rhs_dollar__no_round!(a);
342360
| ^ not found in this scope
@@ -374,6 +392,6 @@ LL | no_curly__rhs_dollar__no_round!(a);
374392
|
375393
= note: this error originates in the macro `no_curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info)
376394

377-
error: aborting due to 39 previous errors
395+
error: aborting due to 42 previous errors
378396

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

0 commit comments

Comments
 (0)