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