diff options
Diffstat (limited to 'crates/ide_db/src/helpers')
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 212 |
1 files changed, 110 insertions, 102 deletions
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index b25786928..2909ecd45 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -130,25 +130,23 @@ impl<'a> ImportAssets<'a> { | |||
130 | 130 | ||
131 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 131 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
132 | pub struct LocatedImport { | 132 | pub struct LocatedImport { |
133 | // TODO kb extract both into a separate struct + add another field: `assoc_item_name: Optional<String|Name>` | ||
133 | import_path: ModPath, | 134 | import_path: ModPath, |
134 | item_to_import: ItemInNs, | 135 | item_to_import: ItemInNs, |
135 | import_display_override: Option<(ModPath, ItemInNs)>, | 136 | data_to_display: Option<(ModPath, ItemInNs)>, |
136 | } | 137 | } |
137 | 138 | ||
138 | impl LocatedImport { | 139 | impl LocatedImport { |
139 | pub fn new( | 140 | pub fn new( |
140 | import_path: ModPath, | 141 | import_path: ModPath, |
141 | item_to_import: ItemInNs, | 142 | item_to_import: ItemInNs, |
142 | import_display_override: Option<(ModPath, ItemInNs)>, | 143 | data_to_display: Option<(ModPath, ItemInNs)>, |
143 | ) -> Self { | 144 | ) -> Self { |
144 | Self { import_path, item_to_import, import_display_override } | 145 | Self { import_path, item_to_import, data_to_display } |
145 | } | 146 | } |
146 | 147 | ||
147 | pub fn display_path(&self) -> &ModPath { | 148 | pub fn display_path(&self) -> &ModPath { |
148 | self.import_display_override | 149 | self.data_to_display.as_ref().map(|(mod_pathh, _)| mod_pathh).unwrap_or(&self.import_path) |
149 | .as_ref() | ||
150 | .map(|(mod_path, _)| mod_path) | ||
151 | .unwrap_or(&self.import_path) | ||
152 | } | 150 | } |
153 | 151 | ||
154 | pub fn import_path(&self) -> &ModPath { | 152 | pub fn import_path(&self) -> &ModPath { |
@@ -156,7 +154,7 @@ impl LocatedImport { | |||
156 | } | 154 | } |
157 | 155 | ||
158 | pub fn item_to_display(&self) -> ItemInNs { | 156 | pub fn item_to_display(&self) -> ItemInNs { |
159 | self.import_display_override.as_ref().map(|&(_, item)| item).unwrap_or(self.item_to_import) | 157 | self.data_to_display.as_ref().map(|&(_, item)| item).unwrap_or(self.item_to_import) |
160 | } | 158 | } |
161 | 159 | ||
162 | pub fn item_to_import(&self) -> ItemInNs { | 160 | pub fn item_to_import(&self) -> ItemInNs { |
@@ -200,7 +198,7 @@ impl<'a> ImportAssets<'a> { | |||
200 | let current_crate = self.module_with_candidate.krate(); | 198 | let current_crate = self.module_with_candidate.krate(); |
201 | let scope_definitions = self.scope_definitions(); | 199 | let scope_definitions = self.scope_definitions(); |
202 | 200 | ||
203 | let imports_for_candidate_name = match self.name_to_import() { | 201 | let defs_for_candidate_name = match self.name_to_import() { |
204 | NameToImport::Exact(exact_name) => { | 202 | NameToImport::Exact(exact_name) => { |
205 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) | 203 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) |
206 | } | 204 | } |
@@ -226,7 +224,7 @@ impl<'a> ImportAssets<'a> { | |||
226 | } | 224 | } |
227 | }; | 225 | }; |
228 | 226 | ||
229 | self.applicable_defs(sema.db, prefixed, imports_for_candidate_name) | 227 | self.applicable_defs(sema.db, prefixed, defs_for_candidate_name) |
230 | .into_iter() | 228 | .into_iter() |
231 | .filter(|import| import.import_path().len() > 1) | 229 | .filter(|import| import.import_path().len() > 1) |
232 | .filter(|import| { | 230 | .filter(|import| { |
@@ -252,32 +250,31 @@ impl<'a> ImportAssets<'a> { | |||
252 | &self, | 250 | &self, |
253 | db: &RootDatabase, | 251 | db: &RootDatabase, |
254 | prefixed: Option<PrefixKind>, | 252 | prefixed: Option<PrefixKind>, |
255 | unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>>, | 253 | defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>, |
256 | ) -> FxHashSet<LocatedImport> { | 254 | ) -> FxHashSet<LocatedImport> { |
257 | let current_crate = self.module_with_candidate.krate(); | 255 | let current_crate = self.module_with_candidate.krate(); |
258 | 256 | ||
259 | let import_path_locator = | 257 | let mod_path = |item| get_mod_path(db, item, &self.module_with_candidate, prefixed); |
260 | |item| get_mod_path(db, item, &self.module_with_candidate, prefixed); | ||
261 | 258 | ||
262 | match &self.import_candidate { | 259 | match &self.import_candidate { |
263 | ImportCandidate::Path(path_candidate) => { | 260 | ImportCandidate::Path(path_candidate) => { |
264 | path_applicable_imports(db, path_candidate, import_path_locator, unfiltered_defs) | 261 | path_applicable_imports(db, path_candidate, mod_path, defs_for_candidate_name) |
265 | } | 262 | } |
266 | ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items( | 263 | ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items( |
267 | db, | 264 | db, |
268 | current_crate, | 265 | current_crate, |
269 | trait_candidate, | 266 | trait_candidate, |
270 | true, | 267 | true, |
271 | import_path_locator, | 268 | mod_path, |
272 | unfiltered_defs, | 269 | defs_for_candidate_name, |
273 | ), | 270 | ), |
274 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( | 271 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( |
275 | db, | 272 | db, |
276 | current_crate, | 273 | current_crate, |
277 | trait_candidate, | 274 | trait_candidate, |
278 | false, | 275 | false, |
279 | import_path_locator, | 276 | mod_path, |
280 | unfiltered_defs, | 277 | defs_for_candidate_name, |
281 | ), | 278 | ), |
282 | } | 279 | } |
283 | } | 280 | } |
@@ -286,103 +283,114 @@ impl<'a> ImportAssets<'a> { | |||
286 | fn path_applicable_imports( | 283 | fn path_applicable_imports( |
287 | db: &RootDatabase, | 284 | db: &RootDatabase, |
288 | path_candidate: &PathImportCandidate, | 285 | path_candidate: &PathImportCandidate, |
289 | import_path_locator: impl Fn(ItemInNs) -> Option<ModPath>, | 286 | mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, |
290 | unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>>, | 287 | defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>, |
291 | ) -> FxHashSet<LocatedImport> { | 288 | ) -> FxHashSet<LocatedImport> { |
292 | let applicable_items = unfiltered_defs | 289 | let items_for_candidate_name = |
293 | .filter_map(|def| { | 290 | defs_for_candidate_name.map(|def| def.either(ItemInNs::from, ItemInNs::from)); |
294 | let (assoc_original, candidate) = match def { | ||
295 | Either::Left(module_def) => match module_def.as_assoc_item(db) { | ||
296 | Some(assoc_item) => match assoc_item.container(db) { | ||
297 | AssocItemContainer::Trait(trait_) => { | ||
298 | (Some(module_def), ItemInNs::from(ModuleDef::from(trait_))) | ||
299 | } | ||
300 | AssocItemContainer::Impl(impl_) => ( | ||
301 | Some(module_def), | ||
302 | ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)), | ||
303 | ), | ||
304 | }, | ||
305 | None => (None, ItemInNs::from(module_def)), | ||
306 | }, | ||
307 | Either::Right(macro_def) => (None, ItemInNs::from(macro_def)), | ||
308 | }; | ||
309 | Some((assoc_original, candidate)) | ||
310 | }) | ||
311 | .filter_map(|(assoc_original, candidate)| { | ||
312 | import_path_locator(candidate).zip(Some((assoc_original, candidate))) | ||
313 | }); | ||
314 | 291 | ||
315 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { | 292 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { |
316 | Qualifier::Absent => { | 293 | Qualifier::Absent => { |
317 | return applicable_items | 294 | return items_for_candidate_name |
318 | .map(|(candidate_path, (_, candidate))| { | 295 | .filter_map(|item| Some(LocatedImport::new(mod_path(item)?, item, None))) |
319 | LocatedImport::new(candidate_path, candidate, None) | ||
320 | }) | ||
321 | .collect(); | 296 | .collect(); |
322 | } | 297 | } |
323 | Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier), | 298 | Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => { |
299 | (first_segment.to_string(), qualifier.to_string()) | ||
300 | } | ||
324 | }; | 301 | }; |
325 | 302 | ||
326 | let unresolved_qualifier_string = unresolved_qualifier.to_string(); | 303 | items_for_candidate_name |
327 | let unresolved_first_segment_string = unresolved_first_segment.to_string(); | 304 | .filter_map(|item| { |
328 | 305 | import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) | |
329 | applicable_items | ||
330 | .filter(|(candidate_path, _)| { | ||
331 | let candidate_path_string = candidate_path.to_string(); | ||
332 | candidate_path_string.contains(&unresolved_qualifier_string) | ||
333 | && candidate_path_string.contains(&unresolved_first_segment_string) | ||
334 | }) | ||
335 | .filter_map(|(candidate_path, (assoc_original, candidate))| { | ||
336 | let found_segment_resolution = item_name(db, candidate) | ||
337 | .map(|name| name.to_string() == unresolved_first_segment_string) | ||
338 | .unwrap_or(false); | ||
339 | let (import_path, item_to_import) = if found_segment_resolution { | ||
340 | (candidate_path.clone(), candidate) | ||
341 | } else { | ||
342 | let matching_module = | ||
343 | module_with_matching_name(db, &unresolved_first_segment_string, candidate)?; | ||
344 | let module_item = ItemInNs::from(ModuleDef::from(matching_module)); | ||
345 | (import_path_locator(module_item)?, module_item) | ||
346 | }; | ||
347 | |||
348 | Some(match assoc_original { | ||
349 | Some(assoc_original) => LocatedImport::new( | ||
350 | import_path.clone(), | ||
351 | item_to_import, | ||
352 | Some((import_path, ItemInNs::from(assoc_original))), | ||
353 | ), | ||
354 | None => LocatedImport::new( | ||
355 | import_path, | ||
356 | item_to_import, | ||
357 | if found_segment_resolution { None } else { Some((candidate_path, candidate)) }, | ||
358 | ), | ||
359 | }) | ||
360 | }) | 306 | }) |
361 | .collect() | 307 | .collect() |
362 | } | 308 | } |
363 | 309 | ||
364 | fn item_module(db: &RootDatabase, item: ItemInNs) -> Option<Module> { | 310 | fn import_for_item( |
365 | match item { | 311 | db: &RootDatabase, |
366 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db), | 312 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, |
367 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db), | 313 | unresolved_first_segment: &str, |
368 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db), | 314 | unresolved_qualifier: &str, |
315 | original_item: ItemInNs, | ||
316 | ) -> Option<LocatedImport> { | ||
317 | let (item_candidate, trait_to_import) = match original_item { | ||
318 | ItemInNs::Types(module_def_id) | ItemInNs::Values(module_def_id) => { | ||
319 | match ModuleDef::from(module_def_id).as_assoc_item(db).map(|assoc| assoc.container(db)) | ||
320 | { | ||
321 | Some(AssocItemContainer::Trait(trait_)) => { | ||
322 | let trait_item = ItemInNs::from(ModuleDef::from(trait_)); | ||
323 | (trait_item, Some(trait_item)) | ||
324 | } | ||
325 | Some(AssocItemContainer::Impl(impl_)) => { | ||
326 | (ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)), None) | ||
327 | } | ||
328 | None => (original_item, None), | ||
329 | } | ||
330 | } | ||
331 | ItemInNs::Macros(_) => (original_item, None), | ||
332 | }; | ||
333 | let import_path_candidate = mod_path(item_candidate)?; | ||
334 | |||
335 | let import_path_string = import_path_candidate.to_string(); | ||
336 | if !import_path_string.contains(unresolved_first_segment) | ||
337 | || !import_path_string.contains(unresolved_qualifier) | ||
338 | { | ||
339 | return None; | ||
369 | } | 340 | } |
341 | |||
342 | let segment_import = find_import_for_segment(db, item_candidate, &unresolved_first_segment)?; | ||
343 | let data_to_display = Some((import_path_candidate.clone(), original_item)); | ||
344 | Some(match (segment_import == item_candidate, trait_to_import) { | ||
345 | (true, Some(_)) => { | ||
346 | // FIXME we should be able to import both the trait and the segment, | ||
347 | // but it's unclear what to do with overlapping edits (merge imports?) | ||
348 | // especially in case of lazy completion edit resolutions. | ||
349 | return None; | ||
350 | } | ||
351 | (false, Some(trait_to_import)) => { | ||
352 | LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, data_to_display) | ||
353 | } | ||
354 | (true, None) => LocatedImport::new(import_path_candidate, item_candidate, data_to_display), | ||
355 | (false, None) => { | ||
356 | LocatedImport::new(mod_path(segment_import)?, segment_import, data_to_display) | ||
357 | } | ||
358 | }) | ||
370 | } | 359 | } |
371 | 360 | ||
372 | fn module_with_matching_name( | 361 | fn find_import_for_segment( |
373 | db: &RootDatabase, | 362 | db: &RootDatabase, |
374 | unresolved_first_segment_string: &str, | 363 | original_item: ItemInNs, |
364 | unresolved_first_segment: &str, | ||
365 | ) -> Option<ItemInNs> { | ||
366 | let segment_is_name = item_name(db, original_item) | ||
367 | .map(|name| name.to_string() == unresolved_first_segment) | ||
368 | .unwrap_or(false); | ||
369 | |||
370 | Some(if segment_is_name { | ||
371 | original_item | ||
372 | } else { | ||
373 | let matching_module = | ||
374 | module_with_segment_name(db, &unresolved_first_segment, original_item)?; | ||
375 | ItemInNs::from(ModuleDef::from(matching_module)) | ||
376 | }) | ||
377 | } | ||
378 | |||
379 | fn module_with_segment_name( | ||
380 | db: &RootDatabase, | ||
381 | segment_name: &str, | ||
375 | candidate: ItemInNs, | 382 | candidate: ItemInNs, |
376 | ) -> Option<Module> { | 383 | ) -> Option<Module> { |
377 | let mut current_module = item_module(db, candidate); | 384 | let mut current_module = match candidate { |
385 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
386 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
387 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db), | ||
388 | }; | ||
378 | while let Some(module) = current_module { | 389 | while let Some(module) = current_module { |
379 | match module.name(db) { | 390 | if let Some(module_name) = module.name(db) { |
380 | Some(module_name) => { | 391 | if module_name.to_string() == segment_name { |
381 | if module_name.to_string().as_str() == unresolved_first_segment_string { | 392 | return Some(module); |
382 | return Some(module); | ||
383 | } | ||
384 | } | 393 | } |
385 | None => {} | ||
386 | } | 394 | } |
387 | current_module = module.parent(db); | 395 | current_module = module.parent(db); |
388 | } | 396 | } |
@@ -394,12 +402,12 @@ fn trait_applicable_items( | |||
394 | current_crate: Crate, | 402 | current_crate: Crate, |
395 | trait_candidate: &TraitImportCandidate, | 403 | trait_candidate: &TraitImportCandidate, |
396 | trait_assoc_item: bool, | 404 | trait_assoc_item: bool, |
397 | import_path_locator: impl Fn(ItemInNs) -> Option<ModPath>, | 405 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, |
398 | unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>>, | 406 | defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>, |
399 | ) -> FxHashSet<LocatedImport> { | 407 | ) -> FxHashSet<LocatedImport> { |
400 | let mut required_assoc_items = FxHashSet::default(); | 408 | let mut required_assoc_items = FxHashSet::default(); |
401 | 409 | ||
402 | let trait_candidates = unfiltered_defs | 410 | let trait_candidates = defs_for_candidate_name |
403 | .filter_map(|input| match input { | 411 | .filter_map(|input| match input { |
404 | Either::Left(module_def) => module_def.as_assoc_item(db), | 412 | Either::Left(module_def) => module_def.as_assoc_item(db), |
405 | _ => None, | 413 | _ => None, |
@@ -428,12 +436,12 @@ fn trait_applicable_items( | |||
428 | } | 436 | } |
429 | 437 | ||
430 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 438 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); |
431 | let item_path = import_path_locator(item)?; | 439 | let item_path = mod_path(item)?; |
432 | 440 | ||
433 | let assoc_item = assoc_to_item(assoc); | 441 | let assoc_item = assoc_to_item(assoc); |
434 | let assoc_item_path = match assoc.container(db) { | 442 | let assoc_item_path = match assoc.container(db) { |
435 | AssocItemContainer::Trait(_) => item_path.clone(), | 443 | AssocItemContainer::Trait(_) => item_path.clone(), |
436 | AssocItemContainer::Impl(impl_) => import_path_locator(ItemInNs::from( | 444 | AssocItemContainer::Impl(impl_) => mod_path(ItemInNs::from( |
437 | ModuleDef::from(impl_.target_ty(db).as_adt()?), | 445 | ModuleDef::from(impl_.target_ty(db).as_adt()?), |
438 | ))?, | 446 | ))?, |
439 | }; | 447 | }; |
@@ -457,12 +465,12 @@ fn trait_applicable_items( | |||
457 | let assoc = function.as_assoc_item(db)?; | 465 | let assoc = function.as_assoc_item(db)?; |
458 | if required_assoc_items.contains(&assoc) { | 466 | if required_assoc_items.contains(&assoc) { |
459 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 467 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); |
460 | let item_path = import_path_locator(item)?; | 468 | let item_path = mod_path(item)?; |
461 | 469 | ||
462 | let assoc_item = assoc_to_item(assoc); | 470 | let assoc_item = assoc_to_item(assoc); |
463 | let assoc_item_path = match assoc.container(db) { | 471 | let assoc_item_path = match assoc.container(db) { |
464 | AssocItemContainer::Trait(_) => item_path.clone(), | 472 | AssocItemContainer::Trait(_) => item_path.clone(), |
465 | AssocItemContainer::Impl(impl_) => import_path_locator(ItemInNs::from( | 473 | AssocItemContainer::Impl(impl_) => mod_path(ItemInNs::from( |
466 | ModuleDef::from(impl_.target_ty(db).as_adt()?), | 474 | ModuleDef::from(impl_.target_ty(db).as_adt()?), |
467 | ))?, | 475 | ))?, |
468 | }; | 476 | }; |