diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index dbbd948fd707..2964ac8cc585 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -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 diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 9b4dc13c703a..7e2ea8de5fca 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -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, -) -> 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)) } diff --git a/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs b/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs index 1acefa314aab..695a752fe17d 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs +++ b/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs @@ -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"); } diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs index b2845c8d1c1f..7673bd3200ff 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs +++ b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs @@ -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'); } diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr index 2fe5842b39eb..2de6d2b3ce3b 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr +++ b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr @@ -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 diff --git a/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs b/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs index b2cfb211e2d1..4eeb2384deb7 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs +++ b/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs @@ -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); + }