1use std::{convert::Infallible, ops::ControlFlow};
4
5use hir::{
6 AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, FindPathConfig, HasCrate,
7 ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
8 SemanticsScope, Trait, Type,
9};
10use itertools::Itertools;
11use rustc_hash::{FxHashMap, FxHashSet};
12use smallvec::{SmallVec, smallvec};
13use syntax::{
14 AstNode, SyntaxNode,
15 ast::{self, HasName, make},
16};
17
18use crate::{
19 FxIndexSet, RootDatabase,
20 items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT},
21};
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
24pub struct ImportPathConfig {
25 pub prefer_no_std: bool,
28 pub prefer_prelude: bool,
30 pub prefer_absolute: bool,
32}
33
34#[derive(Debug)]
40pub enum ImportCandidate<'db> {
41 Path(PathImportCandidate),
43 TraitAssocItem(TraitImportCandidate<'db>),
47 TraitMethod(TraitImportCandidate<'db>),
51}
52
53#[derive(Debug)]
57pub struct TraitImportCandidate<'db> {
58 pub receiver_ty: Type<'db>,
60 pub assoc_item_name: NameToImport,
62}
63
64#[derive(Debug)]
66pub struct PathImportCandidate {
67 pub qualifier: Vec<Name>,
69 pub name: NameToImport,
71 pub after: Vec<Name>,
73}
74
75#[derive(Debug, Clone)]
77pub enum NameToImport {
78 Exact(String, bool),
80 Prefix(String, bool),
82 Fuzzy(String, bool),
85}
86
87impl NameToImport {
88 pub fn exact_case_sensitive(s: String) -> NameToImport {
89 let s = match s.strip_prefix("r#") {
90 Some(s) => s.to_owned(),
91 None => s,
92 };
93 NameToImport::Exact(s, true)
94 }
95
96 pub fn fuzzy(s: String) -> NameToImport {
97 let s = match s.strip_prefix("r#") {
98 Some(s) => s.to_owned(),
99 None => s,
100 };
101 let case_sensitive = s.chars().any(|c| c.is_uppercase());
103 NameToImport::Fuzzy(s, case_sensitive)
104 }
105
106 pub fn text(&self) -> &str {
107 match self {
108 NameToImport::Prefix(text, _)
109 | NameToImport::Exact(text, _)
110 | NameToImport::Fuzzy(text, _) => text.as_str(),
111 }
112 }
113}
114
115#[derive(Debug)]
117pub struct ImportAssets<'db> {
118 import_candidate: ImportCandidate<'db>,
119 candidate_node: SyntaxNode,
120 module_with_candidate: Module,
121}
122
123impl<'db> ImportAssets<'db> {
124 pub fn for_method_call(
125 method_call: &ast::MethodCallExpr,
126 sema: &Semantics<'db, RootDatabase>,
127 ) -> Option<Self> {
128 let candidate_node = method_call.syntax().clone();
129 Some(Self {
130 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
131 module_with_candidate: sema.scope(&candidate_node)?.module(),
132 candidate_node,
133 })
134 }
135
136 pub fn for_exact_path(
137 fully_qualified_path: &ast::Path,
138 sema: &Semantics<'db, RootDatabase>,
139 ) -> Option<Self> {
140 let candidate_node = fully_qualified_path.syntax().clone();
141 if let Some(use_tree) = candidate_node.ancestors().find_map(ast::UseTree::cast) {
142 if use_tree.syntax().parent().and_then(ast::Use::cast).is_none()
144 || fully_qualified_path.qualifier().is_some()
145 {
146 return None;
147 }
148 }
149 Some(Self {
150 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
151 module_with_candidate: sema.scope(&candidate_node)?.module(),
152 candidate_node,
153 })
154 }
155
156 pub fn for_ident_pat(sema: &Semantics<'db, RootDatabase>, pat: &ast::IdentPat) -> Option<Self> {
157 if !pat.is_simple_ident() {
158 return None;
159 }
160 let name = pat.name()?;
161 let candidate_node = pat.syntax().clone();
162 Some(Self {
163 import_candidate: ImportCandidate::for_name(sema, &name)?,
164 module_with_candidate: sema.scope(&candidate_node)?.module(),
165 candidate_node,
166 })
167 }
168
169 pub fn for_fuzzy_path(
170 module_with_candidate: Module,
171 qualifier: Option<ast::Path>,
172 fuzzy_name: String,
173 sema: &Semantics<'db, RootDatabase>,
174 candidate_node: SyntaxNode,
175 ) -> Option<Self> {
176 Some(Self {
177 import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
178 module_with_candidate,
179 candidate_node,
180 })
181 }
182
183 pub fn for_fuzzy_method_call(
184 module_with_method_call: Module,
185 receiver_ty: Type<'db>,
186 fuzzy_method_name: String,
187 candidate_node: SyntaxNode,
188 ) -> Option<Self> {
189 Some(Self {
190 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
191 receiver_ty,
192 assoc_item_name: NameToImport::fuzzy(fuzzy_method_name),
193 }),
194 module_with_candidate: module_with_method_call,
195 candidate_node,
196 })
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
201pub struct CompleteInFlyimport(pub bool);
202
203#[derive(Debug, Clone, PartialEq, Eq, Hash)]
206pub struct LocatedImport {
207 pub import_path: ModPath,
209 pub item_to_import: ItemInNs,
211 pub original_item: ItemInNs,
218 pub complete_in_flyimport: CompleteInFlyimport,
220}
221
222impl LocatedImport {
223 pub fn new(
224 import_path: ModPath,
225 item_to_import: ItemInNs,
226 original_item: ItemInNs,
227 complete_in_flyimport: CompleteInFlyimport,
228 ) -> Self {
229 Self { import_path, item_to_import, original_item, complete_in_flyimport }
230 }
231
232 pub fn new_no_completion(
233 import_path: ModPath,
234 item_to_import: ItemInNs,
235 original_item: ItemInNs,
236 ) -> Self {
237 Self {
238 import_path,
239 item_to_import,
240 original_item,
241 complete_in_flyimport: CompleteInFlyimport(true),
242 }
243 }
244}
245
246impl<'db> ImportAssets<'db> {
247 pub fn import_candidate(&self) -> &ImportCandidate<'db> {
248 &self.import_candidate
249 }
250
251 pub fn search_for_imports(
252 &self,
253 sema: &Semantics<'db, RootDatabase>,
254 cfg: ImportPathConfig,
255 prefix_kind: PrefixKind,
256 ) -> impl Iterator<Item = LocatedImport> {
257 let _p = tracing::info_span!("ImportAssets::search_for_imports").entered();
258 self.search_for(sema, Some(prefix_kind), cfg)
259 }
260
261 pub fn search_for_relative_paths(
263 &self,
264 sema: &Semantics<'db, RootDatabase>,
265 cfg: ImportPathConfig,
266 ) -> impl Iterator<Item = LocatedImport> {
267 let _p = tracing::info_span!("ImportAssets::search_for_relative_paths").entered();
268 self.search_for(sema, None, cfg)
269 }
270
271 pub fn path_fuzzy_name_to_prefix(&mut self) {
273 if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
274 &mut self.import_candidate
275 {
276 let (name, case_sensitive) = match to_import {
277 NameToImport::Fuzzy(name, case_sensitive) => {
278 (std::mem::take(name), *case_sensitive)
279 }
280 _ => return,
281 };
282 *to_import = NameToImport::Prefix(name, case_sensitive);
283 }
284 }
285
286 pub fn path_fuzzy_name_to_exact(&mut self) {
288 if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
289 &mut self.import_candidate
290 {
291 let (name, case_sensitive) = match to_import {
292 NameToImport::Fuzzy(name, case_sensitive) => {
293 (std::mem::take(name), *case_sensitive)
294 }
295 _ => return,
296 };
297 *to_import = NameToImport::Exact(name, case_sensitive);
298 }
299 }
300
301 fn search_for(
302 &self,
303 sema: &Semantics<'db, RootDatabase>,
304 prefixed: Option<PrefixKind>,
305 cfg: ImportPathConfig,
306 ) -> impl Iterator<Item = LocatedImport> {
307 let _p = tracing::info_span!("ImportAssets::search_for").entered();
308
309 let scope = match sema.scope(&self.candidate_node) {
310 Some(it) => it,
311 None => return <FxIndexSet<_>>::default().into_iter(),
312 };
313 let cfg = FindPathConfig {
314 prefer_no_std: cfg.prefer_no_std,
315 prefer_prelude: cfg.prefer_prelude,
316 prefer_absolute: cfg.prefer_absolute,
317 allow_unstable: sema.is_nightly(scope.krate()),
318 };
319 let db = sema.db;
320 let krate = self.module_with_candidate.krate(sema.db);
321 let scope_definitions = self.scope_definitions(sema);
322 let mod_path = |item| {
323 get_mod_path(
324 db,
325 item_for_path_search(db, item)?,
326 &self.module_with_candidate,
327 prefixed,
328 cfg,
329 )
330 .filter(|path| path.len() > 1)
331 };
332
333 match &self.import_candidate {
334 ImportCandidate::Path(path_candidate) => path_applicable_imports(
335 db,
336 &scope,
337 krate,
338 path_candidate,
339 mod_path,
340 |item_to_import| !scope_definitions.contains(&ScopeDef::from(item_to_import)),
341 ),
342 ImportCandidate::TraitAssocItem(trait_candidate)
343 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
344 db,
345 krate,
346 &scope,
347 trait_candidate,
348 matches!(self.import_candidate, ImportCandidate::TraitAssocItem(_)),
349 mod_path,
350 |trait_to_import| {
351 !scope_definitions
352 .contains(&ScopeDef::ModuleDef(ModuleDef::Trait(trait_to_import)))
353 },
354 ),
355 }
356 .into_iter()
357 }
358
359 fn scope_definitions(&self, sema: &Semantics<'_, RootDatabase>) -> FxHashSet<ScopeDef> {
360 let _p = tracing::info_span!("ImportAssets::scope_definitions").entered();
361 let mut scope_definitions = FxHashSet::default();
362 if let Some(scope) = sema.scope(&self.candidate_node) {
363 scope.process_all_names(&mut |_, scope_def| {
364 scope_definitions.insert(scope_def);
365 });
366 }
367 scope_definitions
368 }
369}
370
371fn path_applicable_imports(
372 db: &RootDatabase,
373 scope: &SemanticsScope<'_>,
374 current_crate: Crate,
375 path_candidate: &PathImportCandidate,
376 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
377 scope_filter: impl Fn(ItemInNs) -> bool + Copy,
378) -> FxIndexSet<LocatedImport> {
379 let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered();
380
381 let mut result = match &*path_candidate.qualifier {
382 [] => {
383 items_locator::items_with_name(
384 db,
385 current_crate,
386 path_candidate.name.clone(),
387 AssocSearchMode::Exclude,
396 )
397 .filter_map(|(item, do_not_complete)| {
398 if !scope_filter(item) {
399 return None;
400 }
401 let mod_path = mod_path(item)?;
402 Some(LocatedImport::new(
403 mod_path,
404 item,
405 item,
406 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
407 ))
408 })
409 .take(DEFAULT_QUERY_SEARCH_LIMIT)
410 .collect()
411 }
412 [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
417 db,
418 current_crate,
419 NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
420 AssocSearchMode::Exclude,
421 )
422 .flat_map(|(item, do_not_complete)| {
423 validate_resolvable(
426 db,
427 scope,
428 mod_path,
429 scope_filter,
430 &path_candidate.name,
431 item,
432 qualifier_rest,
433 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
434 )
435 })
436 .take(DEFAULT_QUERY_SEARCH_LIMIT)
437 .collect(),
438 };
439
440 filter_candidates_by_after_path(db, scope, path_candidate, &mut result);
441
442 result
443}
444
445fn filter_candidates_by_after_path(
446 db: &RootDatabase,
447 scope: &SemanticsScope<'_>,
448 path_candidate: &PathImportCandidate,
449 imports: &mut FxIndexSet<LocatedImport>,
450) {
451 if imports.len() <= 1 {
452 return;
454 }
455
456 let Some((last_after, after_except_last)) = path_candidate.after.split_last() else {
457 return;
458 };
459
460 let original_imports = imports.clone();
461
462 let traits_in_scope = scope.visible_traits();
463 imports.retain(|import| {
464 let items = if after_except_last.is_empty() {
465 smallvec![import.original_item]
466 } else {
467 let ItemInNs::Types(ModuleDef::Module(item)) = import.original_item else {
468 return false;
469 };
470 item.resolve_mod_path(db, after_except_last.iter().cloned())
472 .into_iter()
473 .flatten()
474 .collect::<SmallVec<[_; 3]>>()
475 };
476 items.into_iter().any(|item| {
477 let has_last_method = |ty: hir::Type<'_>| {
478 ty.iterate_path_candidates(db, scope, &traits_in_scope, Some(last_after), |_| {
479 Some(())
480 })
481 .is_some()
482 };
483 match item {
485 ItemInNs::Types(ModuleDef::Module(module)) => module
487 .resolve_mod_path(db, [last_after.clone()])
488 .is_some_and(|mut it| it.any(|_| true)),
489 ItemInNs::Types(ModuleDef::Adt(it)) => has_last_method(it.ty(db)),
491 ItemInNs::Types(ModuleDef::BuiltinType(it)) => has_last_method(it.ty(db)),
492 ItemInNs::Types(ModuleDef::TypeAlias(it)) => has_last_method(it.ty(db)),
493 ItemInNs::Types(ModuleDef::Trait(it)) => it
495 .items(db)
496 .into_iter()
497 .any(|assoc_item| assoc_item.name(db) == Some(last_after.clone())),
498 _ => false,
500 }
501 })
502 });
503
504 if imports.is_empty() {
505 *imports = original_imports;
507 }
508}
509
510fn validate_resolvable(
513 db: &RootDatabase,
514 scope: &SemanticsScope<'_>,
515 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
516 scope_filter: impl Fn(ItemInNs) -> bool,
517 candidate: &NameToImport,
518 resolved_qualifier: ItemInNs,
519 unresolved_qualifier: &[Name],
520 complete_in_flyimport: CompleteInFlyimport,
521) -> SmallVec<[LocatedImport; 1]> {
522 let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
523
524 let qualifier = (|| {
525 let mut adjusted_resolved_qualifier = resolved_qualifier;
526 if !unresolved_qualifier.is_empty() {
527 match resolved_qualifier {
528 ItemInNs::Types(ModuleDef::Module(module)) => {
529 adjusted_resolved_qualifier = module
530 .resolve_mod_path(db, unresolved_qualifier.iter().cloned())?
531 .next()?;
532 }
533 _ => return None,
535 }
536 }
537
538 match adjusted_resolved_qualifier {
539 ItemInNs::Types(def) => Some(def),
540 _ => None,
541 }
542 })();
543 let Some(qualifier) = qualifier else { return SmallVec::new() };
544 let Some(import_path_candidate) = mod_path(resolved_qualifier) else { return SmallVec::new() };
545 let mut result = SmallVec::new();
546 let ty = match qualifier {
547 ModuleDef::Module(module) => {
548 items_locator::items_with_name_in_module::<Infallible>(
549 db,
550 module,
551 candidate.clone(),
552 AssocSearchMode::Exclude,
553 |item| {
554 if scope_filter(item) {
555 result.push(LocatedImport::new(
556 import_path_candidate.clone(),
557 resolved_qualifier,
558 item,
559 complete_in_flyimport,
560 ));
561 }
562 ControlFlow::Continue(())
563 },
564 );
565 return result;
566 }
567 ModuleDef::Trait(_) => return SmallVec::new(),
569 ModuleDef::TypeAlias(alias) => alias.ty(db),
570 ModuleDef::BuiltinType(builtin) => builtin.ty(db),
571 ModuleDef::Adt(adt) => adt.ty(db),
572 _ => return SmallVec::new(),
573 };
574 ty.iterate_path_candidates::<Infallible>(db, scope, &FxHashSet::default(), None, |assoc| {
575 if assoc.container_or_implemented_trait(db).is_some() {
577 return None;
578 }
579 let name = assoc.name(db)?;
580 let is_match = match candidate {
581 NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
582 NameToImport::Prefix(text, false) => {
583 name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
584 name_char.eq_ignore_ascii_case(&candidate_char)
585 })
586 }
587 NameToImport::Exact(text, true) => name.as_str() == text,
588 NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
589 NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
590 NameToImport::Fuzzy(text, false) => text
591 .chars()
592 .all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
593 };
594 if !is_match {
595 return None;
596 }
597 result.push(LocatedImport::new(
598 import_path_candidate.clone(),
599 resolved_qualifier,
600 assoc_to_item(assoc),
601 complete_in_flyimport,
602 ));
603 None
604 });
605 result
606}
607
608pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
609 Some(match item {
610 ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
611 Some(assoc_item) => item_for_path_search_assoc(db, assoc_item)?,
612 None => item,
613 },
614 ItemInNs::Macros(_) => item,
615 })
616}
617
618fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Option<ItemInNs> {
619 Some(match assoc_item.container(db) {
620 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
621 AssocItemContainer::Impl(impl_) => {
622 ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
623 }
624 })
625}
626
627fn trait_applicable_items<'db>(
628 db: &'db RootDatabase,
629 current_crate: Crate,
630 scope: &SemanticsScope<'db>,
631 trait_candidate: &TraitImportCandidate<'db>,
632 trait_assoc_item: bool,
633 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
634 scope_filter: impl Fn(hir::Trait) -> bool,
635) -> FxIndexSet<LocatedImport> {
636 let _p = tracing::info_span!("ImportAssets::trait_applicable_items").entered();
637
638 let inherent_traits = trait_candidate.receiver_ty.applicable_inherent_traits(db);
639 let env_traits = trait_candidate.receiver_ty.env_traits(db);
640 let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>();
641
642 let mut required_assoc_items = FxHashMap::default();
643 let mut trait_candidates: FxHashSet<_> = items_locator::items_with_name(
644 db,
645 current_crate,
646 trait_candidate.assoc_item_name.clone(),
647 AssocSearchMode::AssocItemsOnly,
648 )
649 .filter_map(|(input, do_not_complete)| Some((item_as_assoc(db, input)?, do_not_complete)))
650 .filter_map(|(assoc, do_not_complete)| {
651 if !trait_assoc_item && matches!(assoc, AssocItem::Const(_) | AssocItem::TypeAlias(_)) {
652 return None;
653 }
654
655 let assoc_item_trait = assoc.container_trait(db)?;
656 if related_traits.contains(&assoc_item_trait) {
657 return None;
658 }
659 required_assoc_items
660 .insert(assoc, CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport));
661 Some(assoc_item_trait.into())
662 })
663 .collect();
664
665 let autoderef_method_receiver = {
666 let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::<Vec<_>>();
667 if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) {
669 let slice = Type::new_slice(ty);
670 deref_chain.push(slice);
671 }
672 deref_chain
673 .into_iter()
674 .flat_map(|ty| {
675 let fingerprint = ty.fingerprint_for_trait_impl()?;
676 let mut crates = vec![];
677
678 if let Some(adt) = ty.as_adt() {
679 crates.push((adt.krate(db).into(), fingerprint));
681 }
682 crates.push((ty.krate(db).into(), fingerprint));
684 Some(crates)
685 })
686 .flatten()
687 .unique()
688 .collect::<Vec<_>>()
689 };
690
691 if autoderef_method_receiver.is_empty() {
693 return Default::default();
694 }
695
696 if !autoderef_method_receiver
700 .iter()
701 .any(|(_, fingerprint)| matches!(fingerprint, hir::SimplifiedType::Placeholder))
702 {
703 trait_candidates.retain(|&candidate_trait_id| {
704 let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
712
713 let trait_impls_in_crate =
714 hir::TraitImpls::for_crate(db, defining_crate_for_trait.into());
715 let definitions_exist_in_trait_crate =
716 autoderef_method_receiver.iter().any(|(_, fingerprint)| {
717 trait_impls_in_crate
718 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
719 });
720 let definitions_exist_in_receiver_crate = || {
723 autoderef_method_receiver.iter().any(|(krate, fingerprint)| {
724 hir::TraitImpls::for_crate(db, *krate)
725 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
726 })
727 };
728
729 definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
730 });
731 }
732
733 let mut located_imports = FxIndexSet::default();
734 let mut trait_import_paths = FxHashMap::default();
735
736 if trait_assoc_item {
737 trait_candidate.receiver_ty.iterate_path_candidates(
738 db,
739 scope,
740 &trait_candidates,
741 None,
742 |assoc| {
743 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
744 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
745 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
746 let import_path = trait_import_paths
747 .entry(trait_item)
748 .or_insert_with(|| mod_path(trait_item))
749 .clone()?;
750 located_imports.insert(LocatedImport::new(
751 import_path,
752 trait_item,
753 assoc_to_item(assoc),
754 complete_in_flyimport,
755 ));
756 }
757 None::<()>
758 },
759 )
760 } else {
761 trait_candidate.receiver_ty.iterate_method_candidates_with_traits(
762 db,
763 scope,
764 &trait_candidates,
765 None,
766 |function| {
767 let assoc = function.as_assoc_item(db)?;
768 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
769 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
770 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
771 let import_path = trait_import_paths
772 .entry(trait_item)
773 .or_insert_with(|| mod_path(trait_item))
774 .clone()?;
775 located_imports.insert(LocatedImport::new(
776 import_path,
777 trait_item,
778 assoc_to_item(assoc),
779 complete_in_flyimport,
780 ));
781 }
782 None::<()>
783 },
784 )
785 };
786
787 located_imports
788}
789
790fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
791 match assoc {
792 AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
793 AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
794 AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
795 }
796}
797
798#[tracing::instrument(skip_all)]
799fn get_mod_path(
800 db: &RootDatabase,
801 item_to_search: ItemInNs,
802 module_with_candidate: &Module,
803 prefixed: Option<PrefixKind>,
804 cfg: FindPathConfig,
805) -> Option<ModPath> {
806 if let Some(prefix_kind) = prefixed {
807 module_with_candidate.find_use_path(db, item_to_search, prefix_kind, cfg)
808 } else {
809 module_with_candidate.find_path(db, item_to_search, cfg)
810 }
811}
812
813impl<'db> ImportCandidate<'db> {
814 fn for_method_call(
815 sema: &Semantics<'db, RootDatabase>,
816 method_call: &ast::MethodCallExpr,
817 ) -> Option<Self> {
818 match sema.resolve_method_call(method_call) {
819 Some(_) => None,
820 None => Some(Self::TraitMethod(TraitImportCandidate {
821 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?.adjusted(),
822 assoc_item_name: NameToImport::exact_case_sensitive(
823 method_call.name_ref()?.to_string(),
824 ),
825 })),
826 }
827 }
828
829 fn for_regular_path(sema: &Semantics<'db, RootDatabase>, path: &ast::Path) -> Option<Self> {
830 if sema.resolve_path(path).is_some() {
831 return None;
832 }
833 let after = std::iter::successors(path.parent_path(), |it| it.parent_path())
834 .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text())))
835 .collect::<Option<_>>()?;
836 path_import_candidate(
837 sema,
838 path.qualifier(),
839 NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()),
840 after,
841 )
842 }
843
844 fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option<Self> {
845 if sema
846 .scope(name.syntax())?
847 .speculative_resolve(&make::ext::ident_path(&name.text()))
848 .is_some()
849 {
850 return None;
851 }
852 Some(ImportCandidate::Path(PathImportCandidate {
853 qualifier: vec![],
854 name: NameToImport::exact_case_sensitive(name.to_string()),
855 after: vec![],
856 }))
857 }
858
859 fn for_fuzzy_path(
860 qualifier: Option<ast::Path>,
861 fuzzy_name: String,
862 sema: &Semantics<'db, RootDatabase>,
863 ) -> Option<Self> {
864 path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new())
866 }
867}
868
869fn path_import_candidate<'db>(
870 sema: &Semantics<'db, RootDatabase>,
871 qualifier: Option<ast::Path>,
872 name: NameToImport,
873 after: Vec<Name>,
874) -> Option<ImportCandidate<'db>> {
875 Some(match qualifier {
876 Some(qualifier) => match sema.resolve_path(&qualifier) {
877 Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => {
878 if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
879 let qualifier = qualifier
880 .segments()
881 .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
882 .collect::<Option<Vec<_>>>()?;
883 ImportCandidate::Path(PathImportCandidate { qualifier, name, after })
884 } else {
885 return None;
886 }
887 }
888 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
889 ImportCandidate::TraitAssocItem(TraitImportCandidate {
890 receiver_ty: assoc_item_path.ty(sema.db),
891 assoc_item_name: name,
892 })
893 }
894 Some(PathResolution::Def(ModuleDef::TypeAlias(alias))) => {
895 let ty = alias.ty(sema.db);
896 if ty.as_adt().is_some() {
897 ImportCandidate::TraitAssocItem(TraitImportCandidate {
898 receiver_ty: ty,
899 assoc_item_name: name,
900 })
901 } else {
902 return None;
903 }
904 }
905 Some(_) => return None,
906 },
907 None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name, after }),
908 })
909}
910
911fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
912 item.into_module_def().as_assoc_item(db)
913}