Skip to content

Commit

Permalink
Add support for literals
Browse files Browse the repository at this point in the history
  • Loading branch information
c410-f3r committed Jun 22, 2024
1 parent a0f01c3 commit 1a52f42
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 42 deletions.
55 changes: 40 additions & 15 deletions compiler/rustc_expand/src/mbe/metavar_expr.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use rustc_ast::token::{self, Delimiter, IdentIsRaw};
use rustc_ast::token::{self, Delimiter, IdentIsRaw, Token};
use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
use rustc_ast::{LitIntType, LitKind};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, PResult};
use rustc_macros::{Decodable, Encodable};
use rustc_session::parse::ParseSess;
use rustc_span::symbol::Ident;
use rustc_span::Span;
use rustc_span::{Span, Symbol};

pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";

Expand Down Expand Up @@ -51,11 +51,18 @@ impl MetaVarExpr {
let mut result = Vec::new();
loop {
let is_var = try_eat_dollar(&mut iter);
let element_ident = parse_ident(&mut iter, psess, outer_span)?;
let token = parse_token(&mut iter, psess, outer_span)?;
let element = if is_var {
MetaVarExprConcatElem::Var(element_ident)
MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?)
} else if let token::TokenKind::Literal(token::Lit {
kind: token::LitKind::Char | token::LitKind::Integer | token::LitKind::Str,
symbol,
suffix: None,
}) = token.kind
{
MetaVarExprConcatElem::Literal(symbol)
} else {
MetaVarExprConcatElem::Ident(element_ident)
MetaVarExprConcatElem::Ident(parse_ident_from_token(psess, token)?)
};
result.push(element);
if iter.look_ahead(0).is_none() {
Expand Down Expand Up @@ -105,11 +112,13 @@ impl MetaVarExpr {

#[derive(Debug, Decodable, Encodable, PartialEq)]
pub(crate) enum MetaVarExprConcatElem {
/// There is NO preceding dollar sign, which means that this identifier should be interpreted
/// as a literal.
/// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be
/// interpreted as a literal.
Ident(Ident),
/// There is a preceding dollar sign, which means that this identifier should be expanded
/// and interpreted as a variable.
/// For example, a number or a string.
Literal(Symbol),
/// Identifier WITH a preceding dollar sign, which means that this identifier should be
/// expanded and interpreted as a variable.
Var(Ident),
}

Expand Down Expand Up @@ -180,12 +189,14 @@ fn parse_ident<'psess>(
psess: &'psess ParseSess,
fallback_span: Span,
) -> PResult<'psess, Ident> {
let Some(tt) = iter.next() else {
return Err(psess.dcx().struct_span_err(fallback_span, "expected identifier"));
};
let TokenTree::Token(token, _) = tt else {
return Err(psess.dcx().struct_span_err(tt.span(), "expected identifier"));
};
let token = parse_token(iter, psess, fallback_span)?;
parse_ident_from_token(psess, token)
}

fn parse_ident_from_token<'psess>(
psess: &'psess ParseSess,
token: &Token,
) -> PResult<'psess, Ident> {
if let Some((elem, is_raw)) = token.ident() {
if let IdentIsRaw::Yes = is_raw {
return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR));
Expand All @@ -205,6 +216,20 @@ fn parse_ident<'psess>(
Err(err)
}

fn parse_token<'psess, 't>(
iter: &mut RefTokenTreeCursor<'t>,
psess: &'psess ParseSess,
fallback_span: Span,
) -> PResult<'psess, &'t Token> {
let Some(tt) = iter.next() else {
return Err(psess.dcx().struct_span_err(fallback_span, "expected identifier"));
};
let TokenTree::Token(token, _) = tt else {
return Err(psess.dcx().struct_span_err(tt.span(), "expected identifier"));
};
Ok(token)
}

