aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/find_path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/find_path.rs')
-rw-r--r--crates/ra_hir_def/src/find_path.rs105
1 files changed, 55 insertions, 50 deletions
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 70dcb03e6..4db798473 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -1,5 +1,11 @@
1//! An algorithm to find a path to refer to a certain item. 1//! An algorithm to find a path to refer to a certain item.
2 2
3use std::sync::Arc;
4
5use hir_expand::name::{known, AsName, Name};
6use ra_prof::profile;
7use test_utils::mark;
8
3use crate::{ 9use crate::{
4 db::DefDatabase, 10 db::DefDatabase,
5 item_scope::ItemInNs, 11 item_scope::ItemInNs,
@@ -7,25 +13,28 @@ use crate::{
7 visibility::Visibility, 13 visibility::Visibility,
8 CrateId, ModuleDefId, ModuleId, 14 CrateId, ModuleDefId, ModuleId,
9}; 15};
10use hir_expand::name::{known, AsName, Name}; 16
11use test_utils::tested_by; 17// FIXME: handle local items
18
19/// Find a path that can be used to refer to a certain item. This can depend on
20/// *from where* you're referring to the item, hence the `from` parameter.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
22 let _p = profile("find_path");
23 db.find_path_inner(item, from, MAX_PATH_LEN)
24}
12 25
13const MAX_PATH_LEN: usize = 15; 26const MAX_PATH_LEN: usize = 15;
14 27
15impl ModPath { 28impl ModPath {
16 fn starts_with_std(&self) -> bool { 29 fn starts_with_std(&self) -> bool {
17 self.segments.first().filter(|&first_segment| first_segment == &known::std).is_some() 30 self.segments.first() == Some(&known::std)
18 } 31 }
19 32
20 // When std library is present, paths starting with `std::` 33 // When std library is present, paths starting with `std::`
21 // should be preferred over paths starting with `core::` and `alloc::` 34 // should be preferred over paths starting with `core::` and `alloc::`
22 fn can_start_with_std(&self) -> bool { 35 fn can_start_with_std(&self) -> bool {
23 self.segments 36 let first_segment = self.segments.first();
24 .first() 37 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
25 .filter(|&first_segment| {
26 first_segment == &known::alloc || first_segment == &known::core
27 })
28 .is_some()
29 } 38 }
30 39
31 fn len(&self) -> usize { 40 fn len(&self) -> usize {
@@ -40,15 +49,7 @@ impl ModPath {
40 } 49 }
41} 50}
42 51
43// FIXME: handle local items 52pub(crate) fn find_path_inner_query(
44
45/// Find a path that can be used to refer to a certain item. This can depend on
46/// *from where* you're referring to the item, hence the `from` parameter.
47pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
48 find_path_inner(db, item, from, MAX_PATH_LEN)
49}
50
51fn find_path_inner(
52 db: &dyn DefDatabase, 53 db: &dyn DefDatabase,
53 item: ItemInNs, 54 item: ItemInNs,
54 from: ModuleId, 55 from: ModuleId,
@@ -139,8 +140,7 @@ fn find_path_inner(
139 let mut best_path = None; 140 let mut best_path = None;
140 let mut best_path_len = max_len; 141 let mut best_path_len = max_len;
141 for (module_id, name) in importable_locations { 142 for (module_id, name) in importable_locations {
142 let mut path = match find_path_inner( 143 let mut path = match db.find_path_inner(
143 db,
144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)), 144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
145 from, 145 from,
146 best_path_len - 1, 146 best_path_len - 1,
@@ -163,17 +163,19 @@ fn find_path_inner(
163 163
164fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 164fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
165 if old_path.starts_with_std() && new_path.can_start_with_std() { 165 if old_path.starts_with_std() && new_path.can_start_with_std() {
166 tested_by!(prefer_std_paths);
167 if prefer_no_std { 166 if prefer_no_std {
167 mark::hit!(prefer_no_std_paths);
168 new_path 168 new_path
169 } else { 169 } else {
170 mark::hit!(prefer_std_paths);
170 old_path 171 old_path
171 } 172 }
172 } else if new_path.starts_with_std() && old_path.can_start_with_std() { 173 } else if new_path.starts_with_std() && old_path.can_start_with_std() {
173 tested_by!(prefer_std_paths);
174 if prefer_no_std { 174 if prefer_no_std {
175 mark::hit!(prefer_no_std_paths);
175 old_path 176 old_path
176 } else { 177 } else {
178 mark::hit!(prefer_std_paths);
177 new_path 179 new_path
178 } 180 }
179 } else if new_path.len() < old_path.len() { 181 } else if new_path.len() < old_path.len() {
@@ -198,7 +200,7 @@ fn find_importable_locations(
198 .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) 200 .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id))
199 { 201 {
200 result.extend( 202 result.extend(
201 importable_locations_in_crate(db, item, krate) 203 db.importable_locations_of(item, krate)
202 .iter() 204 .iter()
203 .filter(|(_, _, vis)| vis.is_visible_from(db, from)) 205 .filter(|(_, _, vis)| vis.is_visible_from(db, from))
204 .map(|(m, n, _)| (*m, n.clone())), 206 .map(|(m, n, _)| (*m, n.clone())),
@@ -213,11 +215,12 @@ fn find_importable_locations(
213/// 215///
214/// Note that the crate doesn't need to be the one in which the item is defined; 216/// Note that the crate doesn't need to be the one in which the item is defined;
215/// it might be re-exported in other crates. 217/// it might be re-exported in other crates.
216fn importable_locations_in_crate( 218pub(crate) fn importable_locations_of_query(
217 db: &dyn DefDatabase, 219 db: &dyn DefDatabase,
218 item: ItemInNs, 220 item: ItemInNs,
219 krate: CrateId, 221 krate: CrateId,
220) -> Vec<(ModuleId, Name, Visibility)> { 222) -> Arc<[(ModuleId, Name, Visibility)]> {
223 let _p = profile("importable_locations_of_query");
221 let def_map = db.crate_def_map(krate); 224 let def_map = db.crate_def_map(krate);
222 let mut result = Vec::new(); 225 let mut result = Vec::new();
223 for (local_id, data) in def_map.modules.iter() { 226 for (local_id, data) in def_map.modules.iter() {
@@ -243,17 +246,20 @@ fn importable_locations_in_crate(
243 result.push((ModuleId { krate, local_id }, name.clone(), vis)); 246 result.push((ModuleId { krate, local_id }, name.clone(), vis));
244 } 247 }
245 } 248 }
246 result 249
250 Arc::from(result)
247} 251}
248 252
249#[cfg(test)] 253#[cfg(test)]
250mod tests { 254mod tests {
251 use super::*;
252 use crate::test_db::TestDB;
253 use hir_expand::hygiene::Hygiene; 255 use hir_expand::hygiene::Hygiene;
254 use ra_db::fixture::WithFixture; 256 use ra_db::fixture::WithFixture;
255 use ra_syntax::ast::AstNode; 257 use ra_syntax::ast::AstNode;
256 use test_utils::covers; 258 use test_utils::mark;
259
260 use crate::test_db::TestDB;
261
262 use super::*;
257 263
258 /// `code` needs to contain a cursor marker; checks that `find_path` for the 264 /// `code` needs to contain a cursor marker; checks that `find_path` for the
259 /// item the `path` refers to returns that same path when called from the 265 /// item the `path` refers to returns that same path when called from the
@@ -508,7 +514,7 @@ mod tests {
508 514
509 #[test] 515 #[test]
510 fn prefer_std_paths_over_alloc() { 516 fn prefer_std_paths_over_alloc() {
511 covers!(prefer_std_paths); 517 mark::check!(prefer_std_paths);
512 let code = r#" 518 let code = r#"
513 //- /main.rs crate:main deps:alloc,std 519 //- /main.rs crate:main deps:alloc,std
514 <|> 520 <|>
@@ -527,51 +533,50 @@ mod tests {
527 } 533 }
528 534
529 #[test] 535 #[test]
530 fn prefer_alloc_paths_over_std() { 536 fn prefer_core_paths_over_std() {
531 covers!(prefer_std_paths); 537 mark::check!(prefer_no_std_paths);
532 let code = r#" 538 let code = r#"
533 //- /main.rs crate:main deps:alloc,std 539 //- /main.rs crate:main deps:core,std
534 #![no_std] 540 #![no_std]
535 541
536 <|> 542 <|>
537 543
538 //- /std.rs crate:std deps:alloc 544 //- /std.rs crate:std deps:core
539 545
540 pub mod sync { 546 pub mod fmt {
541 pub use alloc::sync::Arc; 547 pub use core::fmt::Error;
542 } 548 }
543 549
544 //- /zzz.rs crate:alloc 550 //- /zzz.rs crate:core
545 551
546 pub mod sync { 552 pub mod fmt {
547 pub struct Arc; 553 pub struct Error;
548 } 554 }
549 "#; 555 "#;
550 check_found_path(code, "alloc::sync::Arc"); 556 check_found_path(code, "core::fmt::Error");
551 } 557 }
552 558
553 #[test] 559 #[test]
554 fn prefer_core_paths_over_std() { 560 fn prefer_alloc_paths_over_std() {
555 covers!(prefer_std_paths);
556 let code = r#" 561 let code = r#"
557 //- /main.rs crate:main deps:core,std 562 //- /main.rs crate:main deps:alloc,std
558 #![no_std] 563 #![no_std]
559 564
560 <|> 565 <|>
561 566
562 //- /std.rs crate:std deps:core 567 //- /std.rs crate:std deps:alloc
563 568
564 pub mod fmt { 569 pub mod sync {
565 pub use core::fmt::Error; 570 pub use alloc::sync::Arc;
566 } 571 }
567 572
568 //- /zzz.rs crate:core 573 //- /zzz.rs crate:alloc
569 574
570 pub mod fmt { 575 pub mod sync {
571 pub struct Error; 576 pub struct Arc;
572 } 577 }
573 "#; 578 "#;
574 check_found_path(code, "core::fmt::Error"); 579 check_found_path(code, "alloc::sync::Arc");
575 } 580 }
576 581
577 #[test] 582 #[test]