Skip to content

Commit ffd407a

Browse files
bors[bot]flodiebold
andcommitted
Merge #778
778: Glob imports r=matklad a=flodiebold This implements glob imports, completing #231 :) Co-authored-by: Florian Diebold <flodiebold@gmail.com>
2 parents b952c27 + 2f24e74 commit ffd407a

File tree

4 files changed

+240
-13
lines changed

4 files changed

+240
-13
lines changed

crates/ra_hir/src/code_model_api.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ impl Enum {
327327
db.enum_data(*self).name.clone()
328328
}
329329

330-
pub fn variants(&self, db: &impl HirDatabase) -> Vec<EnumVariant> {
330+
pub fn variants(&self, db: &impl PersistentHirDatabase) -> Vec<EnumVariant> {
331331
db.enum_data(*self)
332332
.variants
333333
.iter()
@@ -389,7 +389,7 @@ impl EnumVariant {
389389
self.parent
390390
}
391391

392-
pub fn name(&self, db: &impl HirDatabase) -> Option<Name> {
392+
pub fn name(&self, db: &impl PersistentHirDatabase) -> Option<Name> {
393393
db.enum_data(self.parent).variants[self.id].name.clone()
394394
}
395395

crates/ra_hir/src/marks.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ test_utils::marks!(
44
type_var_cycles_resolve_completely
55
type_var_cycles_resolve_as_possible
66
type_var_resolves_to_int_var
7+
glob_enum
8+
glob_across_crates
79
);

crates/ra_hir/src/nameres.rs

Lines changed: 116 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl ModuleScope {
6161

6262
/// `Resolution` is basically `DefId` atm, but it should account for stuff like
6363
/// multiple namespaces, ambiguity and errors.
64-
#[derive(Debug, Clone, PartialEq, Eq)]
64+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
6565
pub struct Resolution {
6666
/// None for unresolved
6767
pub def: PerNs<ModuleDef>,
@@ -154,6 +154,8 @@ struct Resolver<'a, DB> {
154154
krate: Crate,
155155
module_tree: Arc<ModuleTree>,
156156
processed_imports: FxHashSet<(ModuleId, ImportId)>,
157+
/// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import)
158+
glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>,
157159
result: ItemMap,
158160
}
159161

@@ -173,6 +175,7 @@ where
173175
krate,
174176
module_tree,
175177
processed_imports: FxHashSet::default(),
178+
glob_imports: FxHashMap::default(),
176179
result: ItemMap::default(),
177180
}
178181
}
@@ -264,14 +267,72 @@ where
264267
import: &ImportData,
265268
) -> ReachedFixedPoint {
266269
log::debug!("resolving import: {:?}", import);
267-
if import.is_glob {
268-
return ReachedFixedPoint::Yes;
269-
};
270270
let original_module = Module { krate: self.krate, module_id };
271271
let (def, reached_fixedpoint) =
272272
self.result.resolve_path_fp(self.db, original_module, &import.path);
273273

274-
if reached_fixedpoint == ReachedFixedPoint::Yes {
274+
if reached_fixedpoint != ReachedFixedPoint::Yes {
275+
return reached_fixedpoint;
276+
}
277+
278+
if import.is_glob {
279+
log::debug!("glob import: {:?}", import);
280+
match def.take_types() {
281+
Some(ModuleDef::Module(m)) => {
282+
if m.krate != self.krate {
283+
tested_by!(glob_across_crates);
284+
// glob import from other crate => we can just import everything once
285+
let item_map = self.db.item_map(m.krate);
286+
let scope = &item_map[m.module_id];
287+
let items = scope
288+
.items
289+
.iter()
290+
.map(|(name, res)| (name.clone(), res.clone()))
291+
.collect::<Vec<_>>();
292+
self.update(module_id, Some(import_id), &items);
293+
} else {
294+
// glob import from same crate => we do an initial
295+
// import, and then need to propagate any further
296+
// additions
297+
let scope = &self.result[m.module_id];
298+
let items = scope
299+
.items
300+
.iter()
301+
.map(|(name, res)| (name.clone(), res.clone()))
302+
.collect::<Vec<_>>();
303+
self.update(module_id, Some(import_id), &items);
304+
// record the glob import in case we add further items
305+
self.glob_imports
306+
.entry(m.module_id)
307+
.or_default()
308+
.push((module_id, import_id));
309+
}
310+
}
311+
Some(ModuleDef::Enum(e)) => {
312+
tested_by!(glob_enum);
313+
// glob import from enum => just import all the variants
314+
let variants = e.variants(self.db);
315+
let resolutions = variants
316+
.into_iter()
317+
.filter_map(|variant| {
318+
let res = Resolution {
319+
def: PerNs::both(variant.into(), e.into()),
320+
import: Some(import_id),
321+
};
322+
let name = variant.name(self.db)?;
323+
Some((name, res))
324+
})
325+
.collect::<Vec<_>>();
326+
self.update(module_id, Some(import_id), &resolutions);
327+
}
328+
Some(d) => {
329+
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
330+
}
331+
None => {
332+
log::debug!("glob import {:?} didn't resolve as type", import);
333+
}
334+
}
335+
} else {
275336
let last_segment = import.path.segments.last().unwrap();
276337
let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
277338
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
@@ -284,17 +345,61 @@ where
284345
}
285346
}
286347
}
287-
self.update(module_id, |items| {
288-
let res = Resolution { def, import: Some(import_id) };
289-
items.items.insert(name, res);
290-
});
348+
let resolution = Resolution { def, import: Some(import_id) };
349+
self.update(module_id, None, &[(name, resolution)]);
291350
}
292351
reached_fixedpoint
293352
}
294353

