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