2 use rustc_ast::{ptr::P, tokenstream::TokenStream};
3 use rustc_errors::Applicability;
4 use rustc_expand::base::{self, DummyResult};
6 /// Emits errors for literal expressions that are invalid inside and outside of an array.
7 fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_nested: bool) {
8 let ast::ExprKind::Lit(lit) = &expr.kind else {
12 ast::LitKind::Char(_) => {
13 let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals");
14 if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
17 "try using a byte character",
18 format!("b{}", snippet),
19 Applicability::MachineApplicable,
24 ast::LitKind::Str(_, _) => {
25 let mut err = cx.struct_span_err(expr.span, "cannot concatenate string literals");
26 // suggestion would be invalid if we are nested
28 if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
31 "try using a byte string",
32 format!("b{}", snippet),
33 Applicability::MachineApplicable,
39 ast::LitKind::Float(_, _) => {
40 cx.span_err(expr.span, "cannot concatenate float literals");
42 ast::LitKind::Bool(_) => {
43 cx.span_err(expr.span, "cannot concatenate boolean literals");
45 ast::LitKind::Err(_) => {}
46 ast::LitKind::Int(_, _) if !is_nested => {
47 let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals");
48 if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
51 "try wrapping the number in an array",
52 format!("[{}]", snippet),
53 Applicability::MachineApplicable,
60 ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
62 assert!(val > u8::MAX.into()); // must be an error
63 cx.span_err(expr.span, "numeric literal is out of bounds");
65 ast::LitKind::Int(_, _) => {
66 cx.span_err(expr.span, "numeric literal is not a `u8`");
72 fn handle_array_element(
73 cx: &mut base::ExtCtxt<'_>,
74 has_errors: &mut bool,
75 missing_literals: &mut Vec<rustc_span::Span>,
76 expr: &P<rustc_ast::Expr>,
79 ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => {
81 cx.span_err(expr.span, "cannot concatenate doubly nested array");
86 ast::ExprKind::Lit(ref lit) => match lit.kind {
89 ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
90 ) if val <= u8::MAX.into() => Some(val as u8),
92 ast::LitKind::Byte(val) => Some(val),
93 ast::LitKind::ByteStr(_) => {
95 cx.struct_span_err(expr.span, "cannot concatenate doubly nested array")
96 .note("byte strings are treated as arrays of bytes")
97 .help("try flattening the array")
105 invalid_type_err(cx, expr, true);
112 missing_literals.push(expr.span);
118 pub fn expand_concat_bytes(
119 cx: &mut base::ExtCtxt<'_>,
120 sp: rustc_span::Span,
122 ) -> Box<dyn base::MacResult + 'static> {
123 let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
124 return DummyResult::any(sp);
126 let mut accumulator = Vec::new();
127 let mut missing_literals = vec![];
128 let mut has_errors = false;
131 ast::ExprKind::Array(ref exprs) => {
134 handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
136 accumulator.push(elem);
140 ast::ExprKind::Repeat(ref expr, ref count) => {
141 if let ast::ExprKind::Lit(ast::Lit {
142 kind: ast::LitKind::Int(count_val, _), ..
143 }) = count.value.kind
146 handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
148 for _ in 0..count_val {
149 accumulator.push(elem);
153 cx.span_err(count.value.span, "repeat count is not a positive number");
156 ast::ExprKind::Lit(ref lit) => match lit.kind {
157 ast::LitKind::Byte(val) => {
158 accumulator.push(val);
160 ast::LitKind::ByteStr(ref bytes) => {
161 accumulator.extend_from_slice(&bytes);
165 invalid_type_err(cx, &e, false);
170 ast::ExprKind::Err => {
174 missing_literals.push(e.span);
178 if !missing_literals.is_empty() {
179 let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal");
180 err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
182 return base::MacEager::expr(DummyResult::raw_expr(sp, true));
183 } else if has_errors {
184 return base::MacEager::expr(DummyResult::raw_expr(sp, true));
186 let sp = cx.with_def_site_ctxt(sp);
187 base::MacEager::expr(cx.expr_byte_str(sp, accumulator))