diff options
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 260 |
1 files changed, 143 insertions, 117 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index a3bc98958..639726b5e 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -19,6 +19,8 @@ pub(crate) mod lower; | |||
19 | use std::sync::Arc; | 19 | use std::sync::Arc; |
20 | 20 | ||
21 | use ra_db::CrateId; | 21 | use ra_db::CrateId; |
22 | use ra_arena::map::ArenaMap; | ||
23 | use test_utils::tested_by; | ||
22 | use rustc_hash::{FxHashMap, FxHashSet}; | 24 | use rustc_hash::{FxHashMap, FxHashSet}; |
23 | 25 | ||
24 | use crate::{ | 26 | use crate::{ |
@@ -27,16 +29,21 @@ use crate::{ | |||
27 | HirDatabase, Crate, | 29 | HirDatabase, Crate, |
28 | Name, | 30 | Name, |
29 | module_tree::{ModuleId, ModuleTree}, | 31 | module_tree::{ModuleId, ModuleTree}, |
30 | //FIXME: deglobify | 32 | nameres::lower::{ImportId, LoweredModule, ImportData}, |
31 | nameres::lower::*, | ||
32 | }; | 33 | }; |
33 | 34 | ||
34 | /// `ItemMap` is the result of name resolution. It contains, for each | 35 | /// `ItemMap` is the result of name resolution. It contains, for each |
35 | /// module, the set of visible items. | 36 | /// module, the set of visible items. |
36 | // FIXME: currenty we compute item map per source-root. We should do it per crate instead. | ||
37 | #[derive(Default, Debug, PartialEq, Eq)] | 37 | #[derive(Default, Debug, PartialEq, Eq)] |
38 | pub struct ItemMap { | 38 | pub struct ItemMap { |
39 | pub per_module: FxHashMap<ModuleId, ModuleScope>, | 39 | per_module: ArenaMap<ModuleId, ModuleScope>, |
40 | } | ||
41 | |||
42 | impl std::ops::Index<ModuleId> for ItemMap { | ||
43 | type Output = ModuleScope; | ||
44 | fn index(&self, id: ModuleId) -> &ModuleScope { | ||
45 | &self.per_module[id] | ||
46 | } | ||
40 | } | 47 | } |
41 | 48 | ||
42 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 49 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
@@ -58,7 +65,7 @@ impl ModuleScope { | |||
58 | #[derive(Debug, Clone, PartialEq, Eq)] | 65 | #[derive(Debug, Clone, PartialEq, Eq)] |
59 | pub struct Resolution { | 66 | pub struct Resolution { |
60 | /// None for unresolved | 67 | /// None for unresolved |
61 | pub def_id: PerNs<ModuleDef>, | 68 | pub def: PerNs<ModuleDef>, |
62 | /// ident by which this is imported into local scope. | 69 | /// ident by which this is imported into local scope. |
63 | pub import: Option<ImportId>, | 70 | pub import: Option<ImportId>, |
64 | } | 71 | } |
@@ -210,11 +217,11 @@ where | |||
210 | let krate = Crate::new(crate_id); | 217 | let krate = Crate::new(crate_id); |
211 | for dep in krate.dependencies(self.db) { | 218 | for dep in krate.dependencies(self.db) { |
212 | if let Some(module) = dep.krate.root_module(self.db) { | 219 | if let Some(module) = dep.krate.root_module(self.db) { |
213 | let def_id = module.into(); | 220 | let def = module.into(); |
214 | self.add_module_item( | 221 | self.add_module_item( |
215 | &mut module_items, | 222 | &mut module_items, |
216 | dep.name.clone(), | 223 | dep.name.clone(), |
217 | PerNs::types(def_id), | 224 | PerNs::types(def), |
218 | ); | 225 | ); |
219 | } | 226 | } |
220 | } | 227 | } |
@@ -226,7 +233,7 @@ where | |||
226 | module_items.items.insert( | 233 | module_items.items.insert( |
227 | segment.name.clone(), | 234 | segment.name.clone(), |
228 | Resolution { | 235 | Resolution { |
229 | def_id: PerNs::none(), | 236 | def: PerNs::none(), |
230 | import: Some(import_id), | 237 | import: Some(import_id), |
231 | }, | 238 | }, |
232 | ); | 239 | ); |
@@ -234,11 +241,8 @@ where | |||
234 | } | 241 | } |
235 | } | 242 | } |
236 | // Populate explicitly declared items, except modules | 243 | // Populate explicitly declared items, except modules |
237 | for (name, &def_id) in input.declarations.iter() { | 244 | for (name, &def) in input.declarations.iter() { |
238 | let resolution = Resolution { | 245 | let resolution = Resolution { def, import: None }; |
239 | def_id, | ||
240 | import: None, | ||
241 | }; | ||
242 | module_items.items.insert(name.clone(), resolution); | 246 | module_items.items.insert(name.clone(), resolution); |
243 | } | 247 | } |
244 | 248 | ||
@@ -254,16 +258,8 @@ where | |||
254 | self.result.per_module.insert(module_id, module_items); | 258 | self.result.per_module.insert(module_id, module_items); |
255 | } | 259 | } |
256 | 260 | ||
257 | fn add_module_item( | 261 | fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) { |
258 | &self, | 262 | let resolution = Resolution { def, import: None }; |
259 | module_items: &mut ModuleScope, | ||
260 | name: Name, | ||
261 | def_id: PerNs<ModuleDef>, | ||
262 | ) { | ||
263 | let resolution = Resolution { | ||
264 | def_id, | ||
265 | import: None, | ||
266 | }; | ||
267 | module_items.items.insert(name, resolution); | 263 | module_items.items.insert(name, resolution); |
268 | } | 264 | } |
269 | 265 | ||
@@ -273,7 +269,7 @@ where | |||
273 | // already done | 269 | // already done |
274 | continue; | 270 | continue; |
275 | } | 271 | } |
276 | if self.resolve_import(module_id, import_id, import_data) { | 272 | if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes { |
277 | log::debug!("import {:?} resolved (or definite error)", import_id); | 273 | log::debug!("import {:?} resolved (or definite error)", import_id); |
278 | self.processed_imports.insert((module_id, import_id)); | 274 | self.processed_imports.insert((module_id, import_id)); |
279 | } | 275 | } |
@@ -285,116 +281,146 @@ where | |||
285 | module_id: ModuleId, | 281 | module_id: ModuleId, |
286 | import_id: ImportId, | 282 | import_id: ImportId, |
287 | import: &ImportData, | 283 | import: &ImportData, |
288 | ) -> bool { | 284 | ) -> ReachedFixedPoint { |
289 | log::debug!("resolving import: {:?}", import); | 285 | log::debug!("resolving import: {:?}", import); |
290 | if import.is_glob { | 286 | if import.is_glob { |
291 | return false; | 287 | return ReachedFixedPoint::Yes; |
292 | }; | 288 | }; |
289 | let original_module = Module { | ||
290 | krate: self.krate, | ||
291 | module_id, | ||
292 | }; | ||
293 | let (def, reached_fixedpoint) = | ||
294 | self.result | ||
295 | .resolve_path_fp(self.db, original_module, &import.path); | ||
296 | |||
297 | if reached_fixedpoint == ReachedFixedPoint::Yes { | ||
298 | let last_segment = import.path.segments.last().unwrap(); | ||
299 | self.update(module_id, |items| { | ||
300 | let res = Resolution { | ||
301 | def, | ||
302 | import: Some(import_id), | ||
303 | }; | ||
304 | items.items.insert(last_segment.name.clone(), res); | ||
305 | }); | ||
306 | log::debug!( | ||
307 | "resolved import {:?} ({:?}) cross-source root to {:?}", | ||
308 | last_segment.name, | ||
309 | import, | ||
310 | def, | ||
311 | ); | ||
312 | } | ||
313 | reached_fixedpoint | ||
314 | } | ||
315 | |||
316 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | ||
317 | let module_items = self.result.per_module.get_mut(module_id).unwrap(); | ||
318 | f(module_items) | ||
319 | } | ||
320 | } | ||
321 | |||
322 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
323 | enum ReachedFixedPoint { | ||
324 | Yes, | ||
325 | No, | ||
326 | } | ||
293 | 327 | ||
294 | let mut curr: ModuleId = match import.path.kind { | 328 | impl ItemMap { |
295 | PathKind::Plain | PathKind::Self_ => module_id, | 329 | pub(crate) fn resolve_path( |
330 | &self, | ||
331 | db: &impl HirDatabase, | ||
332 | original_module: Module, | ||
333 | path: &Path, | ||
334 | ) -> PerNs<ModuleDef> { | ||
335 | self.resolve_path_fp(db, original_module, path).0 | ||
336 | } | ||
337 | |||
338 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change | ||
339 | // the result. | ||
340 | fn resolve_path_fp( | ||
341 | &self, | ||
342 | db: &impl HirDatabase, | ||
343 | original_module: Module, | ||
344 | path: &Path, | ||
345 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | ||
346 | let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind { | ||
347 | PathKind::Crate => original_module.crate_root(db).into(), | ||
348 | PathKind::Self_ | PathKind::Plain => original_module.into(), | ||
296 | PathKind::Super => { | 349 | PathKind::Super => { |
297 | match module_id.parent(&self.module_tree) { | 350 | if let Some(p) = original_module.parent(db) { |
298 | Some(it) => it, | 351 | p.into() |
299 | None => { | 352 | } else { |
300 | // TODO: error | 353 | log::debug!("super path in root module"); |
301 | log::debug!("super path in root module"); | 354 | return (PerNs::none(), ReachedFixedPoint::Yes); |
302 | return true; // this can't suddenly resolve if we just resolve some other imports | ||
303 | } | ||
304 | } | 355 | } |
305 | } | 356 | } |
306 | PathKind::Crate => module_id.crate_root(&self.module_tree), | ||
307 | PathKind::Abs => { | 357 | PathKind::Abs => { |
308 | // TODO: absolute use is not supported for now | 358 | // TODO: absolute use is not supported |
309 | return false; | 359 | return (PerNs::none(), ReachedFixedPoint::Yes); |
310 | } | 360 | } |
311 | }; | 361 | }); |
312 | 362 | ||
313 | for (i, segment) in import.path.segments.iter().enumerate() { | 363 | for (i, segment) in path.segments.iter().enumerate() { |
314 | let is_last = i == import.path.segments.len() - 1; | 364 | let curr = match curr_per_ns.as_ref().take_types() { |
315 | 365 | Some(r) => r, | |
316 | let def_id = match self.result.per_module[&curr].items.get(&segment.name) { | 366 | None => { |
317 | Some(res) if !res.def_id.is_none() => res.def_id, | 367 | // we still have path segments left, but the path so far |
318 | _ => { | 368 | // didn't resolve in the types namespace => no resolution |
319 | log::debug!("path segment {:?} not found", segment.name); | 369 | // (don't break here because curr_per_ns might contain |
320 | return false; | 370 | // something in the value namespace, and it would be wrong |
371 | // to return that) | ||
372 | return (PerNs::none(), ReachedFixedPoint::No); | ||
321 | } | 373 | } |
322 | }; | 374 | }; |
375 | // resolve segment in curr | ||
376 | |||
377 | curr_per_ns = match curr { | ||
378 | ModuleDef::Module(module) => { | ||
379 | if module.krate != original_module.krate { | ||
380 | let path = Path { | ||
381 | segments: path.segments[i..].iter().cloned().collect(), | ||
382 | kind: PathKind::Crate, | ||
383 | }; | ||
384 | log::debug!("resolving {:?} in other crate", path); | ||
385 | let def = module.resolve_path(db, &path); | ||
386 | return (def, ReachedFixedPoint::Yes); | ||
387 | } | ||
323 | 388 | ||
324 | if !is_last { | 389 | match self[module.module_id].items.get(&segment.name) { |
325 | let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { | 390 | Some(res) if !res.def.is_none() => res.def, |
326 | d | 391 | _ => { |
327 | } else { | 392 | log::debug!("path segment {:?} not found", segment.name); |
328 | log::debug!( | 393 | return (PerNs::none(), ReachedFixedPoint::No); |
329 | "path segment {:?} resolved to value only, but is not last", | ||
330 | segment.name | ||
331 | ); | ||
332 | return false; | ||
333 | }; | ||
334 | curr = match type_def_id { | ||
335 | ModuleDef::Module(module) => { | ||
336 | if module.krate == self.krate { | ||
337 | module.module_id | ||
338 | } else { | ||
339 | let path = Path { | ||
340 | segments: import.path.segments[i + 1..].iter().cloned().collect(), | ||
341 | kind: PathKind::Crate, | ||
342 | }; | ||
343 | log::debug!("resolving {:?} in other source root", path); | ||
344 | let def_id = module.resolve_path(self.db, &path); | ||
345 | if !def_id.is_none() { | ||
346 | let last_segment = path.segments.last().unwrap(); | ||
347 | self.update(module_id, |items| { | ||
348 | let res = Resolution { | ||
349 | def_id, | ||
350 | import: Some(import_id), | ||
351 | }; | ||
352 | items.items.insert(last_segment.name.clone(), res); | ||
353 | }); | ||
354 | log::debug!( | ||
355 | "resolved import {:?} ({:?}) cross-source root to {:?}", | ||
356 | last_segment.name, | ||
357 | import, | ||
358 | def_id, | ||
359 | ); | ||
360 | return true; | ||
361 | } else { | ||
362 | log::debug!("rest of path did not resolve in other source root"); | ||
363 | return true; | ||
364 | } | ||
365 | } | 394 | } |
366 | } | 395 | } |
367 | _ => { | 396 | } |
368 | log::debug!( | 397 | ModuleDef::Enum(e) => { |
369 | "path segment {:?} resolved to non-module {:?}, but is not last", | 398 | // enum variant |
370 | segment.name, | 399 | tested_by!(item_map_enum_importing); |
371 | type_def_id, | 400 | let matching_variant = e |
372 | ); | 401 | .variants(db) |
373 | return true; // this resolved to a non-module, so the path won't ever resolve | 402 | .into_iter() |
403 | .find(|(n, _variant)| n == &segment.name); | ||
404 | |||
405 | match matching_variant { | ||
406 | Some((_n, variant)) => PerNs::both(variant.into(), (*e).into()), | ||
407 | None => PerNs::none(), | ||
374 | } | 408 | } |
375 | } | 409 | } |
376 | } else { | 410 | _ => { |
377 | log::debug!( | 411 | // could be an inherent method call in UFCS form |
378 | "resolved import {:?} ({:?}) within source root to {:?}", | 412 | // (`Struct::method`), or some other kind of associated |
379 | segment.name, | 413 | // item... Which we currently don't handle (TODO) |
380 | import, | 414 | log::debug!( |
381 | def_id, | 415 | "path segment {:?} resolved to non-module {:?}, but is not last", |
382 | ); | 416 | segment.name, |
383 | self.update(module_id, |items| { | 417 | curr, |
384 | let res = Resolution { | 418 | ); |
385 | def_id, | 419 | return (PerNs::none(), ReachedFixedPoint::Yes); |
386 | import: Some(import_id), | 420 | } |
387 | }; | 421 | }; |
388 | items.items.insert(segment.name.clone(), res); | ||
389 | }) | ||
390 | } | ||
391 | } | 422 | } |
392 | true | 423 | (curr_per_ns, ReachedFixedPoint::Yes) |
393 | } | ||
394 | |||
395 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | ||
396 | let module_items = self.result.per_module.get_mut(&module_id).unwrap(); | ||
397 | f(module_items) | ||
398 | } | 424 | } |
399 | } | 425 | } |
400 | 426 | ||