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