diff options
Diffstat (limited to 'crates/hir_expand/src/builtin_macro.rs')
-rw-r--r-- | crates/hir_expand/src/builtin_macro.rs | 90 |
1 files changed, 37 insertions, 53 deletions
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 80b60d59f..eb57ea7d6 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -6,7 +6,7 @@ use crate::{ | |||
6 | 6 | ||
7 | use base_db::{AnchoredPath, FileId}; | 7 | use base_db::{AnchoredPath, FileId}; |
8 | use either::Either; | 8 | use either::Either; |
9 | use mbe::{parse_to_token_tree, ExpandResult}; | 9 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; |
10 | use parser::FragmentKind; | 10 | use parser::FragmentKind; |
11 | use syntax::ast::{self, AstToken}; | 11 | use syntax::ast::{self, AstToken}; |
12 | 12 | ||
@@ -182,25 +182,10 @@ fn assert_expand( | |||
182 | // ```, | 182 | // ```, |
183 | // which is wrong but useful. | 183 | // which is wrong but useful. |
184 | 184 | ||
185 | let mut args = Vec::new(); | 185 | let args = parse_exprs_with_sep(tt, ','); |
186 | let mut current = Vec::new(); | ||
187 | for tt in tt.token_trees.iter().cloned() { | ||
188 | match tt { | ||
189 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { | ||
190 | args.push(current); | ||
191 | current = Vec::new(); | ||
192 | } | ||
193 | _ => { | ||
194 | current.push(tt); | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | if !current.is_empty() { | ||
199 | args.push(current); | ||
200 | } | ||
201 | 186 | ||
202 | let arg_tts = args.into_iter().flat_map(|arg| { | 187 | let arg_tts = args.into_iter().flat_map(|arg| { |
203 | quote! { &(##arg), } | 188 | quote! { &(#arg), } |
204 | }.token_trees).collect::<Vec<_>>(); | 189 | }.token_trees).collect::<Vec<_>>(); |
205 | 190 | ||
206 | let expanded = quote! { | 191 | let expanded = quote! { |
@@ -238,35 +223,21 @@ fn format_args_expand( | |||
238 | // ]) | 223 | // ]) |
239 | // ```, | 224 | // ```, |
240 | // which is still not really correct, but close enough for now | 225 | // which is still not really correct, but close enough for now |
241 | let mut args = Vec::new(); | 226 | let mut args = parse_exprs_with_sep(tt, ','); |
242 | let mut current = Vec::new(); | 227 | |
243 | for tt in tt.token_trees.iter().cloned() { | ||
244 | match tt { | ||
245 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { | ||
246 | args.push(current); | ||
247 | current = Vec::new(); | ||
248 | } | ||
249 | _ => { | ||
250 | current.push(tt); | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | if !current.is_empty() { | ||
255 | args.push(current); | ||
256 | } | ||
257 | if args.is_empty() { | 228 | if args.is_empty() { |
258 | return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule); | 229 | return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule); |
259 | } | 230 | } |
260 | for arg in &mut args { | 231 | for arg in &mut args { |
261 | // Remove `key =`. | 232 | // Remove `key =`. |
262 | if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint) | 233 | if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint) |
263 | { | 234 | { |
264 | arg.drain(..2); | 235 | arg.token_trees.drain(..2); |
265 | } | 236 | } |
266 | } | 237 | } |
267 | let _format_string = args.remove(0); | 238 | let _format_string = args.remove(0); |
268 | let arg_tts = args.into_iter().flat_map(|arg| { | 239 | let arg_tts = args.into_iter().flat_map(|arg| { |
269 | quote! { std::fmt::ArgumentV1::new(&(##arg), std::fmt::Display::fmt), } | 240 | quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), } |
270 | }.token_trees).collect::<Vec<_>>(); | 241 | }.token_trees).collect::<Vec<_>>(); |
271 | let expanded = quote! { | 242 | let expanded = quote! { |
272 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) | 243 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) |
@@ -327,17 +298,14 @@ fn concat_expand( | |||
327 | // concat works with string and char literals, so remove any quotes. | 298 | // concat works with string and char literals, so remove any quotes. |
328 | // It also works with integer, float and boolean literals, so just use the rest | 299 | // It also works with integer, float and boolean literals, so just use the rest |
329 | // as-is. | 300 | // as-is. |
330 | 301 | let component = unquote_str(&it).unwrap_or_else(|| it.text.to_string()); | |
331 | text += it | 302 | text.push_str(&component); |
332 | .text | 303 | } |
333 | .trim_start_matches(|c| match c { | 304 | // handle boolean literals |
334 | 'r' | '#' | '\'' | '"' => true, | 305 | tt::TokenTree::Leaf(tt::Leaf::Ident(id)) |
335 | _ => false, | 306 | if i % 2 == 0 && (id.text == "true" || id.text == "false") => |
336 | }) | 307 | { |
337 | .trim_end_matches(|c| match c { | 308 | text.push_str(id.text.as_str()); |
338 | '#' | '\'' | '"' => true, | ||
339 | _ => false, | ||
340 | }); | ||
341 | } | 309 | } |
342 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), | 310 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), |
343 | _ => { | 311 | _ => { |
@@ -345,7 +313,6 @@ fn concat_expand( | |||
345 | } | 313 | } |
346 | } | 314 | } |
347 | } | 315 | } |
348 | |||
349 | ExpandResult { value: Some((quote!(#text), FragmentKind::Expr)), err } | 316 | ExpandResult { value: Some((quote!(#text), FragmentKind::Expr)), err } |
350 | } | 317 | } |
351 | 318 | ||
@@ -724,6 +691,25 @@ mod tests { | |||
724 | } | 691 | } |
725 | 692 | ||
726 | #[test] | 693 | #[test] |
694 | fn test_format_args_expand_with_comma_exprs() { | ||
695 | let expanded = expand_builtin_macro( | ||
696 | r#" | ||
697 | #[rustc_builtin_macro] | ||
698 | macro_rules! format_args { | ||
699 | ($fmt:expr) => ({ /* compiler built-in */ }); | ||
700 | ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) | ||
701 | } | ||
702 | format_args!("{} {:?}", a::<A,B>(), b); | ||
703 | "#, | ||
704 | ); | ||
705 | |||
706 | assert_eq!( | ||
707 | expanded, | ||
708 | r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"# | ||
709 | ); | ||
710 | } | ||
711 | |||
712 | #[test] | ||
727 | fn test_include_bytes_expand() { | 713 | fn test_include_bytes_expand() { |
728 | let expanded = expand_builtin_macro( | 714 | let expanded = expand_builtin_macro( |
729 | r#" | 715 | r#" |
@@ -745,12 +731,10 @@ mod tests { | |||
745 | r##" | 731 | r##" |
746 | #[rustc_builtin_macro] | 732 | #[rustc_builtin_macro] |
747 | macro_rules! concat {} | 733 | macro_rules! concat {} |
748 | concat!("foo", 0, r#"bar"#); | 734 | concat!("foo", "r", 0, r#"bar"#, false); |
749 | "##, | 735 | "##, |
750 | ); | 736 | ); |
751 | 737 | ||
752 | assert_eq!(expanded, r#""foo0bar""#); | 738 | assert_eq!(expanded, r#""foor0barfalse""#); |
753 | |||
754 | // FIXME: `true`/`false` literals don't work. | ||
755 | } | 739 | } |
756 | } | 740 | } |