aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r--crates/ra_hir/src/nameres.rs127
1 files changed, 116 insertions, 11 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index b7382d9c3..94f7db024 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -61,7 +61,7 @@ impl ModuleScope {
61 61
62/// `Resolution` is basically `DefId` atm, but it should account for stuff like 62/// `Resolution` is basically `DefId` atm, but it should account for stuff like
63/// multiple namespaces, ambiguity and errors. 63/// multiple namespaces, ambiguity and errors.
64#[derive(Debug, Clone, PartialEq, Eq)] 64#[derive(Debug, Clone, PartialEq, Eq, Default)]
65pub struct Resolution { 65pub struct Resolution {
66 /// None for unresolved 66 /// None for unresolved
67 pub def: PerNs<ModuleDef>, 67 pub def: PerNs<ModuleDef>,
@@ -154,6 +154,8 @@ struct Resolver<'a, DB> {
154 krate: Crate, 154 krate: Crate,
155 module_tree: Arc<ModuleTree>, 155 module_tree: Arc<ModuleTree>,
156 processed_imports: FxHashSet<(ModuleId, ImportId)>, 156 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)>>,
157 result: ItemMap, 159 result: ItemMap,
158} 160}
159 161
@@ -173,6 +175,7 @@ where
173 krate, 175 krate,
174 module_tree, 176 module_tree,
175 processed_imports: FxHashSet::default(), 177 processed_imports: FxHashSet::default(),
178 glob_imports: FxHashMap::default(),
176 result: ItemMap::default(), 179 result: ItemMap::default(),
177 } 180 }
178 } 181 }
@@ -264,14 +267,72 @@ where
264 import: &ImportData, 267 import: &ImportData,
265 ) -> ReachedFixedPoint { 268 ) -> ReachedFixedPoint {
266 log::debug!("resolving import: {:?}", import); 269 log::debug!("resolving import: {:?}", import);
267 if import.is_glob {
268 return ReachedFixedPoint::Yes;
269 };
270 let original_module = Module { krate: self.krate, module_id }; 270 let original_module = Module { krate: self.krate, module_id };
271 let (def, reached_fixedpoint) = 271 let (def, reached_fixedpoint) =
272 self.result.resolve_path_fp(self.db, original_module, &import.path); 272 self.result.resolve_path_fp(self.db, original_module, &import.path);
273 273
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 {
275 let last_segment = import.path.segments.last().unwrap(); 336 let last_segment = import.path.segments.last().unwrap();
276 let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); 337 let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
277 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); 338 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
@@ -284,17 +345,61 @@ where
284 } 345 }
285 } 346 }
286 } 347 }
287 self.update(module_id, |items| { 348 let resolution = Resolution { def, import: Some(import_id) };
288 let res = Resolution { def, import: Some(import_id) }; 349 self.update(module_id, None, &[(name, resolution)]);
289 items.items.insert(name, res);
290 });
291 } 350 }
292 reached_fixedpoint 351 reached_fixedpoint
293 } 352 }
294 353
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 }
296 let module_items = self.result.per_module.get_mut(module_id).unwrap(); 374 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 }
298 } 403 }
299} 404}
300 405