rustc_builtin_macros/
concat.rs

1use rustc_ast::tokenstream::TokenStream;
2use rustc_ast::{ExprKind, LitKind, UnOp};
3use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
4use rustc_session::errors::report_lit_error;
5use rustc_span::Symbol;
6
7use crate::errors;
8use crate::util::get_exprs_from_tts;
9
10pub(crate) fn expand_concat(
11    cx: &mut ExtCtxt<'_>,
12    sp: rustc_span::Span,
13    tts: TokenStream,
14) -> MacroExpanderResult<'static> {
15    let ExpandResult::Ready(mac) = get_exprs_from_tts(cx, tts) else {
16        return ExpandResult::Retry(());
17    };
18    let es = match mac {
19        Ok(es) => es,
20        Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
21    };
22    let mut accumulator = String::new();
23    let mut missing_literal = vec![];
24    let mut guar = None;
25    for e in es {
26        match e.kind {
27            ExprKind::Lit(token_lit) => match LitKind::from_token_lit(token_lit) {
28                Ok(LitKind::Str(s, _) | LitKind::Float(s, _)) => {
29                    accumulator.push_str(s.as_str());
30                }
31                Ok(LitKind::Char(c)) => {
32                    accumulator.push(c);
33                }
34                Ok(LitKind::Int(i, _)) => {
35                    accumulator.push_str(&i.to_string());
36                }
37                Ok(LitKind::Bool(b)) => {
38                    accumulator.push_str(&b.to_string());
39                }
40                Ok(LitKind::CStr(..)) => {
41                    guar = Some(cx.dcx().emit_err(errors::ConcatCStrLit { span: e.span }));
42                }
43                Ok(LitKind::Byte(..) | LitKind::ByteStr(..)) => {
44                    guar = Some(cx.dcx().emit_err(errors::ConcatBytestr { span: e.span }));
45                }
46                Ok(LitKind::Err(guarantee)) => {
47                    guar = Some(guarantee);
48                }
49                Err(err) => {
50                    guar = Some(report_lit_error(&cx.sess.psess, err, token_lit, e.span));
51                }
52            },
53            // We also want to allow negative numeric literals.
54            ExprKind::Unary(UnOp::Neg, ref expr) if let ExprKind::Lit(token_lit) = expr.kind => {
55                match LitKind::from_token_lit(token_lit) {
56                    Ok(LitKind::Int(i, _)) => accumulator.push_str(&format!("-{i}")),
57                    Ok(LitKind::Float(f, _)) => accumulator.push_str(&format!("-{f}")),
58                    Err(err) => {
59                        guar = Some(report_lit_error(&cx.sess.psess, err, token_lit, e.span));
60                    }
61                    _ => missing_literal.push(e.span),
62                }
63            }
64            ExprKind::IncludedBytes(..) => {
65                cx.dcx().emit_err(errors::ConcatBytestr { span: e.span });
66            }
67            ExprKind::Err(guarantee) => {
68                guar = Some(guarantee);
69            }
70            ExprKind::Dummy => cx.dcx().span_bug(e.span, "concatenating `ExprKind::Dummy`"),
71            _ => {
72                missing_literal.push(e.span);
73            }
74        }
75    }
76
77    ExpandResult::Ready(if !missing_literal.is_empty() {
78        let guar = cx.dcx().emit_err(errors::ConcatMissingLiteral { spans: missing_literal });
79        DummyResult::any(sp, guar)
80    } else if let Some(guar) = guar {
81        DummyResult::any(sp, guar)
82    } else {
83        let sp = cx.with_def_site_ctxt(sp);
84        MacEager::expr(cx.expr_str(sp, Symbol::intern(&accumulator)))
85    })
86}