]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/concat_bytes.rs
Rollup merge of #104422 - compiler-errors:fix-suggest_associated_call_syntax, r=BoxyUwU
[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
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 {
9         unreachable!();
10     };
11     match lit.kind {
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) {
15                 err.span_suggestion(
16                     expr.span,
17                     "try using a byte character",
18                     format!("b{}", snippet),
19                     Applicability::MachineApplicable,
20                 )
21                 .emit();
22             }
23         }
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
27             if !is_nested {
28                 if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
29                     err.span_suggestion(
30                         expr.span,
31                         "try using a byte string",
32                         format!("b{}", snippet),
33                         Applicability::MachineApplicable,
34                     );
35                 }
36             }
37             err.emit();
38         }
39         ast::LitKind::Float(_, _) => {
40             cx.span_err(expr.span, "cannot concatenate float literals");
41         }
42         ast::LitKind::Bool(_) => {
43             cx.span_err(expr.span, "cannot concatenate boolean literals");
44         }
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) {
49                 err.span_suggestion(
50                     expr.span,
51                     "try wrapping the number in an array",
52                     format!("[{}]", snippet),
53                     Applicability::MachineApplicable,
54                 );
55             }
56             err.emit();
57         }
58         ast::LitKind::Int(
59             val,
60             ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
61         ) => {
62             assert!(val > u8::MAX.into()); // must be an error
63             cx.span_err(expr.span, "numeric literal is out of bounds");
64         }
65         ast::LitKind::Int(_, _) => {
66             cx.span_err(expr.span, "numeric literal is not a `u8`");
67         }
68         _ => unreachable!(),
69     }
70 }
71
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>,
77 ) -> Option<u8> {
78     match expr.kind {
79         ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => {
80             if !*has_errors {
81                 cx.span_err(expr.span, "cannot concatenate doubly nested array");
82             }
83             *has_errors = true;
84             None
85         }
86         ast::ExprKind::Lit(ref lit) => match lit.kind {
87             ast::LitKind::Int(
88                 val,
89                 ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
90             ) if val <= u8::MAX.into() => Some(val as u8),
91
92             ast::LitKind::Byte(val) => Some(val),
93             ast::LitKind::ByteStr(_) => {
94                 if !*has_errors {
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")
98                         .emit();
99                 }
100                 *has_errors = true;
101                 None
102             }
103             _ => {
104                 if !*has_errors {
105                     invalid_type_err(cx, expr, true);
106                 }
107                 *has_errors = true;
108                 None
109             }
110         },
111         ast::ExprKind::IncludedBytes(..) => {
112             if !*has_errors {
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")
116                     .emit();
117             }
118             *has_errors = true;
119             None
120         }
121         _ => {
122             missing_literals.push(expr.span);
123             None
124         }
125     }
126 }
127
128 pub fn expand_concat_bytes(
129     cx: &mut base::ExtCtxt<'_>,
130     sp: rustc_span::Span,
131     tts: TokenStream,
132 ) -> Box<dyn base::MacResult + 'static> {
133     let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
134         return DummyResult::any(sp);
135     };
136     let mut accumulator = Vec::new();
137     let mut missing_literals = vec![];
138     let mut has_errors = false;
139     for e in es {
140         match e.kind {
141             ast::ExprKind::Array(ref exprs) => {
142                 for expr in exprs {
143                     if let Some(elem) =
144                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
145                     {
146                         accumulator.push(elem);
147                     }
148                 }
149             }
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
154                 {
155                     if let Some(elem) =
156                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
157                     {
158                         for _ in 0..count_val {
159                             accumulator.push(elem);
160                         }
161                     }
162                 } else {
163                     cx.span_err(count.value.span, "repeat count is not a positive number");
164                 }
165             }
166             ast::ExprKind::Lit(ref lit) => match lit.kind {
167                 ast::LitKind::Byte(val) => {
168                     accumulator.push(val);
169                 }
170                 ast::LitKind::ByteStr(ref bytes) => {
171                     accumulator.extend_from_slice(&bytes);
172                 }
173                 _ => {
174                     if !has_errors {
175                         invalid_type_err(cx, &e, false);
176                     }
177                     has_errors = true;
178                 }
179             },
180             ast::ExprKind::IncludedBytes(ref bytes) => {
181                 accumulator.extend_from_slice(bytes);
182             }
183             ast::ExprKind::Err => {
184                 has_errors = true;
185             }
186             _ => {
187                 missing_literals.push(e.span);
188             }
189         }
190     }
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!()`");
194         err.emit();
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));
198     }
199     let sp = cx.with_def_site_ctxt(sp);
200     base::MacEager::expr(cx.expr_byte_str(sp, accumulator))
201 }