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)
This commit is contained in:
Jonas Schievink 2020-09-18 17:50:04 +02:00
parent 069045015c
commit baab72e611
3 changed files with 85 additions and 0 deletions

View file

@ -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<Name, MacroDefId> {
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 {

View file

@ -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<LocalModuleId, ModDir>,
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();

View file

@ -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
"#]],
);
}