From 5e8fac52b795edbef54f188832f79d91172d8718 Mon Sep 17 00:00:00 2001 From: Caio Date: Thu, 14 Dec 2023 21:09:24 -0300 Subject: [PATCH] [RFC-3086] Add a new concat metavar expr --- compiler/rustc_expand/src/mbe/metavar_expr.rs | 27 ++++++++++- compiler/rustc_expand/src/mbe/transcribe.rs | 27 ++++++++++- .../ui/macros/rfc-3086-metavar-expr/concat.rs | 47 +++++++++++++++++++ .../rfc-3086-metavar-expr/syntax-errors.rs | 13 +++++ .../syntax-errors.stderr | 22 ++++++++- 5 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 tests/ui/macros/rfc-3086-metavar-expr/concat.rs diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 4b8c6feb93eb2..7c0cca1f53cef 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -10,6 +10,10 @@ use rustc_span::Span; /// A meta-variable expression, for expansions based on properties of meta-variables. #[derive(Debug, Clone, PartialEq, Encodable, Decodable)] pub(crate) enum MetaVarExpr { + /// Unification of two identifiers. The `bool` of each element indicates if there is a + /// preceding dollar sign. + Concat((Ident, bool), (Ident, bool)), + /// The number of repetitions of an identifier. Count(Ident, usize), @@ -41,6 +45,16 @@ impl MetaVarExpr { check_trailing_token(&mut tts, sess)?; let mut iter = args.trees(); let rslt = match ident.as_str() { + "concat" => { + let lhs_sign = try_eat_dollar(&mut iter); + let lhs = parse_ident(&mut iter, sess, ident.span)?; + if !try_eat_comma(&mut iter) { + return Err(sess.span_diagnostic.struct_span_err(ident.span, "expected comma")); + } + let rhs_sign = try_eat_dollar(&mut iter); + let rhs = parse_ident(&mut iter, sess, ident.span)?; + MetaVarExpr::Concat((lhs, lhs_sign), (rhs, rhs_sign)) + } "count" => parse_count(&mut iter, sess, ident.span)?, "ignore" => { eat_dollar(&mut iter, sess, ident.span)?; @@ -67,7 +81,7 @@ impl MetaVarExpr { pub(crate) fn ident(&self) -> Option { match *self { MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident), - MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None, + MetaVarExpr::Concat(..) | MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None, } } } @@ -170,6 +184,17 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { false } +/// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the +/// iterator is not modified and the result is `false`. +fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool { + if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) + { + let _ = iter.next(); + return true; + } + false +} + /// Expects that the next item is a dollar sign. fn eat_dollar<'sess>( iter: &mut RefTokenTreeCursor<'_>, diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 80fd82e030289..e7d339b821857 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -6,14 +6,14 @@ use crate::errors::{ use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch}; use crate::mbe::{self, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, PResult}; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -558,6 +558,29 @@ fn transcribe_metavar_expr<'a>( span }; match *expr { + MetaVarExpr::Concat((lhs, lhs_sign), (rhs, rhs_sign)) => { + let string = |ident: Ident, has_sign: bool| { + if !has_sign { + return ident.to_string(); + } + let mrni = MacroRulesNormalizedIdent::new(ident); + if let Some(nm) = lookup_cur_matched(mrni, interp, &repeats) + && let MatchedNonterminal(nt) = nm + && let Nonterminal::NtIdent(nt_ident, _) = &nt.0 + { + nt_ident.to_string() + } else { + ident.to_string() + } + }; + let symbol_span = lhs.span.to(rhs.span); + let mut symbol_string = string(lhs, lhs_sign); + symbol_string.push_str(&string(rhs, rhs_sign)); + result.push(TokenTree::Token( + Token::from_ast_ident(Ident::new(Symbol::intern(&symbol_string), symbol_span)), + Spacing::Alone, + )); + } MetaVarExpr::Count(original_ident, depth) => { let matched = matched_from_ident(cx, original_ident, interp)?; let count = count_repetitions(cx, depth, matched, repeats, sp)?; diff --git a/tests/ui/macros/rfc-3086-metavar-expr/concat.rs b/tests/ui/macros/rfc-3086-metavar-expr/concat.rs new file mode 100644 index 0000000000000..7739f309cc5b9 --- /dev/null +++ b/tests/ui/macros/rfc-3086-metavar-expr/concat.rs @@ -0,0 +1,47 @@ +// run-pass + +#![allow(dead_code, non_camel_case_types, non_upper_case_globals)] +#![feature(macro_metavar_expr)] + +macro_rules! simple_ident { + ( $lhs:ident, $rhs:ident ) => { ${concat($lhs, $rhs)} }; +} + +macro_rules! create_things { + ( $lhs:ident ) => { + struct ${concat($lhs, _separated_idents_in_a_struct)} { + foo: i32, + ${concat($lhs, _separated_idents_in_a_field)}: i32, + } + + mod ${concat($lhs, _separated_idents_in_a_module)} { + pub const FOO: () = (); + } + + fn ${concat($lhs, _separated_idents_in_a_fn)}() {} + }; +} + +macro_rules! without_dollar_sign_is_an_ident { + ( $ident:ident ) => { + const ${concat(VAR, ident)}: i32 = 1; + const ${concat(VAR, $ident)}: i32 = 2; + }; +} + +fn main() { + let abcdef = 1; + let _another = simple_ident!(abc, def); + + create_things!(behold); + behold_separated_idents_in_a_fn(); + let _ = behold_separated_idents_in_a_module::FOO; + let _ = behold_separated_idents_in_a_struct { + foo: 1, + behold_separated_idents_in_a_field: 2, + }; + + without_dollar_sign_is_an_ident!(_123); + assert_eq!(VARident, 1); + assert_eq!(VAR_123, 2); +} diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs index 05c65fe869073..cdbbf73c750d4 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs +++ b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs @@ -137,6 +137,19 @@ macro_rules! unknown_metavar { //~| ERROR expected expression } +macro_rules! wrong_concat_declarations { + () => { + ${concat()} + //~^ ERROR expected identifier + + ${concat(aaaa)} + //~^ ERROR expected comma + + ${concat(aaaa,)} + //~^ ERROR expected identifier + }; +} + fn main() { curly__no_rhs_dollar__round!(a, b, c); curly__no_rhs_dollar__no_round!(a); diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr index 0dda38290ab6b..60b23c3f0732c 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr +++ b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr @@ -196,6 +196,24 @@ error: unrecognized meta-variable expression LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and length +error: expected identifier + --> $DIR/syntax-errors.rs:142:11 + | +LL | ${concat()} + | ^^^^^^ + +error: expected comma + --> $DIR/syntax-errors.rs:145:11 + | +LL | ${concat(aaaa)} + | ^^^^^^ + +error: expected identifier + --> $DIR/syntax-errors.rs:148:11 + | +LL | ${concat(aaaa,)} + | ^^^^^^ + error: `count` can not be placed inside the inner-most repetition --> $DIR/syntax-errors.rs:12:24 | @@ -336,7 +354,7 @@ LL | no_curly__no_rhs_dollar__no_round!(a); = note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `a` in this scope - --> $DIR/syntax-errors.rs:147:37 + --> $DIR/syntax-errors.rs:160:37 | LL | no_curly__rhs_dollar__no_round!(a); | ^ not found in this scope @@ -374,6 +392,6 @@ LL | no_curly__rhs_dollar__no_round!(a); | = note: this error originates in the macro `no_curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 39 previous errors +error: aborting due to 42 previous errors For more information about this error, try `rustc --explain E0425`.