From 360de5ba71a631a118a088dba7c975e9ae592452 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 21 Dec 2019 05:02:31 +0800 Subject: [PATCH 1/2] Recursive collect macros in impl items --- crates/ra_hir_def/src/data.rs | 38 ++++++++++++++++++++-------- crates/ra_hir_ty/src/tests/macros.rs | 23 +++++++++++++++++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index b0a3f178457..6a65633aefe 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -226,21 +226,39 @@ fn collect_impl_items_in_macros( let mut res = Vec::new(); for m in impl_block.value.syntax().children().filter_map(ast::MacroCall::cast) { - if let Some((mark, items)) = expander.enter_expand(db, m) { - let items: InFile = expander.to_source(items); - expander.exit(db, mark); - res.extend(collect_impl_items( - db, - items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())), - items.file_id, - id, - )); - } + res.extend(collect_impl_items_in_macro(db, &mut expander, m, id)) } res } +fn collect_impl_items_in_macro( + db: &impl DefDatabase, + expander: &mut Expander, + m: ast::MacroCall, + id: ImplId, +) -> Vec { + if let Some((mark, items)) = expander.enter_expand(db, m) { + let items: InFile = expander.to_source(items); + let mut res = collect_impl_items( + db, + items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())), + items.file_id, + id, + ); + // Recursive collect macros + // Note that ast::ModuleItem do not include ast::MacroCall + // We cannot use ModuleItemOwner::items here + for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) { + res.extend(collect_impl_items_in_macro(db, expander, it, id)) + } + expander.exit(db, mark); + res + } else { + Vec::new() + } +} + fn collect_impl_items( db: &impl DefDatabase, impl_items: impl Iterator, diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 7fdbf996f42..69c695cc8f1 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -201,6 +201,29 @@ impl S { assert_eq!(t, "u128"); } +#[test] +fn infer_impl_items_generated_by_macros_chain() { + let t = type_at( + r#" +//- /main.rs +macro_rules! m_inner { + () => {fn foo(&self) -> u128 {0}} +} +macro_rules! m { + () => {m_inner!();} +} + +struct S; +impl S { + m!(); +} + +fn test() { S.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + #[test] fn infer_macro_with_dollar_crate_is_correct_in_expr() { let (db, pos) = TestDB::with_position( From c1166697a74bca690443525ced8cd8bc08894779 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 21 Dec 2019 05:16:29 +0800 Subject: [PATCH 2/2] Add a limit for protect against infinite recursion --- crates/ra_hir_def/src/data.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 6a65633aefe..1aa9a9b7d7d 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -225,8 +225,11 @@ fn collect_impl_items_in_macros( let mut expander = Expander::new(db, impl_block.file_id, module_id); let mut res = Vec::new(); + // We set a limit to protect against infinite recursion + let limit = 100; + for m in impl_block.value.syntax().children().filter_map(ast::MacroCall::cast) { - res.extend(collect_impl_items_in_macro(db, &mut expander, m, id)) + res.extend(collect_impl_items_in_macro(db, &mut expander, m, id, limit)) } res @@ -237,7 +240,12 @@ fn collect_impl_items_in_macro( expander: &mut Expander, m: ast::MacroCall, id: ImplId, + limit: usize, ) -> Vec { + if limit == 0 { + return Vec::new(); + } + if let Some((mark, items)) = expander.enter_expand(db, m) { let items: InFile = expander.to_source(items); let mut res = collect_impl_items( @@ -250,7 +258,7 @@ fn collect_impl_items_in_macro( // Note that ast::ModuleItem do not include ast::MacroCall // We cannot use ModuleItemOwner::items here for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) { - res.extend(collect_impl_items_in_macro(db, expander, it, id)) + res.extend(collect_impl_items_in_macro(db, expander, it, id, limit - 1)) } expander.exit(db, mark); res