aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src')
-rw-r--r--crates/assists/src/handlers/auto_import.rs12
-rw-r--r--crates/assists/src/utils/import_assets.rs134
2 files changed, 84 insertions, 62 deletions
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 7182a2a5d..e595b5b93 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -63,13 +63,13 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
63 63
64fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 64fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
65 let name = match import_candidate { 65 let name = match import_candidate {
66 ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), 66 ImportCandidate::UnqualifiedName(candidate)
67 ImportCandidate::QualifierStart(qualifier_start) => format!("Import {}", qualifier_start), 67 | ImportCandidate::QualifierStart(candidate) => format!("Import {}", &candidate.name),
68 ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => { 68 ImportCandidate::TraitAssocItem(candidate) => {
69 format!("Import a trait for item {}", trait_assoc_item_name) 69 format!("Import a trait for item {}", &candidate.name)
70 } 70 }
71 ImportCandidate::TraitMethod(_, trait_method_name) => { 71 ImportCandidate::TraitMethod(candidate) => {
72 format!("Import a trait for method {}", trait_method_name) 72 format!("Import a trait for method {}", &candidate.name)
73 } 73 }
74 }; 74 };
75 GroupLabel(name) 75 GroupLabel(name)
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs
index b816edc82..601f51098 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/assists/src/utils/import_assets.rs
@@ -12,18 +12,29 @@ use crate::assist_config::InsertUseConfig;
12#[derive(Debug)] 12#[derive(Debug)]
13pub(crate) enum ImportCandidate { 13pub(crate) enum ImportCandidate {
14 /// Simple name like 'HashMap' 14 /// Simple name like 'HashMap'
15 UnqualifiedName(String), 15 UnqualifiedName(PathImportCandidate),
16 /// First part of the qualified name. 16 /// First part of the qualified name.
17 /// For 'std::collections::HashMap', that will be 'std'. 17 /// For 'std::collections::HashMap', that will be 'std'.
18 QualifierStart(String), 18 QualifierStart(PathImportCandidate),
19 /// A trait associated function (with no self parameter) or associated constant. 19 /// A trait associated function (with no self parameter) or associated constant.
20 /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type 20 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
21 /// and `String` is the `test_function` 21 /// and `name` is the `test_function`
22 TraitAssocItem(hir::Type, String), 22 TraitAssocItem(TraitImportCandidate),
23 /// A trait method with self parameter. 23 /// A trait method with self parameter.
24 /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type 24 /// For 'test_enum.test_method()', `ty` is the `test_enum` expression type
25 /// and `String` is the `test_method` 25 /// and `name` is the `test_method`
26 TraitMethod(hir::Type, String), 26 TraitMethod(TraitImportCandidate),
27}
28
29#[derive(Debug)]
30pub(crate) struct TraitImportCandidate {
31 pub ty: hir::Type,
32 pub name: String,
33}
34
35#[derive(Debug)]
36pub(crate) struct PathImportCandidate {
37 pub name: String,
27} 38}
28 39
29#[derive(Debug)] 40#[derive(Debug)]
@@ -74,10 +85,10 @@ impl ImportAssets {
74 85
75 fn get_search_query(&self) -> &str { 86 fn get_search_query(&self) -> &str {
76 match &self.import_candidate { 87 match &self.import_candidate {
77 ImportCandidate::UnqualifiedName(name) => name, 88 ImportCandidate::UnqualifiedName(candidate)
78 ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, 89 | ImportCandidate::QualifierStart(candidate) => &candidate.name,
79 ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name, 90 ImportCandidate::TraitAssocItem(candidate)
80 ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, 91 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
81 } 92 }
82 } 93 }
83 94
@@ -106,27 +117,29 @@ impl ImportAssets {
106 prefixed: Option<hir::PrefixKind>, 117 prefixed: Option<hir::PrefixKind>,
107 ) -> BTreeSet<hir::ModPath> { 118 ) -> BTreeSet<hir::ModPath> {
108 let db = sema.db; 119 let db = sema.db;
120 let mut trait_candidates = FxHashSet::default();
109 let current_crate = self.module_with_name_to_import.krate(); 121 let current_crate = self.module_with_name_to_import.krate();
110 imports_locator::find_imports(sema, current_crate, &self.get_search_query()) 122
111 .into_iter() 123 let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| {
112 .filter_map(|candidate| match &self.import_candidate { 124 trait_candidates.clear();
113 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 125 match &self.import_candidate {
126 ImportCandidate::TraitAssocItem(trait_candidate) => {
114 let located_assoc_item = match candidate { 127 let located_assoc_item = match candidate {
115 Either::Left(ModuleDef::Function(located_function)) => located_function 128 Either::Left(ModuleDef::Function(located_function)) => {
116 .as_assoc_item(db) 129 located_function.as_assoc_item(db)
117 .map(|assoc| assoc.container(db)) 130 }
118 .and_then(Self::assoc_to_trait), 131 Either::Left(ModuleDef::Const(located_const)) => {
119 Either::Left(ModuleDef::Const(located_const)) => located_const 132 located_const.as_assoc_item(db)
120 .as_assoc_item(db) 133 }
121 .map(|assoc| assoc.container(db))
122 .and_then(Self::assoc_to_trait),
123 _ => None, 134 _ => None,
124 }?; 135 }
136 .map(|assoc| assoc.container(db))
137 .and_then(Self::assoc_to_trait)?;
125 138
126 let mut trait_candidates = FxHashSet::default();
127 trait_candidates.insert(located_assoc_item.into()); 139 trait_candidates.insert(located_assoc_item.into());
128 140
129 assoc_item_type 141 trait_candidate
142 .ty
130 .iterate_path_candidates( 143 .iterate_path_candidates(
131 db, 144 db,
132 current_crate, 145 current_crate,
@@ -137,7 +150,7 @@ impl ImportAssets {
137 .map(ModuleDef::from) 150 .map(ModuleDef::from)
138 .map(Either::Left) 151 .map(Either::Left)
139 } 152 }
140 ImportCandidate::TraitMethod(function_callee, _) => { 153 ImportCandidate::TraitMethod(trait_candidate) => {
141 let located_assoc_item = 154 let located_assoc_item =
142 if let Either::Left(ModuleDef::Function(located_function)) = candidate { 155 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
143 located_function 156 located_function
@@ -148,10 +161,10 @@ impl ImportAssets {
148 None 161 None
149 }?; 162 }?;
150 163
151 let mut trait_candidates = FxHashSet::default();
152 trait_candidates.insert(located_assoc_item.into()); 164 trait_candidates.insert(located_assoc_item.into());
153 165
154 function_callee 166 trait_candidate
167 .ty
155 .iterate_method_candidates( 168 .iterate_method_candidates(
156 db, 169 db,
157 current_crate, 170 current_crate,
@@ -165,12 +178,14 @@ impl ImportAssets {
165 .map(Either::Left) 178 .map(Either::Left)
166 } 179 }
167 _ => Some(candidate), 180 _ => Some(candidate),
168 }) 181 }
182 };
183
184 imports_locator::find_imports(sema, current_crate, &self.get_search_query())
185 .into_iter()
186 .filter_map(filter)
169 .filter_map(|candidate| { 187 .filter_map(|candidate| {
170 let item: hir::ItemInNs = match candidate { 188 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
171 Either::Left(module_def) => module_def.into(),
172 Either::Right(macro_def) => macro_def.into(),
173 };
174 if let Some(prefix_kind) = prefixed { 189 if let Some(prefix_kind) = prefixed {
175 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) 190 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
176 } else { 191 } else {
@@ -196,13 +211,13 @@ impl ImportCandidate {
196 sema: &Semantics<RootDatabase>, 211 sema: &Semantics<RootDatabase>,
197 method_call: &ast::MethodCallExpr, 212 method_call: &ast::MethodCallExpr,
198 ) -> Option<Self> { 213 ) -> Option<Self> {
199 if sema.resolve_method_call(method_call).is_some() { 214 match sema.resolve_method_call(method_call) {
200 return None; 215 Some(_) => None,
216 None => Some(Self::TraitMethod(TraitImportCandidate {
217 ty: sema.type_of_expr(&method_call.receiver()?)?,
218 name: method_call.name_ref()?.syntax().to_string(),
219 })),
201 } 220 }
202 Some(Self::TraitMethod(
203 sema.type_of_expr(&method_call.receiver()?)?,
204 method_call.name_ref()?.syntax().to_string(),
205 ))
206 } 221 }
207 222
208 fn for_regular_path( 223 fn for_regular_path(
@@ -214,7 +229,7 @@ impl ImportCandidate {
214 } 229 }
215 230
216 let segment = path_under_caret.segment()?; 231 let segment = path_under_caret.segment()?;
217 if let Some(qualifier) = path_under_caret.qualifier() { 232 let candidate = if let Some(qualifier) = path_under_caret.qualifier() {
218 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 233 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
219 let qualifier_start_path = 234 let qualifier_start_path =
220 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 235 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
@@ -224,23 +239,30 @@ impl ImportCandidate {
224 } else { 239 } else {
225 sema.resolve_path(&qualifier)? 240 sema.resolve_path(&qualifier)?
226 }; 241 };
227 if let hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) = 242 match qualifier_resolution {
228 qualifier_resolution 243 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
229 { 244 ImportCandidate::TraitAssocItem(TraitImportCandidate {
230 Some(ImportCandidate::TraitAssocItem( 245 ty: assoc_item_path.ty(sema.db),
231 assoc_item_path.ty(sema.db), 246 name: segment.syntax().to_string(),
232 segment.syntax().to_string(), 247 })
233 )) 248 }
234 } else { 249 _ => return None,
235 None
236 } 250 }
237 } else { 251 } else {
238 Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string())) 252 ImportCandidate::QualifierStart(PathImportCandidate {
253 name: qualifier_start.syntax().to_string(),
254 })
239 } 255 }
240 } else { 256 } else {
241 Some(ImportCandidate::UnqualifiedName( 257 ImportCandidate::UnqualifiedName(PathImportCandidate {
242 segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(), 258 name: segment
243 )) 259 .syntax()
244 } 260 .descendants()
261 .find_map(ast::NameRef::cast)?
262 .syntax()
263 .to_string(),
264 })
265 };
266 Some(candidate)
245 } 267 }
246} 268}