Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions compiler/rustc_expand/src/mbe/metavar_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or str
/// A meta-variable expression, for expansions based on properties of meta-variables.
#[derive(Debug, PartialEq, Encodable, Decodable)]
pub(crate) enum MetaVarExpr {
/// Unification of two or more identifiers.
Concat(Box<[MetaVarExprConcatElem]>),
/// Unification of two or more identifiers/literals/metavariables into an identifier.
ConcatIdent(Box<[MetaVarExprConcatElem]>),

/// Unification of two or more identifiers/literals/metavariables into a string literal.
ConcatStr(Box<[MetaVarExprConcatElem]>),

/// The number of repetitions of an identifier.
Count(Ident, usize),
Expand Down Expand Up @@ -73,7 +76,12 @@ impl MetaVarExpr {

let mut iter = args.iter();
let rslt = match ident.name {
sym::concat => parse_concat(&mut iter, psess, outer_span, ident.span)?,
sym::concat => {
MetaVarExpr::ConcatIdent(parse_concat(&mut iter, psess, outer_span, ident.span)?)
}
sym::concat_str => {
MetaVarExpr::ConcatStr(parse_concat(&mut iter, psess, outer_span, ident.span)?)
}
sym::count => parse_count(&mut iter, psess, ident.span)?,
sym::ignore => {
eat_dollar(&mut iter, psess, ident.span)?;
Expand All @@ -95,7 +103,7 @@ impl MetaVarExpr {

pub(crate) fn for_each_metavar<A>(&self, mut aux: A, mut cb: impl FnMut(A, &Ident) -> A) -> A {
match self {
MetaVarExpr::Concat(elems) => {
MetaVarExpr::ConcatIdent(elems) | MetaVarExpr::ConcatStr(elems) => {
for elem in elems {
if let MetaVarExprConcatElem::Var(ident) = elem {
aux = cb(aux, ident)
Expand Down Expand Up @@ -175,7 +183,7 @@ fn parse_concat<'psess>(
psess: &'psess ParseSess,
outer_span: Span,
expr_ident_span: Span,
) -> PResult<'psess, MetaVarExpr> {
) -> PResult<'psess, Box<[MetaVarExprConcatElem]>> {
let mut result = Vec::new();
loop {
let is_var = try_eat_dollar(iter);
Expand Down Expand Up @@ -210,7 +218,7 @@ fn parse_concat<'psess>(
.dcx()
.struct_span_err(expr_ident_span, "`concat` must have at least two elements"));
}
Ok(MetaVarExpr::Concat(result.into()))
Ok(result.into())
}

/// Parse a meta-variable `count` expression: `count(ident[, depth])`
Expand Down
21 changes: 17 additions & 4 deletions compiler/rustc_expand/src/mbe/quoted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_feature::Features;
use rustc_session::Session;
use rustc_session::diagnostics::feature_err;
use rustc_span::edition::Edition;
use rustc_span::{Ident, Span, kw, sym};
use rustc_span::{Ident, Span, Symbol, kw, sym};

use crate::diagnostics;
use crate::mbe::macro_parser::count_metavar_decls;
Expand Down Expand Up @@ -204,9 +204,14 @@ fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, sp
}
}

fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Session, span: Span) {
fn maybe_emit_macro_metavar_expr_concat_feature(
features: &Features,
sess: &Session,
span: Span,
what: Symbol,
) {
if !features.macro_metavar_expr_concat() {
let msg = "the `concat` meta-variable expression is unstable";
let msg = format!("the `{what}` meta-variable expression is unstable");
feature_err(sess, sym::macro_metavar_expr_concat, span, msg).emit();
}
}
Expand Down Expand Up @@ -278,11 +283,19 @@ fn parse_tree<'a>(
return TokenTree::token(token::Dollar, dollar_span);
}
Ok(elem) => {
if let MetaVarExpr::Concat(_) = elem {
if matches!(elem, MetaVarExpr::ConcatIdent(_)) {
maybe_emit_macro_metavar_expr_concat_feature(
features,
sess,
delim_span.entire(),
sym::concat,
);
} else if matches!(elem, MetaVarExpr::ConcatStr(_)) {
maybe_emit_macro_metavar_expr_concat_feature(
features,
sess,
delim_span.entire(),
sym::concat_str,
);
} else {
maybe_emit_macro_metavar_expr_feature(
Expand Down
62 changes: 45 additions & 17 deletions compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,8 @@ fn transcribe_metavar_expr<'tx>(
) -> PResult<'tx, ()> {
let dcx = tscx.psess.dcx();
let tt = match *expr {
MetaVarExpr::Concat(ref elements) => metavar_expr_concat(tscx, dspan, elements)?,
MetaVarExpr::ConcatIdent(ref elements) => metavar_expr_concat_ident(tscx, dspan, elements)?,
MetaVarExpr::ConcatStr(ref elements) => metavar_expr_concat_str(tscx, dspan, elements)?,
MetaVarExpr::Count(original_ident, depth) => {
let matched = matched_from_ident(dcx, original_ident, tscx.interp)?;
let count = count_repetitions(dcx, depth, matched, &tscx.repeats, &dspan)?;
Expand Down Expand Up @@ -635,11 +636,52 @@ fn transcribe_metavar_expr<'tx>(
}

/// Handle the `${concat(...)}` metavariable expression.
fn metavar_expr_concat<'tx>(
fn metavar_expr_concat_ident<'tx>(
tscx: &mut TranscrCtx<'tx, '_>,
dspan: DelimSpan,
elements: &[MetaVarExprConcatElem],
) -> PResult<'tx, TokenTree> {
let (symbol, concatenated_span) = metavar_expr_concat(tscx, dspan, elements)?;
if !rustc_lexer::is_ident(symbol.as_str()) {
return Err(tscx.psess.dcx().create_err(ConcatInvalidIdent {
span: concatenated_span,
reason: InvalidIdentReason::new(symbol),
}));
}
tscx.psess.symbol_gallery.insert(symbol, concatenated_span);

// The current implementation marks the span as coming from the macro regardless of
// contexts of the concatenated identifiers but this behavior may change in the
// future.
Ok(TokenTree::Token(
Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
Spacing::Alone,
))
}

/// Handle the `${concat_str(...)}` metavariable expression.
fn metavar_expr_concat_str<'tx>(
tscx: &mut TranscrCtx<'tx, '_>,
dspan: DelimSpan,
elements: &[MetaVarExprConcatElem],
) -> PResult<'tx, TokenTree> {
let (symbol, concatenated_span) = metavar_expr_concat(tscx, dspan, elements)?;

// The current implementation marks the span as coming from the macro regardless of
// contexts of the concatenated identifiers but this behavior may change in the
// future.
Ok(TokenTree::Token(
Token::new(TokenKind::lit(LitKind::Str, symbol, None), concatenated_span),
Comment thread
petrochenkov marked this conversation as resolved.
Spacing::Alone,
))
}

/// Shared logic for concat/concat_str metavariable expressions
fn metavar_expr_concat<'tx>(
tscx: &mut TranscrCtx<'tx, '_>,
dspan: DelimSpan,
elements: &[MetaVarExprConcatElem],
) -> PResult<'tx, (Symbol, Span)> {
let dcx = tscx.psess.dcx();
let mut concatenated = String::new();
for element in elements {
Expand Down Expand Up @@ -668,21 +710,7 @@ fn metavar_expr_concat<'tx>(
}
let symbol = nfc_normalize(&concatenated);
let concatenated_span = tscx.visited_dspan(dspan);
if !rustc_lexer::is_ident(symbol.as_str()) {
return Err(dcx.create_err(ConcatInvalidIdent {
span: concatenated_span,
reason: InvalidIdentReason::new(symbol),
}));
}
tscx.psess.symbol_gallery.insert(symbol, concatenated_span);

// The current implementation marks the span as coming from the macro regardless of
// contexts of the concatenated identifiers but this behavior may change in the
// future.
Ok(TokenTree::Token(
Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
Spacing::Alone,
))
Ok((symbol, concatenated_span))
}

/// Store the metavariable span for this original span into a side table.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ symbols! {
complex,
concat,
concat_bytes,
concat_str,
conservative_impl_trait,
console,
const_allocate,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
macro_rules! join {
($lhs:ident, $rhs:ident) => {
let ${concat($lhs, $rhs)}: () = ();
let ${concat($lhs, $rhs)}: &'static str = ${concat_str($lhs, $rhs)};
//~^ ERROR the `concat` meta-variable expression is unstable
//~| ERROR the `concat_str` meta-variable expression is unstable
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
error[E0658]: the `concat` meta-variable expression is unstable
--> $DIR/feature-gate-macro-metavar-expr-concat.rs:3:14
|
LL | let ${concat($lhs, $rhs)}: () = ();
LL | let ${concat($lhs, $rhs)}: &'static str = ${concat_str($lhs, $rhs)};
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #124225 <https://github.com/rust-lang/rust/issues/124225> for more information
= help: add `#![feature(macro_metavar_expr_concat)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error: aborting due to 1 previous error
error[E0658]: the `concat_str` meta-variable expression is unstable
--> $DIR/feature-gate-macro-metavar-expr-concat.rs:3:52
|
LL | let ${concat($lhs, $rhs)}: &'static str = ${concat_str($lhs, $rhs)};
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #124225 <https://github.com/rust-lang/rust/issues/124225> for more information
= help: add `#![feature(macro_metavar_expr_concat)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0658`.
51 changes: 49 additions & 2 deletions tests/ui/macros/metavar-expressions/concat-allowed-operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

macro_rules! create_things {
($lhs:ident) => {
struct ${concat($lhs, _separated_idents_in_a_struct)} {
struct ${concat($lhs, _separated_idents_in_a_struct)} {
foo: i32,
${concat($lhs, _separated_idents_in_a_field)}: i32,
}
Expand All @@ -20,80 +20,127 @@ macro_rules! create_things {

macro_rules! many_idents {
($a:ident, $c:ident) => {
#[allow(dead_code, reason = ${concat_str($a, B, $c, D)})]
const ${concat($a, B, $c, D)}: i32 = 1;
};
}

macro_rules! valid_tts {
($_0:tt, $_1:tt) => {
#[allow(dead_code, reason = ${concat_str($_0, $_1)})]
const ${concat($_0, $_1)}: i32 = 1;
}
}

macro_rules! without_dollar_sign_is_an_ident {
($ident:ident) => {
#[allow(dead_code, reason = ${concat_str(VAR, ident)})]
const ${concat(VAR, ident)}: i32 = 1;
#[allow(dead_code, reason = ${concat_str(VAR, $ident)})]
const ${concat(VAR, $ident)}: i32 = 2;
};
}

macro_rules! combinations {
($ident:ident, $literal:literal, $tt_ident:tt, $tt_literal:tt) => {{
// tt ident
#[allow(dead_code, reason = ${concat_str($tt_ident, b)})]
let ${concat($tt_ident, b)} = ();
#[allow(dead_code, reason = ${concat_str($tt_ident, _b)})]
let ${concat($tt_ident, _b)} = ();
#[allow(dead_code, reason = ${concat_str($tt_ident, "b")})]
let ${concat($tt_ident, "b")} = ();
#[allow(dead_code, reason = ${concat_str($tt_ident, $tt_ident)})]
let ${concat($tt_ident, $tt_ident)} = ();
#[allow(dead_code, reason = ${concat_str($tt_ident, $tt_literal)})]
let ${concat($tt_ident, $tt_literal)} = ();
#[allow(dead_code, reason = ${concat_str($tt_ident, $ident)})]
let ${concat($tt_ident, $ident)} = ();
#[allow(dead_code, reason = ${concat_str($tt_ident, $ident)})]
let ${concat($tt_ident, $ident)} = ();
let ${concat($tt_ident, $literal)} = ();
// tt literal
#[allow(dead_code, reason = ${concat_str($tt_literal, b)})]
let ${concat($tt_literal, b)} = ();
#[allow(dead_code, reason = ${concat_str($tt_literal, _b)})]
let ${concat($tt_literal, _b)} = ();
#[allow(dead_code, reason = ${concat_str($tt_literal, "b")})]
let ${concat($tt_literal, "b")} = ();
#[allow(dead_code, reason = ${concat_str($tt_literal, $tt_ident)})]
let ${concat($tt_literal, $tt_ident)} = ();
#[allow(dead_code, reason = ${concat_str($tt_literal, $tt_literal)})]
let ${concat($tt_literal, $tt_literal)} = ();
#[allow(dead_code, reason = ${concat_str($tt_literal, $ident)})]
let ${concat($tt_literal, $ident)} = ();
#[allow(dead_code, reason = ${concat_str($tt_literal, $literal)})]
let ${concat($tt_literal, $literal)} = ();

// ident (adhoc)
#[allow(dead_code, reason = ${concat_str(_b, b)})]
let ${concat(_b, b)} = ();
#[allow(dead_code, reason = ${concat_str(_b, _b)})]
let ${concat(_b, _b)} = ();
#[allow(dead_code, reason = ${concat_str(_b, "b")})]
let ${concat(_b, "b")} = ();
#[allow(dead_code, reason = ${concat_str(_b, $tt_ident)})]
let ${concat(_b, $tt_ident)} = ();
#[allow(dead_code, reason = ${concat_str(_b, $tt_literal)})]
let ${concat(_b, $tt_literal)} = ();
#[allow(dead_code, reason = ${concat_str(_b, $ident)})]
let ${concat(_b, $ident)} = ();
#[allow(dead_code, reason = ${concat_str(_b, $literal)})]
let ${concat(_b, $literal)} = ();
// ident (param)
#[allow(dead_code, reason = ${concat_str($ident, b)})]
let ${concat($ident, b)} = ();
#[allow(dead_code, reason = ${concat_str($ident, _b)})]
let ${concat($ident, _b)} = ();
#[allow(dead_code, reason = ${concat_str($ident, "b")})]
let ${concat($ident, "b")} = ();
#[allow(dead_code, reason = ${concat_str($ident, $tt_ident)})]
let ${concat($ident, $tt_ident)} = ();
#[allow(dead_code, reason = ${concat_str($ident, $tt_literal)})]
let ${concat($ident, $tt_literal)} = ();
#[allow(dead_code, reason = ${concat_str($ident, $ident)})]
let ${concat($ident, $ident)} = ();
#[allow(dead_code, reason = ${concat_str($ident, $literal)})]
let ${concat($ident, $literal)} = ();

// literal (adhoc)
#[allow(dead_code, reason = ${concat_str("a", b)})]
let ${concat("a", b)} = ();
#[allow(dead_code, reason = ${concat_str("a", _b)})]
let ${concat("a", _b)} = ();
#[allow(dead_code, reason = ${concat_str("a", "b")})]
let ${concat("a", "b")} = ();
#[allow(dead_code, reason = ${concat_str("a", $tt_ident)})]
let ${concat("a", $tt_ident)} = ();
#[allow(dead_code, reason = ${concat_str("a", $tt_literal)})]
let ${concat("a", $tt_literal)} = ();
#[allow(dead_code, reason = ${concat_str("a", $ident)})]
let ${concat("a", $ident)} = ();
#[allow(dead_code, reason = ${concat_str("a", $literal)})]
let ${concat("a", $literal)} = ();
// literal (param)
#[allow(dead_code, reason = ${concat_str($literal, b)})]
let ${concat($literal, b)} = ();
#[allow(dead_code, reason = ${concat_str($literal, _b)})]
let ${concat($literal, _b)} = ();
#[allow(dead_code, reason = ${concat_str($literal, "b")})]
let ${concat($literal, "b")} = ();
#[allow(dead_code, reason = ${concat_str($literal, $tt_ident)})]
let ${concat($literal, $tt_ident)} = ();
#[allow(dead_code, reason = ${concat_str($literal, $tt_literal)})]
let ${concat($literal, $tt_literal)} = ();
#[allow(dead_code, reason = ${concat_str($literal, $ident)})]
let ${concat($literal, $ident)} = ();
#[allow(dead_code, reason = ${concat_str($literal, $literal)})]
let ${concat($literal, $literal)} = ();
}};
}

macro_rules! int_struct {
($n: literal) => {
#[allow(dead_code, reason = ${concat_str(E, $n)})]
struct ${concat(E, $n)};
}
}
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/macros/metavar-expressions/concat-str-escape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//@ run-pass

#![feature(macro_metavar_expr_concat)]

macro_rules! escape_literal {
($literal:literal, $concatted:literal) => {
let lit = ${concat_str("_", "\"foo", $literal)};
assert_eq!(lit, concat!("_", "\"foo", $literal));
assert_eq!(lit, $concatted);

}
}

fn main(){
escape_literal!("\u{00BD}", "_\"foo\u{00BD}");
escape_literal!("\x41", "_\"foo\x41");
escape_literal!("🤷", "_\"foo🤷");
escape_literal!("\u{1F980}", "_\"foo🦀");
escape_literal!("aaa \"bbb\" ccc", "_\"fooaaa \"bbb\" ccc");
}
Loading
Loading