aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/nameres/collector.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-09-28 12:03:47 +0100
committerGitHub <[email protected]>2020-09-28 12:03:47 +0100
commit0fb5d9d87a1d23563b8311feb4f97cb85894b8f0 (patch)
tree603f01dfa58140033c8902f3d2ba3098e87be858 /crates/hir_def/src/nameres/collector.rs
parent000046cfa08948803607538e27808b6bf9dc7afb (diff)
parente88e4fbb7bb32065c6a7570057de248c2ea3a514 (diff)
Merge #6033
6033: Make name resolution resolve proc macros instead of relying purely on the build system r=matklad a=jonas-schievink This makes name resolution look at proc-macro declaration attributes like `#[proc_macro_derive]` and defines the right proc macro in the macro namespace, fixing unresolved custom derives like `thiserror::Error` (which can cause false positives, now that we emit diagnostics for unresolved imports). This works even when proc-macro support is turned off, in which case we fall back to a dummy expander that always returns an error. IMO this is the right way to handle at least the name resolution part of proc. macros, while the *expansion* itself should rely on the build system to build and provide the macro DLL. It does mean that they may go out of sync, but we can provide diagnostics if that happens (something like "could not find macro X in crate Y – ensure that all files of crate Y are saved"). I think it is valuable to be able to reason about proc macros even when we can't expand them, since proc macro expansion can break between Rust releases or users might not want to turn it on for performance reasons. It allows us to provide better diagnostics on any proc macro invocation we're not expanding (like a weak warning that informs the user that proc macro support is turned off, or that it has been disabled because the server crashed). Fixes https://github.com/rust-analyzer/rust-analyzer/issues/5763 Co-authored-by: Jonas Schievink <[email protected]>
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();