]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/concat_bytes.rs
66e86bf2182672e0453f24aa3837623ea1a43b72
[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         _ => {
112             missing_literals.push(expr.span);
113             None
114         }
115     }
116 }
117
118 pub fn expand_concat_bytes(
119     cx: &mut base::ExtCtxt<'_>,
120     sp: rustc_span::Span,
121     tts: TokenStream,
122 ) -> Box<dyn base::MacResult + 'static> {
123     let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
124         return DummyResult::any(sp);
125     };
126     let mut accumulator = Vec::new();
127     let mut missing_literals = vec![];
128     let mut has_errors = false;
129     for e in es {
130         match e.kind {
131             ast::ExprKind::Array(ref exprs) => {
132                 for expr in exprs {
133                     if let Some(elem) =
134                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
135                     {
136                         accumulator.push(elem);
137                     }
138                 }
139             }
140             ast::ExprKind::Repeat(ref expr, ref count) => {
141                 if let ast::ExprKind::Lit(ast::Lit {
142                     kind: ast::LitKind::Int(count_val, _), ..
143                 }) = count.value.kind
144                 {
145                     if let Some(elem) =
146                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
147                     {
148                         for _ in 0..count_val {
149                             accumulator.push(elem);
150                         }
151                     }
152                 } else {
153                     cx.span_err(count.value.span, "repeat count is not a positive number");
154                 }
155             }
156             ast::ExprKind::Lit(ref lit) => match lit.kind {
157                 ast::LitKind::Byte(val) => {
158                     accumulator.push(val);
159                 }
160                 ast::LitKind::ByteStr(ref bytes) => {
161                     accumulator.extend_from_slice(&bytes);
162                 }
163                 _ => {
164                     if !has_errors {
165                         invalid_type_err(cx, &e, false);
166                     }
167                     has_errors = true;
168                 }
169             },
170             ast::ExprKind::Err => {
171                 has_errors = true;
172             }
173             _ => {
174                 missing_literals.push(e.span);
175             }
176         }
177     }
178     if !missing_literals.is_empty() {
179         let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal");
180         err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
181         err.emit();
182         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
183     } else if has_errors {
184         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
185     }
186     let sp = cx.with_def_site_ctxt(sp);
187     base::MacEager::expr(cx.expr_byte_str(sp, accumulator))
188 }