mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Take visibility into account for glob imports
This commit is contained in:
parent
8ac25f119e
commit
21359c3ab5
7 changed files with 163 additions and 41 deletions
|
@ -149,16 +149,8 @@ impl ItemScope {
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a {
|
||||||
pub(crate) fn collect_resolutions(&self) -> Vec<(Name, PerNs)> {
|
self.visible.iter().map(|(name, res)| (name.clone(), res.clone()))
|
||||||
self.visible.iter().map(|(name, res)| (name.clone(), res.clone())).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn collect_resolutions_with_vis(
|
|
||||||
&self,
|
|
||||||
vis: ResolvedVisibility,
|
|
||||||
) -> Vec<(Name, PerNs)> {
|
|
||||||
self.visible.iter().map(|(name, res)| (name.clone(), res.with_visibility(vis))).collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
|
pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
|
||||||
|
|
|
@ -109,7 +109,7 @@ struct MacroDirective {
|
||||||
struct DefCollector<'a, DB> {
|
struct DefCollector<'a, DB> {
|
||||||
db: &'a DB,
|
db: &'a DB,
|
||||||
def_map: CrateDefMap,
|
def_map: CrateDefMap,
|
||||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, raw::Import)>>,
|
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, ResolvedVisibility)>>,
|
||||||
unresolved_imports: Vec<ImportDirective>,
|
unresolved_imports: Vec<ImportDirective>,
|
||||||
resolved_imports: Vec<ImportDirective>,
|
resolved_imports: Vec<ImportDirective>,
|
||||||
unexpanded_macros: Vec<MacroDirective>,
|
unexpanded_macros: Vec<MacroDirective>,
|
||||||
|
@ -218,6 +218,7 @@ where
|
||||||
self.update(
|
self.update(
|
||||||
self.def_map.root,
|
self.def_map.root,
|
||||||
&[(name, PerNs::macros(macro_, ResolvedVisibility::Public))],
|
&[(name, PerNs::macros(macro_, ResolvedVisibility::Public))],
|
||||||
|
ResolvedVisibility::Public,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,7 +353,6 @@ where
|
||||||
|
|
||||||
fn record_resolved_import(&mut self, directive: &ImportDirective) {
|
fn record_resolved_import(&mut self, directive: &ImportDirective) {
|
||||||
let module_id = directive.module_id;
|
let module_id = directive.module_id;
|
||||||
let import_id = directive.import_id;
|
|
||||||
let import = &directive.import;
|
let import = &directive.import;
|
||||||
let def = directive.status.namespaces();
|
let def = directive.status.namespaces();
|
||||||
let vis = self
|
let vis = self
|
||||||
|
@ -373,28 +373,48 @@ where
|
||||||
let item_map = self.db.crate_def_map(m.krate);
|
let item_map = self.db.crate_def_map(m.krate);
|
||||||
let scope = &item_map[m.local_id].scope;
|
let scope = &item_map[m.local_id].scope;
|
||||||
|
|
||||||
// TODO: only use names we can see
|
|
||||||
|
|
||||||
// Module scoped macros is included
|
// Module scoped macros is included
|
||||||
let items = scope.collect_resolutions_with_vis(vis);
|
let items = scope
|
||||||
|
.resolutions()
|
||||||
|
// only keep visible names...
|
||||||
|
.map(|(n, res)| {
|
||||||
|
(
|
||||||
|
n,
|
||||||
|
res.filter_visibility(|v| {
|
||||||
|
v.visible_from_def_map(&self.def_map, module_id)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.filter(|(_, res)| !res.is_none())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self.update(module_id, &items);
|
self.update(module_id, &items, vis);
|
||||||
} else {
|
} else {
|
||||||
// glob import from same crate => we do an initial
|
// glob import from same crate => we do an initial
|
||||||
// import, and then need to propagate any further
|
// import, and then need to propagate any further
|
||||||
// additions
|
// additions
|
||||||
let scope = &self.def_map[m.local_id].scope;
|
let scope = &self.def_map[m.local_id].scope;
|
||||||
|
|
||||||
// TODO: only use names we can see
|
|
||||||
|
|
||||||
// Module scoped macros is included
|
// Module scoped macros is included
|
||||||
let items = scope.collect_resolutions_with_vis(vis);
|
let items = scope
|
||||||
|
.resolutions()
|
||||||
|
// only keep visible names...
|
||||||
|
.map(|(n, res)| {
|
||||||
|
(
|
||||||
|
n,
|
||||||
|
res.filter_visibility(|v| {
|
||||||
|
v.visible_from_def_map(&self.def_map, module_id)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.filter(|(_, res)| !res.is_none())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self.update(module_id, &items);
|
self.update(module_id, &items, vis);
|
||||||
// record the glob import in case we add further items
|
// record the glob import in case we add further items
|
||||||
let glob = self.glob_imports.entry(m.local_id).or_default();
|
let glob = self.glob_imports.entry(m.local_id).or_default();
|
||||||
if !glob.iter().any(|it| *it == (module_id, import_id)) {
|
if !glob.iter().any(|(mid, _)| *mid == module_id) {
|
||||||
glob.push((module_id, import_id));
|
glob.push((module_id, vis));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,7 +432,7 @@ where
|
||||||
(name, res)
|
(name, res)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
self.update(module_id, &resolutions);
|
self.update(module_id, &resolutions, vis);
|
||||||
}
|
}
|
||||||
Some(d) => {
|
Some(d) => {
|
||||||
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
|
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
|
||||||
|
@ -434,21 +454,29 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update(module_id, &[(name, def.with_visibility(vis))]);
|
self.update(module_id, &[(name, def)], vis);
|
||||||
}
|
}
|
||||||
None => tested_by!(bogus_paths),
|
None => tested_by!(bogus_paths),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)]) {
|
fn update(
|
||||||
self.update_recursive(module_id, resolutions, 0)
|
&mut self,
|
||||||
|
module_id: LocalModuleId,
|
||||||
|
resolutions: &[(Name, PerNs)],
|
||||||
|
vis: ResolvedVisibility,
|
||||||
|
) {
|
||||||
|
self.update_recursive(module_id, resolutions, vis, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_recursive(
|
fn update_recursive(
|
||||||
&mut self,
|
&mut self,
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
resolutions: &[(Name, PerNs)],
|
resolutions: &[(Name, PerNs)],
|
||||||
|
// All resolutions are imported with this visibility; the visibilies in
|
||||||
|
// the `PerNs` values are ignored and overwritten
|
||||||
|
vis: ResolvedVisibility,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) {
|
) {
|
||||||
if depth > 100 {
|
if depth > 100 {
|
||||||
|
@ -458,7 +486,7 @@ where
|
||||||
let scope = &mut self.def_map.modules[module_id].scope;
|
let scope = &mut self.def_map.modules[module_id].scope;
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
for (name, res) in resolutions {
|
for (name, res) in resolutions {
|
||||||
changed |= scope.push_res(name.clone(), *res);
|
changed |= scope.push_res(name.clone(), res.with_visibility(vis));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !changed {
|
if !changed {
|
||||||
|
@ -471,9 +499,13 @@ where
|
||||||
.flat_map(|v| v.iter())
|
.flat_map(|v| v.iter())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for (glob_importing_module, _glob_import) in glob_imports {
|
for (glob_importing_module, glob_import_vis) in glob_imports {
|
||||||
// We pass the glob import so that the tracked import in those modules is that glob import
|
// we know all resolutions have the same visibility (`vis`), so we
|
||||||
self.update_recursive(glob_importing_module, resolutions, depth + 1);
|
// just need to check that once
|
||||||
|
if !vis.visible_from_def_map(&self.def_map, glob_importing_module) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.update_recursive(glob_importing_module, resolutions, glob_import_vis, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,7 +747,7 @@ where
|
||||||
let def: ModuleDefId = module.into();
|
let def: ModuleDefId = module.into();
|
||||||
let vis = ResolvedVisibility::Public; // TODO handle module visibility
|
let vis = ResolvedVisibility::Public; // TODO handle module visibility
|
||||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
||||||
self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))]);
|
self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,7 +812,7 @@ where
|
||||||
.def_map
|
.def_map
|
||||||
.resolve_visibility(self.def_collector.db, self.module_id, vis)
|
.resolve_visibility(self.def_collector.db, self.module_id, vis)
|
||||||
.unwrap_or(ResolvedVisibility::Public);
|
.unwrap_or(ResolvedVisibility::Public);
|
||||||
self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))])
|
self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
|
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ use test_utils::covers;
|
||||||
|
|
||||||
use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId};
|
use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId};
|
||||||
|
|
||||||
fn def_map(fixtute: &str) -> String {
|
fn def_map(fixture: &str) -> String {
|
||||||
let dm = compute_crate_def_map(fixtute);
|
let dm = compute_crate_def_map(fixture);
|
||||||
render_crate_def_map(&dm)
|
render_crate_def_map(&dm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ fn render_crate_def_map(map: &CrateDefMap) -> String {
|
||||||
*buf += path;
|
*buf += path;
|
||||||
*buf += "\n";
|
*buf += "\n";
|
||||||
|
|
||||||
let mut entries = map.modules[module].scope.collect_resolutions();
|
let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
|
||||||
entries.sort_by_key(|(name, _)| name.clone());
|
entries.sort_by_key(|(name, _)| name.clone());
|
||||||
|
|
||||||
for (name, def) in entries {
|
for (name, def) in entries {
|
||||||
|
|
|
@ -73,6 +73,84 @@ fn glob_2() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_privacy_1() {
|
||||||
|
let map = def_map(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo;
|
||||||
|
use foo::*;
|
||||||
|
|
||||||
|
//- /foo/mod.rs
|
||||||
|
pub mod bar;
|
||||||
|
pub use self::bar::*;
|
||||||
|
struct PrivateStructFoo;
|
||||||
|
|
||||||
|
//- /foo/bar.rs
|
||||||
|
pub struct Baz;
|
||||||
|
struct PrivateStructBar;
|
||||||
|
pub use super::*;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
crate
|
||||||
|
Baz: t v
|
||||||
|
bar: t
|
||||||
|
foo: t
|
||||||
|
|
||||||
|
crate::foo
|
||||||
|
Baz: t v
|
||||||
|
PrivateStructFoo: t v
|
||||||
|
bar: t
|
||||||
|
|
||||||
|
crate::foo::bar
|
||||||
|
Baz: t v
|
||||||
|
PrivateStructBar: t v
|
||||||
|
PrivateStructFoo: t v
|
||||||
|
bar: t
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_privacy_2() {
|
||||||
|
let map = def_map(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo;
|
||||||
|
use foo::*;
|
||||||
|
use foo::bar::*;
|
||||||
|
|
||||||
|
//- /foo/mod.rs
|
||||||
|
pub mod bar;
|
||||||
|
fn Foo() {};
|
||||||
|
pub struct Foo {};
|
||||||
|
|
||||||
|
//- /foo/bar.rs
|
||||||
|
pub(super) struct PrivateBaz;
|
||||||
|
struct PrivateBar;
|
||||||
|
pub(crate) struct PubCrateStruct;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
crate
|
||||||
|
Foo: t
|
||||||
|
PubCrateStruct: t v
|
||||||
|
bar: t
|
||||||
|
foo: t
|
||||||
|
|
||||||
|
crate::foo
|
||||||
|
Foo: t v
|
||||||
|
bar: t
|
||||||
|
|
||||||
|
crate::foo::bar
|
||||||
|
PrivateBar: t v
|
||||||
|
PrivateBaz: t v
|
||||||
|
PubCrateStruct: t v
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn glob_across_crates() {
|
fn glob_across_crates() {
|
||||||
covers!(glob_across_crates);
|
covers!(glob_across_crates);
|
||||||
|
|
|
@ -116,7 +116,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
let crate_def_map = db.crate_def_map(krate);
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||||
assert_eq!(module_data.scope.collect_resolutions().len(), 1);
|
assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
|
||||||
});
|
});
|
||||||
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
let crate_def_map = db.crate_def_map(krate);
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||||
assert_eq!(module_data.scope.collect_resolutions().len(), 1);
|
assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
|
||||||
});
|
});
|
||||||
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,14 @@ impl PerNs {
|
||||||
self.macros.map(|it| it.0)
|
self.macros.map(|it| it.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn filter_visibility(self, mut f: impl FnMut(ResolvedVisibility) -> bool) -> PerNs {
|
||||||
|
PerNs {
|
||||||
|
types: self.types.filter(|(_, v)| f(*v)),
|
||||||
|
values: self.values.filter(|(_, v)| f(*v)),
|
||||||
|
macros: self.macros.filter(|(_, v)| f(*v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_visibility(self, vis: ResolvedVisibility) -> PerNs {
|
pub fn with_visibility(self, vis: ResolvedVisibility) -> PerNs {
|
||||||
PerNs {
|
PerNs {
|
||||||
types: self.types.map(|(it, _)| (it, vis)),
|
types: self.types.map(|(it, _)| (it, vis)),
|
||||||
|
|
|
@ -134,13 +134,25 @@ impl ResolvedVisibility {
|
||||||
if from_module.krate != to_module.krate {
|
if from_module.krate != to_module.krate {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// from_module needs to be a descendant of to_module
|
|
||||||
let def_map = db.crate_def_map(from_module.krate);
|
let def_map = db.crate_def_map(from_module.krate);
|
||||||
|
self.visible_from_def_map(&def_map, from_module.local_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn visible_from_def_map(
|
||||||
|
self,
|
||||||
|
def_map: &crate::nameres::CrateDefMap,
|
||||||
|
from_module: crate::LocalModuleId,
|
||||||
|
) -> bool {
|
||||||
|
let to_module = match self {
|
||||||
|
ResolvedVisibility::Module(m) => m,
|
||||||
|
ResolvedVisibility::Public => return true,
|
||||||
|
};
|
||||||
|
// from_module needs to be a descendant of to_module
|
||||||
let mut ancestors = std::iter::successors(Some(from_module), |m| {
|
let mut ancestors = std::iter::successors(Some(from_module), |m| {
|
||||||
let parent_id = def_map[m.local_id].parent?;
|
let parent_id = def_map[*m].parent?;
|
||||||
Some(ModuleId { local_id: parent_id, ..*m })
|
Some(parent_id)
|
||||||
});
|
});
|
||||||
ancestors.any(|m| m == to_module)
|
ancestors.any(|m| m == to_module.local_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue