Skip to content

Commit

Permalink
Rollup merge of rust-lang#127542 - c410-f3r:concat-again, r=petrochenkov
Browse files Browse the repository at this point in the history
[`macro_metavar_expr_concat`] Add support for literals

Adds support for literals in macro parameters.

```rust
macro_rules! with_literal {
    ($literal:literal) => {
        const ${concat(FOO, $literal)}: i32 = 1;
    }
}

fn main() {
    with_literal!("_BAR");
    assert_eq!(FOO_BAR, 1);
}
```

cc rust-lang#124225

r? `@petrochenkov`
  • Loading branch information
matthiaskrgr authored Jul 17, 2024
2 parents f00f850 + 553279b commit 2b60845
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 38 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_expand/src/mbe/metavar_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ impl MetaVarExpr {
}
}

/// Indicates what is placed in a `concat` parameter. For example, literals
/// (`${concat("foo", "bar")}`) or adhoc identifiers (`${concat(foo, bar)}`).
#[derive(Debug, Decodable, Encodable, PartialEq)]
pub(crate) enum MetaVarExprConcatElem {
/// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be
Expand Down
59 changes: 35 additions & 24 deletions compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use crate::mbe::macro_parser::{NamedMatch, NamedMatch::*};
use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR};
use crate::mbe::{self, KleeneOp, MetaVarExpr};
use rustc_ast::mut_visit::{self, MutVisitor};
use rustc_ast::token::IdentIsRaw;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind};
use rustc_ast::token::{IdentIsRaw, Lit, LitKind};
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_ast::ExprKind;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult};
use rustc_parse::lexer::nfc_normalize;
Expand All @@ -17,7 +18,7 @@ use rustc_session::parse::ParseSess;
use rustc_session::parse::SymbolGallery;
use rustc_span::hygiene::{LocalExpnId, Transparency};
use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
use rustc_span::{with_metavar_spans, Span, SyntaxContext};
use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext};
use smallvec::{smallvec, SmallVec};
use std::mem;

Expand Down Expand Up @@ -691,12 +692,12 @@ fn transcribe_metavar_expr<'a>(
MetaVarExpr::Concat(ref elements) => {
let mut concatenated = String::new();
for element in elements.into_iter() {
let string = match element {
MetaVarExprConcatElem::Ident(elem) => elem.to_string(),
MetaVarExprConcatElem::Literal(elem) => elem.as_str().into(),
MetaVarExprConcatElem::Var(elem) => extract_ident(dcx, *elem, interp)?,
let symbol = match element {
MetaVarExprConcatElem::Ident(elem) => elem.name,
MetaVarExprConcatElem::Literal(elem) => *elem,
MetaVarExprConcatElem::Var(elem) => extract_var_symbol(dcx, *elem, interp)?,
};
concatenated.push_str(&string);
concatenated.push_str(symbol.as_str());
}
let symbol = nfc_normalize(&concatenated);
let concatenated_span = visited_span();
Expand Down Expand Up @@ -750,32 +751,42 @@ fn transcribe_metavar_expr<'a>(
Ok(())
}

/// Extracts an identifier that can be originated from a `$var:ident` variable or from a token tree.
fn extract_ident<'a>(
/// Extracts an metavariable symbol that can be an identifier, a token tree or a literal.
fn extract_var_symbol<'a>(
dcx: DiagCtxtHandle<'a>,
ident: Ident,
interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
) -> PResult<'a, String> {
) -> PResult<'a, Symbol> {
if let NamedMatch::MatchedSingle(pnr) = matched_from_ident(dcx, ident, interp)? {
if let ParseNtResult::Ident(nt_ident, is_raw) = pnr {
if let IdentIsRaw::Yes = is_raw {
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
}
return Ok(nt_ident.to_string());
return Ok(nt_ident.name);
}
if let ParseNtResult::Tt(TokenTree::Token(
Token { kind: TokenKind::Ident(token_ident, is_raw), .. },
_,
)) = pnr
{
if let IdentIsRaw::Yes = is_raw {
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));

if let ParseNtResult::Tt(TokenTree::Token(Token { kind, .. }, _)) = pnr {
if let TokenKind::Ident(symbol, is_raw) = kind {
if let IdentIsRaw::Yes = is_raw {
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
}
return Ok(*symbol);
}
return Ok(token_ident.to_string());

if let TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }) = kind {
return Ok(*symbol);
}
}

if let ParseNtResult::Nt(nt) = pnr
&& let Nonterminal::NtLiteral(expr) = &**nt
&& let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = &expr.kind
{
return Ok(*symbol);
}
}
Err(dcx.struct_span_err(
ident.span,
"`${concat(..)}` currently only accepts identifiers or meta-variables as parameters",
))
Err(dcx
.struct_err("metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`")
.with_note("currently only string literals are supported")
.with_span(ident.span))
}
61 changes: 53 additions & 8 deletions tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ run-pass

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

macro_rules! create_things {
Expand Down Expand Up @@ -37,13 +37,58 @@ macro_rules! without_dollar_sign_is_an_ident {
};
}

