diff options
Diffstat (limited to 'crates/ra_hir_expand/src/builtin_macro.rs')
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 35f99b2bc..99303188b 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -49,7 +49,11 @@ register_builtin! { | |||
49 | (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand, | 49 | (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand, |
50 | (FILE_MACRO, File) => file_expand, | 50 | (FILE_MACRO, File) => file_expand, |
51 | (LINE_MACRO, Line) => line_expand, | 51 | (LINE_MACRO, Line) => line_expand, |
52 | (STRINGIFY_MACRO, Stringify) => stringify_expand | 52 | (STRINGIFY_MACRO, Stringify) => stringify_expand, |
53 | (FORMAT_ARGS_MACRO, FormatArgs) => format_args_expand, | ||
54 | // format_args_nl only differs in that it adds a newline in the end, | ||
55 | // so we use the same stub expansion for now | ||
56 | (FORMAT_ARGS_NL_MACRO, FormatArgsNl) => format_args_expand | ||
53 | } | 57 | } |
54 | 58 | ||
55 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | 59 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { |
@@ -200,6 +204,41 @@ fn compile_error_expand( | |||
200 | Err(mbe::ExpandError::BindingError("Must be a string".into())) | 204 | Err(mbe::ExpandError::BindingError("Must be a string".into())) |
201 | } | 205 | } |
202 | 206 | ||
207 | fn format_args_expand( | ||
208 | _db: &dyn AstDatabase, | ||
209 | _id: MacroCallId, | ||
210 | tt: &tt::Subtree, | ||
211 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
212 | // We expand `format_args!("", arg1, arg2)` to | ||
213 | // `std::fmt::Arguments::new_v1(&[], &[&arg1, &arg2])`, | ||
214 | // which is still not really correct, but close enough for now | ||
215 | let mut args = Vec::new(); | ||
216 | let mut current = Vec::new(); | ||
217 | for tt in tt.token_trees.iter().cloned() { | ||
218 | match tt { | ||
219 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { | ||
220 | args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current }); | ||
221 | current = Vec::new(); | ||
222 | } | ||
223 | _ => { | ||
224 | current.push(tt); | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | if !current.is_empty() { | ||
229 | args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current }); | ||
230 | } | ||
231 | if args.is_empty() { | ||
232 | return Err(mbe::ExpandError::NoMatchingRule); | ||
233 | } | ||
234 | let _format_string = args.remove(0); | ||
235 | let arg_tts = args.into_iter().flat_map(|arg| (quote! { & #arg , }).token_trees); | ||
236 | let expanded = quote! { | ||
237 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) | ||
238 | }; | ||
239 | Ok(expanded) | ||
240 | } | ||
241 | |||
203 | #[cfg(test)] | 242 | #[cfg(test)] |
204 | mod tests { | 243 | mod tests { |
205 | use super::*; | 244 | use super::*; |
@@ -307,4 +346,21 @@ mod tests { | |||
307 | 346 | ||
308 | assert_eq!(expanded, r#"loop{"error!"}"#); | 347 | assert_eq!(expanded, r#"loop{"error!"}"#); |
309 | } | 348 | } |
349 | |||
350 | #[test] | ||
351 | fn test_format_args_expand() { | ||
352 | let expanded = expand_builtin_macro( | ||
353 | r#" | ||
354 | #[rustc_builtin_macro] | ||
355 | macro_rules! format_args { | ||
356 | ($fmt:expr) => ({ /* compiler built-in */ }); | ||
357 | ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) | ||
358 | } | ||
359 | format_args!("{} {:?}", arg1(a, b, c), arg2); | ||
360 | "#, | ||
361 | BuiltinFnLikeExpander::FormatArgs, | ||
362 | ); | ||
363 | |||
364 | assert_eq!(expanded, r#"std::fmt::Arguments::new_v1(&[] ,&[&arg1(a,b,c),&arg2,])"#); | ||
365 | } | ||
310 | } | 366 | } |