/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
/// iterator is not modified and the result is `false`.
fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,8 +680,9 @@ fn transcribe_metavar_expr<'a>(
let mut concatenated = String::new();
for element in elements.into_iter() {
let string = match element {
MetaVarExprConcatElem::Ident(ident) => ident.to_string(),
MetaVarExprConcatElem::Var(ident) => extract_ident(dcx, *ident, interp)?,
MetaVarExprConcatElem::Ident(elem) => elem.to_string(),
MetaVarExprConcatElem::Literal(elem) => elem.as_str().into(),
MetaVarExprConcatElem::Var(elem) => extract_ident(dcx, *elem, interp)?,
};
concatenated.push_str(&string);
}
Expand Down Expand Up @@ -751,6 +752,11 @@ fn extract_ident<'a>(
}
return Ok(token_ident.to_string());
}
if let ParseNtResult::Tt(TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _)) =
pnr
{
return Ok(lit.symbol.to_ident_string());
}
}
Err(dcx.struct_span_err(
ident.span,
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ macro_rules! without_dollar_sign_is_an_ident {
};
}

macro_rules! literals {
($var:ident) => {{
let ${concat(_a, 'b')}: () = ();
let ${concat(_a, 1)}: () = ();
let ${concat(_a, "b")}: () = ();

let ${concat($var, 'b')}: () = ();
let ${concat($var, 1)}: () = ();
let ${concat($var, "b")}: () = ();
}};
}

fn main() {
create_things!(behold);
behold_separated_idents_in_a_fn();
Expand All @@ -55,4 +67,6 @@ fn main() {
without_dollar_sign_is_an_ident!(_123);
assert_eq!(VARident, 1);
assert_eq!(VAR_123, 2);

literals!(_hello);
}
6 changes: 0 additions & 6 deletions tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ macro_rules! wrong_concat_declarations {
${concat(aaaa,)}
//~^ ERROR expected identifier

${concat(aaaa, 1)}
//~^ ERROR expected identifier

${concat(_, aaaa)}

${concat(aaaa aaaa)}
Expand All @@ -30,9 +27,6 @@ macro_rules! wrong_concat_declarations {

${concat($ex, aaaa,)}
//~^ ERROR expected identifier

${concat($ex, aaaa, 123)}
//~^ ERROR expected identifier
};
}

Expand Down
26 changes: 7 additions & 19 deletions tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,41 @@ error: expected identifier
LL | ${concat(aaaa,)}
| ^^^^^^^^^^^^^^^

error: expected identifier, found `1`
--> $DIR/syntax-errors.rs:14:24
|
LL | ${concat(aaaa, 1)}
| ^ help: try removing `1`

error: expected comma
--> $DIR/syntax-errors.rs:19:10
--> $DIR/syntax-errors.rs:16:10
|
LL | ${concat(aaaa aaaa)}
| ^^^^^^^^^^^^^^^^^^^

error: `concat` must have at least two elements
--> $DIR/syntax-errors.rs:22:11
--> $DIR/syntax-errors.rs:19:11
|
LL | ${concat($ex)}
| ^^^^^^

error: expected comma
--> $DIR/syntax-errors.rs:28:10
--> $DIR/syntax-errors.rs:25:10
|
LL | ${concat($ex, aaaa 123)}
| ^^^^^^^^^^^^^^^^^^^^^^^

error: expected identifier
--> $DIR/syntax-errors.rs:31:10
--> $DIR/syntax-errors.rs:28:10
|
LL | ${concat($ex, aaaa,)}
| ^^^^^^^^^^^^^^^^^^^^

error: expected identifier, found `123`
--> $DIR/syntax-errors.rs:34:29
|
LL | ${concat($ex, aaaa, 123)}
| ^^^ help: try removing `123`

error: `${concat(..)}` currently only accepts identifiers or meta-variables as parameters
--> $DIR/syntax-errors.rs:25:19
--> $DIR/syntax-errors.rs:22:19
|
LL | ${concat($ex, aaaa)}
| ^^

error: variable `foo` is not recognized in meta-variable expression
--> $DIR/syntax-errors.rs:41:30
--> $DIR/syntax-errors.rs:35:30
|
LL | const ${concat(FOO, $foo)}: i32 = 2;
| ^^^

error: aborting due to 11 previous errors
error: aborting due to 9 previous errors

0 comments on commit 1a52f42

Please sign in to comment.