aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs35
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs4
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs37
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs150
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 {
192fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 192fn 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#"
782mod foo {
783 pub mod bar {
784 pub struct Item;
785 }
786}
787
788fn main() {
789 bar::Ite$0
790}
791"#,
792 r#"
793use foo::bar;
794
795mod foo {
796 pub mod bar {
797 pub struct Item;
798 }
799}
800
801fn 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)]
33pub struct PathImportCandidate { 33pub 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 { 324fn 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}