]>
Commit | Line | Data |
---|---|---|
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_session::errors::report_lit_error; | |
6 | use rustc_span::Span; | |
7 | ||
8 | /// Emits errors for literal expressions that are invalid inside and outside of an array. | |
9 | fn 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 | ||
79 | fn 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 | ||
135 | pub 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 | } |