]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/concat_bytes.rs
Move `{core,std}::stream::Stream` to `{core,std}::async_iter::AsyncIterator`.
[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 lit = if let ast::ExprKind::Lit(lit) = &expr.kind {
10         lit
11     } else {
12         unreachable!();
13     };
14     match lit.kind {
15         ast::LitKind::Char(_) => {
16             let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals");
17             if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
18                 err.span_suggestion(
19                     expr.span,
20                     "try using a byte character",
21                     format!("b{}", snippet),
22                     Applicability::MachineApplicable,
23                 )
24                 .emit();
25             }
26         }
27         ast::LitKind::Str(_, _) => {
28             let mut err = cx.struct_span_err(expr.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(expr.span) {
32                     err.span_suggestion(
33                         expr.span,
34                         "try using a byte string",
35                         format!("b{}", snippet),
36                         Applicability::MachineApplicable,
37                     );
38                 }
39             }
40             err.emit();
41         }
42         ast::LitKind::Float(_, _) => {
43             cx.span_err(expr.span, "cannot concatenate float literals");
44         }
45         ast::LitKind::Bool(_) => {
46             cx.span_err(expr.span, "cannot concatenate boolean literals");
47         }
48         ast::LitKind::Err(_) => {}
49         ast::LitKind::Int(_, _) if !is_nested => {
50             let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals");
51             if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
52                 err.span_suggestion(
53                     expr.span,
54                     "try wrapping the number in an array",
55                     format!("[{}]", snippet),
56                     Applicability::MachineApplicable,
57                 );
58             }
59             err.emit();
60         }
61         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(expr.span, "numeric literal is out of bounds");
67         }
68         ast::LitKind::Int(_, _) => {
69             cx.span_err(expr.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(ref lit) => match lit.kind {
90             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             ast::LitKind::Byte(val) => Some(val),
96             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, expr, true);
109                 }
110                 *has_errors = true;
111                 None
112             }
113         },
114         _ => {
115             missing_literals.push(expr.span);
116             None
117         }
118     }
119 }
120
121 pub fn expand_concat_bytes(
122     cx: &mut base::ExtCtxt<'_>,
123     sp: rustc_span::Span,
124     tts: TokenStream,
125 ) -> Box<dyn base::MacResult + 'static> {
126     let es = match base::get_exprs_from_tts(cx, sp, tts) {
127         Some(e) => e,
128         None => return DummyResult::any(sp),
129     };
130     let mut accumulator = Vec::new();
131     let mut missing_literals = vec![];
132     let mut has_errors = false;
133     for e in es {
134         match e.kind {
135             ast::ExprKind::Array(ref exprs) => {
136                 for expr in exprs {
137                     if let Some(elem) =
138                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
139                     {
140                         accumulator.push(elem);
141                     }
142                 }
143             }
144             ast::ExprKind::Repeat(ref expr, ref count) => {
145                 if let ast::ExprKind::Lit(ast::Lit {
146                     kind: ast::LitKind::Int(count_val, _), ..
147                 }) = count.value.kind
148                 {
149                     if let Some(elem) =
150                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
151                     {
152                         for _ in 0..count_val {
153                             accumulator.push(elem);
154                         }
155                     }
156                 } else {
157                     cx.span_err(count.value.span, "repeat count is not a positive number");
158                 }
159             }
160             ast::ExprKind::Lit(ref lit) => match lit.kind {
161                 ast::LitKind::Byte(val) => {
162                     accumulator.push(val);
163                 }
164                 ast::LitKind::ByteStr(ref bytes) => {
165                     accumulator.extend_from_slice(&bytes);
166                 }
167                 _ => {
168                     if !has_errors {
169                         invalid_type_err(cx, &e, false);
170                     }
171                     has_errors = true;
172                 }
173             },
174             ast::ExprKind::Err => {
175                 has_errors = true;
176             }
177             _ => {
178                 missing_literals.push(e.span);
179             }
180         }
181     }
182     if !missing_literals.is_empty() {
183         let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal");
184         err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
185         err.emit();
186         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
187     } else if has_errors {
188         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
189     }
190     let sp = cx.with_def_site_ctxt(sp);
191     base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::from(accumulator))))
192 }