]>
Commit | Line | Data |
---|---|---|
a2a8927a XL |
1 | use rustc_ast as ast; |
2 | use rustc_ast::{ptr::P, tokenstream::TokenStream}; | |
a2a8927a XL |
3 | use rustc_errors::Applicability; |
4 | use rustc_expand::base::{self, DummyResult}; | |
487cf647 FG |
5 | use rustc_session::errors::report_lit_error; |
6 | use rustc_span::Span; | |
a2a8927a XL |
7 | |
8 | /// Emits errors for literal expressions that are invalid inside and outside of an array. | |
487cf647 FG |
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) { | |
a2a8927a | 19 | err.span_suggestion( |
487cf647 | 20 | span, |
a2a8927a XL |
21 | "try using a byte character", |
22 | format!("b{}", snippet), | |
23 | Applicability::MachineApplicable, | |
24 | ) | |
25 | .emit(); | |
26 | } | |
27 | } | |
487cf647 FG |
28 | Ok(ast::LitKind::Str(_, _)) => { |
29 | let mut err = cx.struct_span_err(span, "cannot concatenate string literals"); | |
a2a8927a XL |
30 | // suggestion would be invalid if we are nested |
31 | if !is_nested { | |
487cf647 | 32 | if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) { |
a2a8927a | 33 | err.span_suggestion( |
487cf647 | 34 | span, |
a2a8927a XL |
35 | "try using a byte string", |
36 | format!("b{}", snippet), | |
37 | Applicability::MachineApplicable, | |
38 | ); | |
39 | } | |
40 | } | |
41 | err.emit(); | |
42 | } | |
487cf647 FG |
43 | Ok(ast::LitKind::Float(_, _)) => { |
44 | cx.span_err(span, "cannot concatenate float literals"); | |
a2a8927a | 45 | } |
487cf647 FG |
46 | Ok(ast::LitKind::Bool(_)) => { |
47 | cx.span_err(span, "cannot concatenate boolean literals"); | |
a2a8927a | 48 | } |
487cf647 FG |
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) { | |
a2a8927a | 53 | err.span_suggestion( |
487cf647 | 54 | span, |
a2a8927a XL |
55 | "try wrapping the number in an array", |
56 | format!("[{}]", snippet), | |
57 | Applicability::MachineApplicable, | |
58 | ); | |
59 | } | |
60 | err.emit(); | |
61 | } | |
487cf647 | 62 | Ok(ast::LitKind::Int( |
a2a8927a XL |
63 | val, |
64 | ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8), | |
487cf647 | 65 | )) => { |
a2a8927a | 66 | assert!(val > u8::MAX.into()); // must be an error |
487cf647 FG |
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`"); | |
a2a8927a | 71 | } |
9c376795 | 72 | Ok(ast::LitKind::ByteStr(..) | ast::LitKind::Byte(_)) => unreachable!(), |
487cf647 FG |
73 | Err(err) => { |
74 | report_lit_error(&cx.sess.parse_sess, err, token_lit, span); | |
a2a8927a | 75 | } |
a2a8927a XL |
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 | } | |
487cf647 FG |
93 | ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { |
94 | Ok(ast::LitKind::Int( | |
a2a8927a XL |
95 | val, |
96 | ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8), | |
487cf647 | 97 | )) if val <= u8::MAX.into() => Some(val as u8), |
a2a8927a | 98 | |
487cf647 | 99 | Ok(ast::LitKind::Byte(val)) => Some(val), |
9c376795 | 100 | Ok(ast::LitKind::ByteStr(..)) => { |
a2a8927a XL |
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 { | |
487cf647 | 112 | invalid_type_err(cx, token_lit, expr.span, true); |
a2a8927a XL |
113 | } |
114 | *has_errors = true; | |
115 | None | |
116 | } | |
117 | }, | |
487cf647 FG |
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 | } | |
a2a8927a XL |
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> { | |
9c376795 | 140 | let Some(es) = base::get_exprs_from_tts(cx, tts) else { |
5e7ed085 | 141 | return DummyResult::any(sp); |
a2a8927a XL |
142 | }; |
143 | let mut accumulator = Vec::new(); | |
144 | let mut missing_literals = vec![]; | |
145 | let mut has_errors = false; | |
146 | for e in es { | |
487cf647 FG |
147 | match &e.kind { |
148 | ast::ExprKind::Array(exprs) => { | |
a2a8927a XL |
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 | } | |
487cf647 FG |
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) | |
a2a8927a XL |
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 | } | |
487cf647 FG |
173 | &ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { |
174 | Ok(ast::LitKind::Byte(val)) => { | |
a2a8927a XL |
175 | accumulator.push(val); |
176 | } | |
9c376795 | 177 | Ok(ast::LitKind::ByteStr(ref bytes, _)) => { |
a2a8927a XL |
178 | accumulator.extend_from_slice(&bytes); |
179 | } | |
180 | _ => { | |
181 | if !has_errors { | |
487cf647 | 182 | invalid_type_err(cx, token_lit, e.span, false); |
a2a8927a XL |
183 | } |
184 | has_errors = true; | |
185 | } | |
186 | }, | |
487cf647 FG |
187 | ast::ExprKind::IncludedBytes(bytes) => { |
188 | accumulator.extend_from_slice(bytes); | |
189 | } | |
a2a8927a XL |
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() { | |
9c376795 | 199 | let mut err = cx.struct_span_err(missing_literals, "expected a byte literal"); |
a2a8927a XL |
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); | |
f2b60f7d | 207 | base::MacEager::expr(cx.expr_byte_str(sp, accumulator)) |
a2a8927a | 208 | } |