aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/import_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/import_map.rs')
-rw-r--r--crates/ra_hir_def/src/import_map.rs370
1 files changed, 218 insertions, 152 deletions
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
index 68e20d06b..9e4c30b1a 100644
--- a/crates/ra_hir_def/src/import_map.rs
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -5,14 +5,16 @@ use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
5use fst::{self, Streamer}; 5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap}; 6use indexmap::{map::Entry, IndexMap};
7use ra_db::CrateId; 7use ra_db::CrateId;
8use rustc_hash::FxHasher; 8use ra_syntax::SmolStr;
9use rustc_hash::{FxHashMap, FxHasher};
10use smallvec::SmallVec;
9 11
10use crate::{ 12use crate::{
11 db::DefDatabase, 13 db::DefDatabase,
12 item_scope::ItemInNs, 14 item_scope::ItemInNs,
13 path::{ModPath, PathKind}, 15 path::{ModPath, PathKind},
14 visibility::Visibility, 16 visibility::Visibility,
15 ModuleDefId, ModuleId, 17 AssocItemId, ModuleDefId, ModuleId, TraitId,
16}; 18};
17 19
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; 20type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -34,6 +36,7 @@ pub struct ImportInfo {
34/// 36///
35/// Note that all paths are relative to the containing crate's root, so the crate name still needs 37/// Note that all paths are relative to the containing crate's root, so the crate name still needs
36/// to be prepended to the `ModPath` before the path is valid. 38/// to be prepended to the `ModPath` before the path is valid.
39#[derive(Default)]
37pub struct ImportMap { 40pub struct ImportMap {
38 map: FxIndexMap<ItemInNs, ImportInfo>, 41 map: FxIndexMap<ItemInNs, ImportInfo>,
39 42
@@ -45,13 +48,17 @@ pub struct ImportMap {
45 /// the index of the first one. 48 /// the index of the first one.
46 importables: Vec<ItemInNs>, 49 importables: Vec<ItemInNs>,
47 fst: fst::Map<Vec<u8>>, 50 fst: fst::Map<Vec<u8>>,
51
52 /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
53 /// exported.
54 assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
48} 55}
49 56
50impl ImportMap { 57impl ImportMap {
51 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { 58 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
52 let _p = ra_prof::profile("import_map_query"); 59 let _p = ra_prof::profile("import_map_query");
53 let def_map = db.crate_def_map(krate); 60 let def_map = db.crate_def_map(krate);
54 let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default()); 61 let mut import_map = Self::default();
55 62
56 // We look only into modules that are public(ly reexported), starting with the crate root. 63 // We look only into modules that are public(ly reexported), starting with the crate root.
57 let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; 64 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
@@ -85,7 +92,7 @@ impl ImportMap {
85 92
86 for item in per_ns.iter_items() { 93 for item in per_ns.iter_items() {
87 let path = mk_path(); 94 let path = mk_path();
88 match import_map.entry(item) { 95 match import_map.map.entry(item) {
89 Entry::Vacant(entry) => { 96 Entry::Vacant(entry) => {
90 entry.insert(ImportInfo { path, container: module }); 97 entry.insert(ImportInfo { path, container: module });
91 } 98 }
@@ -105,11 +112,16 @@ impl ImportMap {
105 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { 112 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
106 worklist.push((mod_id, mk_path())); 113 worklist.push((mod_id, mk_path()));
107 } 114 }
115
116 // If we've added a path to a trait, add the trait's methods to the method map.
117 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
118 import_map.collect_trait_methods(db, tr);
119 }
108 } 120 }
109 } 121 }
110 } 122 }
111 123
112 let mut importables = import_map.iter().collect::<Vec<_>>(); 124 let mut importables = import_map.map.iter().collect::<Vec<_>>();
113 125
114 importables.sort_by(cmp); 126 importables.sort_by(cmp);
115 127
@@ -133,10 +145,10 @@ impl ImportMap {
133 builder.insert(key, start as u64).unwrap(); 145 builder.insert(key, start as u64).unwrap();
134 } 146 }
135 147
136 let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); 148 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
137 let importables = importables.iter().map(|(item, _)| **item).collect(); 149 import_map.importables = importables.iter().map(|(item, _)| **item).collect();
138 150
139 Arc::new(Self { map: import_map, fst, importables }) 151 Arc::new(import_map)
140 } 152 }
141 153
142 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. 154 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
@@ -147,6 +159,13 @@ impl ImportMap {
147 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { 159 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
148 self.map.get(&item) 160 self.map.get(&item)
149 } 161 }
162
163 fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) {
164 let data = db.trait_data(tr);
165 for (name, item) in data.items.iter() {
166 self.assoc_map.entry(name.to_string().into()).or_default().push(*item);
167 }
168 }
150} 169}
151 170
152impl PartialEq for ImportMap { 171impl PartialEq for ImportMap {
@@ -290,37 +309,32 @@ pub fn search_dependencies<'a>(
290 } 309 }
291 } 310 }
292 311
312 // Add all exported associated items whose names match the query (exactly).
313 for map in &import_maps {
314 if let Some(v) = map.assoc_map.get(&*query.query) {
315 res.extend(v.iter().map(|&assoc| {
316 ItemInNs::Types(match assoc {
317 AssocItemId::FunctionId(it) => it.into(),
318 AssocItemId::ConstId(it) => it.into(),
319 AssocItemId::TypeAliasId(it) => it.into(),
320 })
321 }));
322 }
323 }
324
293 res 325 res
294} 326}
295 327
296#[cfg(test)] 328#[cfg(test)]
297mod tests { 329mod tests {
298 use super::*; 330 use expect::{expect, Expect};
299 use crate::test_db::TestDB; 331 use ra_db::{fixture::WithFixture, SourceDatabase, Upcast};
300 use insta::assert_snapshot;
301 use itertools::Itertools;
302 use ra_db::fixture::WithFixture;
303 use ra_db::{SourceDatabase, Upcast};
304 332
305 fn import_map(ra_fixture: &str) -> String { 333 use crate::{test_db::TestDB, AssocContainerId, Lookup};
306 let db = TestDB::with_files(ra_fixture);
307 let crate_graph = db.crate_graph();
308 334
309 let s = crate_graph 335 use super::*;
310 .iter()
311 .filter_map(|krate| {
312 let cdata = &crate_graph[krate];
313 let name = cdata.display_name.as_ref()?;
314
315 let map = db.import_map(krate);
316
317 Some(format!("{}:\n{:?}", name, map))
318 })
319 .join("\n");
320 s
321 }
322 336
323 fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String { 337 fn check_search(ra_fixture: &str, krate_name: &str, query: Query, expect: Expect) {
324 let db = TestDB::with_files(ra_fixture); 338 let db = TestDB::with_files(ra_fixture);
325 let crate_graph = db.crate_graph(); 339 let crate_graph = db.crate_graph();
326 let krate = crate_graph 340 let krate = crate_graph
@@ -331,7 +345,7 @@ mod tests {
331 }) 345 })
332 .unwrap(); 346 .unwrap();
333 347
334 search_dependencies(db.upcast(), krate, query) 348 let actual = search_dependencies(db.upcast(), krate, query)
335 .into_iter() 349 .into_iter()
336 .filter_map(|item| { 350 .filter_map(|item| {
337 let mark = match item { 351 let mark = match item {
@@ -339,23 +353,67 @@ mod tests {
339 ItemInNs::Values(_) => "v", 353 ItemInNs::Values(_) => "v",
340 ItemInNs::Macros(_) => "m", 354 ItemInNs::Macros(_) => "m",
341 }; 355 };
356 let item = assoc_to_trait(&db, item);
342 item.krate(db.upcast()).map(|krate| { 357 item.krate(db.upcast()).map(|krate| {
343 let map = db.import_map(krate); 358 let map = db.import_map(krate);
344 let path = map.path_of(item).unwrap(); 359 let path = map.path_of(item).unwrap();
345 format!( 360 format!(
346 "{}::{} ({})", 361 "{}::{} ({})\n",
347 crate_graph[krate].display_name.as_ref().unwrap(), 362 crate_graph[krate].display_name.as_ref().unwrap(),
348 path, 363 path,
349 mark 364 mark
350 ) 365 )
351 }) 366 })
352 }) 367 })
353 .join("\n") 368 .collect::<String>();
369 expect.assert_eq(&actual)
370 }
371
372 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs {
373 let assoc: AssocItemId = match item {
374 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
375 ModuleDefId::TypeAliasId(it) => it.into(),
376 ModuleDefId::FunctionId(it) => it.into(),
377 ModuleDefId::ConstId(it) => it.into(),
378 _ => return item,
379 },
380 _ => return item,
381 };
382
383 let container = match assoc {
384 AssocItemId::FunctionId(it) => it.lookup(db).container,
385 AssocItemId::ConstId(it) => it.lookup(db).container,
386 AssocItemId::TypeAliasId(it) => it.lookup(db).container,
387 };
388
389 match container {
390 AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()),
391 _ => item,
392 }
393 }
394
395 fn check(ra_fixture: &str, expect: Expect) {
396 let db = TestDB::with_files(ra_fixture);
397 let crate_graph = db.crate_graph();
398
399 let actual = crate_graph
400 .iter()
401 .filter_map(|krate| {
402 let cdata = &crate_graph[krate];
403 let name = cdata.display_name.as_ref()?;
404
405 let map = db.import_map(krate);
406
407 Some(format!("{}:\n{:?}\n", name, map))
408 })
409 .collect::<String>();
410
411 expect.assert_eq(&actual)
354 } 412 }
355 413
356 #[test] 414 #[test]
357 fn smoke() { 415 fn smoke() {
358 let map = import_map( 416 check(
359 r" 417 r"
360 //- /main.rs crate:main deps:lib 418 //- /main.rs crate:main deps:lib
361 419
@@ -380,24 +438,23 @@ mod tests {
380 pub struct Pub2; // t + v 438 pub struct Pub2; // t + v
381 struct Priv; 439 struct Priv;
382 ", 440 ",
441 expect![[r#"
442 main:
443 - publ1 (t)
444 - real_pu2 (t)
445 - real_pub (t)
446 - real_pub::Pub (t)
447 lib:
448 - Pub (t)
449 - Pub2 (t)
450 - Pub2 (v)
451 "#]],
383 ); 452 );
384
385 assert_snapshot!(map, @r###"
386 main:
387 - publ1 (t)
388 - real_pu2 (t)
389 - real_pub (t)
390 - real_pub::Pub (t)
391 lib:
392 - Pub (t)
393 - Pub2 (t)
394 - Pub2 (v)
395 "###);
396 } 453 }
397 454
398 #[test] 455 #[test]
399 fn prefers_shortest_path() { 456 fn prefers_shortest_path() {
400 let map = import_map( 457 check(
401 r" 458 r"
402 //- /main.rs crate:main 459 //- /main.rs crate:main
403 460
@@ -409,21 +466,20 @@ mod tests {
409 pub use super::sub::subsub::Def; 466 pub use super::sub::subsub::Def;
410 } 467 }
411 ", 468 ",
469 expect![[r#"
470 main:
471 - sub (t)
472 - sub::Def (t)
473 - sub::subsub (t)
474 "#]],
412 ); 475 );
413
414 assert_snapshot!(map, @r###"
415 main:
416 - sub (t)
417 - sub::Def (t)
418 - sub::subsub (t)
419 "###);
420 } 476 }
421 477
422 #[test] 478 #[test]
423 fn type_reexport_cross_crate() { 479 fn type_reexport_cross_crate() {
424 // Reexports need to be visible from a crate, even if the original crate exports the item 480 // Reexports need to be visible from a crate, even if the original crate exports the item
425 // at a shorter path. 481 // at a shorter path.
426 let map = import_map( 482 check(
427 r" 483 r"
428 //- /main.rs crate:main deps:lib 484 //- /main.rs crate:main deps:lib
429 pub mod m { 485 pub mod m {
@@ -432,22 +488,21 @@ mod tests {
432 //- /lib.rs crate:lib 488 //- /lib.rs crate:lib
433 pub struct S; 489 pub struct S;
434 ", 490 ",
491 expect![[r#"
492 main:
493 - m (t)
494 - m::S (t)
495 - m::S (v)
496 lib:
497 - S (t)
498 - S (v)
499 "#]],
435 ); 500 );
436
437 assert_snapshot!(map, @r###"
438 main:
439 - m (t)
440 - m::S (t)
441 - m::S (v)
442 lib:
443 - S (t)
444 - S (v)
445 "###);
446 } 501 }
447 502
448 #[test] 503 #[test]
449 fn macro_reexport() { 504 fn macro_reexport() {
450 let map = import_map( 505 check(
451 r" 506 r"
452 //- /main.rs crate:main deps:lib 507 //- /main.rs crate:main deps:lib
453 pub mod m { 508 pub mod m {
@@ -459,21 +514,20 @@ mod tests {
459 () => {}; 514 () => {};
460 } 515 }
461 ", 516 ",
517 expect![[r#"
518 main:
519 - m (t)
520 - m::pub_macro (m)
521 lib:
522 - pub_macro (m)
523 "#]],
462 ); 524 );
463
464 assert_snapshot!(map, @r###"
465 main:
466 - m (t)
467 - m::pub_macro (m)
468 lib:
469 - pub_macro (m)
470 "###);
471 } 525 }
472 526
473 #[test] 527 #[test]
474 fn module_reexport() { 528 fn module_reexport() {
475 // Reexporting modules from a dependency adds all contents to the import map. 529 // Reexporting modules from a dependency adds all contents to the import map.
476 let map = import_map( 530 check(
477 r" 531 r"
478 //- /main.rs crate:main deps:lib 532 //- /main.rs crate:main deps:lib
479 pub use lib::module as reexported_module; 533 pub use lib::module as reexported_module;
@@ -482,24 +536,23 @@ mod tests {
482 pub struct S; 536 pub struct S;
483 } 537 }
484 ", 538 ",
539 expect![[r#"
540 main:
541 - reexported_module (t)
542 - reexported_module::S (t)
543 - reexported_module::S (v)
544 lib:
545 - module (t)
546 - module::S (t)
547 - module::S (v)
548 "#]],
485 ); 549 );
486
487 assert_snapshot!(map, @r###"
488 main:
489 - reexported_module (t)
490 - reexported_module::S (t)
491 - reexported_module::S (v)
492 lib:
493 - module (t)
494 - module::S (t)
495 - module::S (v)
496 "###);
497 } 550 }
498 551
499 #[test] 552 #[test]
500 fn cyclic_module_reexport() { 553 fn cyclic_module_reexport() {
501 // A cyclic reexport does not hang. 554 // A cyclic reexport does not hang.
502 let map = import_map( 555 check(
503 r" 556 r"
504 //- /lib.rs crate:lib 557 //- /lib.rs crate:lib
505 pub mod module { 558 pub mod module {
@@ -511,36 +564,35 @@ mod tests {
511 pub use super::module; 564 pub use super::module;
512 } 565 }
513 ", 566 ",
567 expect![[r#"
568 lib:
569 - module (t)
570 - module::S (t)
571 - module::S (v)
572 - sub (t)
573 "#]],
514 ); 574 );
515
516 assert_snapshot!(map, @r###"
517 lib:
518 - module (t)
519 - module::S (t)
520 - module::S (v)
521 - sub (t)
522 "###);
523 } 575 }
524 576
525 #[test] 577 #[test]
526 fn private_macro() { 578 fn private_macro() {
527 let map = import_map( 579 check(
528 r" 580 r"
529 //- /lib.rs crate:lib 581 //- /lib.rs crate:lib
530 macro_rules! private_macro { 582 macro_rules! private_macro {
531 () => {}; 583 () => {};
532 } 584 }
533 ", 585 ",
534 ); 586 expect![[r#"
587 lib:
535 588
536 assert_snapshot!(map, @r###" 589 "#]],
537 lib: 590 );
538 "###);
539 } 591 }
540 592
541 #[test] 593 #[test]
542 fn namespacing() { 594 fn namespacing() {
543 let map = import_map( 595 check(
544 r" 596 r"
545 //- /lib.rs crate:lib 597 //- /lib.rs crate:lib
546 pub struct Thing; // t + v 598 pub struct Thing; // t + v
@@ -549,16 +601,15 @@ mod tests {
549 () => {}; 601 () => {};
550 } 602 }
551 ", 603 ",
604 expect![[r#"
605 lib:
606 - Thing (m)
607 - Thing (t)
608 - Thing (v)
609 "#]],
552 ); 610 );
553 611
554 assert_snapshot!(map, @r###" 612 check(
555 lib:
556 - Thing (m)
557 - Thing (t)
558 - Thing (v)
559 "###);
560
561 let map = import_map(
562 r" 613 r"
563 //- /lib.rs crate:lib 614 //- /lib.rs crate:lib
564 pub mod Thing {} // t 615 pub mod Thing {} // t
@@ -567,13 +618,12 @@ mod tests {
567 () => {}; 618 () => {};
568 } 619 }
569 ", 620 ",
621 expect![[r#"
622 lib:
623 - Thing (m)
624 - Thing (t)
625 "#]],
570 ); 626 );
571
572 assert_snapshot!(map, @r###"
573 lib:
574 - Thing (m)
575 - Thing (t)
576 "###);
577 } 627 }
578 628
579 #[test] 629 #[test]
@@ -602,23 +652,33 @@ mod tests {
602 } 652 }
603 "#; 653 "#;
604 654
605 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt")); 655 check_search(
606 assert_snapshot!(res, @r###" 656 ra_fixture,
607 dep::fmt (t) 657 "main",
608 dep::Fmt (t) 658 Query::new("fmt"),
609 dep::Fmt (v) 659 expect![[r#"
610 dep::Fmt (m) 660 dep::fmt (t)
611 dep::fmt::Display (t) 661 dep::Fmt (t)
612 dep::format (v) 662 dep::Fmt (v)
613 "###); 663 dep::Fmt (m)
614 664 dep::fmt::Display (t)
615 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); 665 dep::format (v)
616 assert_snapshot!(res, @r###" 666 dep::fmt::Display (t)
617 dep::fmt (t) 667 "#]],
618 dep::Fmt (t) 668 );
619 dep::Fmt (v) 669
620 dep::Fmt (m) 670 check_search(
621 "###); 671 ra_fixture,
672 "main",
673 Query::new("fmt").anchor_end(),
674 expect![[r#"
675 dep::fmt (t)
676 dep::Fmt (t)
677 dep::Fmt (v)
678 dep::Fmt (m)
679 dep::fmt::Display (t)
680 "#]],
681 );
622 } 682 }
623 683
624 #[test] 684 #[test]
@@ -631,26 +691,32 @@ mod tests {
631 pub struct FMT; 691 pub struct FMT;
632 "#; 692 "#;
633 693
634 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT")); 694 check_search(
635 695 ra_fixture,
636 assert_snapshot!(res, @r###" 696 "main",
637 dep::fmt (t) 697 Query::new("FMT"),
638 dep::fmt (v) 698 expect![[r#"
639 dep::FMT (t) 699 dep::fmt (t)
640 dep::FMT (v) 700 dep::fmt (v)
641 "###); 701 dep::FMT (t)
642 702 dep::FMT (v)
643 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive()); 703 "#]],
704 );
644 705
645 assert_snapshot!(res, @r###" 706 check_search(
646 dep::FMT (t) 707 ra_fixture,
647 dep::FMT (v) 708 "main",
648 "###); 709 Query::new("FMT").case_sensitive(),
710 expect![[r#"
711 dep::FMT (t)
712 dep::FMT (v)
713 "#]],
714 );
649 } 715 }
650 716
651 #[test] 717 #[test]
652 fn search_limit() { 718 fn search_limit() {
653 let res = search_dependencies_of( 719 check_search(
654 r#" 720 r#"
655 //- /main.rs crate:main deps:dep 721 //- /main.rs crate:main deps:dep
656 //- /dep.rs crate:dep 722 //- /dep.rs crate:dep
@@ -670,10 +736,10 @@ mod tests {
670 "#, 736 "#,
671 "main", 737 "main",
672 Query::new("").limit(2), 738 Query::new("").limit(2),
739 expect![[r#"
740 dep::fmt (t)
741 dep::Fmt (t)
742 "#]],
673 ); 743 );
674 assert_snapshot!(res, @r###"
675 dep::fmt (t)
676 dep::Fmt (t)
677 "###);
678 } 744 }
679} 745}