mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-31 17:12:53 +00:00
Rollup merge of #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 #124225 r? ``@petrochenkov``
This commit is contained in:
commit
e6f0caf197
@ -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
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
@ -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))
|
||||
}
|
||||
|
@ -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 {
|
||||
@ -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)} = ();
|
||||
}};
|
||||
}
|
||||
|
||||
@ -66,5 +111,5 @@ fn main() {
|
||||
assert_eq!(VARident, 1);
|
||||
assert_eq!(VAR_123, 2);
|
||||
|
||||
literals!(_hello);
|
||||
combinations!(_hello, "a", b, "b");
|
||||
}
|
||||
|
@ -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
|
||||
@ -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);
|
||||
|
||||
@ -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');
|
||||
}
|
||||
|
@ -64,11 +64,13 @@ error: expected identifier or string literal
|
||||
LL | let ${concat($ident, 1)}: () = ();
|
||||
| ^
|
||||
|
||||
error: `${concat(..)}` currently only accepts identifiers or meta-variables as parameters
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:22:19
|
||||
|
|
||||
LL | ${concat($ex, aaaa)}
|
||||
| ^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
|
||||
error: variable `foo` is not recognized in meta-variable expression
|
||||
--> $DIR/syntax-errors.rs:35:30
|
||||
@ -131,5 +133,152 @@ LL | empty!();
|
||||
|
|
||||
= note: this error originates in the macro `empty` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 18 previous errors
|
||||
error: `${concat(..)}` is not generating a valid identifier
|
||||
--> $DIR/syntax-errors.rs:103:16
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | bad_literal_string!("\u{00BD}");
|
||||
| ------------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: `${concat(..)}` is not generating a valid identifier
|
||||
--> $DIR/syntax-errors.rs:103:16
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | bad_literal_string!("\x41");
|
||||
| --------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: `${concat(..)}` is not generating a valid identifier
|
||||
--> $DIR/syntax-errors.rs:103:16
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | bad_literal_string!("🤷");
|
||||
| ------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: `${concat(..)}` is not generating a valid identifier
|
||||
--> $DIR/syntax-errors.rs:103:16
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | bad_literal_string!("d[-_-]b");
|
||||
| ------------------------------ in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: `${concat(..)}` is not generating a valid identifier
|
||||
--> $DIR/syntax-errors.rs:103:16
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | bad_literal_string!("-1");
|
||||
| ------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: `${concat(..)}` is not generating a valid identifier
|
||||
--> $DIR/syntax-errors.rs:103:16
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | bad_literal_string!("1.0");
|
||||
| -------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: `${concat(..)}` is not generating a valid identifier
|
||||
--> $DIR/syntax-errors.rs:103:16
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | bad_literal_string!("'1'");
|
||||
| -------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:116:31
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:116:31
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:116:31
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:116:31
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:116:31
|
||||
|
|
||||
LL | const ${concat(_foo, $literal)}: () = ();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:127:31
|
||||
|
|
||||
LL | const ${concat(_foo, $tt)}: () = ();
|
||||
| ^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:127:31
|
||||
|
|
||||
LL | const ${concat(_foo, $tt)}: () = ();
|
||||
| ^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`
|
||||
--> $DIR/syntax-errors.rs:127:31
|
||||
|
|
||||
LL | const ${concat(_foo, $tt)}: () = ();
|
||||
| ^^
|
||||
|
|
||||
= note: currently only string literals are supported
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 33 previous errors
|
||||
|
||||
|
@ -3,12 +3,17 @@
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
|
||||
macro_rules! turn_to_page {
|
||||
($ident:ident) => {
|
||||
($ident:ident, $literal:literal, $tt:tt) => {
|
||||
const ${concat("Ḧ", $ident)}: i32 = 394;
|
||||
const ${concat("Ḧ", $literal)}: i32 = 394;
|
||||
const ${concat("Ḧ", $tt)}: i32 = 394;
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
turn_to_page!(P);
|
||||
assert_eq!(ḦP, 394);
|
||||
turn_to_page!(P1, "Ṕ2", Ṕ);
|
||||
assert_eq!(ḦṔ, 394);
|
||||
assert_eq!(ḦP1, 394);
|
||||
assert_eq!(ḦṔ2, 394);
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user