diff options
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/marks.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 211 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests.rs | 21 |
3 files changed, 142 insertions, 97 deletions
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index f4d0c3e59..338ed0516 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use test_utils::mark; | 1 | test_utils::marks!( |
2 | 2 | name_res_works_for_broken_modules | |
3 | mark!(name_res_works_for_broken_modules); | 3 | item_map_enum_importing |
4 | ); | ||
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index a3bc98958..8c8494b46 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -19,6 +19,7 @@ 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 test_utils::tested_by; | ||
22 | use rustc_hash::{FxHashMap, FxHashSet}; | 23 | use rustc_hash::{FxHashMap, FxHashSet}; |
23 | 24 | ||
24 | use crate::{ | 25 | use crate::{ |
@@ -273,7 +274,7 @@ where | |||
273 | // already done | 274 | // already done |
274 | continue; | 275 | continue; |
275 | } | 276 | } |
276 | if self.resolve_import(module_id, import_id, import_data) { | 277 | if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes { |
277 | log::debug!("import {:?} resolved (or definite error)", import_id); | 278 | log::debug!("import {:?} resolved (or definite error)", import_id); |
278 | self.processed_imports.insert((module_id, import_id)); | 279 | self.processed_imports.insert((module_id, import_id)); |
279 | } | 280 | } |
@@ -285,116 +286,138 @@ where | |||
285 | module_id: ModuleId, | 286 | module_id: ModuleId, |
286 | import_id: ImportId, | 287 | import_id: ImportId, |
287 | import: &ImportData, | 288 | import: &ImportData, |
288 | ) -> bool { | 289 | ) -> ReachedFixedPoint { |
289 | log::debug!("resolving import: {:?}", import); | 290 | log::debug!("resolving import: {:?}", import); |
290 | if import.is_glob { | 291 | if import.is_glob { |
291 | return false; | 292 | return ReachedFixedPoint::Yes; |
292 | }; | 293 | }; |
294 | let original_module = Module { | ||
295 | krate: self.krate, | ||
296 | module_id, | ||
297 | }; | ||
298 | let (def_id, reached_fixedpoint) = | ||
299 | self.result | ||
300 | .resolve_path(self.db, original_module, &import.path); | ||
301 | |||
302 | if reached_fixedpoint == ReachedFixedPoint::Yes { | ||
303 | let last_segment = import.path.segments.last().unwrap(); | ||
304 | self.update(module_id, |items| { | ||
305 | let res = Resolution { | ||
306 | def_id, | ||
307 | import: Some(import_id), | ||
308 | }; | ||
309 | items.items.insert(last_segment.name.clone(), res); | ||
310 | }); | ||
311 | log::debug!( | ||
312 | "resolved import {:?} ({:?}) cross-source root to {:?}", | ||
313 | last_segment.name, | ||
314 | import, | ||
315 | def_id, | ||
316 | ); | ||
317 | } | ||
318 | reached_fixedpoint | ||
319 | } | ||
320 | |||
321 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | ||
322 | let module_items = self.result.per_module.get_mut(&module_id).unwrap(); | ||
323 | f(module_items) | ||
324 | } | ||
325 | } | ||
293 | 326 | ||
294 | let mut curr: ModuleId = match import.path.kind { | 327 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
295 | PathKind::Plain | PathKind::Self_ => module_id, | 328 | enum ReachedFixedPoint { |
329 | Yes, | ||
330 | No, | ||
331 | } | ||
332 | |||
333 | impl ItemMap { | ||
334 | // returns true if we are sure that additions to `ItemMap` wouldn't change | ||
335 | // the result. That is, if we've reached fixed point at this particular | ||
336 | // import. | ||
337 | fn resolve_path( | ||
338 | &self, | ||
339 | db: &impl HirDatabase, | ||
340 | original_module: Module, | ||
341 | path: &Path, | ||
342 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | ||
343 | let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind { | ||
344 | PathKind::Crate => original_module.crate_root(db).into(), | ||
345 | PathKind::Self_ | PathKind::Plain => original_module.into(), | ||
296 | PathKind::Super => { | 346 | PathKind::Super => { |
297 | match module_id.parent(&self.module_tree) { | 347 | if let Some(p) = original_module.parent(db) { |
298 | Some(it) => it, | 348 | p.into() |
299 | None => { | 349 | } else { |
300 | // TODO: error | 350 | log::debug!("super path in root module"); |
301 | log::debug!("super path in root module"); | 351 | return (PerNs::none(), ReachedFixedPoint::Yes); |
302 | return true; // this can't suddenly resolve if we just resolve some other imports | ||
303 | } | ||
304 | } | 352 | } |
305 | } | 353 | } |
306 | PathKind::Crate => module_id.crate_root(&self.module_tree), | ||
307 | PathKind::Abs => { | 354 | PathKind::Abs => { |
308 | // TODO: absolute use is not supported for now | 355 | // TODO: absolute use is not supported |
309 | return false; | 356 | return (PerNs::none(), ReachedFixedPoint::Yes); |
310 | } | 357 | } |
311 | }; | 358 | }); |
312 | 359 | ||
313 | for (i, segment) in import.path.segments.iter().enumerate() { | 360 | for (i, segment) in path.segments.iter().enumerate() { |
314 | let is_last = i == import.path.segments.len() - 1; | 361 | let curr = match curr_per_ns.as_ref().take_types() { |
315 | 362 | Some(r) => r, | |
316 | let def_id = match self.result.per_module[&curr].items.get(&segment.name) { | 363 | None => { |
317 | Some(res) if !res.def_id.is_none() => res.def_id, | 364 | // we still have path segments left, but the path so far |
318 | _ => { | 365 | // didn't resolve in the types namespace => no resolution |
319 | log::debug!("path segment {:?} not found", segment.name); | 366 | // (don't break here because curr_per_ns might contain |
320 | return false; | 367 | // something in the value namespace, and it would be wrong |
368 | // to return that) | ||
369 | return (PerNs::none(), ReachedFixedPoint::No); | ||
321 | } | 370 | } |
322 | }; | 371 | }; |
372 | // resolve segment in curr | ||
373 | |||
374 | curr_per_ns = match curr { | ||
375 | ModuleDef::Module(module) => { | ||
376 | if module.krate != original_module.krate { | ||
377 | let path = Path { | ||
378 | segments: path.segments[i..].iter().cloned().collect(), | ||
379 | kind: PathKind::Crate, | ||
380 | }; | ||
381 | log::debug!("resolving {:?} in other crate", path); | ||
382 | let def_id = module.resolve_path(db, &path); | ||
383 | return (def_id, ReachedFixedPoint::Yes); | ||
384 | } | ||
323 | 385 | ||
324 | if !is_last { | 386 | match self.per_module[&module.module_id].items.get(&segment.name) { |
325 | let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { | 387 | Some(res) if !res.def_id.is_none() => res.def_id, |
326 | d | 388 | _ => { |
327 | } else { | 389 | log::debug!("path segment {:?} not found", segment.name); |
328 | log::debug!( | 390 | 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 | } | 391 | } |
366 | } | 392 | } |
367 | _ => { | 393 | } |
368 | log::debug!( | 394 | ModuleDef::Enum(e) => { |
369 | "path segment {:?} resolved to non-module {:?}, but is not last", | 395 | // enum variant |
370 | segment.name, | 396 | tested_by!(item_map_enum_importing); |
371 | type_def_id, | 397 | let matching_variant = e |
372 | ); | 398 | .variants(db) |
373 | return true; // this resolved to a non-module, so the path won't ever resolve | 399 | .into_iter() |
400 | .find(|(n, _variant)| n == &segment.name); | ||
401 | |||
402 | match matching_variant { | ||
403 | Some((_n, variant)) => PerNs::both(variant.into(), (*e).into()), | ||
404 | None => PerNs::none(), | ||
374 | } | 405 | } |
375 | } | 406 | } |
376 | } else { | 407 | _ => { |
377 | log::debug!( | 408 | // could be an inherent method call in UFCS form |
378 | "resolved import {:?} ({:?}) within source root to {:?}", | 409 | // (`Struct::method`), or some other kind of associated |
379 | segment.name, | 410 | // item... Which we currently don't handle (TODO) |
380 | import, | 411 | log::debug!( |
381 | def_id, | 412 | "path segment {:?} resolved to non-module {:?}, but is not last", |
382 | ); | 413 | segment.name, |
383 | self.update(module_id, |items| { | 414 | curr, |
384 | let res = Resolution { | 415 | ); |
385 | def_id, | 416 | return (PerNs::none(), ReachedFixedPoint::Yes); |
386 | import: Some(import_id), | 417 | } |
387 | }; | 418 | }; |
388 | items.items.insert(segment.name.clone(), res); | ||
389 | }) | ||
390 | } | ||
391 | } | 419 | } |
392 | true | 420 | (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 | } | 421 | } |
399 | } | 422 | } |
400 | 423 | ||
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 9322bf08c..430d16a2e 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -216,6 +216,27 @@ fn item_map_using_self() { | |||
216 | } | 216 | } |
217 | 217 | ||
218 | #[test] | 218 | #[test] |
219 | fn item_map_enum_importing() { | ||
220 | covers!(item_map_enum_importing); | ||
221 | let (item_map, module_id) = item_map( | ||
222 | " | ||
223 | //- /lib.rs | ||
224 | enum E { V } | ||
225 | use self::E::V; | ||
226 | <|> | ||
227 | ", | ||
228 | ); | ||
229 | check_module_item_map( | ||
230 | &item_map, | ||
231 | module_id, | ||
232 | " | ||
233 | E: t | ||
234 | V: t v | ||
235 | ", | ||
236 | ); | ||
237 | } | ||
238 | |||
239 | #[test] | ||
219 | fn item_map_across_crates() { | 240 | fn item_map_across_crates() { |
220 | let (mut db, sr) = MockDatabase::with_files( | 241 | let (mut db, sr) = MockDatabase::with_files( |
221 | " | 242 | " |