aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--crates/assists/src/handlers/auto_import.rs41
-rw-r--r--crates/assists/src/handlers/qualify_path.rs41
-rw-r--r--crates/completion/src/completions/attribute.rs9
-rw-r--r--crates/completion/src/completions/flyimport.rs448
-rw-r--r--crates/completion/src/config.rs2
-rw-r--r--crates/completion/src/item.rs23
-rw-r--r--crates/completion/src/lib.rs11
-rw-r--r--crates/completion/src/render.rs22
-rw-r--r--crates/completion/src/test_utils.rs2
-rw-r--r--crates/hir/src/code_model.rs16
-rw-r--r--crates/hir_def/src/body.rs4
-rw-r--r--crates/hir_def/src/db.rs10
-rw-r--r--crates/hir_def/src/find_path.rs4
-rw-r--r--crates/hir_def/src/import_map.rs66
-rw-r--r--crates/hir_def/src/nameres.rs12
-rw-r--r--crates/hir_def/src/nameres/collector.rs14
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs4
-rw-r--r--crates/hir_def/src/nameres/tests.rs2
-rw-r--r--crates/hir_def/src/resolver.rs12
-rw-r--r--crates/hir_def/src/visibility.rs6
-rw-r--r--crates/hir_ty/src/tests.rs6
-rw-r--r--crates/ide/src/call_hierarchy.rs2
-rw-r--r--crates/ide/src/diagnostics/fixes.rs2
-rw-r--r--crates/ide/src/doc_links.rs8
-rw-r--r--crates/ide/src/lib.rs8
-rw-r--r--crates/ide/src/references.rs52
-rw-r--r--crates/ide/src/references/rename.rs314
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs356
-rw-r--r--crates/ide_db/src/imports_locator.rs76
-rw-r--r--crates/parser/src/grammar/items/traits.rs4
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs15
-rw-r--r--crates/rust-analyzer/src/handlers.rs7
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast24
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs1
37 files changed, 1096 insertions, 536 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 35713a0c4..f01544324 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1904,9 +1904,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
1904 1904
1905[[package]] 1905[[package]]
1906name = "ungrammar" 1906name = "ungrammar"
1907version = "1.9.2" 1907version = "1.9.3"
1908source = "registry+https://github.com/rust-lang/crates.io-index" 1908source = "registry+https://github.com/rust-lang/crates.io-index"
1909checksum = "58a02e2041a872d56354e843e8e86e6b946fc8e7dc32982fcdc335e29eb4cc8b" 1909checksum = "f5901372c0f3a6a1a9d880aef134c8eaf5e54409343637508c0a344270b42d7b"
1910 1910
1911[[package]] 1911[[package]]
1912name = "unicase" 1912name = "unicase"
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 4e2a4fcd9..e93901cb3 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -3,7 +3,7 @@ use ide_db::helpers::{
3 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
4 mod_path_to_ast, 4 mod_path_to_ast,
5}; 5};
6use syntax::ast; 6use syntax::{ast, AstNode, SyntaxNode};
7 7
8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; 8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
9 9
@@ -82,25 +82,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
82// # pub mod std { pub mod collections { pub struct HashMap { } } } 82// # pub mod std { pub mod collections { pub struct HashMap { } } }
83// ``` 83// ```
84pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 84pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
85 let import_assets = 85 let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
86 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { 86 let proposed_imports =
87 ImportAssets::for_regular_path(path_under_caret, &ctx.sema) 87 import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
88 } else if let Some(method_under_caret) =
89 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
90 {
91 ImportAssets::for_method_call(method_under_caret, &ctx.sema)
92 } else {
93 None
94 }?;
95 let proposed_imports = import_assets.search_for_imports(&ctx.sema, &ctx.config.insert_use);
96 if proposed_imports.is_empty() { 88 if proposed_imports.is_empty() {
97 return None; 89 return None;
98 } 90 }
99 91
100 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 92 let range = ctx.sema.original_range(&syntax_under_caret).range;
101 let group = import_group_message(import_assets.import_candidate()); 93 let group = import_group_message(import_assets.import_candidate());
102 let scope = 94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
103 ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
104 for (import, _) in proposed_imports { 95 for (import, _) in proposed_imports {
105 acc.add_group( 96 acc.add_group(
106 &group, 97 &group,
@@ -117,14 +108,28 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
117 Some(()) 108 Some(())
118} 109}
119 110
111pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
112 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
113 ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
114 .zip(Some(path_under_caret.syntax().clone()))
115 } else if let Some(method_under_caret) =
116 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
117 {
118 ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
119 .zip(Some(method_under_caret.syntax().clone()))
120 } else {
121 None
122 }
123}
124
120fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 125fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
121 let name = match import_candidate { 126 let name = match import_candidate {
122 ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name), 127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
123 ImportCandidate::TraitAssocItem(candidate) => { 128 ImportCandidate::TraitAssocItem(candidate) => {
124 format!("Import a trait for item {}", &candidate.name) 129 format!("Import a trait for item {}", candidate.name.text())
125 } 130 }
126 ImportCandidate::TraitMethod(candidate) => { 131 ImportCandidate::TraitMethod(candidate) => {
127 format!("Import a trait for method {}", &candidate.name) 132 format!("Import a trait for method {}", candidate.name.text())
128 } 133 }
129 }; 134 };
130 GroupLabel(name) 135 GroupLabel(name)
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index a7d9fd4dc..af8a11d03 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,10 +1,7 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::AsName;
4use ide_db::helpers::{ 4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast};
5 import_assets::{ImportAssets, ImportCandidate},
6 mod_path_to_ast,
7};
8use ide_db::RootDatabase; 5use ide_db::RootDatabase;
9use syntax::{ 6use syntax::{
10 ast, 7 ast,
@@ -18,6 +15,8 @@ use crate::{
18 AssistId, AssistKind, GroupLabel, 15 AssistId, AssistKind, GroupLabel,
19}; 16};
20 17
18use super::auto_import::find_importable_node;
19
21// Assist: qualify_path 20// Assist: qualify_path
22// 21//
23// If the name is unresolved, provides all possible qualified paths for it. 22// If the name is unresolved, provides all possible qualified paths for it.
@@ -36,47 +35,38 @@ use crate::{
36// # pub mod std { pub mod collections { pub struct HashMap { } } } 35// # pub mod std { pub mod collections { pub struct HashMap { } } }
37// ``` 36// ```
38pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 let import_assets = 38 let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
40 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
41 ImportAssets::for_regular_path(path_under_caret, &ctx.sema)
42 } else if let Some(method_under_caret) =
43 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
44 {
45 ImportAssets::for_method_call(method_under_caret, &ctx.sema)
46 } else {
47 None
48 }?;
49 let proposed_imports = import_assets.search_for_relative_paths(&ctx.sema); 39 let proposed_imports = import_assets.search_for_relative_paths(&ctx.sema);
50 if proposed_imports.is_empty() { 40 if proposed_imports.is_empty() {
51 return None; 41 return None;
52 } 42 }
53 43
54 let candidate = import_assets.import_candidate(); 44 let candidate = import_assets.import_candidate();
55 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 45 let range = ctx.sema.original_range(&syntax_under_caret).range;
56 46
57 let qualify_candidate = match candidate { 47 let qualify_candidate = match candidate {
58 ImportCandidate::Path(candidate) => { 48 ImportCandidate::Path(candidate) => {
59 if candidate.qualifier.is_some() { 49 if candidate.qualifier.is_some() {
60 mark::hit!(qualify_path_qualifier_start); 50 mark::hit!(qualify_path_qualifier_start);
61 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 51 let path = ast::Path::cast(syntax_under_caret)?;
62 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 52 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
63 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 53 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
64 } else { 54 } else {
65 mark::hit!(qualify_path_unqualified_name); 55 mark::hit!(qualify_path_unqualified_name);
66 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 56 let path = ast::Path::cast(syntax_under_caret)?;
67 let generics = path.segment()?.generic_arg_list(); 57 let generics = path.segment()?.generic_arg_list();
68 QualifyCandidate::UnqualifiedName(generics) 58 QualifyCandidate::UnqualifiedName(generics)
69 } 59 }
70 } 60 }
71 ImportCandidate::TraitAssocItem(_) => { 61 ImportCandidate::TraitAssocItem(_) => {
72 mark::hit!(qualify_path_trait_assoc_item); 62 mark::hit!(qualify_path_trait_assoc_item);
73 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 63 let path = ast::Path::cast(syntax_under_caret)?;
74 let (qualifier, segment) = (path.qualifier()?, path.segment()?); 64 let (qualifier, segment) = (path.qualifier()?, path.segment()?);
75 QualifyCandidate::TraitAssocItem(qualifier, segment) 65 QualifyCandidate::TraitAssocItem(qualifier, segment)
76 } 66 }
77 ImportCandidate::TraitMethod(_) => { 67 ImportCandidate::TraitMethod(_) => {
78 mark::hit!(qualify_path_trait_method); 68 mark::hit!(qualify_path_trait_method);
79 let mcall_expr = ast::MethodCallExpr::cast(import_assets.syntax_under_caret().clone())?; 69 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
80 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) 70 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
81 } 71 }
82 }; 72 };
@@ -140,7 +130,7 @@ impl QualifyCandidate<'_> {
140 let generics = 130 let generics =
141 mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string); 131 mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
142 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); 132 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
143 let trait_ = item_as_trait(item)?; 133 let trait_ = item_as_trait(db, item)?;
144 let method = find_trait_method(db, trait_, &trait_method_name)?; 134 let method = find_trait_method(db, trait_, &trait_method_name)?;
145 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { 135 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
146 let receiver = match self_access { 136 let receiver = match self_access {
@@ -179,11 +169,13 @@ fn find_trait_method(
179 } 169 }
180} 170}
181 171
182fn item_as_trait(item: hir::ItemInNs) -> Option<hir::Trait> { 172fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
183 if let hir::ModuleDef::Trait(trait_) = hir::ModuleDef::from(item.as_module_def_id()?) { 173 let item_module_def = hir::ModuleDef::from(item.as_module_def_id()?);
174
175 if let hir::ModuleDef::Trait(trait_) = item_module_def {
184 Some(trait_) 176 Some(trait_)
185 } else { 177 } else {
186 None 178 item_module_def.as_assoc_item(db)?.containing_trait(db)
187 } 179 }
188} 180}
189 181
@@ -191,7 +183,8 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
191 let name = match candidate { 183 let name = match candidate {
192 ImportCandidate::Path(it) => &it.name, 184 ImportCandidate::Path(it) => &it.name,
193 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 185 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name,
194 }; 186 }
187 .text();
195 GroupLabel(format!("Qualify {}", name)) 188 GroupLabel(format!("Qualify {}", name))
196} 189}
197 190
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index e5522980d..ab25a8c58 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -99,13 +99,14 @@ const ATTRIBUTES: &[AttrCompletion] = &[
99 Some("export_name"), 99 Some("export_name"),
100 Some(r#"export_name = "${0:exported_symbol_name}""#), 100 Some(r#"export_name = "${0:exported_symbol_name}""#),
101 ), 101 ),
102 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
102 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), 103 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
103 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), 104 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
104 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 105 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
105 // FIXME: resolve through macro resolution? 106 // FIXME: resolve through macro resolution?
106 attr("global_allocator", None, None).prefer_inner(), 107 attr("global_allocator", None, None).prefer_inner(),
107 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), 108 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
108 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")), 109 attr("inline", Some("inline"), Some("inline")),
109 attr("link", None, None), 110 attr("link", None, None),
110 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)), 111 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
111 attr( 112 attr(
@@ -468,10 +469,11 @@ struct Test {}
468 at deprecated 469 at deprecated
469 at derive(…) 470 at derive(…)
470 at export_name = "…" 471 at export_name = "…"
472 at doc(alias = "…")
471 at doc = "…" 473 at doc = "…"
472 at forbid(…) 474 at forbid(…)
473 at ignore = "…" 475 at ignore = "…"
474 at inline(…) 476 at inline
475 at link 477 at link
476 at link_name = "…" 478 at link_name = "…"
477 at link_section = "…" 479 at link_section = "…"
@@ -515,12 +517,13 @@ struct Test {}
515 at deprecated 517 at deprecated
516 at derive(…) 518 at derive(…)
517 at export_name = "…" 519 at export_name = "…"
520 at doc(alias = "…")
518 at doc = "…" 521 at doc = "…"
519 at feature(…) 522 at feature(…)
520 at forbid(…) 523 at forbid(…)
521 at global_allocator 524 at global_allocator
522 at ignore = "…" 525 at ignore = "…"
523 at inline(…) 526 at inline
524 at link 527 at link
525 at link_name = "…" 528 at link_name = "…"
526 at link_section = "…" 529 at link_section = "…"
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
index 222809638..47e797ac8 100644
--- a/crates/completion/src/completions/flyimport.rs
+++ b/crates/completion/src/completions/flyimport.rs
@@ -20,11 +20,14 @@
20//! # pub mod std { pub mod marker { pub struct PhantomData { } } } 20//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
21//! ``` 21//! ```
22//! 22//!
23//! Also completes associated items, that require trait imports.
24//!
23//! .Fuzzy search details 25//! .Fuzzy search details
24//! 26//!
25//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only 27//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
26//! (i.e. in `HashMap` in the `std::collections::HashMap` path). 28//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
27//! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols. 29//! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols
30//! (but shows all associated items for any input length).
28//! 31//!
29//! .Import configuration 32//! .Import configuration
30//! 33//!
@@ -45,10 +48,12 @@
45//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
46//! capability enabled. 49//! capability enabled.
47 50
48use either::Either;
49use hir::{ModPath, ScopeDef}; 51use hir::{ModPath, ScopeDef};
50use ide_db::{helpers::insert_use::ImportScope, imports_locator}; 52use ide_db::helpers::{
51use syntax::AstNode; 53 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope,
55};
56use syntax::{AstNode, SyntaxNode, T};
52use test_utils::mark; 57use test_utils::mark;
53 58
54use crate::{ 59use crate::{
@@ -60,58 +65,108 @@ use crate::{
60use super::Completions; 65use super::Completions;
61 66
62pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 67pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
63 if !ctx.config.enable_autoimport_completions { 68 if !ctx.config.enable_imports_on_the_fly {
64 return None; 69 return None;
65 } 70 }
66 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { 71 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
67 return None; 72 return None;
68 } 73 }
69 let potential_import_name = ctx.token.to_string(); 74 let potential_import_name = {
70 if potential_import_name.len() < 2 { 75 let token_kind = ctx.token.kind();
71 return None; 76 if matches!(token_kind, T![.] | T![::]) {
72 } 77 String::new()
73 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string()); 78 } else {
79 ctx.token.to_string()
80 }
81 };
74 82
75 let current_module = ctx.scope.module()?; 83 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
76 let anchor = ctx.name_ref_syntax.as_ref()?;
77 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
78 84
79 let user_input_lowercased = potential_import_name.to_lowercase(); 85 let user_input_lowercased = potential_import_name.to_lowercase();
80 let mut all_mod_paths = imports_locator::find_similar_imports( 86 let import_assets = import_assets(ctx, potential_import_name)?;
87 let import_scope = ImportScope::find_insert_use_container(
88 position_for_import(ctx, Some(import_assets.import_candidate()))?,
81 &ctx.sema, 89 &ctx.sema,
82 ctx.krate?, 90 )?;
83 Some(40), 91 let mut all_mod_paths = import_assets
84 potential_import_name, 92 .search_for_relative_paths(&ctx.sema)
85 true, 93 .into_iter()
86 true, 94 .map(|(mod_path, item_in_ns)| {
87 ) 95 let scope_item = match item_in_ns {
88 .filter_map(|import_candidate| { 96 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
89 Some(match import_candidate { 97 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
90 Either::Left(module_def) => { 98 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
91 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) 99 };
92 } 100 (mod_path, scope_item)
93 Either::Right(macro_def) => {
94 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
95 }
96 }) 101 })
97 }) 102 .collect::<Vec<_>>();
98 .filter(|(mod_path, _)| mod_path.len() > 1)
99 .collect::<Vec<_>>();
100
101 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 103 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
102 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 104 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
103 }); 105 });
104 106
105 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { 107 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
106 render_resolution_with_import( 108 let import_for_trait_assoc_item = match definition {
107 RenderContext::new(ctx), 109 ScopeDef::ModuleDef(module_def) => module_def
108 ImportEdit { import_path, import_scope: import_scope.clone() }, 110 .as_assoc_item(ctx.db)
109 &definition, 111 .and_then(|assoc| assoc.containing_trait(ctx.db))
110 ) 112 .is_some(),
113 _ => false,
114 };
115 let import_edit = ImportEdit {
116 import_path,
117 import_scope: import_scope.clone(),
118 import_for_trait_assoc_item,
119 };
120 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
111 })); 121 }));
112 Some(()) 122 Some(())
113} 123}
114 124
125pub(crate) fn position_for_import<'a>(
126 ctx: &'a CompletionContext,
127 import_candidate: Option<&ImportCandidate>,
128) -> Option<&'a SyntaxNode> {
129 Some(match import_candidate {
130 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
131 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
132 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
133 None => ctx
134 .name_ref_syntax
135 .as_ref()
136 .map(|name_ref| name_ref.syntax())
137 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
138 .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
139 })
140}
141
142fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
143 let current_module = ctx.scope.module()?;
144 if let Some(dot_receiver) = &ctx.dot_receiver {
145 ImportAssets::for_fuzzy_method_call(
146 current_module,
147 ctx.sema.type_of_expr(dot_receiver)?,
148 fuzzy_name,
149 )
150 } else {
151 let fuzzy_name_length = fuzzy_name.len();
152 let assets_for_path = ImportAssets::for_fuzzy_path(
153 current_module,
154 ctx.path_qual.clone(),
155 fuzzy_name,
156 &ctx.sema,
157 );
158
159 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
160 && fuzzy_name_length < 2
161 {
162 mark::hit!(ignore_short_input_for_path);
163 None
164 } else {
165 assets_for_path
166 }
167 }
168}
169
115fn compute_fuzzy_completion_order_key( 170fn compute_fuzzy_completion_order_key(
116 proposed_mod_path: &ModPath, 171 proposed_mod_path: &ModPath,
117 user_input_lowercased: &str, 172 user_input_lowercased: &str,
@@ -224,6 +279,30 @@ fn main() {
224 } 279 }
225 280
226 #[test] 281 #[test]
282 fn short_paths_are_ignored() {
283 mark::check!(ignore_short_input_for_path);
284
285 check(
286 r#"
287//- /lib.rs crate:dep
288pub struct FirstStruct;
289pub mod some_module {
290 pub struct SecondStruct;
291 pub struct ThirdStruct;
292}
293
294//- /main.rs crate:main deps:dep
295use dep::{FirstStruct, some_module::SecondStruct};
296
297fn main() {
298 t$0
299}
300"#,
301 expect![[r#""#]],
302 );
303 }
304
305 #[test]
227 fn fuzzy_completions_come_in_specific_order() { 306 fn fuzzy_completions_come_in_specific_order() {
228 mark::check!(certain_fuzzy_order_test); 307 mark::check!(certain_fuzzy_order_test);
229 check( 308 check(
@@ -259,6 +338,176 @@ fn main() {
259 } 338 }
260 339
261 #[test] 340 #[test]
341 fn trait_function_fuzzy_completion() {
342 let fixture = r#"
343 //- /lib.rs crate:dep
344 pub mod test_mod {
345 pub trait TestTrait {
346 const SPECIAL_CONST: u8;
347 type HumbleType;
348 fn weird_function();
349 fn random_method(&self);
350 }
351 pub struct TestStruct {}
352 impl TestTrait for TestStruct {
353 const SPECIAL_CONST: u8 = 42;
354 type HumbleType = ();
355 fn weird_function() {}
356 fn random_method(&self) {}
357 }
358 }
359
360 //- /main.rs crate:main deps:dep
361 fn main() {
362 dep::test_mod::TestStruct::wei$0
363 }
364 "#;
365
366 check(
367 fixture,
368 expect![[r#"
369 fn weird_function() (dep::test_mod::TestTrait) fn weird_function()
370 "#]],
371 );
372
373 check_edit(
374 "weird_function",
375 fixture,
376 r#"
377use dep::test_mod::TestTrait;
378
379fn main() {
380 dep::test_mod::TestStruct::weird_function()$0
381}
382"#,
383 );
384 }
385
386 #[test]
387 fn trait_const_fuzzy_completion() {
388 let fixture = r#"
389 //- /lib.rs crate:dep
390 pub mod test_mod {
391 pub trait TestTrait {
392 const SPECIAL_CONST: u8;
393 type HumbleType;
394 fn weird_function();
395 fn random_method(&self);
396 }
397 pub struct TestStruct {}
398 impl TestTrait for TestStruct {
399 const SPECIAL_CONST: u8 = 42;
400 type HumbleType = ();
401 fn weird_function() {}
402 fn random_method(&self) {}
403 }
404 }
405
406 //- /main.rs crate:main deps:dep
407 fn main() {
408 dep::test_mod::TestStruct::spe$0
409 }
410 "#;
411
412 check(
413 fixture,
414 expect![[r#"
415 ct SPECIAL_CONST (dep::test_mod::TestTrait)
416 "#]],
417 );
418
419 check_edit(
420 "SPECIAL_CONST",
421 fixture,
422 r#"
423use dep::test_mod::TestTrait;
424
425fn main() {
426 dep::test_mod::TestStruct::SPECIAL_CONST
427}
428"#,
429 );
430 }
431
432 #[test]
433 fn trait_method_fuzzy_completion() {
434 let fixture = r#"
435 //- /lib.rs crate:dep
436 pub mod test_mod {
437 pub trait TestTrait {
438 const SPECIAL_CONST: u8;
439 type HumbleType;
440 fn weird_function();
441 fn random_method(&self);
442 }
443 pub struct TestStruct {}
444 impl TestTrait for TestStruct {
445 const SPECIAL_CONST: u8 = 42;
446 type HumbleType = ();
447 fn weird_function() {}
448 fn random_method(&self) {}
449 }
450 }
451
452 //- /main.rs crate:main deps:dep
453 fn main() {
454 let test_struct = dep::test_mod::TestStruct {};
455 test_struct.ran$0
456 }
457 "#;
458
459 check(
460 fixture,
461 expect![[r#"
462 me random_method() (dep::test_mod::TestTrait) fn random_method(&self)
463 "#]],
464 );
465
466 check_edit(
467 "random_method",
468 fixture,
469 r#"
470use dep::test_mod::TestTrait;
471
472fn main() {
473 let test_struct = dep::test_mod::TestStruct {};
474 test_struct.random_method()$0
475}
476"#,
477 );
478 }
479
480 #[test]
481 fn no_trait_type_fuzzy_completion() {
482 check(
483 r#"
484//- /lib.rs crate:dep
485pub mod test_mod {
486 pub trait TestTrait {
487 const SPECIAL_CONST: u8;
488 type HumbleType;
489 fn weird_function();
490 fn random_method(&self);
491 }
492 pub struct TestStruct {}
493 impl TestTrait for TestStruct {
494 const SPECIAL_CONST: u8 = 42;
495 type HumbleType = ();
496 fn weird_function() {}
497 fn random_method(&self) {}
498 }
499}
500
501//- /main.rs crate:main deps:dep
502fn main() {
503 dep::test_mod::TestStruct::hum$0
504}
505"#,
506 expect![[r#""#]],
507 );
508 }
509
510 #[test]
262 fn does_not_propose_names_in_scope() { 511 fn does_not_propose_names_in_scope() {
263 check( 512 check(
264 r#" 513 r#"
@@ -288,4 +537,129 @@ fn main() {
288 expect![[r#""#]], 537 expect![[r#""#]],
289 ); 538 );
290 } 539 }
540
541 #[test]
542 fn does_not_propose_traits_in_scope() {
543 check(
544 r#"
545//- /lib.rs crate:dep
546pub mod test_mod {
547 pub trait TestTrait {
548 const SPECIAL_CONST: u8;
549 type HumbleType;
550 fn weird_function();
551 fn random_method(&self);
552 }
553 pub struct TestStruct {}
554 impl TestTrait for TestStruct {
555 const SPECIAL_CONST: u8 = 42;
556 type HumbleType = ();
557 fn weird_function() {}
558 fn random_method(&self) {}
559 }
560}
561
562//- /main.rs crate:main deps:dep
563use dep::test_mod::{TestStruct, TestTrait};
564fn main() {
565 dep::test_mod::TestStruct::hum$0
566}
567"#,
568 expect![[r#""#]],
569 );
570 }
571
572 #[test]
573 fn blanket_trait_impl_import() {
574 check_edit(
575 "another_function",
576 r#"
577//- /lib.rs crate:dep
578pub mod test_mod {
579 pub struct TestStruct {}
580 pub trait TestTrait {
581 fn another_function();
582 }
583 impl<T> TestTrait for T {
584 fn another_function() {}
585 }
586}
587
588//- /main.rs crate:main deps:dep
589fn main() {
590 dep::test_mod::TestStruct::ano$0
591}
592"#,
593 r#"
594use dep::test_mod::TestTrait;
595
596fn main() {
597 dep::test_mod::TestStruct::another_function()$0
598}
599"#,
600 );
601 }
602
603 #[test]
604 fn zero_input_assoc_item_completion() {
605 check(
606 r#"
607//- /lib.rs crate:dep
608pub mod test_mod {
609 pub trait TestTrait {
610 const SPECIAL_CONST: u8;
611 type HumbleType;
612 fn weird_function();
613 fn random_method(&self);
614 }
615 pub struct TestStruct {}
616 impl TestTrait for TestStruct {
617 const SPECIAL_CONST: u8 = 42;
618 type HumbleType = ();
619 fn weird_function() {}
620 fn random_method(&self) {}
621 }
622}
623
624//- /main.rs crate:main deps:dep
625fn main() {
626 let test_struct = dep::test_mod::TestStruct {};
627 test_struct.$0
628}
629 "#,
630 expect![[r#"
631 me random_method() (dep::test_mod::TestTrait) fn random_method(&self)
632 "#]],
633 );
634
635 check(
636 r#"
637//- /lib.rs crate:dep
638pub mod test_mod {
639 pub trait TestTrait {
640 const SPECIAL_CONST: u8;
641 type HumbleType;
642 fn weird_function();
643 fn random_method(&self);
644 }
645 pub struct TestStruct {}
646 impl TestTrait for TestStruct {
647 const SPECIAL_CONST: u8 = 42;
648 type HumbleType = ();
649 fn weird_function() {}
650 fn random_method(&self) {}
651 }
652}
653
654//- /main.rs crate:main deps:dep
655fn main() {
656 dep::test_mod::TestStruct::$0
657}
658"#,
659 expect![[r#"
660 ct SPECIAL_CONST (dep::test_mod::TestTrait)
661 fn weird_function() (dep::test_mod::TestTrait) fn weird_function()
662 "#]],
663 );
664 }
291} 665}
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 58fc700f3..d70ed6c1c 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -9,7 +9,7 @@ use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
11 pub enable_postfix_completions: bool, 11 pub enable_postfix_completions: bool,
12 pub enable_autoimport_completions: bool, 12 pub enable_imports_on_the_fly: bool,
13 pub add_call_parenthesis: bool, 13 pub add_call_parenthesis: bool,
14 pub add_call_argument_snippets: bool, 14 pub add_call_argument_snippets: bool,
15 pub snippet_cap: Option<SnippetCap>, 15 pub snippet_cap: Option<SnippetCap>,
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 5d91d3a5c..4147853e7 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -270,6 +270,7 @@ impl CompletionItem {
270pub struct ImportEdit { 270pub struct ImportEdit {
271 pub import_path: ModPath, 271 pub import_path: ModPath,
272 pub import_scope: ImportScope, 272 pub import_scope: ImportScope,
273 pub import_for_trait_assoc_item: bool,
273} 274}
274 275
275impl ImportEdit { 276impl ImportEdit {
@@ -321,17 +322,19 @@ impl Builder {
321 let mut insert_text = self.insert_text; 322 let mut insert_text = self.insert_text;
322 323
323 if let Some(import_to_add) = self.import_to_add.as_ref() { 324 if let Some(import_to_add) = self.import_to_add.as_ref() {
324 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 325 if import_to_add.import_for_trait_assoc_item {
325 let _ = import_path_without_last_segment.segments.pop(); 326 lookup = lookup.or_else(|| Some(label.clone()));
326 327 insert_text = insert_text.or_else(|| Some(label.clone()));
327 if !import_path_without_last_segment.segments.is_empty() { 328 label = format!("{} ({})", label, import_to_add.import_path);
328 if lookup.is_none() { 329 } else {
329 lookup = Some(label.clone()); 330 let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
330 } 331 let _ = import_path_without_last_segment.segments.pop();
331 if insert_text.is_none() { 332
332 insert_text = Some(label.clone()); 333 if !import_path_without_last_segment.segments.is_empty() {
334 lookup = lookup.or_else(|| Some(label.clone()));
335 insert_text = insert_text.or_else(|| Some(label.clone()));
336 label = format!("{}::{}", import_path_without_last_segment, label);
333 } 337 }
334 label = format!("{}::{}", import_path_without_last_segment, label);
335 } 338 }
336 } 339 }
337 340
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index ee1b822e7..2c4e54524 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -11,10 +11,10 @@ mod render;
11 11
12mod completions; 12mod completions;
13 13
14use completions::flyimport::position_for_import;
14use ide_db::{ 15use ide_db::{
15 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, 16 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
16}; 17};
17use syntax::AstNode;
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; 20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
@@ -139,12 +139,13 @@ pub fn resolve_completion_edits(
139 position: FilePosition, 139 position: FilePosition,
140 full_import_path: &str, 140 full_import_path: &str,
141 imported_name: String, 141 imported_name: String,
142 import_for_trait_assoc_item: bool,
142) -> Option<Vec<TextEdit>> { 143) -> Option<Vec<TextEdit>> {
143 let ctx = CompletionContext::new(db, position, config)?; 144 let ctx = CompletionContext::new(db, position, config)?;
144 let anchor = ctx.name_ref_syntax.as_ref()?; 145 let position_for_import = position_for_import(&ctx, None)?;
145 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; 146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
146 147
147 let current_module = ctx.sema.scope(anchor.syntax()).module()?; 148 let current_module = ctx.sema.scope(position_for_import).module()?;
148 let current_crate = current_module.krate(); 149 let current_crate = current_module.krate();
149 150
150 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) 151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
@@ -154,7 +155,7 @@ pub fn resolve_completion_edits(
154 }) 155 })
155 .find(|mod_path| mod_path.to_string() == full_import_path)?; 156 .find(|mod_path| mod_path.to_string() == full_import_path)?;
156 157
157 ImportEdit { import_path, import_scope } 158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
158 .to_text_edit(config.insert_use.merge) 159 .to_text_edit(config.insert_use.merge)
159 .map(|edit| vec![edit]) 160 .map(|edit| vec![edit])
160} 161}
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 820dd01d1..4b3c9702a 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -10,7 +10,7 @@ pub(crate) mod type_alias;
10 10
11mod builder_ext; 11mod builder_ext;
12 12
13use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; 13use hir::{Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type};
14use ide_db::{helpers::SnippetCap, RootDatabase}; 14use ide_db::{helpers::SnippetCap, RootDatabase};
15use syntax::TextRange; 15use syntax::TextRange;
16use test_utils::mark; 16use test_utils::mark;
@@ -51,16 +51,16 @@ pub(crate) fn render_resolution_with_import<'a>(
51 import_edit: ImportEdit, 51 import_edit: ImportEdit,
52 resolution: &ScopeDef, 52 resolution: &ScopeDef,
53) -> Option<CompletionItem> { 53) -> Option<CompletionItem> {
54 Render::new(ctx) 54 let local_name = match resolution {
55 .render_resolution( 55 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
56 import_edit.import_path.segments.last()?.to_string(), 56 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
57 Some(import_edit), 57 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
58 resolution, 58 _ => import_edit.import_path.segments.last()?.to_string(),
59 ) 59 };
60 .map(|mut item| { 60 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| {
61 item.completion_kind = CompletionKind::Magic; 61 item.completion_kind = CompletionKind::Magic;
62 item 62 item
63 }) 63 })
64} 64}
65 65
66/// Interface for data and methods required for items rendering. 66/// Interface for data and methods required for items rendering.
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index 6ea6da989..3faf861b9 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -18,7 +18,7 @@ use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
18 18
19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { 19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
20 enable_postfix_completions: true, 20 enable_postfix_completions: true,
21 enable_autoimport_completions: true, 21 enable_imports_on_the_fly: true,
22 add_call_parenthesis: true, 22 add_call_parenthesis: true,
23 add_call_argument_snippets: true, 23 add_call_argument_snippets: true,
24 snippet_cap: SnippetCap::new(true), 24 snippet_cap: SnippetCap::new(true),
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 6cbf5cecf..2950f08b8 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -272,6 +272,15 @@ impl ModuleDef {
272 272
273 hir_ty::diagnostics::validate_module_item(db, module.id.krate, id, sink) 273 hir_ty::diagnostics::validate_module_item(db, module.id.krate, id, sink)
274 } 274 }
275
276 pub fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
277 match self {
278 ModuleDef::Function(f) => f.as_assoc_item(db),
279 ModuleDef::Const(c) => c.as_assoc_item(db),
280 ModuleDef::TypeAlias(t) => t.as_assoc_item(db),
281 _ => None,
282 }
283 }
275} 284}
276 285
277impl Module { 286impl Module {
@@ -1091,6 +1100,13 @@ impl AssocItem {
1091 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), 1100 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"),
1092 } 1101 }
1093 } 1102 }
1103
1104 pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
1105 match self.container(db) {
1106 AssocItemContainer::Trait(t) => Some(t),
1107 _ => None,
1108 }
1109 }
1094} 1110}
1095 1111
1096impl HasVisibility for AssocItem { 1112impl HasVisibility for AssocItem {
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 344f0b6c0..3b2dd0f6e 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -29,7 +29,7 @@ use crate::{
29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, 29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
30 item_scope::BuiltinShadowMode, 30 item_scope::BuiltinShadowMode,
31 item_scope::ItemScope, 31 item_scope::ItemScope,
32 nameres::CrateDefMap, 32 nameres::DefMap,
33 path::{ModPath, Path}, 33 path::{ModPath, Path},
34 src::HasSource, 34 src::HasSource,
35 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 35 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
@@ -45,7 +45,7 @@ pub(crate) struct CfgExpander {
45 45
46pub(crate) struct Expander { 46pub(crate) struct Expander {
47 cfg_expander: CfgExpander, 47 cfg_expander: CfgExpander,
48 crate_def_map: Arc<CrateDefMap>, 48 crate_def_map: Arc<DefMap>,
49 current_file_id: HirFileId, 49 current_file_id: HirFileId,
50 ast_id_map: Arc<AstIdMap>, 50 ast_id_map: Arc<AstIdMap>,
51 module: ModuleId, 51 module: ModuleId,
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 6ef9fe790..91c8d45cd 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -15,7 +15,7 @@ use crate::{
15 import_map::ImportMap, 15 import_map::ImportMap,
16 item_tree::ItemTree, 16 item_tree::ItemTree,
17 lang_item::{LangItemTarget, LangItems}, 17 lang_item::{LangItemTarget, LangItems},
18 nameres::CrateDefMap, 18 nameres::DefMap,
19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
20 GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, 20 GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId,
21 StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, 21 StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
@@ -50,10 +50,10 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
50 50
51 #[salsa::invoke(crate_def_map_wait)] 51 #[salsa::invoke(crate_def_map_wait)]
52 #[salsa::transparent] 52 #[salsa::transparent]
53 fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>; 53 fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
54 54
55 #[salsa::invoke(CrateDefMap::crate_def_map_query)] 55 #[salsa::invoke(DefMap::crate_def_map_query)]
56 fn crate_def_map_query(&self, krate: CrateId) -> Arc<CrateDefMap>; 56 fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>;
57 57
58 #[salsa::invoke(StructData::struct_data_query)] 58 #[salsa::invoke(StructData::struct_data_query)]
59 fn struct_data(&self, id: StructId) -> Arc<StructData>; 59 fn struct_data(&self, id: StructId) -> Arc<StructData>;
@@ -112,7 +112,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
112 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; 112 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
113} 113}
114 114
115fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 115fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<DefMap> {
116 let _p = profile::span("crate_def_map:wait"); 116 let _p = profile::span("crate_def_map:wait");
117 db.crate_def_map_query(krate) 117 db.crate_def_map_query(krate)
118} 118}
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 4a212d291..422a6eeb4 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -4,7 +4,7 @@ use hir_expand::name::{known, AsName, Name};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::nameres::CrateDefMap; 7use crate::nameres::DefMap;
8use crate::{ 8use crate::{
9 db::DefDatabase, 9 db::DefDatabase,
10 item_scope::ItemInNs, 10 item_scope::ItemInNs,
@@ -47,7 +47,7 @@ impl ModPath {
47 } 47 }
48} 48}
49 49
50fn check_self_super(def_map: &CrateDefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 50fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
51 if item == ItemInNs::Types(from.into()) { 51 if item == ItemInNs::Types(from.into()) {
52 // - if the item is the module we're in, use `self` 52 // - if the item is the module we're in, use `self`
53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) 53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index e5368b293..fac0de90c 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -263,6 +263,7 @@ pub enum ImportKind {
263 Trait, 263 Trait,
264 TypeAlias, 264 TypeAlias,
265 BuiltinType, 265 BuiltinType,
266 AssociatedItem,
266} 267}
267 268
268/// A way to match import map contents against the search query. 269/// A way to match import map contents against the search query.
@@ -282,6 +283,7 @@ pub struct Query {
282 query: String, 283 query: String,
283 lowercased: String, 284 lowercased: String,
284 name_only: bool, 285 name_only: bool,
286 assoc_items_only: bool,
285 search_mode: SearchMode, 287 search_mode: SearchMode,
286 case_sensitive: bool, 288 case_sensitive: bool,
287 limit: usize, 289 limit: usize,
@@ -295,6 +297,7 @@ impl Query {
295 query, 297 query,
296 lowercased, 298 lowercased,
297 name_only: false, 299 name_only: false,
300 assoc_items_only: false,
298 search_mode: SearchMode::Contains, 301 search_mode: SearchMode::Contains,
299 case_sensitive: false, 302 case_sensitive: false,
300 limit: usize::max_value(), 303 limit: usize::max_value(),
@@ -309,6 +312,11 @@ impl Query {
309 Self { name_only: true, ..self } 312 Self { name_only: true, ..self }
310 } 313 }
311 314
315 /// Matches only the entries that are associated items, ignoring the rest.
316 pub fn assoc_items_only(self) -> Self {
317 Self { assoc_items_only: true, ..self }
318 }
319
312 /// Specifies the way to search for the entries using the query. 320 /// Specifies the way to search for the entries using the query.
313 pub fn search_mode(self, search_mode: SearchMode) -> Self { 321 pub fn search_mode(self, search_mode: SearchMode) -> Self {
314 Self { search_mode, ..self } 322 Self { search_mode, ..self }
@@ -331,6 +339,14 @@ impl Query {
331 } 339 }
332 340
333 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { 341 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
342 if import.is_trait_assoc_item {
343 if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
344 return false;
345 }
346 } else if self.assoc_items_only {
347 return false;
348 }
349
334 let mut input = if import.is_trait_assoc_item || self.name_only { 350 let mut input = if import.is_trait_assoc_item || self.name_only {
335 import.path.segments.last().unwrap().to_string() 351 import.path.segments.last().unwrap().to_string()
336 } else { 352 } else {
@@ -814,6 +830,56 @@ mod tests {
814 } 830 }
815 831
816 #[test] 832 #[test]
833 fn assoc_items_filtering() {
834 let ra_fixture = r#"
835 //- /main.rs crate:main deps:dep
836 //- /dep.rs crate:dep
837 pub mod fmt {
838 pub trait Display {
839 type FmtTypeAlias;
840 const FMT_CONST: bool;
841
842 fn format_function();
843 fn format_method(&self);
844 }
845 }
846 "#;
847
848 check_search(
849 ra_fixture,
850 "main",
851 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(),
852 expect![[r#"
853 dep::fmt::Display::FMT_CONST (a)
854 dep::fmt::Display::format_function (a)
855 dep::fmt::Display::format_method (a)
856 "#]],
857 );
858
859 check_search(
860 ra_fixture,
861 "main",
862 Query::new("fmt".to_string())
863 .search_mode(SearchMode::Fuzzy)
864 .exclude_import_kind(ImportKind::AssociatedItem),
865 expect![[r#"
866 dep::fmt (t)
867 dep::fmt::Display (t)
868 "#]],
869 );
870
871 check_search(
872 ra_fixture,
873 "main",
874 Query::new("fmt".to_string())
875 .search_mode(SearchMode::Fuzzy)
876 .assoc_items_only()
877 .exclude_import_kind(ImportKind::AssociatedItem),
878 expect![[r#""#]],
879 );
880 }
881
882 #[test]
817 fn search_mode() { 883 fn search_mode() {
818 let ra_fixture = r#" 884 let ra_fixture = r#"
819 //- /main.rs crate:main deps:dep 885 //- /main.rs crate:main deps:dep
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 50acc3f54..769a557ad 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -74,7 +74,7 @@ use crate::{
74 74
75/// Contains all top-level defs from a macro-expanded crate 75/// Contains all top-level defs from a macro-expanded crate
76#[derive(Debug, PartialEq, Eq)] 76#[derive(Debug, PartialEq, Eq)]
77pub struct CrateDefMap { 77pub struct DefMap {
78 pub root: LocalModuleId, 78 pub root: LocalModuleId,
79 pub modules: Arena<ModuleData>, 79 pub modules: Arena<ModuleData>,
80 pub(crate) krate: CrateId, 80 pub(crate) krate: CrateId,
@@ -88,7 +88,7 @@ pub struct CrateDefMap {
88 diagnostics: Vec<DefDiagnostic>, 88 diagnostics: Vec<DefDiagnostic>,
89} 89}
90 90
91impl std::ops::Index<LocalModuleId> for CrateDefMap { 91impl std::ops::Index<LocalModuleId> for DefMap {
92 type Output = ModuleData; 92 type Output = ModuleData;
93 fn index(&self, id: LocalModuleId) -> &ModuleData { 93 fn index(&self, id: LocalModuleId) -> &ModuleData {
94 &self.modules[id] 94 &self.modules[id]
@@ -169,8 +169,8 @@ pub struct ModuleData {
169 pub origin: ModuleOrigin, 169 pub origin: ModuleOrigin,
170} 170}
171 171
172impl CrateDefMap { 172impl DefMap {
173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
174 let _p = profile::span("crate_def_map_query").detail(|| { 174 let _p = profile::span("crate_def_map_query").detail(|| {
175 db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() 175 db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
176 }); 176 });
@@ -178,7 +178,7 @@ impl CrateDefMap {
178 let edition = db.crate_graph()[krate].edition; 178 let edition = db.crate_graph()[krate].edition;
179 let mut modules: Arena<ModuleData> = Arena::default(); 179 let mut modules: Arena<ModuleData> = Arena::default();
180 let root = modules.alloc(ModuleData::default()); 180 let root = modules.alloc(ModuleData::default());
181 CrateDefMap { 181 DefMap {
182 krate, 182 krate,
183 edition, 183 edition,
184 extern_prelude: FxHashMap::default(), 184 extern_prelude: FxHashMap::default(),
@@ -227,7 +227,7 @@ impl CrateDefMap {
227 go(&mut buf, self, "crate", self.root); 227 go(&mut buf, self, "crate", self.root);
228 return buf; 228 return buf;
229 229
230 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: LocalModuleId) { 230 fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
231 format_to!(buf, "{}\n", path); 231 format_to!(buf, "{}\n", path);
232 232
233 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect(); 233 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 0cd61698c..61da56340 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -31,7 +31,7 @@ use crate::{
31 }, 31 },
32 nameres::{ 32 nameres::{
33 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 33 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
34 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, 34 BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
35 }, 35 },
36 path::{ImportAlias, ModPath, PathKind}, 36 path::{ImportAlias, ModPath, PathKind},
37 per_ns::PerNs, 37 per_ns::PerNs,
@@ -45,7 +45,7 @@ const GLOB_RECURSION_LIMIT: usize = 100;
45const EXPANSION_DEPTH_LIMIT: usize = 128; 45const EXPANSION_DEPTH_LIMIT: usize = 128;
46const FIXED_POINT_LIMIT: usize = 8192; 46const FIXED_POINT_LIMIT: usize = 8192;
47 47
48pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { 48pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap {
49 let crate_graph = db.crate_graph(); 49 let crate_graph = db.crate_graph();
50 50
51 // populate external prelude 51 // populate external prelude
@@ -210,7 +210,7 @@ struct DefData<'a> {
210/// Walks the tree of module recursively 210/// Walks the tree of module recursively
211struct DefCollector<'a> { 211struct DefCollector<'a> {
212 db: &'a dyn DefDatabase, 212 db: &'a dyn DefDatabase,
213 def_map: CrateDefMap, 213 def_map: DefMap,
214 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, 214 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
215 unresolved_imports: Vec<ImportDirective>, 215 unresolved_imports: Vec<ImportDirective>,
216 resolved_imports: Vec<ImportDirective>, 216 resolved_imports: Vec<ImportDirective>,
@@ -859,7 +859,7 @@ impl DefCollector<'_> {
859 .collect(item_tree.top_level_items()); 859 .collect(item_tree.top_level_items());
860 } 860 }
861 861
862 fn finish(mut self) -> CrateDefMap { 862 fn finish(mut self) -> DefMap {
863 // Emit diagnostics for all remaining unexpanded macros. 863 // Emit diagnostics for all remaining unexpanded macros.
864 864
865 for directive in &self.unexpanded_macros { 865 for directive in &self.unexpanded_macros {
@@ -1474,7 +1474,7 @@ mod tests {
1474 1474
1475 use super::*; 1475 use super::*;
1476 1476
1477 fn do_collect_defs(db: &dyn DefDatabase, def_map: CrateDefMap) -> CrateDefMap { 1477 fn do_collect_defs(db: &dyn DefDatabase, def_map: DefMap) -> DefMap {
1478 let mut collector = DefCollector { 1478 let mut collector = DefCollector {
1479 db, 1479 db,
1480 def_map, 1480 def_map,
@@ -1493,7 +1493,7 @@ mod tests {
1493 collector.def_map 1493 collector.def_map
1494 } 1494 }
1495 1495
1496 fn do_resolve(code: &str) -> CrateDefMap { 1496 fn do_resolve(code: &str) -> DefMap {
1497 let (db, _file_id) = TestDB::with_single_file(&code); 1497 let (db, _file_id) = TestDB::with_single_file(&code);
1498 let krate = db.test_crate(); 1498 let krate = db.test_crate();
1499 1499
@@ -1501,7 +1501,7 @@ mod tests {
1501 let edition = db.crate_graph()[krate].edition; 1501 let edition = db.crate_graph()[krate].edition;
1502 let mut modules: Arena<ModuleData> = Arena::default(); 1502 let mut modules: Arena<ModuleData> = Arena::default();
1503 let root = modules.alloc(ModuleData::default()); 1503 let root = modules.alloc(ModuleData::default());
1504 CrateDefMap { 1504 DefMap {
1505 krate, 1505 krate,
1506 edition, 1506 edition,
1507 extern_prelude: FxHashMap::default(), 1507 extern_prelude: FxHashMap::default(),
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index 88e10574e..096a7d0ac 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -19,7 +19,7 @@ use test_utils::mark;
19use crate::{ 19use crate::{
20 db::DefDatabase, 20 db::DefDatabase,
21 item_scope::BUILTIN_SCOPE, 21 item_scope::BUILTIN_SCOPE,
22 nameres::{BuiltinShadowMode, CrateDefMap}, 22 nameres::{BuiltinShadowMode, DefMap},
23 path::{ModPath, PathKind}, 23 path::{ModPath, PathKind},
24 per_ns::PerNs, 24 per_ns::PerNs,
25 visibility::{RawVisibility, Visibility}, 25 visibility::{RawVisibility, Visibility},
@@ -61,7 +61,7 @@ impl ResolvePathResult {
61 } 61 }
62} 62}
63 63
64impl CrateDefMap { 64impl DefMap {
65 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { 65 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
66 self.extern_prelude 66 self.extern_prelude
67 .get(name) 67 .get(name)
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index c459fa66d..723481c36 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -13,7 +13,7 @@ use test_utils::mark;
13 13
14use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; 14use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
15 15
16fn compute_crate_def_map(ra_fixture: &str) -> Arc<CrateDefMap> { 16fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
17 let db = TestDB::with_files(ra_fixture); 17 let db = TestDB::with_files(ra_fixture);
18 let krate = db.crate_graph().iter().next().unwrap(); 18 let krate = db.crate_graph().iter().next().unwrap();
19 db.crate_def_map(krate) 19 db.crate_def_map(krate)
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 85ddc2c47..e7e92c72d 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -16,7 +16,7 @@ use crate::{
16 expr::{ExprId, PatId}, 16 expr::{ExprId, PatId},
17 generics::GenericParams, 17 generics::GenericParams,
18 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, 18 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
19 nameres::CrateDefMap, 19 nameres::DefMap,
20 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
21 per_ns::PerNs, 21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility}, 22 visibility::{RawVisibility, Visibility},
@@ -34,7 +34,7 @@ pub struct Resolver {
34// FIXME how to store these best 34// FIXME how to store these best
35#[derive(Debug, Clone)] 35#[derive(Debug, Clone)]
36struct ModuleItemMap { 36struct ModuleItemMap {
37 crate_def_map: Arc<CrateDefMap>, 37 crate_def_map: Arc<DefMap>,
38 module_id: LocalModuleId, 38 module_id: LocalModuleId,
39} 39}
40 40
@@ -425,7 +425,7 @@ impl Resolver {
425 traits 425 traits
426 } 426 }
427 427
428 fn module_scope(&self) -> Option<(&CrateDefMap, LocalModuleId)> { 428 fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> {
429 self.scopes.iter().rev().find_map(|scope| match scope { 429 self.scopes.iter().rev().find_map(|scope| match scope {
430 Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), 430 Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),
431 431
@@ -588,11 +588,7 @@ impl Resolver {
588 self.push_scope(Scope::ImplDefScope(impl_def)) 588 self.push_scope(Scope::ImplDefScope(impl_def))
589 } 589 }
590 590
591 fn push_module_scope( 591 fn push_module_scope(self, crate_def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver {
592 self,
593 crate_def_map: Arc<CrateDefMap>,
594 module_id: LocalModuleId,
595 ) -> Resolver {
596 self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) 592 self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id }))
597 } 593 }
598 594
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index f3bc9d680..3134fa43d 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -5,7 +5,7 @@ use syntax::ast;
5 5
6use crate::{ 6use crate::{
7 db::DefDatabase, 7 db::DefDatabase,
8 nameres::CrateDefMap, 8 nameres::DefMap,
9 path::{ModPath, PathKind}, 9 path::{ModPath, PathKind},
10 ModuleId, 10 ModuleId,
11}; 11};
@@ -116,7 +116,7 @@ impl Visibility {
116 116
117 pub(crate) fn is_visible_from_def_map( 117 pub(crate) fn is_visible_from_def_map(
118 self, 118 self,
119 def_map: &CrateDefMap, 119 def_map: &DefMap,
120 from_module: crate::LocalModuleId, 120 from_module: crate::LocalModuleId,
121 ) -> bool { 121 ) -> bool {
122 let to_module = match self { 122 let to_module = match self {
@@ -135,7 +135,7 @@ impl Visibility {
135 /// 135 ///
136 /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only 136 /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
137 /// visible in unrelated modules). 137 /// visible in unrelated modules).
138 pub(crate) fn max(self, other: Visibility, def_map: &CrateDefMap) -> Option<Visibility> { 138 pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
139 match (self, other) { 139 match (self, other) {
140 (Visibility::Module(_), Visibility::Public) 140 (Visibility::Module(_), Visibility::Public)
141 | (Visibility::Public, Visibility::Module(_)) 141 | (Visibility::Public, Visibility::Module(_))
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 5ff755321..4a3fcea8d 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -18,7 +18,7 @@ use hir_def::{
18 db::DefDatabase, 18 db::DefDatabase,
19 item_scope::ItemScope, 19 item_scope::ItemScope,
20 keys, 20 keys,
21 nameres::CrateDefMap, 21 nameres::DefMap,
22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
23}; 23};
24use hir_expand::{db::AstDatabase, InFile}; 24use hir_expand::{db::AstDatabase, InFile};
@@ -221,7 +221,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
221 221
222fn visit_module( 222fn visit_module(
223 db: &TestDB, 223 db: &TestDB,
224 crate_def_map: &CrateDefMap, 224 crate_def_map: &DefMap,
225 module_id: LocalModuleId, 225 module_id: LocalModuleId,
226 cb: &mut dyn FnMut(DefWithBodyId), 226 cb: &mut dyn FnMut(DefWithBodyId),
227) { 227) {
@@ -249,7 +249,7 @@ fn visit_module(
249 249
250 fn visit_scope( 250 fn visit_scope(
251 db: &TestDB, 251 db: &TestDB,
252 crate_def_map: &CrateDefMap, 252 crate_def_map: &DefMap,
253 scope: &ItemScope, 253 scope: &ItemScope,
254 cb: &mut dyn FnMut(DefWithBodyId), 254 cb: &mut dyn FnMut(DefWithBodyId),
255 ) { 255 ) {
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index e8999a7f3..b10a0a78b 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -47,7 +47,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
47 47
48 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
49 49
50 for (&file_id, references) in refs.info.references().iter() { 50 for (&file_id, references) in refs.references().iter() {
51 let file = sema.parse(file_id); 51 let file = sema.parse(file_id);
52 let file = file.syntax(); 52 let file = file.syntax();
53 for reference in references { 53 for reference in references {
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 4b4afd40c..579d5a308 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -140,7 +140,7 @@ impl DiagnosticWithFix for IncorrectCase {
140 rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; 140 rename_with_semantics(sema, file_position, &self.suggested_text).ok()?;
141 141
142 let label = format!("Rename to {}", self.suggested_text); 142 let label = format!("Rename to {}", self.suggested_text);
143 Some(Fix::new(&label, rename_changes.info, frange.range)) 143 Some(Fix::new(&label, rename_changes, frange.range))
144 } 144 }
145} 145}
146 146
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index de10406bc..1f08d7810 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -438,10 +438,10 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem)
438 FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)), 438 FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)),
439 FieldOrAssocItem::AssocItem(assoc) => match assoc { 439 FieldOrAssocItem::AssocItem(assoc) => match assoc {
440 AssocItem::Function(function) => { 440 AssocItem::Function(function) => {
441 let is_trait_method = matches!( 441 let is_trait_method = function
442 function.as_assoc_item(db).map(|assoc| assoc.container(db)), 442 .as_assoc_item(db)
443 Some(AssocItemContainer::Trait(..)) 443 .and_then(|assoc| assoc.containing_trait(db))
444 ); 444 .is_some();
445 // This distinction may get more complicated when specialization is available. 445 // This distinction may get more complicated when specialization is available.
446 // Rustdoc makes this decision based on whether a method 'has defaultness'. 446 // Rustdoc makes this decision based on whether a method 'has defaultness'.
447 // Currently this is only the case for provided trait methods. 447 // Currently this is only the case for provided trait methods.
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 6c94c26b5..567b8117e 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -369,9 +369,7 @@ impl Analysis {
369 position: FilePosition, 369 position: FilePosition,
370 search_scope: Option<SearchScope>, 370 search_scope: Option<SearchScope>,
371 ) -> Cancelable<Option<ReferenceSearchResult>> { 371 ) -> Cancelable<Option<ReferenceSearchResult>> {
372 self.with_db(|db| { 372 self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
373 references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info)
374 })
375 } 373 }
376 374
377 /// Finds all methods and free functions for the file. Does not return tests! 375 /// Finds all methods and free functions for the file. Does not return tests!
@@ -478,6 +476,7 @@ impl Analysis {
478 position: FilePosition, 476 position: FilePosition,
479 full_import_path: &str, 477 full_import_path: &str,
480 imported_name: String, 478 imported_name: String,
479 import_for_trait_assoc_item: bool,
481 ) -> Cancelable<Vec<TextEdit>> { 480 ) -> Cancelable<Vec<TextEdit>> {
482 Ok(self 481 Ok(self
483 .with_db(|db| { 482 .with_db(|db| {
@@ -487,6 +486,7 @@ impl Analysis {
487 position, 486 position,
488 full_import_path, 487 full_import_path,
489 imported_name, 488 imported_name,
489 import_for_trait_assoc_item,
490 ) 490 )
491 })? 491 })?
492 .unwrap_or_default()) 492 .unwrap_or_default())
@@ -520,7 +520,7 @@ impl Analysis {
520 &self, 520 &self,
521 position: FilePosition, 521 position: FilePosition,
522 new_name: &str, 522 new_name: &str,
523 ) -> Cancelable<Result<RangeInfo<SourceChange>, RenameError>> { 523 ) -> Cancelable<Result<SourceChange, RenameError>> {
524 self.with_db(|db| references::rename::rename(db, position, new_name)) 524 self.with_db(|db| references::rename::rename(db, position, new_name))
525 } 525 }
526 526
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index df9c31aef..3a4f4d80b 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -25,7 +25,7 @@ use syntax::{
25 AstNode, SyntaxNode, TextRange, TokenAtOffset, T, 25 AstNode, SyntaxNode, TextRange, TokenAtOffset, T,
26}; 26};
27 27
28use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; 28use crate::{display::TryToNav, FilePosition, NavigationTarget};
29 29
30#[derive(Debug, Clone)] 30#[derive(Debug, Clone)]
31pub struct ReferenceSearchResult { 31pub struct ReferenceSearchResult {
@@ -41,14 +41,6 @@ pub struct Declaration {
41} 41}
42 42
43impl ReferenceSearchResult { 43impl ReferenceSearchResult {
44 pub fn declaration(&self) -> &Declaration {
45 &self.declaration
46 }
47
48 pub fn decl_target(&self) -> &NavigationTarget {
49 &self.declaration.nav
50 }
51
52 pub fn references(&self) -> &UsageSearchResult { 44 pub fn references(&self) -> &UsageSearchResult {
53 &self.references 45 &self.references
54 } 46 }
@@ -87,7 +79,7 @@ pub(crate) fn find_all_refs(
87 sema: &Semantics<RootDatabase>, 79 sema: &Semantics<RootDatabase>,
88 position: FilePosition, 80 position: FilePosition,
89 search_scope: Option<SearchScope>, 81 search_scope: Option<SearchScope>,
90) -> Option<RangeInfo<ReferenceSearchResult>> { 82) -> Option<ReferenceSearchResult> {
91 let _p = profile::span("find_all_refs"); 83 let _p = profile::span("find_all_refs");
92 let syntax = sema.parse(position.file_id).syntax().clone(); 84 let syntax = sema.parse(position.file_id).syntax().clone();
93 85
@@ -105,7 +97,7 @@ pub(crate) fn find_all_refs(
105 ) 97 )
106 }; 98 };
107 99
108 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 100 let def = find_name(&sema, &syntax, position, opt_name)?;
109 101
110 let mut usages = def.usages(sema).set_scope(search_scope).all(); 102 let mut usages = def.usages(sema).set_scope(search_scope).all();
111 usages 103 usages
@@ -139,7 +131,7 @@ pub(crate) fn find_all_refs(
139 131
140 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; 132 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
141 133
142 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages })) 134 Some(ReferenceSearchResult { declaration, references: usages })
143} 135}
144 136
145fn find_name( 137fn find_name(
@@ -147,35 +139,27 @@ fn find_name(
147 syntax: &SyntaxNode, 139 syntax: &SyntaxNode,
148 position: FilePosition, 140 position: FilePosition,
149 opt_name: Option<ast::Name>, 141 opt_name: Option<ast::Name>,
150) -> Option<RangeInfo<Definition>> { 142) -> Option<Definition> {
151 if let Some(name) = opt_name { 143 let def = if let Some(name) = opt_name {
152 let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db); 144 NameClass::classify(sema, &name)?.referenced_or_defined(sema.db)
153 let FileRange { range, .. } = sema.original_range(name.syntax()); 145 } else if let Some(lifetime) =
154 return Some(RangeInfo::new(range, def));
155 }
156
157 let (FileRange { range, .. }, def) = if let Some(lifetime) =
158 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) 146 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
159 { 147 {
160 if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) 148 if let Some(def) =
161 .map(|class| NameRefClass::referenced(class, sema.db)) 149 NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db))
162 { 150 {
163 (sema.original_range(lifetime.syntax()), def) 151 def
164 } else { 152 } else {
165 ( 153 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db)
166 sema.original_range(lifetime.syntax()),
167 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db),
168 )
169 } 154 }
155 } else if let Some(name_ref) =
156 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)
157 {
158 NameRefClass::classify(sema, &name_ref)?.referenced(sema.db)
170 } else { 159 } else {
171 let name_ref = 160 return None;
172 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
173 (
174 sema.original_range(name_ref.syntax()),
175 NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
176 )
177 }; 161 };
178 Some(RangeInfo::new(range, def)) 162 Some(def)
179} 163}
180 164
181fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 165fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index c5010df0a..c25bcce50 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -63,7 +63,7 @@ pub(crate) fn rename(
63 db: &RootDatabase, 63 db: &RootDatabase,
64 position: FilePosition, 64 position: FilePosition,
65 new_name: &str, 65 new_name: &str,
66) -> RenameResult<RangeInfo<SourceChange>> { 66) -> RenameResult<SourceChange> {
67 let sema = Semantics::new(db); 67 let sema = Semantics::new(db);
68 rename_with_semantics(&sema, position, new_name) 68 rename_with_semantics(&sema, position, new_name)
69} 69}
@@ -72,7 +72,7 @@ pub(crate) fn rename_with_semantics(
72 sema: &Semantics<RootDatabase>, 72 sema: &Semantics<RootDatabase>,
73 position: FilePosition, 73 position: FilePosition,
74 new_name: &str, 74 new_name: &str,
75) -> RenameResult<RangeInfo<SourceChange>> { 75) -> RenameResult<SourceChange> {
76 let source_file = sema.parse(position.file_id); 76 let source_file = sema.parse(position.file_id);
77 let syntax = source_file.syntax(); 77 let syntax = source_file.syntax();
78 78
@@ -91,7 +91,7 @@ pub(crate) fn will_rename_file(
91) -> Option<SourceChange> { 91) -> Option<SourceChange> {
92 let sema = Semantics::new(db); 92 let sema = Semantics::new(db);
93 let module = sema.to_module_def(file_id)?; 93 let module = sema.to_module_def(file_id)?;
94 let mut change = rename_mod(&sema, module, new_name_stem).ok()?.info; 94 let mut change = rename_mod(&sema, module, new_name_stem).ok()?;
95 change.file_system_edits.clear(); 95 change.file_system_edits.clear();
96 Some(change) 96 Some(change)
97} 97}
@@ -243,7 +243,7 @@ fn rename_mod(
243 sema: &Semantics<RootDatabase>, 243 sema: &Semantics<RootDatabase>,
244 module: Module, 244 module: Module,
245 new_name: &str, 245 new_name: &str,
246) -> RenameResult<RangeInfo<SourceChange>> { 246) -> RenameResult<SourceChange> {
247 if IdentifierKind::Ident != check_identifier(new_name)? { 247 if IdentifierKind::Ident != check_identifier(new_name)? {
248 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); 248 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
249 } 249 }
@@ -281,13 +281,10 @@ fn rename_mod(
281 }); 281 });
282 source_change.extend(ref_edits); 282 source_change.extend(ref_edits);
283 283
284 Ok(RangeInfo::new(TextRange::default(), source_change)) 284 Ok(source_change)
285} 285}
286 286
287fn rename_to_self( 287fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> {
288 sema: &Semantics<RootDatabase>,
289 local: hir::Local,
290) -> RenameResult<RangeInfo<SourceChange>> {
291 if assert_never!(local.is_self(sema.db)) { 288 if assert_never!(local.is_self(sema.db)) {
292 bail!("rename_to_self invoked on self"); 289 bail!("rename_to_self invoked on self");
293 } 290 }
@@ -356,7 +353,7 @@ fn rename_to_self(
356 TextEdit::replace(first_param_range, String::from(self_param)), 353 TextEdit::replace(first_param_range, String::from(self_param)),
357 ); 354 );
358 355
359 Ok(RangeInfo::new(TextRange::default(), source_change)) 356 Ok(source_change)
360} 357}
361 358
362fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> { 359fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> {
@@ -387,7 +384,7 @@ fn rename_self_to_param(
387 local: hir::Local, 384 local: hir::Local,
388 new_name: &str, 385 new_name: &str,
389 identifier_kind: IdentifierKind, 386 identifier_kind: IdentifierKind,
390) -> RenameResult<RangeInfo<SourceChange>> { 387) -> RenameResult<SourceChange> {
391 let (file_id, self_param) = match local.source(sema.db) { 388 let (file_id, self_param) = match local.source(sema.db) {
392 InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param), 389 InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param),
393 _ => { 390 _ => {
@@ -408,14 +405,14 @@ fn rename_self_to_param(
408 source_change.extend(usages.iter().map(|(&file_id, references)| { 405 source_change.extend(usages.iter().map(|(&file_id, references)| {
409 source_edit_from_references(sema, file_id, &references, new_name) 406 source_edit_from_references(sema, file_id, &references, new_name)
410 })); 407 }));
411 Ok(RangeInfo::new(TextRange::default(), source_change)) 408 Ok(source_change)
412} 409}
413 410
414fn rename_reference( 411fn rename_reference(
415 sema: &Semantics<RootDatabase>, 412 sema: &Semantics<RootDatabase>,
416 def: Definition, 413 def: Definition,
417 new_name: &str, 414 new_name: &str,
418) -> RenameResult<RangeInfo<SourceChange>> { 415) -> RenameResult<SourceChange> {
419 let ident_kind = check_identifier(new_name)?; 416 let ident_kind = check_identifier(new_name)?;
420 417
421 let def_is_lbl_or_lt = matches!(def, 418 let def_is_lbl_or_lt = matches!(def,
@@ -439,7 +436,7 @@ fn rename_reference(
439 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { 436 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
440 // no-op 437 // no-op
441 mark::hit!(rename_self_to_self); 438 mark::hit!(rename_self_to_self);
442 return Ok(RangeInfo::new(TextRange::default(), SourceChange::default())); 439 return Ok(SourceChange::default());
443 } 440 }
444 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { 441 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
445 mark::hit!(rename_self_to_param); 442 mark::hit!(rename_self_to_param);
@@ -465,7 +462,7 @@ fn rename_reference(
465 462
466 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; 463 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
467 source_change.insert_source_edit(file_id, edit); 464 source_change.insert_source_edit(file_id, edit);
468 Ok(RangeInfo::new(TextRange::default(), source_change)) 465 Ok(source_change)
469} 466}
470 467
471fn source_edit_from_def( 468fn source_edit_from_def(
@@ -518,7 +515,7 @@ mod tests {
518 Ok(source_change) => { 515 Ok(source_change) => {
519 let mut text_edit_builder = TextEdit::builder(); 516 let mut text_edit_builder = TextEdit::builder();
520 let mut file_id: Option<FileId> = None; 517 let mut file_id: Option<FileId> = None;
521 for edit in source_change.info.source_file_edits { 518 for edit in source_change.source_file_edits {
522 file_id = Some(edit.0); 519 file_id = Some(edit.0);
523 for indel in edit.1.into_iter() { 520 for indel in edit.1.into_iter() {
524 text_edit_builder.replace(indel.delete, indel.insert); 521 text_edit_builder.replace(indel.delete, indel.insert);
@@ -917,36 +914,33 @@ mod foo$0;
917// empty 914// empty
918"#, 915"#,
919 expect![[r#" 916 expect![[r#"
920 RangeInfo { 917 SourceChange {
921 range: 0..0, 918 source_file_edits: {
922 info: SourceChange { 919 FileId(
923 source_file_edits: { 920 1,
924 FileId( 921 ): TextEdit {
925 1, 922 indels: [
926 ): TextEdit { 923 Indel {
927 indels: [ 924 insert: "foo2",
928 Indel { 925 delete: 4..7,
929 insert: "foo2", 926 },
930 delete: 4..7, 927 ],
931 },
932 ],
933 },
934 }, 928 },
935 file_system_edits: [ 929 },
936 MoveFile { 930 file_system_edits: [
937 src: FileId( 931 MoveFile {
932 src: FileId(
933 2,
934 ),
935 dst: AnchoredPathBuf {
936 anchor: FileId(
938 2, 937 2,
939 ), 938 ),
940 dst: AnchoredPathBuf { 939 path: "foo2.rs",
941 anchor: FileId(
942 2,
943 ),
944 path: "foo2.rs",
945 },
946 }, 940 },
947 ], 941 },
948 is_snippet: false, 942 ],
949 }, 943 is_snippet: false,
950 } 944 }
951 "#]], 945 "#]],
952 ); 946 );
@@ -969,46 +963,43 @@ pub struct FooContent;
969use crate::foo$0::FooContent; 963use crate::foo$0::FooContent;
970"#, 964"#,
971 expect![[r#" 965 expect![[r#"
972 RangeInfo { 966 SourceChange {
973 range: 0..0, 967 source_file_edits: {
974 info: SourceChange { 968 FileId(
975 source_file_edits: { 969 0,
976 FileId( 970 ): TextEdit {
977 0, 971 indels: [
978 ): TextEdit { 972 Indel {
979 indels: [ 973 insert: "quux",
980 Indel { 974 delete: 8..11,
981 insert: "quux", 975 },
982 delete: 8..11, 976 ],
983 }, 977 },
984 ], 978 FileId(
985 }, 979 2,
986 FileId( 980 ): TextEdit {
987 2, 981 indels: [
988 ): TextEdit { 982 Indel {
989 indels: [ 983 insert: "quux",
990 Indel { 984 delete: 11..14,
991 insert: "quux", 985 },
992 delete: 11..14, 986 ],
993 },
994 ],
995 },
996 }, 987 },
997 file_system_edits: [ 988 },
998 MoveFile { 989 file_system_edits: [
999 src: FileId( 990 MoveFile {
991 src: FileId(
992 1,
993 ),
994 dst: AnchoredPathBuf {
995 anchor: FileId(
1000 1, 996 1,
1001 ), 997 ),
1002 dst: AnchoredPathBuf { 998 path: "quux.rs",
1003 anchor: FileId(
1004 1,
1005 ),
1006 path: "quux.rs",
1007 },
1008 }, 999 },
1009 ], 1000 },
1010 is_snippet: false, 1001 ],
1011 }, 1002 is_snippet: false,
1012 } 1003 }
1013 "#]], 1004 "#]],
1014 ); 1005 );
@@ -1025,36 +1016,33 @@ mod fo$0o;
1025// empty 1016// empty
1026"#, 1017"#,
1027 expect![[r#" 1018 expect![[r#"
1028 RangeInfo { 1019 SourceChange {
1029 range: 0..0, 1020 source_file_edits: {
1030 info: SourceChange { 1021 FileId(
1031 source_file_edits: { 1022 0,
1032 FileId( 1023 ): TextEdit {
1033 0, 1024 indels: [
1034 ): TextEdit { 1025 Indel {
1035 indels: [ 1026 insert: "foo2",
1036 Indel { 1027 delete: 4..7,
1037 insert: "foo2", 1028 },
1038 delete: 4..7, 1029 ],
1039 },
1040 ],
1041 },
1042 }, 1030 },
1043 file_system_edits: [ 1031 },
1044 MoveFile { 1032 file_system_edits: [
1045 src: FileId( 1033 MoveFile {
1034 src: FileId(
1035 1,
1036 ),
1037 dst: AnchoredPathBuf {
1038 anchor: FileId(
1046 1, 1039 1,
1047 ), 1040 ),
1048 dst: AnchoredPathBuf { 1041 path: "../foo2/mod.rs",
1049 anchor: FileId(
1050 1,
1051 ),
1052 path: "../foo2/mod.rs",
1053 },
1054 }, 1042 },
1055 ], 1043 },
1056 is_snippet: false, 1044 ],
1057 }, 1045 is_snippet: false,
1058 } 1046 }
1059 "#]], 1047 "#]],
1060 ); 1048 );
@@ -1072,36 +1060,33 @@ mod outer { mod fo$0o; }
1072// empty 1060// empty
1073"#, 1061"#,
1074 expect![[r#" 1062 expect![[r#"
1075 RangeInfo { 1063 SourceChange {
1076 range: 0..0, 1064 source_file_edits: {
1077 info: SourceChange { 1065 FileId(
1078 source_file_edits: { 1066 0,
1079 FileId( 1067 ): TextEdit {
1080 0, 1068 indels: [
1081 ): TextEdit { 1069 Indel {
1082 indels: [ 1070 insert: "bar",
1083 Indel { 1071 delete: 16..19,
1084 insert: "bar", 1072 },
1085 delete: 16..19, 1073 ],
1086 },
1087 ],
1088 },
1089 }, 1074 },
1090 file_system_edits: [ 1075 },
1091 MoveFile { 1076 file_system_edits: [
1092 src: FileId( 1077 MoveFile {
1078 src: FileId(
1079 1,
1080 ),
1081 dst: AnchoredPathBuf {
1082 anchor: FileId(
1093 1, 1083 1,
1094 ), 1084 ),
1095 dst: AnchoredPathBuf { 1085 path: "bar.rs",
1096 anchor: FileId(
1097 1,
1098 ),
1099 path: "bar.rs",
1100 },
1101 }, 1086 },
1102 ], 1087 },
1103 is_snippet: false, 1088 ],
1104 }, 1089 is_snippet: false,
1105 } 1090 }
1106 "#]], 1091 "#]],
1107 ); 1092 );
@@ -1142,46 +1127,43 @@ pub mod foo$0;
1142// pub fn fun() {} 1127// pub fn fun() {}
1143"#, 1128"#,
1144 expect![[r#" 1129 expect![[r#"
1145 RangeInfo { 1130 SourceChange {
1146 range: 0..0, 1131 source_file_edits: {
1147 info: SourceChange { 1132 FileId(
1148 source_file_edits: { 1133 0,
1149 FileId( 1134 ): TextEdit {
1150 0, 1135 indels: [
1151 ): TextEdit { 1136 Indel {
1152 indels: [ 1137 insert: "foo2",
1153 Indel { 1138 delete: 27..30,
1154 insert: "foo2", 1139 },
1155 delete: 27..30, 1140 ],
1156 }, 1141 },
1157 ], 1142 FileId(
1158 }, 1143 1,
1159 FileId( 1144 ): TextEdit {
1160 1, 1145 indels: [
1161 ): TextEdit { 1146 Indel {
1162 indels: [ 1147 insert: "foo2",
1163 Indel { 1148 delete: 8..11,
1164 insert: "foo2", 1149 },
1165 delete: 8..11, 1150 ],
1166 },
1167 ],
1168 },
1169 }, 1151 },
1170 file_system_edits: [ 1152 },
1171 MoveFile { 1153 file_system_edits: [
1172 src: FileId( 1154 MoveFile {
1155 src: FileId(
1156 2,
1157 ),
1158 dst: AnchoredPathBuf {
1159 anchor: FileId(
1173 2, 1160 2,
1174 ), 1161 ),
1175 dst: AnchoredPathBuf { 1162 path: "foo2.rs",
1176 anchor: FileId(
1177 2,
1178 ),
1179 path: "foo2.rs",
1180 },
1181 }, 1163 },
1182 ], 1164 },
1183 is_snippet: false, 1165 ],
1184 }, 1166 is_snippet: false,
1185 } 1167 }
1186 "#]], 1168 "#]],
1187 ); 1169 );
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index edc3da318..517abbb4b 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,12 +1,13 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use either::Either;
3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; 3use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode, SyntaxNode}; 5use syntax::{ast, AstNode};
6 6
7use crate::{imports_locator, RootDatabase}; 7use crate::{
8 8 imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
9use super::insert_use::InsertUseConfig; 9 RootDatabase,
10};
10 11
11#[derive(Debug)] 12#[derive(Debug)]
12pub enum ImportCandidate { 13pub enum ImportCandidate {
@@ -24,86 +25,141 @@ pub enum ImportCandidate {
24 25
25#[derive(Debug)] 26#[derive(Debug)]
26pub struct TraitImportCandidate { 27pub struct TraitImportCandidate {
27 pub ty: hir::Type, 28 pub receiver_ty: hir::Type,
28 pub name: ast::NameRef, 29 pub name: NameToImport,
29} 30}
30 31
31#[derive(Debug)] 32#[derive(Debug)]
32pub struct PathImportCandidate { 33pub struct PathImportCandidate {
33 pub qualifier: Option<ast::Path>, 34 pub qualifier: Option<ast::Path>,
34 pub name: ast::NameRef, 35 pub name: NameToImport,
36}
37
38#[derive(Debug)]
39pub enum NameToImport {
40 Exact(String),
41 Fuzzy(String),
42}
43
44impl NameToImport {
45 pub fn text(&self) -> &str {
46 match self {
47 NameToImport::Exact(text) => text.as_str(),
48 NameToImport::Fuzzy(text) => text.as_str(),
49 }
50 }
35} 51}
36 52
37#[derive(Debug)] 53#[derive(Debug)]
38pub struct ImportAssets { 54pub struct ImportAssets {
39 import_candidate: ImportCandidate, 55 import_candidate: ImportCandidate,
40 module_with_name_to_import: hir::Module, 56 module_with_candidate: hir::Module,
41 syntax_under_caret: SyntaxNode,
42} 57}
43 58
44impl ImportAssets { 59impl ImportAssets {
45 pub fn for_method_call( 60 pub fn for_method_call(
46 method_call: ast::MethodCallExpr, 61 method_call: &ast::MethodCallExpr,
47 sema: &Semantics<RootDatabase>, 62 sema: &Semantics<RootDatabase>,
48 ) -> Option<Self> { 63 ) -> Option<Self> {
49 let syntax_under_caret = method_call.syntax().to_owned();
50 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
51 Some(Self { 64 Some(Self {
52 import_candidate: ImportCandidate::for_method_call(sema, &method_call)?, 65 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
53 module_with_name_to_import, 66 module_with_candidate: sema.scope(method_call.syntax()).module()?,
54 syntax_under_caret,
55 }) 67 })
56 } 68 }
57 69
58 pub fn for_regular_path( 70 pub fn for_exact_path(
59 path_under_caret: ast::Path, 71 fully_qualified_path: &ast::Path,
60 sema: &Semantics<RootDatabase>, 72 sema: &Semantics<RootDatabase>,
61 ) -> Option<Self> { 73 ) -> Option<Self> {
62 let syntax_under_caret = path_under_caret.syntax().to_owned(); 74 let syntax_under_caret = fully_qualified_path.syntax();
63 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 75 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
64 return None; 76 return None;
65 } 77 }
66
67 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
68 Some(Self { 78 Some(Self {
69 import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?, 79 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
70 module_with_name_to_import, 80 module_with_candidate: sema.scope(syntax_under_caret).module()?,
71 syntax_under_caret,
72 }) 81 })
73 } 82 }
74 83
75 pub fn syntax_under_caret(&self) -> &SyntaxNode { 84 pub fn for_fuzzy_path(
76 &self.syntax_under_caret 85 module_with_path: Module,
86 qualifier: Option<ast::Path>,
87 fuzzy_name: String,
88 sema: &Semantics<RootDatabase>,
89 ) -> Option<Self> {
90 Some(match qualifier {
91 Some(qualifier) => {
92 let qualifier_resolution = sema.resolve_path(&qualifier)?;
93 match qualifier_resolution {
94 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self {
95 import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate {
96 receiver_ty: assoc_item_path.ty(sema.db),
97 name: NameToImport::Fuzzy(fuzzy_name),
98 }),
99 module_with_candidate: module_with_path,
100 },
101 _ => Self {
102 import_candidate: ImportCandidate::Path(PathImportCandidate {
103 qualifier: Some(qualifier),
104 name: NameToImport::Fuzzy(fuzzy_name),
105 }),
106 module_with_candidate: module_with_path,
107 },
108 }
109 }
110 None => Self {
111 import_candidate: ImportCandidate::Path(PathImportCandidate {
112 qualifier: None,
113 name: NameToImport::Fuzzy(fuzzy_name),
114 }),
115 module_with_candidate: module_with_path,
116 },
117 })
77 } 118 }
78 119
120 pub fn for_fuzzy_method_call(
121 module_with_method_call: Module,
122 receiver_ty: hir::Type,
123 fuzzy_method_name: String,
124 ) -> Option<Self> {
125 Some(Self {
126 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
127 receiver_ty,
128 name: NameToImport::Fuzzy(fuzzy_method_name),
129 }),
130 module_with_candidate: module_with_method_call,
131 })
132 }
133}
134
135impl ImportAssets {
79 pub fn import_candidate(&self) -> &ImportCandidate { 136 pub fn import_candidate(&self) -> &ImportCandidate {
80 &self.import_candidate 137 &self.import_candidate
81 } 138 }
82 139
83 fn get_search_query(&self) -> &str { 140 fn name_to_import(&self) -> &NameToImport {
84 match &self.import_candidate { 141 match &self.import_candidate {
85 ImportCandidate::Path(candidate) => candidate.name.text(), 142 ImportCandidate::Path(candidate) => &candidate.name,
86 ImportCandidate::TraitAssocItem(candidate) 143 ImportCandidate::TraitAssocItem(candidate)
87 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), 144 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
88 } 145 }
89 } 146 }
90 147
91 pub fn search_for_imports( 148 pub fn search_for_imports(
92 &self, 149 &self,
93 sema: &Semantics<RootDatabase>, 150 sema: &Semantics<RootDatabase>,
94 config: &InsertUseConfig, 151 prefix_kind: PrefixKind,
95 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 152 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
96 let _p = profile::span("import_assists::search_for_imports"); 153 let _p = profile::span("import_assets::search_for_imports");
97 self.search_for(sema, Some(config.prefix_kind)) 154 self.search_for(sema, Some(prefix_kind))
98 } 155 }
99 156
100 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 157 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
101 #[allow(dead_code)]
102 pub fn search_for_relative_paths( 158 pub fn search_for_relative_paths(
103 &self, 159 &self,
104 sema: &Semantics<RootDatabase>, 160 sema: &Semantics<RootDatabase>,
105 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 161 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
106 let _p = profile::span("import_assists::search_for_relative_paths"); 162 let _p = profile::span("import_assets::search_for_relative_paths");
107 self.search_for(sema, None) 163 self.search_for(sema, None)
108 } 164 }
109 165
@@ -112,99 +168,142 @@ impl ImportAssets {
112 sema: &Semantics<RootDatabase>, 168 sema: &Semantics<RootDatabase>,
113 prefixed: Option<hir::PrefixKind>, 169 prefixed: Option<hir::PrefixKind>,
114 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 170 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
115 let db = sema.db; 171 let current_crate = self.module_with_candidate.krate();
116 let mut trait_candidates = FxHashSet::default();
117 let current_crate = self.module_with_name_to_import.krate();
118 172
119 let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { 173 let unfiltered_imports = match self.name_to_import() {
120 trait_candidates.clear(); 174 NameToImport::Exact(exact_name) => {
121 match &self.import_candidate { 175 imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
122 ImportCandidate::TraitAssocItem(trait_candidate) => { 176 }
123 let located_assoc_item = match candidate { 177 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
124 Either::Left(ModuleDef::Function(located_function)) => { 178 // instead, we need to look up all trait impls for a certain struct and search through them only
125 located_function.as_assoc_item(db) 179 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
126 } 180 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
127 Either::Left(ModuleDef::Const(located_const)) => { 181 // for the details
128 located_const.as_assoc_item(db) 182 NameToImport::Fuzzy(fuzzy_name) => {
129 } 183 let (assoc_item_search, limit) = match self.import_candidate {
130 _ => None, 184 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
185 (AssocItemSearch::AssocItemsOnly, None)
131 } 186 }
132 .map(|assoc| assoc.container(db)) 187 _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
133 .and_then(Self::assoc_to_trait)?; 188 };
134 189 imports_locator::find_similar_imports(
135 trait_candidates.insert(located_assoc_item.into()); 190 sema,
191 current_crate,
192 fuzzy_name.clone(),
193 assoc_item_search,
194 limit,
195 )
196 }
197 };
136 198
137 trait_candidate 199 let db = sema.db;
138 .ty 200 let mut res =
139 .iterate_path_candidates( 201 applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports)
140 db, 202 .filter_map(|candidate| {
141 current_crate, 203 let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into);
142 &trait_candidates,
143 None,
144 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
145 )
146 .map(ModuleDef::from)
147 .map(Either::Left)
148 }
149 ImportCandidate::TraitMethod(trait_candidate) => {
150 let located_assoc_item =
151 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
152 located_function
153 .as_assoc_item(db)
154 .map(|assoc| assoc.container(db))
155 .and_then(Self::assoc_to_trait)
156 } else {
157 None
158 }?;
159 204
160 trait_candidates.insert(located_assoc_item.into()); 205 let item_to_search = match self.import_candidate {
206 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
207 let canidate_trait = match candidate {
208 Either::Left(module_def) => {
209 module_def.as_assoc_item(db)?.containing_trait(db)
210 }
211 _ => None,
212 }?;
213 ModuleDef::from(canidate_trait).into()
214 }
215 _ => item,
216 };
161 217
162 trait_candidate 218 if let Some(prefix_kind) = prefixed {
163 .ty 219 self.module_with_candidate.find_use_path_prefixed(
164 .iterate_method_candidates(
165 db, 220 db,
166 current_crate, 221 item_to_search,
167 &trait_candidates, 222 prefix_kind,
168 None,
169 |_, function| {
170 Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
171 },
172 ) 223 )
173 .map(ModuleDef::from) 224 } else {
174 .map(Either::Left) 225 self.module_with_candidate.find_use_path(db, item_to_search)
175 } 226 }
176 _ => Some(candidate), 227 .map(|path| (path, item))
177 } 228 })
178 }; 229 .filter(|(use_path, _)| use_path.len() > 1)
179 230 .collect::<Vec<_>>();
180 let mut res = imports_locator::find_exact_imports( 231 res.sort_by_cached_key(|(path, _)| path.clone());
181 sema,
182 current_crate,
183 self.get_search_query().to_string(),
184 )
185 .filter_map(filter)
186 .filter_map(|candidate| {
187 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
188 if let Some(prefix_kind) = prefixed {
189 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
190 } else {
191 self.module_with_name_to_import.find_use_path(db, item)
192 }
193 .map(|path| (path, item))
194 })
195 .filter(|(use_path, _)| use_path.len() > 1)
196 .take(20)
197 .collect::<Vec<_>>();
198 res.sort_by_key(|(path, _)| path.clone());
199 res 232 res
200 } 233 }
234}
201 235
202 fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { 236fn applicable_defs<'a>(
203 if let AssocItemContainer::Trait(extracted_trait) = assoc { 237 import_candidate: &ImportCandidate,
204 Some(extracted_trait) 238 current_crate: Crate,
205 } else { 239 db: &RootDatabase,
206 None 240 unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>,
241) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
242 let receiver_ty = match import_candidate {
243 ImportCandidate::Path(_) => return unfiltered_imports,
244 ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => {
245 &candidate.receiver_ty
207 } 246 }
247 };
248
249 let mut required_assoc_items = FxHashSet::default();
250
251 let trait_candidates = unfiltered_imports
252 .filter_map(|input| match input {
253 Either::Left(module_def) => module_def.as_assoc_item(db),
254 _ => None,
255 })
256 .filter_map(|assoc| {
257 let assoc_item_trait = assoc.containing_trait(db)?;
258 required_assoc_items.insert(assoc);
259 Some(assoc_item_trait.into())
260 })
261 .collect();
262
263 let mut applicable_defs = FxHashSet::default();
264
265 match import_candidate {
266 ImportCandidate::Path(_) => unreachable!(),
267 ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
268 db,
269 current_crate,
270 &trait_candidates,
271 None,
272 |_, assoc| {
273 if required_assoc_items.contains(&assoc) {
274 if let AssocItem::Function(f) = assoc {
275 if f.self_param(db).is_some() {
276 return None;
277 }
278 }
279 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
280 }
281 None::<()>
282 },
283 ),
284 ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates(
285 db,
286 current_crate,
287 &trait_candidates,
288 None,
289 |_, function| {
290 let assoc = function.as_assoc_item(db)?;
291 if required_assoc_items.contains(&assoc) {
292 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
293 }
294 None::<()>
295 },
296 ),
297 };
298
299 Box::new(applicable_defs.into_iter())
300}
301
302fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef {
303 match assoc {
304 AssocItem::Function(f) => f.into(),
305 AssocItem::Const(c) => c.into(),
306 AssocItem::TypeAlias(t) => t.into(),
208 } 307 }
209} 308}
210 309
@@ -216,22 +315,19 @@ impl ImportCandidate {
216 match sema.resolve_method_call(method_call) { 315 match sema.resolve_method_call(method_call) {
217 Some(_) => None, 316 Some(_) => None,
218 None => Some(Self::TraitMethod(TraitImportCandidate { 317 None => Some(Self::TraitMethod(TraitImportCandidate {
219 ty: sema.type_of_expr(&method_call.receiver()?)?, 318 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
220 name: method_call.name_ref()?, 319 name: NameToImport::Exact(method_call.name_ref()?.to_string()),
221 })), 320 })),
222 } 321 }
223 } 322 }
224 323
225 fn for_regular_path( 324 fn for_regular_path(sema: &Semantics<RootDatabase>, path: &ast::Path) -> Option<Self> {
226 sema: &Semantics<RootDatabase>, 325 if sema.resolve_path(path).is_some() {
227 path_under_caret: &ast::Path,
228 ) -> Option<Self> {
229 if sema.resolve_path(path_under_caret).is_some() {
230 return None; 326 return None;
231 } 327 }
232 328
233 let segment = path_under_caret.segment()?; 329 let segment = path.segment()?;
234 let candidate = if let Some(qualifier) = path_under_caret.qualifier() { 330 let candidate = if let Some(qualifier) = path.qualifier() {
235 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 331 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
236 let qualifier_start_path = 332 let qualifier_start_path =
237 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 333 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
@@ -244,8 +340,8 @@ impl ImportCandidate {
244 match qualifier_resolution { 340 match qualifier_resolution {
245 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { 341 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
246 ImportCandidate::TraitAssocItem(TraitImportCandidate { 342 ImportCandidate::TraitAssocItem(TraitImportCandidate {
247 ty: assoc_item_path.ty(sema.db), 343 receiver_ty: assoc_item_path.ty(sema.db),
248 name: segment.name_ref()?, 344 name: NameToImport::Exact(segment.name_ref()?.to_string()),
249 }) 345 })
250 } 346 }
251 _ => return None, 347 _ => return None,
@@ -253,13 +349,15 @@ impl ImportCandidate {
253 } else { 349 } else {
254 ImportCandidate::Path(PathImportCandidate { 350 ImportCandidate::Path(PathImportCandidate {
255 qualifier: Some(qualifier), 351 qualifier: Some(qualifier),
256 name: qualifier_start, 352 name: NameToImport::Exact(qualifier_start.to_string()),
257 }) 353 })
258 } 354 }
259 } else { 355 } else {
260 ImportCandidate::Path(PathImportCandidate { 356 ImportCandidate::Path(PathImportCandidate {
261 qualifier: None, 357 qualifier: None,
262 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, 358 name: NameToImport::Exact(
359 segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(),
360 ),
263 }) 361 })
264 }; 362 };
265 Some(candidate) 363 Some(candidate)
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index d111fba92..502e8281a 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,7 +1,10 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{
5 import_map::{self, ImportKind},
6 AsAssocItem, Crate, MacroDef, ModuleDef, Semantics,
7};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 8use syntax::{ast, AstNode, SyntaxKind::NAME};
6 9
7use crate::{ 10use crate::{
@@ -12,69 +15,84 @@ use crate::{
12use either::Either; 15use either::Either;
13use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
14 17
15const QUERY_SEARCH_LIMIT: usize = 40; 18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
16 19
17pub fn find_exact_imports<'a>( 20pub fn find_exact_imports<'a>(
18 sema: &Semantics<'a, RootDatabase>, 21 sema: &Semantics<'a, RootDatabase>,
19 krate: Crate, 22 krate: Crate,
20 name_to_import: String, 23 name_to_import: String,
21) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 24) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
22 let _p = profile::span("find_exact_imports"); 25 let _p = profile::span("find_exact_imports");
23 find_imports( 26 Box::new(find_imports(
24 sema, 27 sema,
25 krate, 28 krate,
26 { 29 {
27 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 30 let mut local_query = symbol_index::Query::new(name_to_import.clone());
28 local_query.exact(); 31 local_query.exact();
29 local_query.limit(QUERY_SEARCH_LIMIT); 32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
30 local_query 33 local_query
31 }, 34 },
32 import_map::Query::new(name_to_import) 35 import_map::Query::new(name_to_import)
33 .limit(QUERY_SEARCH_LIMIT) 36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
34 .name_only() 37 .name_only()
35 .search_mode(import_map::SearchMode::Equals) 38 .search_mode(import_map::SearchMode::Equals)
36 .case_sensitive(), 39 .case_sensitive(),
37 ) 40 ))
41}
42
43pub enum AssocItemSearch {
44 Include,
45 Exclude,
46 AssocItemsOnly,
38} 47}
39 48
40pub fn find_similar_imports<'a>( 49pub fn find_similar_imports<'a>(
41 sema: &Semantics<'a, RootDatabase>, 50 sema: &Semantics<'a, RootDatabase>,
42 krate: Crate, 51 krate: Crate,
43 limit: Option<usize>,
44 fuzzy_search_string: String, 52 fuzzy_search_string: String,
45 ignore_assoc_items: bool, 53 assoc_item_search: AssocItemSearch,
46 name_only: bool, 54 limit: Option<usize>,
47) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a { 55) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
48 let _p = profile::span("find_similar_imports"); 56 let _p = profile::span("find_similar_imports");
49 57
50 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 58 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
51 .search_mode(import_map::SearchMode::Fuzzy); 59 .search_mode(import_map::SearchMode::Fuzzy)
52 if name_only { 60 .name_only();
53 external_query = external_query.name_only(); 61
62 match assoc_item_search {
63 AssocItemSearch::Include => {}
64 AssocItemSearch::Exclude => {
65 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
66 }
67 AssocItemSearch::AssocItemsOnly => {
68 external_query = external_query.assoc_items_only();
69 }
54 } 70 }
55 71
56 let mut local_query = symbol_index::Query::new(fuzzy_search_string); 72 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
57 73
58 if let Some(limit) = limit { 74 if let Some(limit) = limit {
59 local_query.limit(limit);
60 external_query = external_query.limit(limit); 75 external_query = external_query.limit(limit);
76 local_query.limit(limit);
61 } 77 }
62 78
63 let db = sema.db; 79 let db = sema.db;
64 find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| { 80 Box::new(find_imports(sema, krate, local_query, external_query).filter(
65 if ignore_assoc_items { 81 move |import_candidate| match assoc_item_search {
66 match import_candidate { 82 AssocItemSearch::Include => true,
67 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(), 83 AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db),
68 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(), 84 AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db),
69 Either::Left(ModuleDef::TypeAlias(type_alias)) => { 85 },
70 type_alias.as_assoc_item(db).is_none() 86 ))
71 } 87}
72 _ => true, 88
73 } 89fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool {
74 } else { 90 match import_candidate {
75 true 91 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(),
76 } 92 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(),
77 }) 93 Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
94 _ => false,
95 }
78} 96}
79 97
80fn find_imports<'a>( 98fn find_imports<'a>(
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs
index d076974ed..d3327271c 100644
--- a/crates/parser/src/grammar/items/traits.rs
+++ b/crates/parser/src/grammar/items/traits.rs
@@ -40,6 +40,10 @@ pub(super) fn impl_(p: &mut Parser) {
40 type_params::opt_generic_param_list(p); 40 type_params::opt_generic_param_list(p);
41 } 41 }
42 42
43 // test impl_def_const
44 // impl const Send for X {}
45 p.eat(T![const]);
46
43 // FIXME: never type 47 // FIXME: never type
44 // impl ! {} 48 // impl ! {}
45 49
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index a02c8327f..a01b49822 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -93,7 +93,7 @@ impl BenchCmd {
93 if is_completion { 93 if is_completion {
94 let options = CompletionConfig { 94 let options = CompletionConfig {
95 enable_postfix_completions: true, 95 enable_postfix_completions: true,
96 enable_autoimport_completions: true, 96 enable_imports_on_the_fly: true,
97 add_call_parenthesis: true, 97 add_call_parenthesis: true,
98 add_call_argument_snippets: true, 98 add_call_argument_snippets: true,
99 snippet_cap: SnippetCap::new(true), 99 snippet_cap: SnippetCap::new(true),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index ce9655818..3ddb9e19a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -559,7 +559,7 @@ impl Config {
559 pub fn completion(&self) -> CompletionConfig { 559 pub fn completion(&self) -> CompletionConfig {
560 CompletionConfig { 560 CompletionConfig {
561 enable_postfix_completions: self.data.completion_postfix_enable, 561 enable_postfix_completions: self.data.completion_postfix_enable,
562 enable_autoimport_completions: self.data.completion_autoimport_enable 562 enable_imports_on_the_fly: self.data.completion_autoimport_enable
563 && completion_item_edit_resolve(&self.caps), 563 && completion_item_edit_resolve(&self.caps),
564 add_call_parenthesis: self.data.completion_addCallParenthesis, 564 add_call_parenthesis: self.data.completion_addCallParenthesis,
565 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, 565 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets,
@@ -581,18 +581,7 @@ impl Config {
581 AssistConfig { 581 AssistConfig {
582 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), 582 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
583 allowed: None, 583 allowed: None,
584 insert_use: InsertUseConfig { 584 insert_use: self.insert_use_config(),
585 merge: match self.data.assist_importMergeBehavior {
586 MergeBehaviorDef::None => None,
587 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
588 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
589 },
590 prefix_kind: match self.data.assist_importPrefix {
591 ImportPrefixDef::Plain => PrefixKind::Plain,
592 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
593 ImportPrefixDef::BySelf => PrefixKind::BySelf,
594 },
595 },
596 } 585 }
597 } 586 }
598 pub fn call_info_full(&self) -> bool { 587 pub fn call_info_full(&self) -> bool {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 1a4e0dd32..001f3a37d 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -653,7 +653,7 @@ pub(crate) fn handle_completion(
653 let mut new_completion_items = 653 let mut new_completion_items =
654 to_proto::completion_item(&line_index, line_endings, item.clone()); 654 to_proto::completion_item(&line_index, line_endings, item.clone());
655 655
656 if completion_config.enable_autoimport_completions { 656 if completion_config.enable_imports_on_the_fly {
657 for new_item in &mut new_completion_items { 657 for new_item in &mut new_completion_items {
658 fill_resolve_data(&mut new_item.data, &item, &text_document_position); 658 fill_resolve_data(&mut new_item.data, &item, &text_document_position);
659 } 659 }
@@ -703,6 +703,7 @@ pub(crate) fn handle_completion_resolve(
703 FilePosition { file_id, offset }, 703 FilePosition { file_id, offset },
704 &resolve_data.full_import_path, 704 &resolve_data.full_import_path,
705 resolve_data.imported_name, 705 resolve_data.imported_name,
706 resolve_data.import_for_trait_assoc_item,
706 )? 707 )?
707 .into_iter() 708 .into_iter()
708 .flat_map(|edit| { 709 .flat_map(|edit| {
@@ -809,7 +810,7 @@ pub(crate) fn handle_rename(
809 810
810 let change = 811 let change =
811 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; 812 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
812 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; 813 let workspace_edit = to_proto::workspace_edit(&snap, change)?;
813 Ok(Some(workspace_edit)) 814 Ok(Some(workspace_edit))
814} 815}
815 816
@@ -1694,6 +1695,7 @@ struct CompletionResolveData {
1694 position: lsp_types::TextDocumentPositionParams, 1695 position: lsp_types::TextDocumentPositionParams,
1695 full_import_path: String, 1696 full_import_path: String,
1696 imported_name: String, 1697 imported_name: String,
1698 import_for_trait_assoc_item: bool,
1697} 1699}
1698 1700
1699fn fill_resolve_data( 1701fn fill_resolve_data(
@@ -1710,6 +1712,7 @@ fn fill_resolve_data(
1710 position: position.to_owned(), 1712 position: position.to_owned(),
1711 full_import_path, 1713 full_import_path,
1712 imported_name, 1714 imported_name,
1715 import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
1713 }) 1716 })
1714 .unwrap(), 1717 .unwrap(),
1715 ); 1718 );
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1ff2d3fea..0e3550002 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -884,7 +884,7 @@ mod tests {
884 .completions( 884 .completions(
885 &ide::CompletionConfig { 885 &ide::CompletionConfig {
886 enable_postfix_completions: true, 886 enable_postfix_completions: true,
887 enable_autoimport_completions: true, 887 enable_imports_on_the_fly: true,
888 add_call_parenthesis: true, 888 add_call_parenthesis: true,
889 add_call_argument_snippets: true, 889 add_call_argument_snippets: true,
890 snippet_cap: SnippetCap::new(true), 890 snippet_cap: SnippetCap::new(true),
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast
new file mode 100644
index 000000000..dcd39535b
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast
@@ -0,0 +1,24 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected] "const"
6 [email protected] " "
7 [email protected]
8 [email protected]
9 [email protected]
10 [email protected]
11 [email protected] "Send"
12 [email protected] " "
13 [email protected] "for"
14 [email protected] " "
15 [email protected]
16 [email protected]
17 [email protected]
18 [email protected]
19 [email protected] "X"
20 [email protected] " "
21 [email protected]
22 [email protected] "{"
23 [email protected] "}"
24 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs
new file mode 100644
index 000000000..8d6886469
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs
@@ -0,0 +1 @@
impl const Send for X {}