From 828196be3bd92ba33e62270d24dae79bd2eb5125 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 18 Mar 2022 11:55:53 +0100 Subject: [PATCH] fix: Fix runnables trying to add doc tests in the crate root from #[macro_export] macros --- crates/hir/src/lib.rs | 10 +++++ crates/hir_def/src/data.rs | 8 +++- crates/hir_def/src/item_scope.rs | 1 + crates/ide/src/runnables.rs | 72 +++++++++++++++++++++++++++++++- crates/ide_db/src/defs.rs | 2 +- crates/ide_db/src/helpers.rs | 8 ++++ 6 files changed, 98 insertions(+), 3 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ee4ff0aebb..9207614c68 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -570,6 +570,12 @@ impl Module { .collect() } + pub fn legacy_macros(self, db: &dyn HirDatabase) -> Vec { + let def_map = self.id.def_map(db.upcast()); + let scope = &def_map[self.id.local_id].scope; + scope.legacy_macros().map(|(_, it)| MacroId::from(it).into()).collect() + } + pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec { let def_map = self.id.def_map(db.upcast()); def_map[self.id.local_id].scope.impls().map(Impl::from).collect() @@ -1789,6 +1795,10 @@ impl Macro { } } + pub fn is_macro_export(self, db: &dyn HirDatabase) -> bool { + matches!(self.id, MacroId::MacroRulesId(id) if db.macro_rules_data(id).macro_export) + } + pub fn kind(&self, db: &dyn HirDatabase) -> MacroKind { match self.id { MacroId::Macro2Id(it) => match it.lookup(db.upcast()).expander { diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 456ed9e610..ffb733c2b9 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -315,6 +315,7 @@ impl Macro2Data { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MacroRulesData { pub name: Name, + pub macro_export: bool, } impl MacroRulesData { @@ -326,7 +327,12 @@ impl MacroRulesData { let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; - Arc::new(MacroRulesData { name: makro.name.clone() }) + let macro_export = item_tree + .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) + .by_key("macro_export") + .exists(); + + Arc::new(MacroRulesData { name: makro.name.clone(), macro_export }) } } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index e6fdd41bd8..f1330b34d1 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs @@ -63,6 +63,7 @@ pub struct ItemScope { // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will // be all resolved to the last one defined if shadowing happens. legacy_macros: FxHashMap, + /// The derive macro invocations in this scope. attr_macros: FxHashMap, MacroCallId>, /// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes /// paired with the derive macro invocations for the specific attribute. diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 1507e7cdd0..2c8e898d72 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -157,7 +157,13 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { Definition::SelfType(impl_) => runnable_impl(&sema, &impl_), _ => None, }; - add_opt(runnable.or_else(|| module_def_doctest(sema.db, def)), Some(def)); + add_opt( + runnable + .or_else(|| module_def_doctest(sema.db, def)) + // #[macro_export] mbe macros are declared in the root, while their definition may reside in a different module + .filter(|it| it.nav.file_id == file_id), + Some(def), + ); if let Definition::SelfType(impl_) = def { impl_.items(db).into_iter().for_each(|assoc| { let runnable = match assoc { @@ -2074,4 +2080,68 @@ impl Foo { "#]], ); } + + #[test] + fn doc_test_macro_export_mbe() { + check( + r#" +//- /lib.rs +$0 +mod foo; + +//- /foo.rs +/// ``` +/// fn foo() { +/// } +/// ``` +#[macro_export] +macro_rules! foo { + () => { + + }; +} +"#, + &[], + expect![[r#" + [] + "#]], + ); + check( + r#" +//- /lib.rs +$0 +/// ``` +/// fn foo() { +/// } +/// ``` +#[macro_export] +macro_rules! foo { + () => { + + }; +} +"#, + &[DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..94, + name: "foo", + }, + kind: DocTest { + test_id: Path( + "foo", + ), + }, + cfg: None, + }, + ] + "#]], + ); + } } diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index e60c3b04e8..36457a5364 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -447,7 +447,7 @@ impl NameRefClass { impl_from!( Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local, - GenericParam, Label + GenericParam, Label, Macro for Definition ); diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 4046c2febb..d4fda397d6 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -76,6 +76,14 @@ pub fn visit_file_defs( cb(def.into()); } module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); + + let is_root = module.is_crate_root(db); + module + .legacy_macros(db) + .into_iter() + // don't show legacy macros declared in the crate-root that were already covered in declarations earlier + .filter(|it| !(is_root && it.is_macro_export(db))) + .for_each(|mac| cb(mac.into())); } /// Checks if the given lint is equal or is contained by the other lint which may or may not be a group.