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);
111 ast::ExprKind::IncludedBytes(..) => {
113 cx.struct_span_err(expr.span, "cannot concatenate doubly nested array")
114 .note("byte strings are treated as arrays of bytes")
115 .help("try flattening the array")
122 missing_literals.push(expr.span);
128 pub fn expand_concat_bytes(
129 cx: &mut base::ExtCtxt<'_>,
130 sp: rustc_span::Span,
132 ) -> Box<dyn base::MacResult + 'static> {
133 let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
134 return DummyResult::any(sp);
136 let mut accumulator = Vec::new();
137 let mut missing_literals = vec![];
138 let mut has_errors = false;
141 ast::ExprKind::Array(ref exprs) => {
144 handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
146 accumulator.push(elem);
150 ast::ExprKind::Repeat(ref expr, ref count) => {
151 if let ast::ExprKind::Lit(ast::Lit {
152 kind: ast::LitKind::Int(count_val, _), ..
153 }) = count.value.kind
156 handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
158 for _ in 0..count_val {
159 accumulator.push(elem);
163 cx.span_err(count.value.span, "repeat count is not a positive number");
166 ast::ExprKind::Lit(ref lit) => match lit.kind {
167 ast::LitKind::Byte(val) => {
168 accumulator.push(val);
170 ast::LitKind::ByteStr(ref bytes) => {
171 accumulator.extend_from_slice(&bytes);
175 invalid_type_err(cx, &e, false);
180 ast::ExprKind::IncludedBytes(ref bytes) => {
181 accumulator.extend_from_slice(bytes);
183 ast::ExprKind::Err => {
187 missing_literals.push(e.span);
191 if !missing_literals.is_empty() {
192 let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal");
193 err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
195 return base::MacEager::expr(DummyResult::raw_expr(sp, true));
196 } else if has_errors {
197 return base::MacEager::expr(DummyResult::raw_expr(sp, true));
199 let sp = cx.with_def_site_ctxt(sp);
200 base::MacEager::expr(cx.expr_byte_str(sp, accumulator))