diff options
-rw-r--r-- | crates/ide_assists/src/handlers/auto_import.rs | 35 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/qualify_path.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 37 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 150 |
4 files changed, 101 insertions, 125 deletions
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index 1422224ac..5fe3f47fd 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -221,41 +221,6 @@ mod tests { | |||
221 | } | 221 | } |
222 | 222 | ||
223 | #[test] | 223 | #[test] |
224 | fn auto_imports_are_merged() { | ||
225 | check_assist( | ||
226 | auto_import, | ||
227 | r" | ||
228 | use PubMod::PubStruct1; | ||
229 | |||
230 | struct Test { | ||
231 | test: Pub$0Struct2<u8>, | ||
232 | } | ||
233 | |||
234 | pub mod PubMod { | ||
235 | pub struct PubStruct1; | ||
236 | pub struct PubStruct2<T> { | ||
237 | _t: T, | ||
238 | } | ||
239 | } | ||
240 | ", | ||
241 | r" | ||
242 | use PubMod::{PubStruct1, PubStruct2}; | ||
243 | |||
244 | struct Test { | ||
245 | test: PubStruct2<u8>, | ||
246 | } | ||
247 | |||
248 | pub mod PubMod { | ||
249 | pub struct PubStruct1; | ||
250 | pub struct PubStruct2<T> { | ||
251 | _t: T, | ||
252 | } | ||
253 | } | ||
254 | ", | ||
255 | ); | ||
256 | } | ||
257 | |||
258 | #[test] | ||
259 | fn applicable_when_found_multiple_imports() { | 224 | fn applicable_when_found_multiple_imports() { |
260 | check_assist( | 225 | check_assist( |
261 | auto_import, | 226 | auto_import, |
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index d3e34e540..1fb4931cd 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs | |||
@@ -45,7 +45,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
45 | 45 | ||
46 | let qualify_candidate = match candidate { | 46 | let qualify_candidate = match candidate { |
47 | ImportCandidate::Path(candidate) => { | 47 | ImportCandidate::Path(candidate) => { |
48 | if candidate.qualifier.is_some() { | 48 | if candidate.unresolved_qualifier.is_some() { |
49 | cov_mark::hit!(qualify_path_qualifier_start); | 49 | cov_mark::hit!(qualify_path_qualifier_start); |
50 | let path = ast::Path::cast(syntax_under_caret)?; | 50 | let path = ast::Path::cast(syntax_under_caret)?; |
51 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); | 51 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); |
@@ -192,7 +192,7 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel { | |||
192 | fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { | 192 | fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { |
193 | match candidate { | 193 | match candidate { |
194 | ImportCandidate::Path(candidate) => { | 194 | ImportCandidate::Path(candidate) => { |
195 | if candidate.qualifier.is_some() { | 195 | if candidate.unresolved_qualifier.is_some() { |
196 | format!("Qualify with `{}`", &import) | 196 | format!("Qualify with `{}`", &import) |
197 | } else { | 197 | } else { |
198 | format!("Qualify as `{}`", &import) | 198 | format!("Qualify as `{}`", &import) |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index f34764b61..16384551c 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -168,15 +168,15 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
168 | ctx.path_qual.clone(), | 168 | ctx.path_qual.clone(), |
169 | fuzzy_name, | 169 | fuzzy_name, |
170 | &ctx.sema, | 170 | &ctx.sema, |
171 | ); | 171 | )?; |
172 | 172 | ||
173 | if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) | 173 | if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_)) |
174 | && fuzzy_name_length < 2 | 174 | && fuzzy_name_length < 2 |
175 | { | 175 | { |
176 | cov_mark::hit!(ignore_short_input_for_path); | 176 | cov_mark::hit!(ignore_short_input_for_path); |
177 | None | 177 | None |
178 | } else { | 178 | } else { |
179 | assets_for_path | 179 | Some(assets_for_path) |
180 | } | 180 | } |
181 | } | 181 | } |
182 | } | 182 | } |
@@ -773,4 +773,35 @@ fn main() { | |||
773 | }"#, | 773 | }"#, |
774 | ); | 774 | ); |
775 | } | 775 | } |
776 | |||
777 | #[test] | ||
778 | fn unresolved_qualifiers() { | ||
779 | check_edit( | ||
780 | "Item", | ||
781 | r#" | ||
782 | mod foo { | ||
783 | pub mod bar { | ||
784 | pub struct Item; | ||
785 | } | ||
786 | } | ||
787 | |||
788 | fn main() { | ||
789 | bar::Ite$0 | ||
790 | } | ||
791 | "#, | ||
792 | r#" | ||
793 | use foo::bar; | ||
794 | |||
795 | mod foo { | ||
796 | pub mod bar { | ||
797 | pub struct Item; | ||
798 | } | ||
799 | } | ||
800 | |||
801 | fn main() { | ||
802 | bar::Item | ||
803 | } | ||
804 | "#, | ||
805 | ); | ||
806 | } | ||
776 | } | 807 | } |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 517abbb4b..513128eae 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -31,7 +31,7 @@ pub struct TraitImportCandidate { | |||
31 | 31 | ||
32 | #[derive(Debug)] | 32 | #[derive(Debug)] |
33 | pub struct PathImportCandidate { | 33 | pub struct PathImportCandidate { |
34 | pub qualifier: Option<ast::Path>, | 34 | pub unresolved_qualifier: Option<ast::Path>, |
35 | pub name: NameToImport, | 35 | pub name: NameToImport, |
36 | } | 36 | } |
37 | 37 | ||
@@ -82,38 +82,14 @@ impl ImportAssets { | |||
82 | } | 82 | } |
83 | 83 | ||
84 | pub fn for_fuzzy_path( | 84 | pub fn for_fuzzy_path( |
85 | module_with_path: Module, | 85 | module_with_candidate: Module, |
86 | qualifier: Option<ast::Path>, | 86 | qualifier: Option<ast::Path>, |
87 | fuzzy_name: String, | 87 | fuzzy_name: String, |
88 | sema: &Semantics<RootDatabase>, | 88 | sema: &Semantics<RootDatabase>, |
89 | ) -> Option<Self> { | 89 | ) -> Option<Self> { |
90 | Some(match qualifier { | 90 | Some(Self { |
91 | Some(qualifier) => { | 91 | import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, |
92 | let qualifier_resolution = sema.resolve_path(&qualifier)?; | 92 | module_with_candidate, |
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 | }) | 93 | }) |
118 | } | 94 | } |
119 | 95 | ||
@@ -169,8 +145,9 @@ impl ImportAssets { | |||
169 | prefixed: Option<hir::PrefixKind>, | 145 | prefixed: Option<hir::PrefixKind>, |
170 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 146 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
171 | let current_crate = self.module_with_candidate.krate(); | 147 | let current_crate = self.module_with_candidate.krate(); |
148 | let import_candidate = &self.import_candidate; | ||
172 | 149 | ||
173 | let unfiltered_imports = match self.name_to_import() { | 150 | let imports_for_candidate_name = match self.name_to_import() { |
174 | NameToImport::Exact(exact_name) => { | 151 | NameToImport::Exact(exact_name) => { |
175 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) | 152 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) |
176 | } | 153 | } |
@@ -180,11 +157,10 @@ impl ImportAssets { | |||
180 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup | 157 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup |
181 | // for the details | 158 | // for the details |
182 | NameToImport::Fuzzy(fuzzy_name) => { | 159 | NameToImport::Fuzzy(fuzzy_name) => { |
183 | let (assoc_item_search, limit) = match self.import_candidate { | 160 | let (assoc_item_search, limit) = if import_candidate.is_trait_candidate() { |
184 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 161 | (AssocItemSearch::AssocItemsOnly, None) |
185 | (AssocItemSearch::AssocItemsOnly, None) | 162 | } else { |
186 | } | 163 | (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)) |
187 | _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)), | ||
188 | }; | 164 | }; |
189 | imports_locator::find_similar_imports( | 165 | imports_locator::find_similar_imports( |
190 | sema, | 166 | sema, |
@@ -198,24 +174,22 @@ impl ImportAssets { | |||
198 | 174 | ||
199 | let db = sema.db; | 175 | let db = sema.db; |
200 | let mut res = | 176 | let mut res = |
201 | applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) | 177 | applicable_defs(import_candidate, current_crate, db, imports_for_candidate_name) |
202 | .filter_map(|candidate| { | 178 | .filter_map(|candidate| { |
203 | let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); | 179 | let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); |
204 | 180 | ||
205 | let item_to_search = match self.import_candidate { | 181 | let item_to_search = if import_candidate.is_trait_candidate() { |
206 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 182 | let canidate_trait = match candidate { |
207 | let canidate_trait = match candidate { | 183 | Either::Left(module_def) => { |
208 | Either::Left(module_def) => { | 184 | module_def.as_assoc_item(db)?.containing_trait(db) |
209 | module_def.as_assoc_item(db)?.containing_trait(db) | 185 | } |
210 | } | 186 | _ => None, |
211 | _ => None, | 187 | }?; |
212 | }?; | 188 | ModuleDef::from(canidate_trait).into() |
213 | ModuleDef::from(canidate_trait).into() | 189 | } else { |
214 | } | 190 | item |
215 | _ => item, | ||
216 | }; | 191 | }; |
217 | 192 | let mod_path = if let Some(prefix_kind) = prefixed { | |
218 | if let Some(prefix_kind) = prefixed { | ||
219 | self.module_with_candidate.find_use_path_prefixed( | 193 | self.module_with_candidate.find_use_path_prefixed( |
220 | db, | 194 | db, |
221 | item_to_search, | 195 | item_to_search, |
@@ -223,8 +197,9 @@ impl ImportAssets { | |||
223 | ) | 197 | ) |
224 | } else { | 198 | } else { |
225 | self.module_with_candidate.find_use_path(db, item_to_search) | 199 | self.module_with_candidate.find_use_path(db, item_to_search) |
226 | } | 200 | }; |
227 | .map(|path| (path, item)) | 201 | |
202 | mod_path.zip(Some(item)) | ||
228 | }) | 203 | }) |
229 | .filter(|(use_path, _)| use_path.len() > 1) | 204 | .filter(|(use_path, _)| use_path.len() > 1) |
230 | .collect::<Vec<_>>(); | 205 | .collect::<Vec<_>>(); |
@@ -239,6 +214,7 @@ fn applicable_defs<'a>( | |||
239 | db: &RootDatabase, | 214 | db: &RootDatabase, |
240 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, | 215 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, |
241 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 216 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { |
217 | // TODO kb this needs to consider various path prefixes, etc. | ||
242 | let receiver_ty = match import_candidate { | 218 | let receiver_ty = match import_candidate { |
243 | ImportCandidate::Path(_) => return unfiltered_imports, | 219 | ImportCandidate::Path(_) => return unfiltered_imports, |
244 | ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { | 220 | ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { |
@@ -325,41 +301,45 @@ impl ImportCandidate { | |||
325 | if sema.resolve_path(path).is_some() { | 301 | if sema.resolve_path(path).is_some() { |
326 | return None; | 302 | return None; |
327 | } | 303 | } |
304 | path_import_candidate( | ||
305 | sema, | ||
306 | path.qualifier(), | ||
307 | NameToImport::Exact(path.segment()?.name_ref()?.to_string()), | ||
308 | ) | ||
309 | } | ||
328 | 310 | ||
329 | let segment = path.segment()?; | 311 | fn for_fuzzy_path( |
330 | let candidate = if let Some(qualifier) = path.qualifier() { | 312 | qualifier: Option<ast::Path>, |
331 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 313 | fuzzy_name: String, |
332 | let qualifier_start_path = | 314 | sema: &Semantics<RootDatabase>, |
333 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 315 | ) -> Option<Self> { |
334 | if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { | 316 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) |
335 | let qualifier_resolution = if qualifier_start_path == qualifier { | 317 | } |
336 | qualifier_start_resolution | 318 | |
337 | } else { | 319 | fn is_trait_candidate(&self) -> bool { |
338 | sema.resolve_path(&qualifier)? | 320 | matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_)) |
339 | }; | 321 | } |
340 | match qualifier_resolution { | 322 | } |
341 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { | 323 | |
342 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | 324 | fn path_import_candidate( |
343 | receiver_ty: assoc_item_path.ty(sema.db), | 325 | sema: &Semantics<RootDatabase>, |
344 | name: NameToImport::Exact(segment.name_ref()?.to_string()), | 326 | qualifier: Option<ast::Path>, |
345 | }) | 327 | name: NameToImport, |
346 | } | 328 | ) -> Option<ImportCandidate> { |
347 | _ => return None, | 329 | Some(match qualifier { |
348 | } | 330 | Some(qualifier) => match sema.resolve_path(&qualifier) { |
349 | } else { | 331 | None => ImportCandidate::Path(PathImportCandidate { |
350 | ImportCandidate::Path(PathImportCandidate { | 332 | unresolved_qualifier: Some(qualifier), |
351 | qualifier: Some(qualifier), | 333 | name, |
352 | name: NameToImport::Exact(qualifier_start.to_string()), | 334 | }), |
335 | Some(hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path))) => { | ||
336 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
337 | receiver_ty: assoc_item_path.ty(sema.db), | ||
338 | name, | ||
353 | }) | 339 | }) |
354 | } | 340 | } |
355 | } else { | 341 | Some(_) => return None, |
356 | ImportCandidate::Path(PathImportCandidate { | 342 | }, |
357 | qualifier: None, | 343 | None => ImportCandidate::Path(PathImportCandidate { unresolved_qualifier: None, name }), |
358 | name: NameToImport::Exact( | 344 | }) |
359 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), | ||
360 | ), | ||
361 | }) | ||
362 | }; | ||
363 | Some(candidate) | ||
364 | } | ||
365 | } | 345 | } |