From baab72e611fa985c2e62e964f3a48ad31367220f Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 18 Sep 2020 17:50:04 +0200 Subject: Reduce visibility of non-proc-macros proc-macro crates only export proc-macros, but currently other items are also considered public (and show up in completion) --- crates/hir_def/src/item_scope.rs | 25 ++++++++++++++++++ crates/hir_def/src/nameres/collector.rs | 19 ++++++++++++++ crates/hir_def/src/nameres/tests/macros.rs | 41 ++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index f1e9dfd5b..99820c275 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; use base_db::CrateId; use hir_expand::name::Name; +use hir_expand::MacroDefKind; use once_cell::sync::Lazy; use rustc_hash::{FxHashMap, FxHashSet}; use test_utils::mark; +use crate::ModuleId; use crate::{ db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, @@ -265,6 +267,29 @@ impl ItemScope { pub(crate) fn collect_legacy_macros(&self) -> FxHashMap { self.legacy_macros.clone() } + + /// Marks everything that is not a procedural macro as private to `this_module`. + pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { + for vis in self + .types + .values_mut() + .chain(self.values.values_mut()) + .map(|(_, v)| v) + .chain(self.unnamed_trait_imports.values_mut()) + { + *vis = Visibility::Module(this_module); + } + + for (mac, vis) in self.macros.values_mut() { + if let MacroDefKind::ProcMacro(_) = mac.kind { + // FIXME: Technically this is insufficient since reexports of proc macros are also + // forbidden. Practically nobody does that. + continue; + } + + *vis = Visibility::Module(this_module); + } + } } impl PerNs { diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 28ef49488..f98a42643 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -87,6 +87,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr mod_dirs: FxHashMap::default(), cfg_options, proc_macros, + exports_proc_macros: false, from_glob_import: Default::default(), }; collector.collect(); @@ -203,6 +204,7 @@ struct DefCollector<'a> { mod_dirs: FxHashMap, cfg_options: &'a CfgOptions, proc_macros: Vec<(Name, ProcMacroExpander)>, + exports_proc_macros: bool, from_glob_import: PerNsGlobImports, } @@ -260,9 +262,25 @@ impl DefCollector<'_> { self.record_resolved_import(directive) } self.unresolved_imports = unresolved_imports; + + // FIXME: This condition should instead check if this is a `proc-macro` type crate. + if self.exports_proc_macros { + // A crate exporting procedural macros is not allowed to export anything else. + // + // Additionally, while the proc macro entry points must be `pub`, they are not publicly + // exported in type/value namespace. This function reduces the visibility of all items + // in the crate root that aren't proc macros. + let root = self.def_map.root; + let root = &mut self.def_map.modules[root]; + root.scope.censor_non_proc_macros(ModuleId { + krate: self.def_map.krate, + local_id: self.def_map.root, + }); + } } fn resolve_proc_macro(&mut self, name: &Name) { + self.exports_proc_macros = true; let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) { Some((_, expander)) => MacroDefId { ast_id: None, @@ -1310,6 +1328,7 @@ mod tests { mod_dirs: FxHashMap::default(), cfg_options: &CfgOptions::default(), proc_macros: Default::default(), + exports_proc_macros: false, from_glob_import: Default::default(), }; collector.collect(); diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 98cb5a0fd..0851c3b7d 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs @@ -699,3 +699,44 @@ fn resolves_proc_macros() { "#]], ); } + +#[test] +fn proc_macro_censoring() { + // Make sure that only proc macros are publicly exported from proc-macro crates. + + check( + r" + //- /main.rs crate:main deps:macros + pub use macros::*; + + //- /macros.rs crate:macros + pub struct TokenStream; + + #[proc_macro] + pub fn function_like_macro(args: TokenStream) -> TokenStream { + args + } + + #[proc_macro_attribute] + pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream { + item + } + + #[proc_macro_derive(DummyTrait)] + pub fn derive_macro(_item: TokenStream) -> TokenStream { + TokenStream + } + + #[macro_export] + macro_rules! mbe { + () => {}; + } + ", + expect![[r#" + crate + DummyTrait: m + attribute_macro: m + function_like_macro: m + "#]], + ); +} -- cgit v1.2.3