295-
fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
354+
fn update(
355+
&mut self,
356+
module_id: ModuleId,
357+
import: Option<ImportId>,
358+
resolutions: &[(Name, Resolution)],
359+
) {
360+
self.update_recursive(module_id, import, resolutions, 0)
361+
}
362+
363+
fn update_recursive(
364+
&mut self,
365+
module_id: ModuleId,
366+
import: Option<ImportId>,
367+
resolutions: &[(Name, Resolution)],
368+
depth: usize,
369+
) {
370+
if depth > 100 {
371+
// prevent stack overflows (but this shouldn't be possible)
372+
panic!("infinite recursion in glob imports!");
373+
}
296374
let module_items = self.result.per_module.get_mut(module_id).unwrap();
297-
f(module_items)
375+
let mut changed = false;
376+
for (name, res) in resolutions {
377+
let existing = module_items.items.entry(name.clone()).or_default();
378+
if existing.def.types.is_none() && res.def.types.is_some() {
379+
existing.def.types = res.def.types;
380+
existing.import = import.or(res.import);
381+
changed = true;
382+
}
383+
if existing.def.values.is_none() && res.def.values.is_some() {
384+
existing.def.values = res.def.values;
385+
existing.import = import.or(res.import);
386+
changed = true;
387+
}
388+
}
389+
if !changed {
390+
return;
391+
}
392+
let glob_imports = self
393+
.glob_imports
394+
.get(&module_id)
395+
.into_iter()
396+
.flat_map(|v| v.iter())
397+
.cloned()
398+
.collect::<Vec<_>>();
399+
for (glob_importing_module, glob_import) in glob_imports {
400+
// We pass the glob import so that the tracked import in those modules is that glob import
401+
self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
402+
}
298403
}
299404
}
300405

crates/ra_hir/src/nameres/tests.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,126 @@ fn re_exports() {
164164
);
165165
}
166166

167+
#[test]
168+
fn glob_1() {
169+
let (item_map, module_id) = item_map(
170+
"
171+
//- /lib.rs
172+
mod foo;
173+
use foo::*;
174+
<|>
175+
176+
//- /foo/mod.rs
177+
pub mod bar;
178+
pub use self::bar::Baz;
179+
pub struct Foo;
180+
181+
//- /foo/bar.rs
182+
pub struct Baz;
183+
",
184+
);
185+
check_module_item_map(
186+
&item_map,
187+
module_id,
188+
"
189+
Baz: t v
190+
Foo: t v
191+
bar: t
192+
foo: t
193+
",
194+
);
195+
}
196+
197+
#[test]
198+
fn glob_2() {
199+
let (item_map, module_id) = item_map(
200+
"
201+
//- /lib.rs
202+
mod foo;
203+
use foo::*;
204+
<|>
205+
206+
//- /foo/mod.rs
207+
pub mod bar;
208+
pub use self::bar::*;
209+
pub struct Foo;
210+
211+
//- /foo/bar.rs
212+
pub struct Baz;
213+
pub use super::*;
214+
",
215+
);
216+
check_module_item_map(
217+
&item_map,
218+
module_id,
219+
"
220+
Baz: t v
221+
Foo: t v
222+
bar: t
223+
foo: t
224+
",
225+
);
226+
}
227+
228+
#[test]
229+
fn glob_enum() {
230+
covers!(glob_enum);
231+
let (item_map, module_id) = item_map(
232+
"
233+
//- /lib.rs
234+
enum Foo {
235+
Bar, Baz
236+
}
237+
use self::Foo::*;
238+
<|>
239+
",
240+
);
241+
check_module_item_map(
242+
&item_map,
243+
module_id,
244+
"
245+
Bar: t v
246+
Baz: t v
247+
Foo: t
248+
",
249+
);
250+
}
251+
252+
#[test]
253+
fn glob_across_crates() {
254+
covers!(glob_across_crates);
255+
let (mut db, sr) = MockDatabase::with_files(
256+
"
257+
//- /main.rs
258+
use test_crate::*;
259+
260+
//- /lib.rs
261+
pub struct Baz;
262+
",
263+
);
264+
let main_id = sr.files[RelativePath::new("/main.rs")];
265+
let lib_id = sr.files[RelativePath::new("/lib.rs")];
266+
267+
let mut crate_graph = CrateGraph::default();
268+
let main_crate = crate_graph.add_crate_root(main_id);
269+
let lib_crate = crate_graph.add_crate_root(lib_id);
270+
crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate).unwrap();
271+
272+
db.set_crate_graph(Arc::new(crate_graph));
273+
274+
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
275+
let krate = module.krate(&db).unwrap();
276+
let item_map = db.item_map(krate);
277+
278+
check_module_item_map(
279+
&item_map,
280+
module.module_id,
281+
"
282+
Baz: t v
283+
",
284+
);
285+
}
286+
167287
#[test]
168288
fn module_resolution_works_for_non_standard_filenames() {
169289
let (item_map, module_id) = item_map_custom_crate_root(

0 commit comments

Comments
 (0)