macro_rules! literals {
($ident:ident) => {{
let ${concat(_a, "_b")}: () = ();
let ${concat("_b", _a)}: () = ();
macro_rules! combinations {
($ident:ident, $literal:literal, $tt_ident:tt, $tt_literal:tt) => {{
// tt ident
let ${concat($tt_ident, b)} = ();
let ${concat($tt_ident, _b)} = ();
let ${concat($tt_ident, "b")} = ();
let ${concat($tt_ident, $tt_ident)} = ();
let ${concat($tt_ident, $tt_literal)} = ();
let ${concat($tt_ident, $ident)} = ();
let ${concat($tt_ident, $literal)} = ();
// tt literal
let ${concat($tt_literal, b)} = ();
let ${concat($tt_literal, _b)} = ();
let ${concat($tt_literal, "b")} = ();
let ${concat($tt_literal, $tt_ident)} = ();
let ${concat($tt_literal, $tt_literal)} = ();
let ${concat($tt_literal, $ident)} = ();
let ${concat($tt_literal, $literal)} = ();

let ${concat($ident, "_b")}: () = ();
let ${concat("_b", $ident)}: () = ();
// ident (adhoc)
let ${concat(_b, b)} = ();
let ${concat(_b, _b)} = ();
let ${concat(_b, "b")} = ();
let ${concat(_b, $tt_ident)} = ();
let ${concat(_b, $tt_literal)} = ();
let ${concat(_b, $ident)} = ();
let ${concat(_b, $literal)} = ();
// ident (param)
let ${concat($ident, b)} = ();
let ${concat($ident, _b)} = ();
let ${concat($ident, "b")} = ();
let ${concat($ident, $tt_ident)} = ();
let ${concat($ident, $tt_literal)} = ();
let ${concat($ident, $ident)} = ();
let ${concat($ident, $literal)} = ();

// literal (adhoc)
let ${concat("a", b)} = ();
let ${concat("a", _b)} = ();
let ${concat("a", "b")} = ();
let ${concat("a", $tt_ident)} = ();
let ${concat("a", $tt_literal)} = ();
let ${concat("a", $ident)} = ();
let ${concat("a", $literal)} = ();
// literal (param)
let ${concat($literal, b)} = ();
let ${concat($literal, _b)} = ();
let ${concat($literal, "b")} = ();
let ${concat($literal, $tt_ident)} = ();
let ${concat($literal, $tt_literal)} = ();
let ${concat($literal, $ident)} = ();
let ${concat($literal, $literal)} = ();
}};
}

Expand All @@ -66,5 +111,5 @@ fn main() {
assert_eq!(VARident, 1);
assert_eq!(VAR_123, 2);

literals!(_hello);
combinations!(_hello, "a", b, "b");
}
54 changes: 53 additions & 1 deletion tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ macro_rules! wrong_concat_declarations {
//~^ ERROR `concat` must have at least two elements

${concat($ex, aaaa)}
//~^ ERROR `${concat(..)}` currently only accepts identifiers
//~^ ERROR metavariables of `${concat(..)}` must be of type

${concat($ex, aaaa 123)}
//~^ ERROR expected comma
Expand Down Expand Up @@ -98,6 +98,39 @@ macro_rules! unsupported_literals {
}};
}

macro_rules! bad_literal_string {
($literal:literal) => {
const ${concat(_foo, $literal)}: () = ();
//~^ ERROR `${concat(..)}` is not generating a valid identifier
//~| ERROR `${concat(..)}` is not generating a valid identifier
//~| ERROR `${concat(..)}` is not generating a valid identifier
//~| ERROR `${concat(..)}` is not generating a valid identifier
//~| ERROR `${concat(..)}` is not generating a valid identifier
//~| ERROR `${concat(..)}` is not generating a valid identifier
//~| ERROR `${concat(..)}` is not generating a valid identifier
}
}

macro_rules! bad_literal_non_string {
($literal:literal) => {
const ${concat(_foo, $literal)}: () = ();
//~^ ERROR metavariables of `${concat(..)}` must be of type
//~| ERROR metavariables of `${concat(..)}` must be of type
//~| ERROR metavariables of `${concat(..)}` must be of type
//~| ERROR metavariables of `${concat(..)}` must be of type
//~| ERROR metavariables of `${concat(..)}` must be of type
}
}

macro_rules! bad_tt_literal {
($tt:tt) => {
const ${concat(_foo, $tt)}: () = ();
//~^ ERROR metavariables of `${concat(..)}` must be of type
//~| ERROR metavariables of `${concat(..)}` must be of type
//~| ERROR metavariables of `${concat(..)}` must be of type
}
}

fn main() {
wrong_concat_declarations!(1);

Expand All @@ -113,4 +146,23 @@ fn main() {
unsupported_literals!(_abc);

empty!();

bad_literal_string!("\u{00BD}");
bad_literal_string!("\x41");
bad_literal_string!("🤷");
bad_literal_string!("d[-_-]b");

bad_literal_string!("-1");
bad_literal_string!("1.0");
bad_literal_string!("'1'");

bad_literal_non_string!(1);
bad_literal_non_string!(-1);
bad_literal_non_string!(1.0);
bad_literal_non_string!('1');
bad_literal_non_string!(false);

bad_tt_literal!(1);
bad_tt_literal!(1.0);
bad_tt_literal!('1');
}
Loading

0 comments on commit 2b60845

Please sign in to comment.