diff options
author | Florian Diebold <[email protected]> | 2019-02-10 15:19:50 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-02-10 15:48:29 +0000 |
commit | 2f24e740db3365afac56aad3e8a533340369ef7d (patch) | |
tree | 18084cb38cb0fe1faa621c3209a9f3f411faf5f8 /crates/ra_hir | |
parent | 2e1d739a8063307facf9ff098c26b02005092e05 (diff) |
Implement glob imports within the same crate
Fixes #231.
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 93 |
1 files changed, 77 insertions, 16 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 2442e754e..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)] |
65 | pub struct Resolution { | 65 | pub 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 | } |
@@ -281,12 +284,28 @@ where | |||
281 | // glob import from other crate => we can just import everything once | 284 | // glob import from other crate => we can just import everything once |
282 | let item_map = self.db.item_map(m.krate); | 285 | let item_map = self.db.item_map(m.krate); |
283 | let scope = &item_map[m.module_id]; | 286 | let scope = &item_map[m.module_id]; |
284 | self.update(module_id, |items| { | 287 | let items = scope |
285 | // TODO: handle shadowing and visibility | 288 | .items |
286 | items.items.extend( | 289 | .iter() |
287 | scope.items.iter().map(|(name, res)| (name.clone(), res.clone())), | 290 | .map(|(name, res)| (name.clone(), res.clone())) |
288 | ); | 291 | .collect::<Vec<_>>(); |
289 | }); | 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)); | ||
290 | } | 309 | } |
291 | } | 310 | } |
292 | Some(ModuleDef::Enum(e)) => { | 311 | Some(ModuleDef::Enum(e)) => { |
@@ -304,9 +323,7 @@ where | |||
304 | Some((name, res)) | 323 | Some((name, res)) |
305 | }) | 324 | }) |
306 | .collect::<Vec<_>>(); | 325 | .collect::<Vec<_>>(); |
307 | self.update(module_id, |items| { | 326 | self.update(module_id, Some(import_id), &resolutions); |
308 | items.items.extend(resolutions); | ||
309 | }); | ||
310 | } | 327 | } |
311 | Some(d) => { | 328 | Some(d) => { |
312 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | 329 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); |
@@ -328,17 +345,61 @@ where | |||
328 | } | 345 | } |
329 | } | 346 | } |
330 | } | 347 | } |
331 | self.update(module_id, |items| { | 348 | let resolution = Resolution { def, import: Some(import_id) }; |
332 | let res = Resolution { def, import: Some(import_id) }; | 349 | self.update(module_id, None, &[(name, resolution)]); |
333 | items.items.insert(name, res); | ||
334 | }); | ||
335 | } | 350 | } |
336 | reached_fixedpoint | 351 | reached_fixedpoint |
337 | } | 352 | } |
338 | 353 | ||
339 | 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 | } | ||
340 | 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(); |
341 | 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 | } | ||
342 | } | 403 | } |
343 | } | 404 | } |
344 | 405 | ||