Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2b60845

Browse files
authoredJul 17, 2024
Rollup merge of rust-lang#127542 - c410-f3r:concat-again, r=petrochenkov
[`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`
2 parents f00f850 + 553279b commit 2b60845

File tree

6 files changed

+302
-38
lines changed

6 files changed

+302
-38
lines changed
 

‎compiler/rustc_expand/src/mbe/metavar_expr.rs

+2
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ impl MetaVarExpr {
119119
}
120120
}
121121

122+
/// Indicates what is placed in a `concat` parameter. For example, literals
123+
/// (`${concat("foo", "bar")}`) or adhoc identifiers (`${concat(foo, bar)}`).
122124
#[derive(Debug, Decodable, Encodable, PartialEq)]
123125
pub(crate) enum MetaVarExprConcatElem {
124126
/// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be

‎compiler/rustc_expand/src/mbe/transcribe.rs

+35-24
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use crate::mbe::macro_parser::{NamedMatch, NamedMatch::*};
66
use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR};
77
use crate::mbe::{self, KleeneOp, MetaVarExpr};
88
use rustc_ast::mut_visit::{self, MutVisitor};
9-
use rustc_ast::token::IdentIsRaw;
10-
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
9+
use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind};
10+
use rustc_ast::token::{IdentIsRaw, Lit, LitKind};
1111
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
12+
use rustc_ast::ExprKind;
1213
use rustc_data_structures::fx::FxHashMap;
1314
use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult};
1415
use rustc_parse::lexer::nfc_normalize;
@@ -17,7 +18,7 @@ use rustc_session::parse::ParseSess;
1718
use rustc_session::parse::SymbolGallery;
1819
use rustc_span::hygiene::{LocalExpnId, Transparency};
1920
use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
20-
use rustc_span::{with_metavar_spans, Span, SyntaxContext};
21+
use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext};
2122
use smallvec::{smallvec, SmallVec};
2223
use std::mem;
2324

@@ -691,12 +692,12 @@ fn transcribe_metavar_expr<'a>(
691692
MetaVarExpr::Concat(ref elements) => {
692693
let mut concatenated = String::new();
693694
for element in elements.into_iter() {
694-
let string = match element {
695-
MetaVarExprConcatElem::Ident(elem) => elem.to_string(),
696-
MetaVarExprConcatElem::Literal(elem) => elem.as_str().into(),
697-
MetaVarExprConcatElem::Var(elem) => extract_ident(dcx, *elem, interp)?,
695+
let symbol = match element {
696+
MetaVarExprConcatElem::Ident(elem) => elem.name,
697+
MetaVarExprConcatElem::Literal(elem) => *elem,
698+
MetaVarExprConcatElem::Var(elem) => extract_var_symbol(dcx, *elem, interp)?,
698699
};
699-
concatenated.push_str(&string);
700+
concatenated.push_str(symbol.as_str());
700701
}
701702
let symbol = nfc_normalize(&concatenated);
702703
let concatenated_span = visited_span();
@@ -750,32 +751,42 @@ fn transcribe_metavar_expr<'a>(
750751
Ok(())
751752
}
752753

753-
/// Extracts an identifier that can be originated from a `$var:ident` variable or from a token tree.
754-
fn extract_ident<'a>(
754+
/// Extracts an metavariable symbol that can be an identifier, a token tree or a literal.
755+
fn extract_var_symbol<'a>(
755756
dcx: DiagCtxtHandle<'a>,
756757
ident: Ident,
757758
interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
758-
) -> PResult<'a, String> {
759+
) -> PResult<'a, Symbol> {
759760
if let NamedMatch::MatchedSingle(pnr) = matched_from_ident(dcx, ident, interp)? {
760761
if let ParseNtResult::Ident(nt_ident, is_raw) = pnr {
761762
if let IdentIsRaw::Yes = is_raw {
762763
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
763764
}
764-
return Ok(nt_ident.to_string());
765+
return Ok(nt_ident.name);
765766
}
766-
if let ParseNtResult::Tt(TokenTree::Token(
767-
Token { kind: TokenKind::Ident(token_ident, is_raw), .. },
768-
_,
769-
)) = pnr
770-
{
771-
if let IdentIsRaw::Yes = is_raw {
772-
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
767+
768+
if let ParseNtResult::Tt(TokenTree::Token(Token { kind, .. }, _)) = pnr {
769+
if let TokenKind::Ident(symbol, is_raw) = kind {
770+
if let IdentIsRaw::Yes = is_raw {
771+
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
772+
}
773+
return Ok(*symbol);
773774
}
774-
return Ok(token_ident.to_string());
775+
776+
if let TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }) = kind {
777+
return Ok(*symbol);
778+
}
779+
}
780+
781+
if let ParseNtResult::Nt(nt) = pnr
782+
&& let Nonterminal::NtLiteral(expr) = &**nt
783+
&& let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = &expr.kind
784+
{
785+
return Ok(*symbol);
775786
}
776787
}
777-
Err(dcx.struct_span_err(
778-
ident.span,
779-
"`${concat(..)}` currently only accepts identifiers or meta-variables as parameters",
780-
))
788+
Err(dcx
789+
.struct_err("metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`")
790+
.with_note("currently only string literals are supported")
791+
.with_span(ident.span))
781792
}

