aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/nameres/collector.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/nameres/collector.rs')
-rw-r--r--crates/hir_def/src/nameres/collector.rs96
1 files changed, 81 insertions, 15 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 4c3993ff0..100e25ffc 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -16,10 +16,10 @@ use hir_expand::{
16 proc_macro::ProcMacroExpander, 16 proc_macro::ProcMacroExpander,
17 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 17 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
18}; 18};
19use rustc_hash::FxHashMap; 19use rustc_hash::{FxHashMap, FxHashSet};
20use rustc_hash::FxHashSet;
21use syntax::ast; 20use syntax::ast;
22use test_utils::mark; 21use test_utils::mark;
22use tt::{Leaf, TokenTree};
23 23
24use crate::{ 24use crate::{
25 attr::Attrs, 25 attr::Attrs,
@@ -87,6 +87,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
87 mod_dirs: FxHashMap::default(), 87 mod_dirs: FxHashMap::default(),
88 cfg_options, 88 cfg_options,
89 proc_macros, 89 proc_macros,
90 exports_proc_macros: false,
90 from_glob_import: Default::default(), 91 from_glob_import: Default::default(),
91 }; 92 };
92 collector.collect(); 93 collector.collect();
@@ -202,7 +203,12 @@ struct DefCollector<'a> {
202 unexpanded_attribute_macros: Vec<DeriveDirective>, 203 unexpanded_attribute_macros: Vec<DeriveDirective>,
203 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 204 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
204 cfg_options: &'a CfgOptions, 205 cfg_options: &'a CfgOptions,
206 /// List of procedural macros defined by this crate. This is read from the dynamic library
207 /// built by the build system, and is the list of proc. macros we can actually expand. It is
208 /// empty when proc. macro support is disabled (in which case we still do name resolution for
209 /// them).
205 proc_macros: Vec<(Name, ProcMacroExpander)>, 210 proc_macros: Vec<(Name, ProcMacroExpander)>,
211 exports_proc_macros: bool,
206 from_glob_import: PerNsGlobImports, 212 from_glob_import: PerNsGlobImports,
207} 213}
208 214
@@ -261,24 +267,56 @@ impl DefCollector<'_> {
261 } 267 }
262 self.unresolved_imports = unresolved_imports; 268 self.unresolved_imports = unresolved_imports;
263 269
264 // Record proc-macros 270 // FIXME: This condition should instead check if this is a `proc-macro` type crate.
265 self.collect_proc_macro(); 271 if self.exports_proc_macros {
272 // A crate exporting procedural macros is not allowed to export anything else.
273 //
274 // Additionally, while the proc macro entry points must be `pub`, they are not publicly
275 // exported in type/value namespace. This function reduces the visibility of all items
276 // in the crate root that aren't proc macros.
277 let root = self.def_map.root;
278 let root = &mut self.def_map.modules[root];
279 root.scope.censor_non_proc_macros(ModuleId {
280 krate: self.def_map.krate,
281 local_id: self.def_map.root,
282 });
283 }
266 } 284 }
267 285
268 fn collect_proc_macro(&mut self) { 286 /// Adds a definition of procedural macro `name` to the root module.
269 let proc_macros = std::mem::take(&mut self.proc_macros); 287 ///
270 for (name, expander) in proc_macros { 288 /// # Notes on procedural macro resolution
271 let krate = self.def_map.krate; 289 ///
272 290 /// Procedural macro functionality is provided by the build system: It has to build the proc
273 let macro_id = MacroDefId { 291 /// macro and pass the resulting dynamic library to rust-analyzer.
292 ///
293 /// When procedural macro support is enabled, the list of proc macros exported by a crate is
294 /// known before we resolve names in the crate. This list is stored in `self.proc_macros` and is
295 /// derived from the dynamic library.
296 ///
297 /// However, we *also* would like to be able to at least *resolve* macros on our own, without
298 /// help by the build system. So, when the macro isn't found in `self.proc_macros`, we instead
299 /// use a dummy expander that always errors. This comes with the drawback of macros potentially
300 /// going out of sync with what the build system sees (since we resolve using VFS state, but
301 /// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
302 fn resolve_proc_macro(&mut self, name: &Name) {
303 self.exports_proc_macros = true;
304 let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) {
305 Some((_, expander)) => MacroDefId {
306 ast_id: None,
307 krate: Some(self.def_map.krate),
308 kind: MacroDefKind::ProcMacro(*expander),
309 local_inner: false,
310 },
311 None => MacroDefId {
274 ast_id: None, 312 ast_id: None,
275 krate: Some(krate), 313 krate: Some(self.def_map.krate),
276 kind: MacroDefKind::ProcMacro(expander), 314 kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)),
277 local_inner: false, 315 local_inner: false,
278 }; 316 },
317 };
279 318
280 self.define_proc_macro(name.clone(), macro_id); 319 self.define_proc_macro(name.clone(), macro_def);
281 }
282 } 320 }
283 321
284 /// Define a macro with `macro_rules`. 322 /// Define a macro with `macro_rules`.
@@ -917,6 +955,9 @@ impl ModCollector<'_, '_> {
917 } 955 }
918 ModItem::Function(id) => { 956 ModItem::Function(id) => {
919 let func = &self.item_tree[id]; 957 let func = &self.item_tree[id];
958
959 self.collect_proc_macro_def(&func.name, attrs);
960
920 def = Some(DefData { 961 def = Some(DefData {
921 id: FunctionLoc { 962 id: FunctionLoc {
922 container: container.into(), 963 container: container.into(),
@@ -1177,6 +1218,30 @@ impl ModCollector<'_, '_> {
1177 } 1218 }
1178 } 1219 }
1179 1220
1221 /// If `attrs` registers a procedural macro, collects its definition.
1222 fn collect_proc_macro_def(&mut self, func_name: &Name, attrs: &Attrs) {
1223 // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
1224 // FIXME: distinguish the type of macro
1225 let macro_name = if attrs.by_key("proc_macro").exists()
1226 || attrs.by_key("proc_macro_attribute").exists()
1227 {
1228 func_name.clone()
1229 } else {
1230 let derive = attrs.by_key("proc_macro_derive");
1231 if let Some(arg) = derive.tt_values().next() {
1232 if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees {
1233 trait_name.as_name()
1234 } else {
1235 return;
1236 }
1237 } else {
1238 return;
1239 }
1240 };
1241
1242 self.def_collector.resolve_proc_macro(&macro_name);
1243 }
1244
1180 fn collect_macro(&mut self, mac: &MacroCall) { 1245 fn collect_macro(&mut self, mac: &MacroCall) {
1181 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); 1246 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
1182 1247
@@ -1283,6 +1348,7 @@ mod tests {
1283 mod_dirs: FxHashMap::default(), 1348 mod_dirs: FxHashMap::default(),
1284 cfg_options: &CfgOptions::default(), 1349 cfg_options: &CfgOptions::default(),
1285 proc_macros: Default::default(), 1350 proc_macros: Default::default(),
1351 exports_proc_macros: false,
1286 from_glob_import: Default::default(), 1352 from_glob_import: Default::default(),
1287 }; 1353 };
1288 collector.collect(); 1354 collector.collect();