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