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