Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-def/src/macro_expansion_tests/proc_macros.rs | 25 | ||||
| -rw-r--r-- | crates/hir-def/src/nameres/collector.rs | 52 |
2 files changed, 74 insertions, 3 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index 3f0afe61e0..5216246910 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -316,3 +316,28 @@ use proc_macros::disallow_cfg; expect![[r#""#]], ); } + +#[test] +fn derive_helpers_are_ignored() { + check( + r#" +//- proc_macros: identity, helper_should_be_ignored, helper_should_be_ignored_derive +//- minicore: derive +use proc_macros::{identity, helper_should_be_ignored, HelperShouldBeIgnoredDerive}; + +#[derive(HelperShouldBeIgnoredDerive)] +#[helper_should_be_ignored] +#[identity] +struct Foo; +"#, + expect![[r#" +use proc_macros::{identity, helper_should_be_ignored, HelperShouldBeIgnoredDerive}; + +#[derive(HelperShouldBeIgnoredDerive)] +#[helper_should_be_ignored] +#[identity] +struct Foo; + +#[helper_should_be_ignored] struct Foo;"#]], + ); +} diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 9ac8a43999..9aa7febdfd 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -1238,7 +1238,17 @@ impl<'db> DefCollector<'db> { let mut macros = mem::take(&mut self.unresolved_macros); let mut resolved = Vec::new(); let mut push_resolved = |directive: &MacroDirective<'_>, call_id| { - resolved.push((directive.module_id, directive.depth, directive.container, call_id)); + let attr_macro_item = match &directive.kind { + MacroDirectiveKind::Attr { ast_id, .. } => Some(ast_id.ast_id), + MacroDirectiveKind::FnLike { .. } | MacroDirectiveKind::Derive { .. } => None, + }; + resolved.push(( + directive.module_id, + directive.depth, + directive.container, + call_id, + attr_macro_item, + )); }; #[derive(PartialEq, Eq)] @@ -1530,8 +1540,14 @@ impl<'db> DefCollector<'db> { self.def_map.modules[module_id].scope.add_macro_invoc(ptr.map(|(_, it)| it), call_id); } - for (module_id, depth, container, macro_call_id) in resolved { - self.collect_macro_expansion(module_id, macro_call_id, depth, container); + for (module_id, depth, container, macro_call_id, attr_macro_item) in resolved { + self.collect_macro_expansion( + module_id, + macro_call_id, + depth, + container, + attr_macro_item, + ); } res @@ -1543,6 +1559,7 @@ impl<'db> DefCollector<'db> { macro_call_id: MacroCallId, depth: usize, container: ItemContainerId, + attr_macro_item: Option<AstId<ast::Item>>, ) { if depth > self.def_map.recursion_limit() as usize { cov_mark::hit!(macro_expansion_overflow); @@ -1553,6 +1570,34 @@ impl<'db> DefCollector<'db> { let item_tree = self.db.file_item_tree(file_id); + // Derive helpers that are in scope for an item are also in scope for attribute macro expansions + // of that item (but not derive or fn like macros). + // FIXME: This is a hack. The proper way to do this is by having a chain of derive helpers scope, + // where the next scope in the chain is the parent hygiene context of the span. Unfortunately + // it's difficult to implement with our current name resolution and hygiene system. + // This hack is also incorrect since it ignores item in blocks. But the main reason to bring derive + // helpers into scope in this case is to help with: + // ``` + // #[derive(DeriveWithHelper)] + // #[helper] + // #[attr_macro] + // struct Foo; + // ``` + // Where `attr_macro`'s input will include `#[helper]` but not the derive, and it will likely therefore + // also include it in its output. Therefore I hope not supporting blocks is fine at least for now. + if let Some(attr_macro_item) = attr_macro_item + && let Some(derive_helpers) = self.def_map.derive_helpers_in_scope.get(&attr_macro_item) + { + let derive_helpers = derive_helpers.clone(); + for item in item_tree.top_level_items() { + self.def_map + .derive_helpers_in_scope + .entry(InFile::new(file_id, item.ast_id())) + .or_default() + .extend(derive_helpers.iter().cloned()); + } + } + let mod_dir = if macro_call_id.is_include_macro(self.db) { ModDir::root() } else { @@ -2454,6 +2499,7 @@ impl ModCollector<'_, '_> { call_id, self.macro_depth + 1, container, + None, ); } |