]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/concat_bytes.rs
Rollup merge of #105640 - lukas-code:miri-beta, r=Mark-Simulacrum
[rust.git] / compiler / rustc_builtin_macros / src / concat_bytes.rs
1 use rustc_ast as ast;
2 use rustc_ast::{ptr::P, tokenstream::TokenStream};
3 use rustc_errors::Applicability;
4 use rustc_expand::base::{self, DummyResult};
5 use rustc_session::errors::report_lit_error;
6 use rustc_span::Span;
7
8 /// Emits errors for literal expressions that are invalid inside and outside of an array.
9 fn invalid_type_err(
10     cx: &mut base::ExtCtxt<'_>,
11     token_lit: ast::token::Lit,
12     span: Span,
13     is_nested: bool,
14 ) {
15     match ast::LitKind::from_token_lit(token_lit) {
16         Ok(ast::LitKind::Char(_)) => {
17             let mut err = cx.struct_span_err(span, "cannot concatenate character literals");
18             if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) {
19                 err.span_suggestion(
20                     span,
21                     "try using a byte character",
22                     format!("b{}", snippet),
23                     Applicability::MachineApplicable,
24                 )
25                 .emit();
26             }
27         }
28         Ok(ast::LitKind::Str(_, _)) => {
29             let mut err = cx.struct_span_err(span, "cannot concatenate string literals");
30             // suggestion would be invalid if we are nested
31             if !is_nested {
32                 if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) {
33                     err.span_suggestion(
34                         span,
35                         "try using a byte string",
36                         format!("b{}", snippet),
37                         Applicability::MachineApplicable,
38                     );
39                 }
40             }
41             err.emit();
42         }
43         Ok(ast::LitKind::Float(_, _)) => {
44             cx.span_err(span, "cannot concatenate float literals");
45         }
46         Ok(ast::LitKind::Bool(_)) => {
47             cx.span_err(span, "cannot concatenate boolean literals");
48         }
49         Ok(ast::LitKind::Err) => {}
50         Ok(ast::LitKind::Int(_, _)) if !is_nested => {
51             let mut err = cx.struct_span_err(span, "cannot concatenate numeric literals");
52             if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) {
53                 err.span_suggestion(
54                     span,
55                     "try wrapping the number in an array",
56                     format!("[{}]", snippet),
57                     Applicability::MachineApplicable,
58                 );
59             }
60             err.emit();
61         }
62         Ok(ast::LitKind::Int(
63             val,
64             ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
65         )) => {
66             assert!(val > u8::MAX.into()); // must be an error
67             cx.span_err(span, "numeric literal is out of bounds");
68         }
69         Ok(ast::LitKind::Int(_, _)) => {
70             cx.span_err(span, "numeric literal is not a `u8`");
71         }
72         Ok(ast::LitKind::ByteStr(..) | ast::LitKind::Byte(_)) => unreachable!(),
73         Err(err) => {
74             report_lit_error(&cx.sess.parse_sess, err, token_lit, span);
75         }
76     }
77 }
78
79 fn handle_array_element(
80     cx: &mut base::ExtCtxt<'_>,
81     has_errors: &mut bool,
82     missing_literals: &mut Vec<rustc_span::Span>,
83     expr: &P<rustc_ast::Expr>,
84 ) -> Option<u8> {
85     match expr.kind {
86         ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => {
87             if !*has_errors {
88                 cx.span_err(expr.span, "cannot concatenate doubly nested array");
89             }
90             *has_errors = true;
91             None
92         }
93         ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
94             Ok(ast::LitKind::Int(
95                 val,
96                 ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
97             )) if val <= u8::MAX.into() => Some(val as u8),
98
99             Ok(ast::LitKind::Byte(val)) => Some(val),
100             Ok(ast::LitKind::ByteStr(..)) => {
101                 if !*has_errors {
102                     cx.struct_span_err(expr.span, "cannot concatenate doubly nested array")
103                         .note("byte strings are treated as arrays of bytes")
104                         .help("try flattening the array")
105                         .emit();
106                 }
107                 *has_errors = true;
108                 None
109             }
110             _ => {
111                 if !*has_errors {
112                     invalid_type_err(cx, token_lit, expr.span, true);
113                 }
114                 *has_errors = true;
115                 None
116             }
117         },
118         ast::ExprKind::IncludedBytes(..) => {
119             if !*has_errors {
120                 cx.struct_span_err(expr.span, "cannot concatenate doubly nested array")
121                     .note("byte strings are treated as arrays of bytes")
122                     .help("try flattening the array")
123                     .emit();
124             }
125             *has_errors = true;
126             None
127         }
128         _ => {
129             missing_literals.push(expr.span);
130             None
131         }
132     }
133 }
134
135 pub fn expand_concat_bytes(
136     cx: &mut base::ExtCtxt<'_>,
137     sp: rustc_span::Span,
138     tts: TokenStream,
139 ) -> Box<dyn base::MacResult + 'static> {
140     let Some(es) = base::get_exprs_from_tts(cx, tts) else {
141         return DummyResult::any(sp);
142     };
143     let mut accumulator = Vec::new();
144     let mut missing_literals = vec![];
145     let mut has_errors = false;
146     for e in es {
147         match &e.kind {
148             ast::ExprKind::Array(exprs) => {
149                 for expr in exprs {
150                     if let Some(elem) =
151                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
152                     {
153                         accumulator.push(elem);
154                     }
155                 }
156             }
157             ast::ExprKind::Repeat(expr, count) => {
158                 if let ast::ExprKind::Lit(token_lit) = count.value.kind
159                 && let Ok(ast::LitKind::Int(count_val, _)) =
160                     ast::LitKind::from_token_lit(token_lit)
161                 {
162                     if let Some(elem) =
163                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
164                     {
165                         for _ in 0..count_val {
166                             accumulator.push(elem);
167                         }
168                     }
169                 } else {
170                     cx.span_err(count.value.span, "repeat count is not a positive number");
171                 }
172             }
173             &ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
174                 Ok(ast::LitKind::Byte(val)) => {
175                     accumulator.push(val);
176                 }
177                 Ok(ast::LitKind::ByteStr(ref bytes, _)) => {
178                     accumulator.extend_from_slice(&bytes);
179                 }
180                 _ => {
181                     if !has_errors {
182                         invalid_type_err(cx, token_lit, e.span, false);
183                     }
184                     has_errors = true;
185                 }
186             },
187             ast::ExprKind::IncludedBytes(bytes) => {
188                 accumulator.extend_from_slice(bytes);
189             }
190             ast::ExprKind::Err => {
191                 has_errors = true;
192             }
193             _ => {
194                 missing_literals.push(e.span);
195             }
196         }
197     }
198     if !missing_literals.is_empty() {
199         let mut err = cx.struct_span_err(missing_literals, "expected a byte literal");
200         err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
201         err.emit();
202         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
203     } else if has_errors {
204         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
205     }
206     let sp = cx.with_def_site_ctxt(sp);
207     base::MacEager::expr(cx.expr_byte_str(sp, accumulator))
208 }