aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r--crates/ra_hir/src/marks.rs7
-rw-r--r--crates/ra_hir/src/nameres.rs211
-rw-r--r--crates/ra_hir/src/nameres/tests.rs21
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 @@
1use test_utils::mark; 1test_utils::marks!(
2 2 name_res_works_for_broken_modules
3mark!(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;
19use std::sync::Arc; 19use std::sync::Arc;
20 20
21use ra_db::CrateId; 21use ra_db::CrateId;
22use test_utils::tested_by;
22use rustc_hash::{FxHashMap, FxHashSet}; 23use rustc_hash::{FxHashMap, FxHashSet};
23 24
24use crate::{ 25use 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, 328enum ReachedFixedPoint {
329 Yes,
330 No,
331}
332
333impl 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]
219fn 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]
219fn item_map_across_crates() { 240fn item_map_across_crates() {
220 let (mut db, sr) = MockDatabase::with_files( 241 let (mut db, sr) = MockDatabase::with_files(
221 " 242 "