1use rustc_ast::ptr::P;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_ast::{ExprKind, LitIntType, LitKind, StrStyle, UintTy, token};
4use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
5use rustc_session::errors::report_lit_error;
6use rustc_span::{ErrorGuaranteed, Span};
7
8use crate::errors;
9use crate::util::get_exprs_from_tts;
10
11fn invalid_type_err(
13 cx: &ExtCtxt<'_>,
14 token_lit: token::Lit,
15 span: Span,
16 is_nested: bool,
17) -> ErrorGuaranteed {
18 use errors::{
19 ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob,
20 };
21 let snippet = cx.sess.source_map().span_to_snippet(span).ok();
22 let dcx = cx.dcx();
23 match LitKind::from_token_lit(token_lit) {
24 Ok(LitKind::CStr(_, style)) => {
25 let sugg = if let Some(mut as_bstr) = snippet
28 && style == StrStyle::Cooked
29 && as_bstr.starts_with('c')
30 && as_bstr.ends_with('"')
31 {
32 as_bstr.replace_range(0..1, "b");
34 as_bstr.pop();
35 as_bstr.push_str(r#"\0""#);
36 Some(ConcatBytesInvalidSuggestion::CStrLit { span, as_bstr })
37 } else {
38 None
42 };
43 dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "C string", sugg, cs_note: Some(()) })
45 }
46 Ok(LitKind::Char(_)) => {
47 let sugg =
48 snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
49 dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg, cs_note: None })
50 }
51 Ok(LitKind::Str(_, _)) => {
52 let sugg = if !is_nested {
54 snippet.map(|snippet| ConcatBytesInvalidSuggestion::StrLit { span, snippet })
55 } else {
56 None
57 };
58 dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg, cs_note: None })
59 }
60 Ok(LitKind::Float(_, _)) => {
61 dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None, cs_note: None })
62 }
63 Ok(LitKind::Bool(_)) => dcx.emit_err(ConcatBytesInvalid {
64 span,
65 lit_kind: "boolean",
66 sugg: None,
67 cs_note: None,
68 }),
69 Ok(LitKind::Int(_, _)) if !is_nested => {
70 let sugg =
71 snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
72 dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg, cs_note: None })
73 }
74 Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
75 assert!(val.get() > u8::MAX.into()); dcx.emit_err(ConcatBytesOob { span })
77 }
78 Ok(LitKind::Int(_, _)) => dcx.emit_err(ConcatBytesNonU8 { span }),
79 Ok(LitKind::ByteStr(..) | LitKind::Byte(_)) => unreachable!(),
80 Ok(LitKind::Err(guar)) => guar,
81 Err(err) => report_lit_error(&cx.sess.psess, err, token_lit, span),
82 }
83}
84
85fn handle_array_element(
90 cx: &ExtCtxt<'_>,
91 guar: &mut Option<ErrorGuaranteed>,
92 missing_literals: &mut Vec<rustc_span::Span>,
93 expr: &P<rustc_ast::Expr>,
94) -> Option<u8> {
95 let dcx = cx.dcx();
96
97 match expr.kind {
98 ExprKind::Lit(token_lit) => {
99 match LitKind::from_token_lit(token_lit) {
100 Ok(LitKind::Int(
101 val,
102 LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8),
103 )) if let Ok(val) = u8::try_from(val.get()) => {
104 return Some(val);
105 }
106 Ok(LitKind::Byte(val)) => return Some(val),
107 Ok(LitKind::ByteStr(..)) => {
108 guar.get_or_insert_with(|| {
109 dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true })
110 });
111 }
112 _ => {
113 guar.get_or_insert_with(|| invalid_type_err(cx, token_lit, expr.span, true));
114 }
115 };
116 }
117 ExprKind::Array(_) | ExprKind::Repeat(_, _) => {
118 guar.get_or_insert_with(|| {
119 dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false })
120 });
121 }
122 ExprKind::IncludedBytes(..) => {
123 guar.get_or_insert_with(|| {
124 dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false })
125 });
126 }
127 _ => missing_literals.push(expr.span),
128 }
129
130 None
131}
132
133pub(crate) fn expand_concat_bytes(
134 cx: &mut ExtCtxt<'_>,
135 sp: Span,
136 tts: TokenStream,
137) -> MacroExpanderResult<'static> {
138 let ExpandResult::Ready(mac) = get_exprs_from_tts(cx, tts) else {
139 return ExpandResult::Retry(());
140 };
141 let es = match mac {
142 Ok(es) => es,
143 Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
144 };
145 let mut accumulator = Vec::new();
146 let mut missing_literals = vec![];
147 let mut guar = None;
148 for e in es {
149 match &e.kind {
150 ExprKind::Array(exprs) => {
151 for expr in exprs {
152 if let Some(elem) =
153 handle_array_element(cx, &mut guar, &mut missing_literals, expr)
154 {
155 accumulator.push(elem);
156 }
157 }
158 }
159 ExprKind::Repeat(expr, count) => {
160 if let ExprKind::Lit(token_lit) = count.value.kind
161 && let Ok(LitKind::Int(count_val, _)) = LitKind::from_token_lit(token_lit)
162 {
163 if let Some(elem) =
164 handle_array_element(cx, &mut guar, &mut missing_literals, expr)
165 {
166 for _ in 0..count_val.get() {
167 accumulator.push(elem);
168 }
169 }
170 } else {
171 guar = Some(
172 cx.dcx().emit_err(errors::ConcatBytesBadRepeat { span: count.value.span }),
173 );
174 }
175 }
176 &ExprKind::Lit(token_lit) => match LitKind::from_token_lit(token_lit) {
177 Ok(LitKind::Byte(val)) => {
178 accumulator.push(val);
179 }
180 Ok(LitKind::ByteStr(ref bytes, _)) => {
181 accumulator.extend_from_slice(bytes);
182 }
183 _ => {
184 guar.get_or_insert_with(|| invalid_type_err(cx, token_lit, e.span, false));
185 }
186 },
187 ExprKind::IncludedBytes(bytes) => {
188 accumulator.extend_from_slice(bytes);
189 }
190 ExprKind::Err(guarantee) => {
191 guar = Some(*guarantee);
192 }
193 ExprKind::Dummy => cx.dcx().span_bug(e.span, "concatenating `ExprKind::Dummy`"),
194 _ => {
195 missing_literals.push(e.span);
196 }
197 }
198 }
199 ExpandResult::Ready(if !missing_literals.is_empty() {
200 let guar = cx.dcx().emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals });
201 MacEager::expr(DummyResult::raw_expr(sp, Some(guar)))
202 } else if let Some(guar) = guar {
203 MacEager::expr(DummyResult::raw_expr(sp, Some(guar)))
204 } else {
205 let sp = cx.with_def_site_ctxt(sp);
206 MacEager::expr(cx.expr_byte_str(sp, accumulator))
207 })
208}