aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db/src/helpers')
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs212
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)]
132pub struct LocatedImport { 132pub 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
138impl LocatedImport { 139impl 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> {
286fn path_applicable_imports( 283fn 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
364fn item_module(db: &RootDatabase, item: ItemInNs) -> Option<Module> { 310fn 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
372fn module_with_matching_name( 361fn 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
379fn 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 };