‎tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs

+53-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-pass
22

3-
#![allow(dead_code, non_camel_case_types, non_upper_case_globals)]
3+
#![allow(dead_code, non_camel_case_types, non_upper_case_globals, unused_variables)]
44
#![feature(macro_metavar_expr_concat)]
55

66
macro_rules! create_things {
@@ -37,13 +37,58 @@ macro_rules! without_dollar_sign_is_an_ident {
3737
};
3838
}
3939

40-
macro_rules! literals {
41-
($ident:ident) => {{
42-
let ${concat(_a, "_b")}: () = ();
43-
let ${concat("_b", _a)}: () = ();
40+
macro_rules! combinations {
41+
($ident:ident, $literal:literal, $tt_ident:tt, $tt_literal:tt) => {{
42+
// tt ident
43+
let ${concat($tt_ident, b)} = ();
44+
let ${concat($tt_ident, _b)} = ();
45+
let ${concat($tt_ident, "b")} = ();
46+
let ${concat($tt_ident, $tt_ident)} = ();
47+
let ${concat($tt_ident, $tt_literal)} = ();
48+
let ${concat($tt_ident, $ident)} = ();
49+
let ${concat($tt_ident, $literal)} = ();
50+
// tt literal
51+
let ${concat($tt_literal, b)} = ();
52+
let ${concat($tt_literal, _b)} = ();
53+
let ${concat($tt_literal, "b")} = ();
54+
let ${concat($tt_literal, $tt_ident)} = ();
55+
let ${concat($tt_literal, $tt_literal)} = ();
56+
let ${concat($tt_literal, $ident)} = ();
57+
let ${concat($tt_literal, $literal)} = ();
4458

45-
let ${concat($ident, "_b")}: () = ();
46-
let ${concat("_b", $ident)}: () = ();
59+
// ident (adhoc)
60+
let ${concat(_b, b)} = ();
61+
let ${concat(_b, _b)} = ();
62+
let ${concat(_b, "b")} = ();
63+
let ${concat(_b, $tt_ident)} = ();
64+
let ${concat(_b, $tt_literal)} = ();
65+
let ${concat(_b, $ident)} = ();
66+
let ${concat(_b, $literal)} = ();
67+
// ident (param)
68+
let ${concat($ident, b)} = ();
69+
let ${concat($ident, _b)} = ();
70+
let ${concat($ident, "b")} = ();
71+
let ${concat($ident, $tt_ident)} = ();
72+
let ${concat($ident, $tt_literal)} = ();
73+
let ${concat($ident, $ident)} = ();
74+
let ${concat($ident, $literal)} = ();
75+
76+
// literal (adhoc)
77+
let ${concat("a", b)} = ();
78+
let ${concat("a", _b)} = ();
79+
let ${concat("a", "b")} = ();
80+
let ${concat("a", $tt_ident)} = ();
81+
let ${concat("a", $tt_literal)} = ();
82+
let ${concat("a", $ident)} = ();
83+
let ${concat("a", $literal)} = ();
84+
// literal (param)
85+
let ${concat($literal, b)} = ();
86+
let ${concat($literal, _b)} = ();
87+
let ${concat($literal, "b")} = ();
88+
let ${concat($literal, $tt_ident)} = ();
89+
let ${concat($literal, $tt_literal)} = ();
90+
let ${concat($literal, $ident)} = ();
91+
let ${concat($literal, $literal)} = ();
4792
}};
4893
}
4994

@@ -66,5 +111,5 @@ fn main() {
66111
assert_eq!(VARident, 1);
67112
assert_eq!(VAR_123, 2);
68113

69-
literals!(_hello);
114+
combinations!(_hello, "a", b, "b");
70115
}

‎tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ macro_rules! wrong_concat_declarations {
2020
//~^ ERROR `concat` must have at least two elements
2121

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

2525
${concat($ex, aaaa 123)}
2626
//~^ ERROR expected comma
@@ -98,6 +98,39 @@ macro_rules! unsupported_literals {
9898
}};
9999
}
100100

101+
macro_rules! bad_literal_string {
102+
($literal:literal) => {
103+
const ${concat(_foo, $literal)}: () = ();
104+
//~^ ERROR `${concat(..)}` is not generating a valid identifier
105+
//~| ERROR `${concat(..)}` is not generating a valid identifier
106+
//~| ERROR `${concat(..)}` is not generating a valid identifier
107+
//~| ERROR `${concat(..)}` is not generating a valid identifier
108+
//~| ERROR `${concat(..)}` is not generating a valid identifier
109+
//~| ERROR `${concat(..)}` is not generating a valid identifier
110+
//~| ERROR `${concat(..)}` is not generating a valid identifier
111+
}
112+
}
113+
114+
macro_rules! bad_literal_non_string {
115+
($literal:literal) => {
116+
const ${concat(_foo, $literal)}: () = ();
117+
//~^ ERROR metavariables of `${concat(..)}` must be of type
118+
//~| ERROR metavariables of `${concat(..)}` must be of type
119+
//~| ERROR metavariables of `${concat(..)}` must be of type
120+
//~| ERROR metavariables of `${concat(..)}` must be of type
121+
//~| ERROR metavariables of `${concat(..)}` must be of type
122+
}
123+
}
124+
125+
macro_rules! bad_tt_literal {
126+
($tt:tt) => {
127+
const ${concat(_foo, $tt)}: () = ();
128+
//~^ ERROR metavariables of `${concat(..)}` must be of type
129+
//~| ERROR metavariables of `${concat(..)}` must be of type
130+
//~| ERROR metavariables of `${concat(..)}` must be of type
131+
}
132+
}
133+
101134
fn main() {
102135
wrong_concat_declarations!(1);
103136

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

115148
empty!();
149+
150+
bad_literal_string!("\u{00BD}");
151+
bad_literal_string!("\x41");
152+
bad_literal_string!("🤷");
153+
bad_literal_string!("d[-_-]b");
154+
155+
bad_literal_string!("-1");
156+
bad_literal_string!("1.0");
157+
bad_literal_string!("'1'");
158+
159+
bad_literal_non_string!(1);
160+
bad_literal_non_string!(-1);
161+
bad_literal_non_string!(1.0);
162+
bad_literal_non_string!('1');
163+
bad_literal_non_string!(false);
164+
165+
bad_tt_literal!(1);
166+
bad_tt_literal!(1.0);
167+
bad_tt_literal!('1');
116168
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.