mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 21:05:02 +00:00
Merge #778
778: Glob imports r=matklad a=flodiebold This implements glob imports, completing #231 :) Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
ffd407afff
4 changed files with 240 additions and 13 deletions
|
@ -327,7 +327,7 @@ impl Enum {
|
||||||
db.enum_data(*self).name.clone()
|
db.enum_data(*self).name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variants(&self, db: &impl HirDatabase) -> Vec<EnumVariant> {
|
pub fn variants(&self, db: &impl PersistentHirDatabase) -> Vec<EnumVariant> {
|
||||||
db.enum_data(*self)
|
db.enum_data(*self)
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -389,7 +389,7 @@ impl EnumVariant {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self, db: &impl HirDatabase) -> Option<Name> {
|
pub fn name(&self, db: &impl PersistentHirDatabase) -> Option<Name> {
|
||||||
db.enum_data(self.parent).variants[self.id].name.clone()
|
db.enum_data(self.parent).variants[self.id].name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,6 @@ test_utils::marks!(
|
||||||
type_var_cycles_resolve_completely
|
type_var_cycles_resolve_completely
|
||||||
type_var_cycles_resolve_as_possible
|
type_var_cycles_resolve_as_possible
|
||||||
type_var_resolves_to_int_var
|
type_var_resolves_to_int_var
|
||||||
|
glob_enum
|
||||||
|
glob_across_crates
|
||||||
);
|
);
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl ModuleScope {
|
||||||
|
|
||||||
/// `Resolution` is basically `DefId` atm, but it should account for stuff like
|
/// `Resolution` is basically `DefId` atm, but it should account for stuff like
|
||||||
/// multiple namespaces, ambiguity and errors.
|
/// multiple namespaces, ambiguity and errors.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
pub struct Resolution {
|
pub struct Resolution {
|
||||||
/// None for unresolved
|
/// None for unresolved
|
||||||
pub def: PerNs<ModuleDef>,
|
pub def: PerNs<ModuleDef>,
|
||||||
|
@ -154,6 +154,8 @@ struct Resolver<'a, DB> {
|
||||||
krate: Crate,
|
krate: Crate,
|
||||||
module_tree: Arc<ModuleTree>,
|
module_tree: Arc<ModuleTree>,
|
||||||
processed_imports: FxHashSet<(ModuleId, ImportId)>,
|
processed_imports: FxHashSet<(ModuleId, ImportId)>,
|
||||||
|
/// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import)
|
||||||
|
glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>,
|
||||||
result: ItemMap,
|
result: ItemMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +175,7 @@ where
|
||||||
krate,
|
krate,
|
||||||
module_tree,
|
module_tree,
|
||||||
processed_imports: FxHashSet::default(),
|
processed_imports: FxHashSet::default(),
|
||||||
|
glob_imports: FxHashMap::default(),
|
||||||
result: ItemMap::default(),
|
result: ItemMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,14 +267,72 @@ where
|
||||||
import: &ImportData,
|
import: &ImportData,
|
||||||
) -> ReachedFixedPoint {
|
) -> ReachedFixedPoint {
|
||||||
log::debug!("resolving import: {:?}", import);
|
log::debug!("resolving import: {:?}", import);
|
||||||
if import.is_glob {
|
|
||||||
return ReachedFixedPoint::Yes;
|
|
||||||
};
|
|
||||||
let original_module = Module { krate: self.krate, module_id };
|
let original_module = Module { krate: self.krate, module_id };
|
||||||
let (def, reached_fixedpoint) =
|
let (def, reached_fixedpoint) =
|
||||||
self.result.resolve_path_fp(self.db, original_module, &import.path);
|
self.result.resolve_path_fp(self.db, original_module, &import.path);
|
||||||
|
|
||||||
if reached_fixedpoint == ReachedFixedPoint::Yes {
|
if reached_fixedpoint != ReachedFixedPoint::Yes {
|
||||||
|
return reached_fixedpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if import.is_glob {
|
||||||
|
log::debug!("glob import: {:?}", import);
|
||||||
|
match def.take_types() {
|
||||||
|
Some(ModuleDef::Module(m)) => {
|
||||||
|
if m.krate != self.krate {
|
||||||
|
tested_by!(glob_across_crates);
|
||||||
|
// glob import from other crate => we can just import everything once
|
||||||
|
let item_map = self.db.item_map(m.krate);
|
||||||
|
let scope = &item_map[m.module_id];
|
||||||
|
let items = scope
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|(name, res)| (name.clone(), res.clone()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.update(module_id, Some(import_id), &items);
|
||||||
|
} else {
|
||||||
|
// glob import from same crate => we do an initial
|
||||||
|
// import, and then need to propagate any further
|
||||||
|
// additions
|
||||||
|
let scope = &self.result[m.module_id];
|
||||||
|
let items = scope
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|(name, res)| (name.clone(), res.clone()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.update(module_id, Some(import_id), &items);
|
||||||
|
// record the glob import in case we add further items
|
||||||
|
self.glob_imports
|
||||||
|
.entry(m.module_id)
|
||||||
|
.or_default()
|
||||||
|
.push((module_id, import_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ModuleDef::Enum(e)) => {
|
||||||
|
tested_by!(glob_enum);
|
||||||
|
// glob import from enum => just import all the variants
|
||||||
|
let variants = e.variants(self.db);
|
||||||
|
let resolutions = variants
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|variant| {
|
||||||
|
let res = Resolution {
|
||||||
|
def: PerNs::both(variant.into(), e.into()),
|
||||||
|
import: Some(import_id),
|
||||||
|
};
|
||||||
|
let name = variant.name(self.db)?;
|
||||||
|
Some((name, res))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.update(module_id, Some(import_id), &resolutions);
|
||||||
|
}
|
||||||
|
Some(d) => {
|
||||||
|
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
log::debug!("glob import {:?} didn't resolve as type", import);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let last_segment = import.path.segments.last().unwrap();
|
let last_segment = import.path.segments.last().unwrap();
|
||||||
let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
|
let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
|
||||||
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
|
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
|
||||||
|
@ -284,17 +345,61 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.update(module_id, |items| {
|
let resolution = Resolution { def, import: Some(import_id) };
|
||||||
let res = Resolution { def, import: Some(import_id) };
|
self.update(module_id, None, &[(name, resolution)]);
|
||||||
items.items.insert(name, res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
reached_fixedpoint
|
reached_fixedpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
module_id: ModuleId,
|
||||||
|
import: Option<ImportId>,
|
||||||
|
resolutions: &[(Name, Resolution)],
|
||||||
|
) {
|
||||||
|
self.update_recursive(module_id, import, resolutions, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_recursive(
|
||||||
|
&mut self,
|
||||||
|
module_id: ModuleId,
|
||||||
|
import: Option<ImportId>,
|
||||||
|
resolutions: &[(Name, Resolution)],
|
||||||
|
depth: usize,
|
||||||
|
) {
|
||||||
|
if depth > 100 {
|
||||||
|
// prevent stack overflows (but this shouldn't be possible)
|
||||||
|
panic!("infinite recursion in glob imports!");
|
||||||
|
}
|
||||||
let module_items = self.result.per_module.get_mut(module_id).unwrap();
|
let module_items = self.result.per_module.get_mut(module_id).unwrap();
|
||||||
f(module_items)
|
let mut changed = false;
|
||||||
|
for (name, res) in resolutions {
|
||||||
|
let existing = module_items.items.entry(name.clone()).or_default();
|
||||||
|
if existing.def.types.is_none() && res.def.types.is_some() {
|
||||||
|
existing.def.types = res.def.types;
|
||||||
|
existing.import = import.or(res.import);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if existing.def.values.is_none() && res.def.values.is_some() {
|
||||||
|
existing.def.values = res.def.values;
|
||||||
|
existing.import = import.or(res.import);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !changed {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let glob_imports = self
|
||||||
|
.glob_imports
|
||||||
|
.get(&module_id)
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|v| v.iter())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
for (glob_importing_module, glob_import) in glob_imports {
|
||||||
|
// We pass the glob import so that the tracked import in those modules is that glob import
|
||||||
|
self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,126 @@ fn re_exports() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_1() {
|
||||||
|
let (item_map, module_id) = item_map(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo;
|
||||||
|
use foo::*;
|
||||||
|
<|>
|
||||||
|
|
||||||
|
//- /foo/mod.rs
|
||||||
|
pub mod bar;
|
||||||
|
pub use self::bar::Baz;
|
||||||
|
pub struct Foo;
|
||||||
|
|
||||||
|
//- /foo/bar.rs
|
||||||
|
pub struct Baz;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
check_module_item_map(
|
||||||
|
&item_map,
|
||||||
|
module_id,
|
||||||
|
"
|
||||||
|
Baz: t v
|
||||||
|
Foo: t v
|
||||||
|
bar: t
|
||||||
|
foo: t
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_2() {
|
||||||
|
let (item_map, module_id) = item_map(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo;
|
||||||
|
use foo::*;
|
||||||
|
<|>
|
||||||
|
|
||||||
|
//- /foo/mod.rs
|
||||||
|
pub mod bar;
|
||||||
|
pub use self::bar::*;
|
||||||
|
pub struct Foo;
|
||||||
|
|
||||||
|
//- /foo/bar.rs
|
||||||
|
pub struct Baz;
|
||||||
|
pub use super::*;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
check_module_item_map(
|
||||||
|
&item_map,
|
||||||
|
module_id,
|
||||||
|
"
|
||||||
|
Baz: t v
|
||||||
|
Foo: t v
|
||||||
|
bar: t
|
||||||
|
foo: t
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_enum() {
|
||||||
|
covers!(glob_enum);
|
||||||
|
let (item_map, module_id) = item_map(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
enum Foo {
|
||||||
|
Bar, Baz
|
||||||
|
}
|
||||||
|
use self::Foo::*;
|
||||||
|
<|>
|
||||||
|
",
|
||||||
|
);
|
||||||
|
check_module_item_map(
|
||||||
|
&item_map,
|
||||||
|
module_id,
|
||||||
|
"
|
||||||
|
Bar: t v
|
||||||
|
Baz: t v
|
||||||
|
Foo: t
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_across_crates() {
|
||||||
|
covers!(glob_across_crates);
|
||||||
|
let (mut db, sr) = MockDatabase::with_files(
|
||||||
|
"
|
||||||
|
//- /main.rs
|
||||||
|
use test_crate::*;
|
||||||
|
|
||||||
|
//- /lib.rs
|
||||||
|
pub struct Baz;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let main_id = sr.files[RelativePath::new("/main.rs")];
|
||||||
|
let lib_id = sr.files[RelativePath::new("/lib.rs")];
|
||||||
|
|
||||||
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
let main_crate = crate_graph.add_crate_root(main_id);
|
||||||
|
let lib_crate = crate_graph.add_crate_root(lib_id);
|
||||||
|
crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate).unwrap();
|
||||||
|
|
||||||
|
db.set_crate_graph(Arc::new(crate_graph));
|
||||||
|
|
||||||
|
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
|
||||||
|
let krate = module.krate(&db).unwrap();
|
||||||
|
let item_map = db.item_map(krate);
|
||||||
|
|
||||||
|
check_module_item_map(
|
||||||
|
&item_map,
|
||||||
|
module.module_id,
|
||||||
|
"
|
||||||
|
Baz: t v
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn module_resolution_works_for_non_standard_filenames() {
|
fn module_resolution_works_for_non_standard_filenames() {
|
||||||
let (item_map, module_id) = item_map_custom_crate_root(
|
let (item_map, module_id) = item_map_custom_crate_root(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue