aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db')
-rw-r--r--crates/ide_db/src/apply_change.rs1
-rw-r--r--crates/ide_db/src/call_info.rs14
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs212
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs8
-rw-r--r--crates/ide_db/src/items_locator.rs178
-rw-r--r--crates/ide_db/src/search.rs170
-rw-r--r--crates/ide_db/src/symbol_index.rs16
7 files changed, 313 insertions, 286 deletions
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 9b590d919..047a9b6bc 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -181,6 +181,7 @@ impl RootDatabase {
181 hir::db::GenericPredicatesQuery 181 hir::db::GenericPredicatesQuery
182 hir::db::GenericDefaultsQuery 182 hir::db::GenericDefaultsQuery
183 hir::db::InherentImplsInCrateQuery 183 hir::db::InherentImplsInCrateQuery
184 hir::db::TraitEnvironmentQuery
184 hir::db::TraitImplsInCrateQuery 185 hir::db::TraitImplsInCrateQuery
185 hir::db::TraitImplsInDepsQuery 186 hir::db::TraitImplsInDepsQuery
186 hir::db::AssociatedTyDataQuery 187 hir::db::AssociatedTyDataQuery
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index 7e26c3ccf..e583a52f4 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -4,7 +4,7 @@ use either::Either;
4use hir::{HasAttrs, HirDisplay, Semantics, Type}; 4use hir::{HasAttrs, HirDisplay, Semantics, Type};
5use stdx::format_to; 5use stdx::format_to;
6use syntax::{ 6use syntax::{
7 ast::{self, ArgListOwner}, 7 ast::{self, ArgListOwner, NameOwner},
8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, 8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
9}; 9};
10 10
@@ -142,7 +142,7 @@ fn call_info_impl(
142#[derive(Debug)] 142#[derive(Debug)]
143pub struct ActiveParameter { 143pub struct ActiveParameter {
144 pub ty: Type, 144 pub ty: Type,
145 pub name: String, 145 pub pat: Either<ast::SelfParam, ast::Pat>,
146} 146}
147 147
148impl ActiveParameter { 148impl ActiveParameter {
@@ -165,8 +165,14 @@ impl ActiveParameter {
165 return None; 165 return None;
166 } 166 }
167 let (pat, ty) = params.swap_remove(idx); 167 let (pat, ty) = params.swap_remove(idx);
168 let name = pat?.to_string(); 168 pat.map(|pat| ActiveParameter { ty, pat })
169 Some(ActiveParameter { ty, name }) 169 }
170
171 pub fn ident(&self) -> Option<ast::Name> {
172 self.pat.as_ref().right().and_then(|param| match param {
173 ast::Pat::IdentPat(ident) => ident.name(),
174 _ => None,
175 })
170 } 176 }
171} 177}
172 178
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 7c8844e95..1881c746f 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -61,7 +61,7 @@ pub struct FirstSegmentUnresolved {
61} 61}
62 62
63/// A name that will be used during item lookups. 63/// A name that will be used during item lookups.
64#[derive(Debug)] 64#[derive(Debug, Clone)]
65pub enum NameToImport { 65pub enum NameToImport {
66 /// Requires items with names that exactly match the given string, case-sensitive. 66 /// Requires items with names that exactly match the given string, case-sensitive.
67 Exact(String), 67 Exact(String),
@@ -201,129 +201,101 @@ impl ImportAssets {
201 sema: &Semantics<RootDatabase>, 201 sema: &Semantics<RootDatabase>,
202 prefixed: Option<PrefixKind>, 202 prefixed: Option<PrefixKind>,
203 ) -> Vec<LocatedImport> { 203 ) -> Vec<LocatedImport> {
204 let items_with_candidate_name = match self.name_to_import() { 204 let _p = profile::span("import_assets::search_for");
205 NameToImport::Exact(exact_name) => items_locator::with_exact_name(
206 sema,
207 self.module_with_candidate.krate(),
208 exact_name.clone(),
209 ),
210 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
211 // instead, we need to look up all trait impls for a certain struct and search through them only
212 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
213 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
214 // for the details
215 NameToImport::Fuzzy(fuzzy_name) => {
216 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
217 (AssocItemSearch::AssocItemsOnly, None)
218 } else {
219 (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
220 };
221
222 items_locator::with_similar_name(
223 sema,
224 self.module_with_candidate.krate(),
225 fuzzy_name.clone(),
226 assoc_item_search,
227 limit,
228 )
229 }
230 };
231 205
232 let scope_definitions = self.scope_definitions(sema); 206 let scope_definitions = self.scope_definitions(sema);
233 self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
234 .into_iter()
235 .filter(|import| import.import_path.len() > 1)
236 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
237 .sorted_by_key(|import| import.import_path.clone())
238 .collect()
239 }
240
241 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
242 let mut scope_definitions = FxHashSet::default();
243 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
244 scope_definitions.insert(scope_def);
245 });
246 scope_definitions
247 }
248
249 fn name_to_import(&self) -> &NameToImport {
250 match &self.import_candidate {
251 ImportCandidate::Path(candidate) => &candidate.name,
252 ImportCandidate::TraitAssocItem(candidate)
253 | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
254 }
255 }
256
257 fn applicable_defs(
258 &self,
259 db: &RootDatabase,
260 prefixed: Option<PrefixKind>,
261 items_with_candidate_name: FxHashSet<ItemInNs>,
262 ) -> FxHashSet<LocatedImport> {
263 let _p = profile::span("import_assets::applicable_defs");
264 let current_crate = self.module_with_candidate.krate(); 207 let current_crate = self.module_with_candidate.krate();
265
266 let mod_path = |item| { 208 let mod_path = |item| {
267 get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) 209 get_mod_path(
210 sema.db,
211 item_for_path_search(sema.db, item)?,
212 &self.module_with_candidate,
213 prefixed,
214 )
268 }; 215 };
269 216
270 match &self.import_candidate { 217 match &self.import_candidate {
271 ImportCandidate::Path(path_candidate) => { 218 ImportCandidate::Path(path_candidate) => {
272 path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name) 219 path_applicable_imports(sema, current_crate, path_candidate, mod_path)
220 }
221 ImportCandidate::TraitAssocItem(trait_candidate) => {
222 trait_applicable_items(sema, current_crate, trait_candidate, true, mod_path)
223 }
224 ImportCandidate::TraitMethod(trait_candidate) => {
225 trait_applicable_items(sema, current_crate, trait_candidate, false, mod_path)
273 } 226 }
274 ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
275 db,
276 current_crate,
277 trait_candidate,
278 true,
279 mod_path,
280 items_with_candidate_name,
281 ),
282 ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
283 db,
284 current_crate,
285 trait_candidate,
286 false,
287 mod_path,
288 items_with_candidate_name,
289 ),
290 } 227 }
228 .into_iter()
229 .filter(|import| import.import_path.len() > 1)
230 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
231 .sorted_by_key(|import| import.import_path.clone())
232 .collect()
233 }
234
235 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
236 let _p = profile::span("import_assets::scope_definitions");
237 let mut scope_definitions = FxHashSet::default();
238 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
239 scope_definitions.insert(scope_def);
240 });
241 scope_definitions
291 } 242 }
292} 243}
293 244
294fn path_applicable_imports( 245fn path_applicable_imports(
295 db: &RootDatabase, 246 sema: &Semantics<RootDatabase>,
247 current_crate: Crate,
296 path_candidate: &PathImportCandidate, 248 path_candidate: &PathImportCandidate,
297 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, 249 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
298 items_with_candidate_name: FxHashSet<ItemInNs>,
299) -> FxHashSet<LocatedImport> { 250) -> FxHashSet<LocatedImport> {
300 let _p = profile::span("import_assets::path_applicable_imports"); 251 let _p = profile::span("import_assets::path_applicable_imports");
301 252
302 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { 253 match &path_candidate.qualifier {
303 None => { 254 None => {
304 return items_with_candidate_name 255 items_locator::items_with_name(
305 .into_iter() 256 sema,
306 .filter_map(|item| { 257 current_crate,
307 let mut mod_path = mod_path(item)?; 258 path_candidate.name.clone(),
308 if let Some(assoc_item) = item_as_assoc(db, item) { 259 // FIXME: we could look up assoc items by the input and propose those in completion,
309 mod_path.push_segment(assoc_item.name(db)?); 260 // but that requries more preparation first:
310 } 261 // * store non-trait assoc items in import_map to fully enable this lookup
311 Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path))) 262 // * ensure that does not degrade the performance (bencmark it)
312 }) 263 // * write more logic to check for corresponding trait presence requirement (we're unable to flyimport multiple item right now)
313 .collect(); 264 // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear
265 //
266 // see also an ignored test under FIXME comment in the qualify_path.rs module
267 AssocItemSearch::Exclude,
268 Some(DEFAULT_QUERY_SEARCH_LIMIT),
269 )
270 .filter_map(|item| {
271 let mod_path = mod_path(item)?;
272 Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path)))
273 })
274 .collect()
314 } 275 }
315 Some(first_segment_unresolved) => ( 276 Some(first_segment_unresolved) => {
316 first_segment_unresolved.fist_segment.to_string(), 277 let unresolved_qualifier =
317 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), 278 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier);
318 ), 279 let unresolved_first_segment = first_segment_unresolved.fist_segment.text();
319 }; 280 items_locator::items_with_name(
320 281 sema,
321 items_with_candidate_name 282 current_crate,
322 .into_iter() 283 path_candidate.name.clone(),
323 .filter_map(|item| { 284 AssocItemSearch::Include,
324 import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) 285 Some(DEFAULT_QUERY_SEARCH_LIMIT),
325 }) 286 )
326 .collect() 287 .filter_map(|item| {
288 import_for_item(
289 sema.db,
290 mod_path,
291 unresolved_first_segment,
292 &unresolved_qualifier,
293 item,
294 )
295 })
296 .collect()
297 }
298 }
327} 299}
328 300
329fn import_for_item( 301fn import_for_item(
@@ -438,25 +410,31 @@ fn module_with_segment_name(
438} 410}
439 411
440fn trait_applicable_items( 412fn trait_applicable_items(
441 db: &RootDatabase, 413 sema: &Semantics<RootDatabase>,
442 current_crate: Crate, 414 current_crate: Crate,
443 trait_candidate: &TraitImportCandidate, 415 trait_candidate: &TraitImportCandidate,
444 trait_assoc_item: bool, 416 trait_assoc_item: bool,
445 mod_path: impl Fn(ItemInNs) -> Option<ModPath>, 417 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
446 items_with_candidate_name: FxHashSet<ItemInNs>,
447) -> FxHashSet<LocatedImport> { 418) -> FxHashSet<LocatedImport> {
448 let _p = profile::span("import_assets::trait_applicable_items"); 419 let _p = profile::span("import_assets::trait_applicable_items");
449 let mut required_assoc_items = FxHashSet::default();
450 420
451 let trait_candidates = items_with_candidate_name 421 let db = sema.db;
452 .into_iter() 422
453 .filter_map(|input| item_as_assoc(db, input)) 423 let mut required_assoc_items = FxHashSet::default();
454 .filter_map(|assoc| { 424 let trait_candidates = items_locator::items_with_name(
455 let assoc_item_trait = assoc.containing_trait(db)?; 425 sema,
456 required_assoc_items.insert(assoc); 426 current_crate,
457 Some(assoc_item_trait.into()) 427 trait_candidate.assoc_item_name.clone(),
458 }) 428 AssocItemSearch::AssocItemsOnly,
459 .collect(); 429 Some(DEFAULT_QUERY_SEARCH_LIMIT),
430 )
431 .filter_map(|input| item_as_assoc(db, input))
432 .filter_map(|assoc| {
433 let assoc_item_trait = assoc.containing_trait(db)?;
434 required_assoc_items.insert(assoc);
435 Some(assoc_item_trait.into())
436 })
437 .collect();
460 438
461 let mut located_imports = FxHashSet::default(); 439 let mut located_imports = FxHashSet::default();
462 440
@@ -565,10 +543,6 @@ impl ImportCandidate {
565 ) -> Option<Self> { 543 ) -> Option<Self> {
566 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) 544 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
567 } 545 }
568
569 fn is_trait_candidate(&self) -> bool {
570 matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
571 }
572} 546}
573 547
574fn path_import_candidate( 548fn path_import_candidate(
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 9e0cb91c3..20c195f82 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -14,10 +14,12 @@ use syntax::{
14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, 14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
15}; 15};
16 16
17pub use hir::PrefixKind;
18
17#[derive(Clone, Copy, Debug, PartialEq, Eq)] 19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct InsertUseConfig { 20pub struct InsertUseConfig {
19 pub merge: Option<MergeBehavior>, 21 pub merge: Option<MergeBehavior>,
20 pub prefix_kind: hir::PrefixKind, 22 pub prefix_kind: PrefixKind,
21 pub group: bool, 23 pub group: bool,
22} 24}
23 25
@@ -211,7 +213,7 @@ pub fn try_merge_imports(
211 let lhs_tree = lhs.use_tree()?; 213 let lhs_tree = lhs.use_tree()?;
212 let rhs_tree = rhs.use_tree()?; 214 let rhs_tree = rhs.use_tree()?;
213 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; 215 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?;
214 Some(lhs.with_use_tree(merged)) 216 Some(lhs.with_use_tree(merged).clone_for_update())
215} 217}
216 218
217pub fn try_merge_trees( 219pub fn try_merge_trees(
@@ -232,7 +234,7 @@ pub fn try_merge_trees(
232 } else { 234 } else {
233 (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) 235 (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix))
234 }; 236 };
235 recursive_merge(&lhs, &rhs, merge) 237 recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update())
236} 238}
237 239
238/// Recursively "zips" together lhs and rhs. 240/// Recursively "zips" together lhs and rhs.
diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs
index 8a7f02935..ef796b6f7 100644
--- a/crates/ide_db/src/items_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -1,6 +1,7 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module has the functionality to search the project and its dependencies for a certain item,
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! by its name and a few criteria.
3 3//! The main reason for this module to exist is the fact that project's items and dependencies' items
4//! are located in different caches, with different APIs.
4use either::Either; 5use either::Either;
5use hir::{ 6use hir::{
6 import_map::{self, ImportKind}, 7 import_map::{self, ImportKind},
@@ -10,122 +11,121 @@ use syntax::{ast, AstNode, SyntaxKind::NAME};
10 11
11use crate::{ 12use crate::{
12 defs::{Definition, NameClass}, 13 defs::{Definition, NameClass},
14 helpers::import_assets::NameToImport,
13 symbol_index::{self, FileSymbol}, 15 symbol_index::{self, FileSymbol},
14 RootDatabase, 16 RootDatabase,
15}; 17};
16use rustc_hash::FxHashSet;
17
18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
19 18
20pub fn with_exact_name( 19/// A value to use, when uncertain which limit to pick.
21 sema: &Semantics<'_, RootDatabase>, 20pub const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
22 krate: Crate,
23 exact_name: String,
24) -> FxHashSet<ItemInNs> {
25 let _p = profile::span("find_exact_imports");
26 find_items(
27 sema,
28 krate,
29 {
30 let mut local_query = symbol_index::Query::new(exact_name.clone());
31 local_query.exact();
32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
33 local_query
34 },
35 import_map::Query::new(exact_name)
36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
37 .name_only()
38 .search_mode(import_map::SearchMode::Equals)
39 .case_sensitive(),
40 )
41}
42 21
43#[derive(Debug)] 22/// Three possible ways to search for the name in associated and/or other items.
23#[derive(Debug, Clone, Copy)]
44pub enum AssocItemSearch { 24pub enum AssocItemSearch {
25 /// Search for the name in both associated and other items.
45 Include, 26 Include,
27 /// Search for the name in other items only.
46 Exclude, 28 Exclude,
29 /// Search for the name in the associated items only.
47 AssocItemsOnly, 30 AssocItemsOnly,
48} 31}
49 32
50pub fn with_similar_name( 33/// Searches for importable items with the given name in the crate and its dependencies.
51 sema: &Semantics<'_, RootDatabase>, 34pub fn items_with_name<'a>(
35 sema: &'a Semantics<'_, RootDatabase>,
52 krate: Crate, 36 krate: Crate,
53 fuzzy_search_string: String, 37 name: NameToImport,
54 assoc_item_search: AssocItemSearch, 38 assoc_item_search: AssocItemSearch,
55 limit: Option<usize>, 39 limit: Option<usize>,
56) -> FxHashSet<ItemInNs> { 40) -> impl Iterator<Item = ItemInNs> + 'a {
57 let _p = profile::span("find_similar_imports"); 41 let _p = profile::span("items_with_name").detail(|| {
42 format!(
43 "Name: {}, crate: {:?}, assoc items: {:?}, limit: {:?}",
44 name.text(),
45 assoc_item_search,
46 krate.display_name(sema.db).map(|name| name.to_string()),
47 limit,
48 )
49 });
50
51 let (mut local_query, mut external_query) = match name {
52 NameToImport::Exact(exact_name) => {
53 let mut local_query = symbol_index::Query::new(exact_name.clone());
54 local_query.exact();
58 55
59 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 56 let external_query = import_map::Query::new(exact_name)
60 .search_mode(import_map::SearchMode::Fuzzy) 57 .name_only()
61 .name_only(); 58 .search_mode(import_map::SearchMode::Equals)
59 .case_sensitive();
62 60
63 match assoc_item_search { 61 (local_query, external_query)
64 AssocItemSearch::Include => {}
65 AssocItemSearch::Exclude => {
66 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
67 } 62 }
68 AssocItemSearch::AssocItemsOnly => { 63 NameToImport::Fuzzy(fuzzy_search_string) => {
69 external_query = external_query.assoc_items_only(); 64 let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
65
66 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
67 .search_mode(import_map::SearchMode::Fuzzy)
68 .name_only();
69 match assoc_item_search {
70 AssocItemSearch::Include => {}
71 AssocItemSearch::Exclude => {
72 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
73 }
74 AssocItemSearch::AssocItemsOnly => {
75 external_query = external_query.assoc_items_only();
76 }
77 }
78
79 if fuzzy_search_string.to_lowercase() != fuzzy_search_string {
80 local_query.case_sensitive();
81 external_query = external_query.case_sensitive();
82 }
83
84 (local_query, external_query)
70 } 85 }
71 } 86 };
72
73 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
74 87
75 if let Some(limit) = limit { 88 if let Some(limit) = limit {
76 external_query = external_query.limit(limit); 89 external_query = external_query.limit(limit);
77 local_query.limit(limit); 90 local_query.limit(limit);
78 } 91 }
79 92
80 find_items(sema, krate, local_query, external_query) 93 find_items(sema, krate, assoc_item_search, local_query, external_query)
81 .into_iter()
82 .filter(move |&item| match assoc_item_search {
83 AssocItemSearch::Include => true,
84 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
85 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
86 })
87 .collect()
88} 94}
89 95
90fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { 96fn find_items<'a>(
91 item.as_module_def_id() 97 sema: &'a Semantics<'_, RootDatabase>,
92 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
93 .is_some()
94}
95
96fn find_items(
97 sema: &Semantics<'_, RootDatabase>,
98 krate: Crate, 98 krate: Crate,
99 assoc_item_search: AssocItemSearch,
99 local_query: symbol_index::Query, 100 local_query: symbol_index::Query,
100 external_query: import_map::Query, 101 external_query: import_map::Query,
101) -> FxHashSet<ItemInNs> { 102) -> impl Iterator<Item = ItemInNs> + 'a {
102 let _p = profile::span("find_similar_imports"); 103 let _p = profile::span("find_items");
103 let db = sema.db; 104 let db = sema.db;
104 105
105 // Query dependencies first. 106 let external_importables =
106 let mut candidates = krate 107 krate.query_external_importables(db, external_query).map(|external_importable| {
107 .query_external_importables(db, external_query) 108 match external_importable {
108 .map(|external_importable| match external_importable { 109 Either::Left(module_def) => ItemInNs::from(module_def),
109 Either::Left(module_def) => ItemInNs::from(module_def), 110 Either::Right(macro_def) => ItemInNs::from(macro_def),
110 Either::Right(macro_def) => ItemInNs::from(macro_def), 111 }
111 }) 112 });
112 .collect::<FxHashSet<_>>();
113 113
114 // Query the local crate using the symbol index. 114 // Query the local crate using the symbol index.
115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); 115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query)
116 116 .into_iter()
117 candidates.extend( 117 .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate))
118 local_results 118 .filter_map(|name_definition_to_import| match name_definition_to_import {
119 .into_iter() 119 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
120 .filter_map(|local_candidate| get_name_definition(sema, &local_candidate)) 120 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
121 .filter_map(|name_definition_to_import| match name_definition_to_import { 121 _ => None,
122 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)), 122 });
123 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), 123
124 _ => None, 124 external_importables.chain(local_results).filter(move |&item| match assoc_item_search {
125 }), 125 AssocItemSearch::Include => true,
126 ); 126 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
127 127 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
128 candidates 128 })
129} 129}
130 130
131fn get_name_definition( 131fn get_name_definition(
@@ -144,3 +144,9 @@ fn get_name_definition(
144 let name = ast::Name::cast(candidate_name_node)?; 144 let name = ast::Name::cast(candidate_name_node)?;
145 NameClass::classify(sema, &name)?.defined(sema.db) 145 NameClass::classify(sema, &name)?.defined(sema.db)
146} 146}
147
148fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
149 item.as_module_def_id()
150 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
151 .is_some()
152}
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 324817cd0..3634b2b26 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -7,7 +7,7 @@
7use std::{convert::TryInto, mem}; 7use std::{convert::TryInto, mem};
8 8
9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; 9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
10use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; 10use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility};
11use once_cell::unsync::Lazy; 11use once_cell::unsync::Lazy;
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; 13use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
@@ -78,6 +78,76 @@ impl SearchScope {
78 SearchScope { entries } 78 SearchScope { entries }
79 } 79 }
80 80
81 fn crate_graph(db: &RootDatabase) -> SearchScope {
82 let mut entries = FxHashMap::default();
83
84 let graph = db.crate_graph();
85 for krate in graph.iter() {
86 let root_file = graph[krate].root_file_id;
87 let source_root_id = db.file_source_root(root_file);
88 let source_root = db.source_root(source_root_id);
89 entries.extend(source_root.iter().map(|id| (id, None)));
90 }
91 SearchScope { entries }
92 }
93
94 fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
95 let mut entries = FxHashMap::default();
96 for rev_dep in of.transitive_reverse_dependencies(db) {
97 let root_file = rev_dep.root_file(db);
98 let source_root_id = db.file_source_root(root_file);
99 let source_root = db.source_root(source_root_id);
100 entries.extend(source_root.iter().map(|id| (id, None)));
101 }
102 SearchScope { entries }
103 }
104
105 fn krate(db: &RootDatabase, of: hir::Crate) -> SearchScope {
106 let root_file = of.root_file(db);
107 let source_root_id = db.file_source_root(root_file);
108 let source_root = db.source_root(source_root_id);
109 SearchScope {
110 entries: source_root.iter().map(|id| (id, None)).collect::<FxHashMap<_, _>>(),
111 }
112 }
113
114 fn module(db: &RootDatabase, module: hir::Module) -> SearchScope {
115 let mut entries = FxHashMap::default();
116
117 let mut to_visit = vec![module];
118 let mut is_first = true;
119 while let Some(module) = to_visit.pop() {
120 let src = module.definition_source(db);
121 let file_id = src.file_id.original_file(db);
122 match src.value {
123 ModuleSource::Module(m) => {
124 if is_first {
125 let range = Some(m.syntax().text_range());
126 entries.insert(file_id, range);
127 } else {
128 // We have already added the enclosing file to the search scope,
129 // so do nothing.
130 }
131 }
132 ModuleSource::BlockExpr(b) => {
133 if is_first {
134 let range = Some(b.syntax().text_range());
135 entries.insert(file_id, range);
136 } else {
137 // We have already added the enclosing file to the search scope,
138 // so do nothing.
139 }
140 }
141 ModuleSource::SourceFile(_) => {
142 entries.insert(file_id, None);
143 }
144 };
145 is_first = false;
146 to_visit.extend(module.children(db));
147 }
148 SearchScope { entries }
149 }
150
81 pub fn empty() -> SearchScope { 151 pub fn empty() -> SearchScope {
82 SearchScope::new(FxHashMap::default()) 152 SearchScope::new(FxHashMap::default())
83 } 153 }
@@ -140,24 +210,15 @@ impl Definition {
140 let _p = profile::span("search_scope"); 210 let _p = profile::span("search_scope");
141 211
142 if let Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) = self { 212 if let Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) = self {
143 let mut res = FxHashMap::default(); 213 return SearchScope::crate_graph(db);
144
145 let graph = db.crate_graph();
146 for krate in graph.iter() {
147 let root_file = graph[krate].root_file_id;
148 let source_root_id = db.file_source_root(root_file);
149 let source_root = db.source_root(source_root_id);
150 res.extend(source_root.iter().map(|id| (id, None)));
151 }
152 return SearchScope::new(res);
153 } 214 }
154 215
155 let module = match self.module(db) { 216 let module = match self.module(db) {
156 Some(it) => it, 217 Some(it) => it,
157 None => return SearchScope::empty(), 218 None => return SearchScope::empty(),
158 }; 219 };
159 let module_src = module.definition_source(db); 220 let InFile { file_id, value: module_source } = module.definition_source(db);
160 let file_id = module_src.file_id.original_file(db); 221 let file_id = file_id.original_file(db);
161 222
162 if let Definition::Local(var) = self { 223 if let Definition::Local(var) = self {
163 let range = match var.parent(db) { 224 let range = match var.parent(db) {
@@ -165,9 +226,10 @@ impl Definition {
165 DefWithBody::Const(c) => c.source(db).map(|src| src.value.syntax().text_range()), 226 DefWithBody::Const(c) => c.source(db).map(|src| src.value.syntax().text_range()),
166 DefWithBody::Static(s) => s.source(db).map(|src| src.value.syntax().text_range()), 227 DefWithBody::Static(s) => s.source(db).map(|src| src.value.syntax().text_range()),
167 }; 228 };
168 let mut res = FxHashMap::default(); 229 return match range {
169 res.insert(file_id, range); 230 Some(range) => SearchScope::file_range(FileRange { file_id, range }),
170 return SearchScope::new(res); 231 None => SearchScope::single_file(file_id),
232 };
171 } 233 }
172 234
173 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { 235 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
@@ -198,75 +260,39 @@ impl Definition {
198 it.source(db).map(|src| src.value.syntax().text_range()) 260 it.source(db).map(|src| src.value.syntax().text_range())
199 } 261 }
200 }; 262 };
201 let mut res = FxHashMap::default(); 263 return match range {
202 res.insert(file_id, range); 264 Some(range) => SearchScope::file_range(FileRange { file_id, range }),
203 return SearchScope::new(res); 265 None => SearchScope::single_file(file_id),
266 };
204 } 267 }
205 268
206 let vis = self.visibility(db); 269 if let Definition::Macro(macro_def) = self {
207 270 if macro_def.kind() == hir::MacroKind::Declarative {
208 if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) { 271 return if macro_def.attrs(db).by_key("macro_export").exists() {
209 let module: Module = module.into(); 272 SearchScope::reverse_dependencies(db, module.krate())
210 let mut res = FxHashMap::default(); 273 } else {
211 274 SearchScope::krate(db, module.krate())
212 let mut to_visit = vec![module];
213 let mut is_first = true;
214 while let Some(module) = to_visit.pop() {
215 let src = module.definition_source(db);
216 let file_id = src.file_id.original_file(db);
217 match src.value {
218 ModuleSource::Module(m) => {
219 if is_first {
220 let range = Some(m.syntax().text_range());
221 res.insert(file_id, range);
222 } else {
223 // We have already added the enclosing file to the search scope,
224 // so do nothing.
225 }
226 }
227 ModuleSource::BlockExpr(b) => {
228 if is_first {
229 let range = Some(b.syntax().text_range());
230 res.insert(file_id, range);
231 } else {
232 // We have already added the enclosing file to the search scope,
233 // so do nothing.
234 }
235 }
236 ModuleSource::SourceFile(_) => {
237 res.insert(file_id, None);
238 }
239 }; 275 };
240 is_first = false;
241 to_visit.extend(module.children(db));
242 } 276 }
243
244 return SearchScope::new(res);
245 } 277 }
246 278
279 let vis = self.visibility(db);
247 if let Some(Visibility::Public) = vis { 280 if let Some(Visibility::Public) = vis {
248 let source_root_id = db.file_source_root(file_id); 281 return SearchScope::reverse_dependencies(db, module.krate());
249 let source_root = db.source_root(source_root_id); 282 }
250 let mut res = source_root.iter().map(|id| (id, None)).collect::<FxHashMap<_, _>>(); 283 if let Some(Visibility::Module(module)) = vis {
251 284 return SearchScope::module(db, module.into());
252 let krate = module.krate();
253 for rev_dep in krate.transitive_reverse_dependencies(db) {
254 let root_file = rev_dep.root_file(db);
255 let source_root_id = db.file_source_root(root_file);
256 let source_root = db.source_root(source_root_id);
257 res.extend(source_root.iter().map(|id| (id, None)));
258 }
259 return SearchScope::new(res);
260 } 285 }
261 286
262 let mut res = FxHashMap::default(); 287 let range = match module_source {
263 let range = match module_src.value {
264 ModuleSource::Module(m) => Some(m.syntax().text_range()), 288 ModuleSource::Module(m) => Some(m.syntax().text_range()),
265 ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()), 289 ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
266 ModuleSource::SourceFile(_) => None, 290 ModuleSource::SourceFile(_) => None,
267 }; 291 };
268 res.insert(file_id, range); 292 match range {
269 SearchScope::new(res) 293 Some(range) => SearchScope::file_range(FileRange { file_id, range }),
294 None => SearchScope::single_file(file_id),
295 }
270 } 296 }
271 297
272 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { 298 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> {
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 9ed9568ce..35e382b5c 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -52,6 +52,7 @@ pub struct Query {
52 only_types: bool, 52 only_types: bool,
53 libs: bool, 53 libs: bool,
54 exact: bool, 54 exact: bool,
55 case_sensitive: bool,
55 limit: usize, 56 limit: usize,
56} 57}
57 58
@@ -64,6 +65,7 @@ impl Query {
64 only_types: false, 65 only_types: false,
65 libs: false, 66 libs: false,
66 exact: false, 67 exact: false,
68 case_sensitive: false,
67 limit: usize::max_value(), 69 limit: usize::max_value(),
68 } 70 }
69 } 71 }
@@ -80,6 +82,10 @@ impl Query {
80 self.exact = true; 82 self.exact = true;
81 } 83 }
82 84
85 pub fn case_sensitive(&mut self) {
86 self.case_sensitive = true;
87 }
88
83 pub fn limit(&mut self, limit: usize) { 89 pub fn limit(&mut self, limit: usize) {
84 self.limit = limit 90 self.limit = limit
85 } 91 }
@@ -326,8 +332,14 @@ impl Query {
326 if self.only_types && !symbol.kind.is_type() { 332 if self.only_types && !symbol.kind.is_type() {
327 continue; 333 continue;
328 } 334 }
329 if self.exact && symbol.name != self.query { 335 if self.exact {
330 continue; 336 if symbol.name != self.query {
337 continue;
338 }
339 } else if self.case_sensitive {
340 if self.query.chars().any(|c| !symbol.name.contains(c)) {
341 continue;
342 }
331 } 343 }
332 344
333 res.push(symbol.clone()); 345 res.push(symbol.clone());