aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/builtin_macro.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/builtin_macro.rs')
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs58
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
55fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { 59fn 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
207fn 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)]
204mod tests { 243mod 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}