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/item_scope.rs22
-rw-r--r--crates/hir_def/src/nameres/collector.rs96
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs73
3 files changed, 176 insertions, 15 deletions
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index f1e9dfd5b..12c24e1ca 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -5,10 +5,12 @@ use std::collections::hash_map::Entry;
5 5
6use base_db::CrateId; 6use base_db::CrateId;
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use hir_expand::MacroDefKind;
8use once_cell::sync::Lazy; 9use once_cell::sync::Lazy;
9use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark; 11use test_utils::mark;
11 12
13use crate::ModuleId;
12use crate::{ 14use crate::{
13 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, 15 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
14 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, 16 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId,
@@ -265,6 +267,26 @@ impl ItemScope {
265 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { 267 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
266 self.legacy_macros.clone() 268 self.legacy_macros.clone()
267 } 269 }
270
271 /// Marks everything that is not a procedural macro as private to `this_module`.
272 pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
273 self.types
274 .values_mut()
275 .chain(self.values.values_mut())
276 .map(|(_, v)| v)
277 .chain(self.unnamed_trait_imports.values_mut())
278 .for_each(|vis| *vis = Visibility::Module(this_module));
279
280 for (mac, vis) in self.macros.values_mut() {
281 if let MacroDefKind::ProcMacro(_) = mac.kind {
282 // FIXME: Technically this is insufficient since reexports of proc macros are also
283 // forbidden. Practically nobody does that.
284 continue;
285 }
286
287 *vis = Visibility::Module(this_module);
288 }
289 }
268} 290}
269 291
270impl PerNs { 292impl PerNs {
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();
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index e0fb8bdef..0851c3b7d 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -667,3 +667,76 @@ b! { static = #[] (); }
667 "#]], 667 "#]],
668 ); 668 );
669} 669}
670
671#[test]
672fn resolves_proc_macros() {
673 check(
674 r"
675 struct TokenStream;
676
677 #[proc_macro]
678 pub fn function_like_macro(args: TokenStream) -> TokenStream {
679 args
680 }
681
682 #[proc_macro_attribute]
683 pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
684 item
685 }
686
687 #[proc_macro_derive(DummyTrait)]
688 pub fn derive_macro(_item: TokenStream) -> TokenStream {
689 TokenStream
690 }
691 ",
692 expect![[r#"
693 crate
694 DummyTrait: m
695 TokenStream: t v
696 attribute_macro: v m
697 derive_macro: v
698 function_like_macro: v m
699 "#]],
700 );
701}
702
703#[test]
704fn proc_macro_censoring() {
705 // Make sure that only proc macros are publicly exported from proc-macro crates.
706
707 check(
708 r"
709 //- /main.rs crate:main deps:macros
710 pub use macros::*;
711
712 //- /macros.rs crate:macros
713 pub struct TokenStream;
714
715 #[proc_macro]
716 pub fn function_like_macro(args: TokenStream) -> TokenStream {
717 args
718 }
719
720 #[proc_macro_attribute]
721 pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
722 item
723 }
724
725 #[proc_macro_derive(DummyTrait)]
726 pub fn derive_macro(_item: TokenStream) -> TokenStream {
727 TokenStream
728 }
729
730 #[macro_export]
731 macro_rules! mbe {
732 () => {};
733 }
734 ",
735 expect![[r#"
736 crate
737 DummyTrait: m
738 attribute_macro: m
739 function_like_macro: m
740 "#]],
741 );
742}