aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r--crates/hir_def/src/attr.rs2
-rw-r--r--crates/hir_def/src/import_map.rs18
-rw-r--r--crates/hir_def/src/item_scope.rs2
-rw-r--r--crates/hir_def/src/item_tree.rs4
-rw-r--r--crates/hir_def/src/item_tree/lower.rs8
-rw-r--r--crates/hir_def/src/lib.rs2
-rw-r--r--crates/hir_def/src/nameres.rs12
-rw-r--r--crates/hir_def/src/nameres/collector.rs47
-rw-r--r--crates/hir_def/src/nameres/proc_macro.rs71
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs26
10 files changed, 147 insertions, 45 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 739e3f5e3..1cab0e363 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -209,7 +209,7 @@ impl Attrs {
209 }, 209 },
210 AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), 210 AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
211 AttrDefId::MacroDefId(it) => { 211 AttrDefId::MacroDefId(it) => {
212 it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) 212 it.ast_id().map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
213 } 213 }
214 AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), 214 AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
215 AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), 215 AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 369bc3350..960cabb5f 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -912,10 +912,10 @@ mod tests {
912 dep::fmt (t) 912 dep::fmt (t)
913 dep::format (f) 913 dep::format (f)
914 dep::Fmt (v) 914 dep::Fmt (v)
915 dep::fmt::Display (t) 915 dep::Fmt (m)
916 dep::Fmt (t) 916 dep::Fmt (t)
917 dep::fmt::Display::fmt (a) 917 dep::fmt::Display::fmt (a)
918 dep::Fmt (m) 918 dep::fmt::Display (t)
919 "#]], 919 "#]],
920 ); 920 );
921 921
@@ -926,9 +926,9 @@ mod tests {
926 expect![[r#" 926 expect![[r#"
927 dep::fmt (t) 927 dep::fmt (t)
928 dep::Fmt (v) 928 dep::Fmt (v)
929 dep::Fmt (m)
929 dep::Fmt (t) 930 dep::Fmt (t)
930 dep::fmt::Display::fmt (a) 931 dep::fmt::Display::fmt (a)
931 dep::Fmt (m)
932 "#]], 932 "#]],
933 ); 933 );
934 934
@@ -939,10 +939,10 @@ mod tests {
939 expect![[r#" 939 expect![[r#"
940 dep::fmt (t) 940 dep::fmt (t)
941 dep::Fmt (v) 941 dep::Fmt (v)
942 dep::fmt::Display (t) 942 dep::Fmt (m)
943 dep::Fmt (t) 943 dep::Fmt (t)
944 dep::fmt::Display::fmt (a) 944 dep::fmt::Display::fmt (a)
945 dep::Fmt (m) 945 dep::fmt::Display (t)
946 "#]], 946 "#]],
947 ); 947 );
948 } 948 }
@@ -980,10 +980,10 @@ mod tests {
980 expect![[r#" 980 expect![[r#"
981 dep::fmt (t) 981 dep::fmt (t)
982 dep::Fmt (v) 982 dep::Fmt (v)
983 dep::fmt::Display (t) 983 dep::Fmt (m)
984 dep::Fmt (t) 984 dep::Fmt (t)
985 dep::fmt::Display::fmt (a) 985 dep::fmt::Display::fmt (a)
986 dep::Fmt (m) 986 dep::fmt::Display (t)
987 "#]], 987 "#]],
988 ); 988 );
989 989
@@ -994,9 +994,9 @@ mod tests {
994 expect![[r#" 994 expect![[r#"
995 dep::fmt (t) 995 dep::fmt (t)
996 dep::Fmt (v) 996 dep::Fmt (v)
997 dep::Fmt (m)
997 dep::Fmt (t) 998 dep::Fmt (t)
998 dep::fmt::Display::fmt (a) 999 dep::fmt::Display::fmt (a)
999 dep::Fmt (m)
1000 "#]], 1000 "#]],
1001 ); 1001 );
1002 } 1002 }
@@ -1058,8 +1058,8 @@ mod tests {
1058 Query::new("".to_string()).limit(2), 1058 Query::new("".to_string()).limit(2),
1059 expect![[r#" 1059 expect![[r#"
1060 dep::fmt (t) 1060 dep::fmt (t)
1061 dep::Fmt (t)
1062 dep::Fmt (m) 1061 dep::Fmt (m)
1062 dep::Fmt (t)
1063 dep::Fmt (v) 1063 dep::Fmt (v)
1064 "#]], 1064 "#]],
1065 ); 1065 );
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index aafd73b60..f3ebe7c72 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -252,7 +252,7 @@ impl ItemScope {
252 .for_each(|vis| *vis = Visibility::Module(this_module)); 252 .for_each(|vis| *vis = Visibility::Module(this_module));
253 253
254 for (mac, vis) in self.macros.values_mut() { 254 for (mac, vis) in self.macros.values_mut() {
255 if let MacroDefKind::ProcMacro(_) = mac.kind { 255 if let MacroDefKind::ProcMacro(..) = mac.kind {
256 // FIXME: Technically this is insufficient since reexports of proc macros are also 256 // FIXME: Technically this is insufficient since reexports of proc macros are also
257 // forbidden. Practically nobody does that. 257 // forbidden. Practically nobody does that.
258 continue; 258 continue;
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 5f5b7151a..ae2475b4e 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -115,6 +115,10 @@ impl ItemTree {
115 // still need to collect inner items. 115 // still need to collect inner items.
116 ctx.lower_inner_items(stmt.syntax()) 116 ctx.lower_inner_items(stmt.syntax())
117 }, 117 },
118 ast::Item(item) => {
119 // Macros can expand to stmt and other item, and we add it as top level item
120 ctx.lower_single_item(item)
121 },
118 _ => { 122 _ => {
119 panic!("cannot create item tree from {:?} {}", syntax, syntax); 123 panic!("cannot create item tree from {:?} {}", syntax, syntax);
120 }, 124 },
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 3f558edd8..d3fe1ce1e 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -87,6 +87,14 @@ impl Ctx {
87 self.tree 87 self.tree
88 } 88 }
89 89
90 pub(super) fn lower_single_item(mut self, item: ast::Item) -> ItemTree {
91 self.tree.top_level = self
92 .lower_mod_item(&item, false)
93 .map(|item| item.0)
94 .unwrap_or_else(|| Default::default());
95 self.tree
96 }
97
90 pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree { 98 pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree {
91 self.collect_inner_items(within); 99 self.collect_inner_items(within);
92 self.tree 100 self.tree
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 6758411a0..21add086d 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -650,7 +650,7 @@ fn macro_call_as_call_id(
650) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { 650) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
651 let def: MacroDefId = resolver(call.path.clone()).ok_or(UnresolvedMacro)?; 651 let def: MacroDefId = resolver(call.path.clone()).ok_or(UnresolvedMacro)?;
652 652
653 let res = if let MacroDefKind::BuiltInEager(_) = def.kind { 653 let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
654 let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); 654 let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
655 let hygiene = Hygiene::new(db.upcast(), call.ast_id.file_id); 655 let hygiene = Hygiene::new(db.upcast(), call.ast_id.file_id);
656 656
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index c97be584e..1ac326f97 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -53,11 +53,12 @@ mod path_resolution;
53 53
54#[cfg(test)] 54#[cfg(test)]
55mod tests; 55mod tests;
56mod proc_macro;
56 57
57use std::sync::Arc; 58use std::sync::Arc;
58 59
59use base_db::{CrateId, Edition, FileId}; 60use base_db::{CrateId, Edition, FileId};
60use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; 61use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile, MacroDefId};
61use la_arena::Arena; 62use la_arena::Arena;
62use profile::Count; 63use profile::Count;
63use rustc_hash::FxHashMap; 64use rustc_hash::FxHashMap;
@@ -73,6 +74,8 @@ use crate::{
73 AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId, 74 AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId,
74}; 75};
75 76
77use self::proc_macro::ProcMacroDef;
78
76/// Contains the results of (early) name resolution. 79/// Contains the results of (early) name resolution.
77/// 80///
78/// A `DefMap` stores the module tree and the definitions that are in scope in every module after 81/// A `DefMap` stores the module tree and the definitions that are in scope in every module after
@@ -95,6 +98,12 @@ pub struct DefMap {
95 prelude: Option<ModuleId>, 98 prelude: Option<ModuleId>,
96 extern_prelude: FxHashMap<Name, ModuleDefId>, 99 extern_prelude: FxHashMap<Name, ModuleDefId>,
97 100
101 /// Side table with additional proc. macro info, for use by name resolution in downstream
102 /// crates.
103 ///
104 /// (the primary purpose is to resolve derive helpers)
105 exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
106
98 edition: Edition, 107 edition: Edition,
99 diagnostics: Vec<DefDiagnostic>, 108 diagnostics: Vec<DefDiagnostic>,
100} 109}
@@ -237,6 +246,7 @@ impl DefMap {
237 krate, 246 krate,
238 edition, 247 edition,
239 extern_prelude: FxHashMap::default(), 248 extern_prelude: FxHashMap::default(),
249 exported_proc_macros: FxHashMap::default(),
240 prelude: None, 250 prelude: None,
241 root, 251 root,
242 modules, 252 modules,
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d0fefb5af..dcedf7766 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -18,7 +18,6 @@ use hir_expand::{
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21use tt::{Leaf, TokenTree};
22 21
23use crate::{ 22use crate::{
24 attr::Attrs, 23 attr::Attrs,
@@ -42,6 +41,8 @@ use crate::{
42 UnresolvedMacro, 41 UnresolvedMacro,
43}; 42};
44 43
44use super::proc_macro::ProcMacroDef;
45
45const GLOB_RECURSION_LIMIT: usize = 100; 46const GLOB_RECURSION_LIMIT: usize = 100;
46const EXPANSION_DEPTH_LIMIT: usize = 128; 47const EXPANSION_DEPTH_LIMIT: usize = 128;
47const FIXED_POINT_LIMIT: usize = 8192; 48const FIXED_POINT_LIMIT: usize = 8192;
@@ -353,24 +354,23 @@ impl DefCollector<'_> {
353 /// use a dummy expander that always errors. This comes with the drawback of macros potentially 354 /// use a dummy expander that always errors. This comes with the drawback of macros potentially
354 /// going out of sync with what the build system sees (since we resolve using VFS state, but 355 /// going out of sync with what the build system sees (since we resolve using VFS state, but
355 /// Cargo builds only on-disk files). We could and probably should add diagnostics for that. 356 /// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
356 fn resolve_proc_macro(&mut self, name: &Name) { 357 fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) {
357 self.exports_proc_macros = true; 358 self.exports_proc_macros = true;
358 let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) { 359 let macro_def = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
359 Some((_, expander)) => MacroDefId { 360 Some((_, expander)) => MacroDefId {
360 ast_id: None,
361 krate: self.def_map.krate, 361 krate: self.def_map.krate,
362 kind: MacroDefKind::ProcMacro(*expander), 362 kind: MacroDefKind::ProcMacro(*expander, ast_id),
363 local_inner: false, 363 local_inner: false,
364 }, 364 },
365 None => MacroDefId { 365 None => MacroDefId {
366 ast_id: None,
367 krate: self.def_map.krate, 366 krate: self.def_map.krate,
368 kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)), 367 kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate), ast_id),
369 local_inner: false, 368 local_inner: false,
370 }, 369 },
371 }; 370 };
372 371
373 self.define_proc_macro(name.clone(), macro_def); 372 self.define_proc_macro(def.name.clone(), macro_def);
373 self.def_map.exported_proc_macros.insert(macro_def, def);
374 } 374 }
375 375
376 /// Define a macro with `macro_rules`. 376 /// Define a macro with `macro_rules`.
@@ -1118,7 +1118,8 @@ impl ModCollector<'_, '_> {
1118 ModItem::Function(id) => { 1118 ModItem::Function(id) => {
1119 let func = &self.item_tree[id]; 1119 let func = &self.item_tree[id];
1120 1120
1121 self.collect_proc_macro_def(&func.name, &attrs); 1121 let ast_id = InFile::new(self.file_id, func.ast_id);
1122 self.collect_proc_macro_def(&func.name, ast_id, &attrs);
1122 1123
1123 def = Some(DefData { 1124 def = Some(DefData {
1124 id: FunctionLoc { 1125 id: FunctionLoc {
@@ -1385,28 +1386,11 @@ impl ModCollector<'_, '_> {
1385 } 1386 }
1386 1387
1387 /// If `attrs` registers a procedural macro, collects its definition. 1388 /// If `attrs` registers a procedural macro, collects its definition.
1388 fn collect_proc_macro_def(&mut self, func_name: &Name, attrs: &Attrs) { 1389 fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) {
1389 // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere 1390 // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
1390 // FIXME: distinguish the type of macro 1391 if let Some(proc_macro) = attrs.parse_proc_macro_decl(func_name) {
1391 let macro_name = if attrs.by_key("proc_macro").exists() 1392 self.def_collector.export_proc_macro(proc_macro, ast_id);
1392 || attrs.by_key("proc_macro_attribute").exists() 1393 }
1393 {
1394 func_name.clone()
1395 } else {
1396 let derive = attrs.by_key("proc_macro_derive");
1397 if let Some(arg) = derive.tt_values().next() {
1398 if let [TokenTree::Leaf(Leaf::Ident(trait_name)), ..] = &*arg.token_trees {
1399 trait_name.as_name()
1400 } else {
1401 log::trace!("malformed `#[proc_macro_derive]`: {}", arg);
1402 return;
1403 }
1404 } else {
1405 return;
1406 }
1407 };
1408
1409 self.def_collector.resolve_proc_macro(&macro_name);
1410 } 1394 }
1411 1395
1412 fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) { 1396 fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
@@ -1445,9 +1429,8 @@ impl ModCollector<'_, '_> {
1445 1429
1446 // Case 2: normal `macro_rules!` macro 1430 // Case 2: normal `macro_rules!` macro
1447 let macro_id = MacroDefId { 1431 let macro_id = MacroDefId {
1448 ast_id: Some(ast_id),
1449 krate: self.def_collector.def_map.krate, 1432 krate: self.def_collector.def_map.krate,
1450 kind: MacroDefKind::Declarative, 1433 kind: MacroDefKind::Declarative(ast_id),
1451 local_inner: is_local_inner, 1434 local_inner: is_local_inner,
1452 }; 1435 };
1453 self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, is_export); 1436 self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, is_export);
diff --git a/crates/hir_def/src/nameres/proc_macro.rs b/crates/hir_def/src/nameres/proc_macro.rs
new file mode 100644
index 000000000..156598f19
--- /dev/null
+++ b/crates/hir_def/src/nameres/proc_macro.rs
@@ -0,0 +1,71 @@
1//! Nameres-specific procedural macro data and helpers.
2
3use hir_expand::name::{AsName, Name};
4use tt::{Leaf, TokenTree};
5
6use crate::attr::Attrs;
7
8#[derive(Debug, PartialEq, Eq)]
9pub(super) struct ProcMacroDef {
10 pub(super) name: Name,
11 pub(super) kind: ProcMacroKind,
12}
13
14#[derive(Debug, PartialEq, Eq)]
15pub(super) enum ProcMacroKind {
16 CustomDerive { helpers: Box<[Name]> },
17 FnLike,
18 Attr,
19}
20
21impl Attrs {
22 #[rustfmt::skip]
23 pub(super) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
24 if self.by_key("proc_macro").exists() {
25 Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike })
26 } else if self.by_key("proc_macro_attribute").exists() {
27 Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
28 } else if self.by_key("proc_macro_derive").exists() {
29 let derive = self.by_key("proc_macro_derive").tt_values().next().unwrap();
30
31 match &*derive.token_trees {
32 // `#[proc_macro_derive(Trait)]`
33 [TokenTree::Leaf(Leaf::Ident(trait_name))] => Some(ProcMacroDef {
34 name: trait_name.as_name(),
35 kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) },
36 }),
37
38 // `#[proc_macro_derive(Trait, attibutes(helper1, helper2, ...))]`
39 [
40 TokenTree::Leaf(Leaf::Ident(trait_name)),
41 TokenTree::Leaf(Leaf::Punct(comma)),
42 TokenTree::Leaf(Leaf::Ident(attributes)),
43 TokenTree::Subtree(helpers)
44 ] if comma.char == ',' && attributes.text == "attributes" =>
45 {
46 let helpers = helpers.token_trees.iter()
47 .filter(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','))
48 .map(|tt| {
49 match tt {
50 TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
51 _ => None
52 }
53 })
54 .collect::<Option<Box<[_]>>>()?;
55
56 Some(ProcMacroDef {
57 name: trait_name.as_name(),
58 kind: ProcMacroKind::CustomDerive { helpers },
59 })
60 }
61
62 _ => {
63 log::trace!("malformed `#[proc_macro_derive]`: {}", derive);
64 None
65 }
66 }
67 } else {
68 None
69 }
70 }
71}
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index f65a655bf..d59d3c0db 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -1,4 +1,5 @@
1use super::*; 1use super::*;
2use crate::nameres::proc_macro::{ProcMacroDef, ProcMacroKind};
2 3
3#[test] 4#[test]
4fn macro_rules_are_globally_visible() { 5fn macro_rules_are_globally_visible() {
@@ -790,3 +791,28 @@ fn proc_macro_censoring() {
790 "#]], 791 "#]],
791 ); 792 );
792} 793}
794
795#[test]
796fn collects_derive_helpers() {
797 let def_map = compute_crate_def_map(
798 r"
799 struct TokenStream;
800
801 #[proc_macro_derive(AnotherTrait, attributes(helper_attr))]
802 pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
803 TokenStream
804 }
805 ",
806 );
807
808 assert_eq!(def_map.exported_proc_macros.len(), 1);
809 match def_map.exported_proc_macros.values().next() {
810 Some(ProcMacroDef { kind: ProcMacroKind::CustomDerive { helpers }, .. }) => {
811 match &**helpers {
812 [attr] => assert_eq!(attr.to_string(), "helper_attr"),
813 _ => unreachable!(),
814 }
815 }
816 _ => unreachable!(),
817 }
818}