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;
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}
72
73#[derive(Debug, Clone)]
75pub enum NameToImport {
76 Exact(String, bool),
78 Prefix(String, bool),
80 Fuzzy(String, bool),
83}
84
85impl NameToImport {
86 pub fn exact_case_sensitive(s: String) -> NameToImport {
87 let s = match s.strip_prefix("r#") {
88 Some(s) => s.to_owned(),
89 None => s,
90 };
91 NameToImport::Exact(s, true)
92 }
93
94 pub fn fuzzy(s: String) -> NameToImport {
95 let s = match s.strip_prefix("r#") {
96 Some(s) => s.to_owned(),
97 None => s,
98 };
99 let case_sensitive = s.chars().any(|c| c.is_uppercase());
101 NameToImport::Fuzzy(s, case_sensitive)
102 }
103
104 pub fn text(&self) -> &str {
105 match self {
106 NameToImport::Prefix(text, _)
107 | NameToImport::Exact(text, _)
108 | NameToImport::Fuzzy(text, _) => text.as_str(),
109 }
110 }
111}
112
113#[derive(Debug)]
115pub struct ImportAssets<'db> {
116 import_candidate: ImportCandidate<'db>,
117 candidate_node: SyntaxNode,
118 module_with_candidate: Module,
119}
120
121impl<'db> ImportAssets<'db> {
122 pub fn for_method_call(
123 method_call: &ast::MethodCallExpr,
124 sema: &Semantics<'db, RootDatabase>,
125 ) -> Option<Self> {
126 let candidate_node = method_call.syntax().clone();
127 Some(Self {
128 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
129 module_with_candidate: sema.scope(&candidate_node)?.module(),
130 candidate_node,
131 })
132 }
133
134 pub fn for_exact_path(
135 fully_qualified_path: &ast::Path,
136 sema: &Semantics<'db, RootDatabase>,
137 ) -> Option<Self> {
138 let candidate_node = fully_qualified_path.syntax().clone();
139 if let Some(use_tree) = candidate_node.ancestors().find_map(ast::UseTree::cast) {
140 if use_tree.syntax().parent().and_then(ast::Use::cast).is_none()
142 || fully_qualified_path.qualifier().is_some()
143 {
144 return None;
145 }
146 }
147 Some(Self {
148 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
149 module_with_candidate: sema.scope(&candidate_node)?.module(),
150 candidate_node,
151 })
152 }
153
154 pub fn for_ident_pat(sema: &Semantics<'db, RootDatabase>, pat: &ast::IdentPat) -> Option<Self> {
155 if !pat.is_simple_ident() {
156 return None;
157 }
158 let name = pat.name()?;
159 let candidate_node = pat.syntax().clone();
160 Some(Self {
161 import_candidate: ImportCandidate::for_name(sema, &name)?,
162 module_with_candidate: sema.scope(&candidate_node)?.module(),
163 candidate_node,
164 })
165 }
166
167 pub fn for_fuzzy_path(
168 module_with_candidate: Module,
169 qualifier: Option<ast::Path>,
170 fuzzy_name: String,
171 sema: &Semantics<'db, RootDatabase>,
172 candidate_node: SyntaxNode,
173 ) -> Option<Self> {
174 Some(Self {
175 import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
176 module_with_candidate,
177 candidate_node,
178 })
179 }
180
181 pub fn for_fuzzy_method_call(
182 module_with_method_call: Module,
183 receiver_ty: Type<'db>,
184 fuzzy_method_name: String,
185 candidate_node: SyntaxNode,
186 ) -> Option<Self> {
187 Some(Self {
188 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
189 receiver_ty,
190 assoc_item_name: NameToImport::fuzzy(fuzzy_method_name),
191 }),
192 module_with_candidate: module_with_method_call,
193 candidate_node,
194 })
195 }
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
199pub struct CompleteInFlyimport(pub bool);
200
201#[derive(Debug, Clone, PartialEq, Eq, Hash)]
204pub struct LocatedImport {
205 pub import_path: ModPath,
207 pub item_to_import: ItemInNs,
209 pub original_item: ItemInNs,
216 pub complete_in_flyimport: CompleteInFlyimport,
218}
219
220impl LocatedImport {
221 pub fn new(
222 import_path: ModPath,
223 item_to_import: ItemInNs,
224 original_item: ItemInNs,
225 complete_in_flyimport: CompleteInFlyimport,
226 ) -> Self {
227 Self { import_path, item_to_import, original_item, complete_in_flyimport }
228 }
229
230 pub fn new_no_completion(
231 import_path: ModPath,
232 item_to_import: ItemInNs,
233 original_item: ItemInNs,
234 ) -> Self {
235 Self {
236 import_path,
237 item_to_import,
238 original_item,
239 complete_in_flyimport: CompleteInFlyimport(true),
240 }
241 }
242}
243
244impl<'db> ImportAssets<'db> {
245 pub fn import_candidate(&self) -> &ImportCandidate<'db> {
246 &self.import_candidate
247 }
248
249 pub fn search_for_imports(
250 &self,
251 sema: &Semantics<'db, RootDatabase>,
252 cfg: ImportPathConfig,
253 prefix_kind: PrefixKind,
254 ) -> impl Iterator<Item = LocatedImport> {
255 let _p = tracing::info_span!("ImportAssets::search_for_imports").entered();
256 self.search_for(sema, Some(prefix_kind), cfg)
257 }
258
259 pub fn search_for_relative_paths(
261 &self,
262 sema: &Semantics<'db, RootDatabase>,
263 cfg: ImportPathConfig,
264 ) -> impl Iterator<Item = LocatedImport> {
265 let _p = tracing::info_span!("ImportAssets::search_for_relative_paths").entered();
266 self.search_for(sema, None, cfg)
267 }
268
269 pub fn path_fuzzy_name_to_prefix(&mut self) {
271 if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
272 &mut self.import_candidate
273 {
274 let (name, case_sensitive) = match to_import {
275 NameToImport::Fuzzy(name, case_sensitive) => {
276 (std::mem::take(name), *case_sensitive)
277 }
278 _ => return,
279 };
280 *to_import = NameToImport::Prefix(name, case_sensitive);
281 }
282 }
283
284 pub fn path_fuzzy_name_to_exact(&mut self) {
286 if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
287 &mut self.import_candidate
288 {
289 let (name, case_sensitive) = match to_import {
290 NameToImport::Fuzzy(name, case_sensitive) => {
291 (std::mem::take(name), *case_sensitive)
292 }
293 _ => return,
294 };
295 *to_import = NameToImport::Exact(name, case_sensitive);
296 }
297 }
298
299 fn search_for(
300 &self,
301 sema: &Semantics<'db, RootDatabase>,
302 prefixed: Option<PrefixKind>,
303 cfg: ImportPathConfig,
304 ) -> impl Iterator<Item = LocatedImport> {
305 let _p = tracing::info_span!("ImportAssets::search_for").entered();
306
307 let scope = match sema.scope(&self.candidate_node) {
308 Some(it) => it,
309 None => return <FxIndexSet<_>>::default().into_iter(),
310 };
311 let cfg = FindPathConfig {
312 prefer_no_std: cfg.prefer_no_std,
313 prefer_prelude: cfg.prefer_prelude,
314 prefer_absolute: cfg.prefer_absolute,
315 allow_unstable: sema.is_nightly(scope.krate()),
316 };
317 let db = sema.db;
318 let krate = self.module_with_candidate.krate();
319 let scope_definitions = self.scope_definitions(sema);
320 let mod_path = |item| {
321 get_mod_path(
322 db,
323 item_for_path_search(db, item)?,
324 &self.module_with_candidate,
325 prefixed,
326 cfg,
327 )
328 .filter(|path| path.len() > 1)
329 };
330
331 match &self.import_candidate {
332 ImportCandidate::Path(path_candidate) => path_applicable_imports(
333 db,
334 &scope,
335 krate,
336 path_candidate,
337 mod_path,
338 |item_to_import| !scope_definitions.contains(&ScopeDef::from(item_to_import)),
339 ),
340 ImportCandidate::TraitAssocItem(trait_candidate)
341 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
342 db,
343 krate,
344 &scope,
345 trait_candidate,
346 matches!(self.import_candidate, ImportCandidate::TraitAssocItem(_)),
347 mod_path,
348 |trait_to_import| {
349 !scope_definitions
350 .contains(&ScopeDef::ModuleDef(ModuleDef::Trait(trait_to_import)))
351 },
352 ),
353 }
354 .into_iter()
355 }
356
357 fn scope_definitions(&self, sema: &Semantics<'_, RootDatabase>) -> FxHashSet<ScopeDef> {
358 let _p = tracing::info_span!("ImportAssets::scope_definitions").entered();
359 let mut scope_definitions = FxHashSet::default();
360 if let Some(scope) = sema.scope(&self.candidate_node) {
361 scope.process_all_names(&mut |_, scope_def| {
362 scope_definitions.insert(scope_def);
363 });
364 }
365 scope_definitions
366 }
367}
368
369fn path_applicable_imports(
370 db: &RootDatabase,
371 scope: &SemanticsScope<'_>,
372 current_crate: Crate,
373 path_candidate: &PathImportCandidate,
374 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
375 scope_filter: impl Fn(ItemInNs) -> bool + Copy,
376) -> FxIndexSet<LocatedImport> {
377 let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered();
378
379 match &*path_candidate.qualifier {
380 [] => {
381 items_locator::items_with_name(
382 db,
383 current_crate,
384 path_candidate.name.clone(),
385 AssocSearchMode::Exclude,
394 )
395 .filter_map(|(item, do_not_complete)| {
396 if !scope_filter(item) {
397 return None;
398 }
399 let mod_path = mod_path(item)?;
400 Some(LocatedImport::new(
401 mod_path,
402 item,
403 item,
404 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
405 ))
406 })
407 .take(DEFAULT_QUERY_SEARCH_LIMIT)
408 .collect()
409 }
410 [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
415 db,
416 current_crate,
417 NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
418 AssocSearchMode::Exclude,
419 )
420 .flat_map(|(item, do_not_complete)| {
421 validate_resolvable(
424 db,
425 scope,
426 mod_path,
427 scope_filter,
428 &path_candidate.name,
429 item,
430 qualifier_rest,
431 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
432 )
433 })
434 .take(DEFAULT_QUERY_SEARCH_LIMIT)
435 .collect(),
436 }
437}
438
439fn validate_resolvable(
442 db: &RootDatabase,
443 scope: &SemanticsScope<'_>,
444 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
445 scope_filter: impl Fn(ItemInNs) -> bool,
446 candidate: &NameToImport,
447 resolved_qualifier: ItemInNs,
448 unresolved_qualifier: &[Name],
449 complete_in_flyimport: CompleteInFlyimport,
450) -> SmallVec<[LocatedImport; 1]> {
451 let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
452
453 let qualifier = (|| {
454 let mut adjusted_resolved_qualifier = resolved_qualifier;
455 if !unresolved_qualifier.is_empty() {
456 match resolved_qualifier {
457 ItemInNs::Types(ModuleDef::Module(module)) => {
458 adjusted_resolved_qualifier = module
459 .resolve_mod_path(db, unresolved_qualifier.iter().cloned())?
460 .next()?;
461 }
462 _ => return None,
464 }
465 }
466
467 match adjusted_resolved_qualifier {
468 ItemInNs::Types(def) => Some(def),
469 _ => None,
470 }
471 })();
472 let Some(qualifier) = qualifier else { return SmallVec::new() };
473 let Some(import_path_candidate) = mod_path(resolved_qualifier) else { return SmallVec::new() };
474 let mut result = SmallVec::new();
475 let ty = match qualifier {
476 ModuleDef::Module(module) => {
477 items_locator::items_with_name_in_module::<Infallible>(
478 db,
479 module,
480 candidate.clone(),
481 AssocSearchMode::Exclude,
482 |item| {
483 if scope_filter(item) {
484 result.push(LocatedImport::new(
485 import_path_candidate.clone(),
486 resolved_qualifier,
487 item,
488 complete_in_flyimport,
489 ));
490 }
491 ControlFlow::Continue(())
492 },
493 );
494 return result;
495 }
496 ModuleDef::Trait(_) => return SmallVec::new(),
498 ModuleDef::TypeAlias(alias) => alias.ty(db),
499 ModuleDef::BuiltinType(builtin) => builtin.ty(db),
500 ModuleDef::Adt(adt) => adt.ty(db),
501 _ => return SmallVec::new(),
502 };
503 ty.iterate_path_candidates::<Infallible>(db, scope, &FxHashSet::default(), None, |assoc| {
504 if assoc.container_or_implemented_trait(db).is_some() {
506 return None;
507 }
508 let name = assoc.name(db)?;
509 let is_match = match candidate {
510 NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
511 NameToImport::Prefix(text, false) => {
512 name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
513 name_char.eq_ignore_ascii_case(&candidate_char)
514 })
515 }
516 NameToImport::Exact(text, true) => name.as_str() == text,
517 NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
518 NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
519 NameToImport::Fuzzy(text, false) => text
520 .chars()
521 .all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
522 };
523 if !is_match {
524 return None;
525 }
526 result.push(LocatedImport::new(
527 import_path_candidate.clone(),
528 resolved_qualifier,
529 assoc_to_item(assoc),
530 complete_in_flyimport,
531 ));
532 None
533 });
534 result
535}
536
537pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
538 Some(match item {
539 ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
540 Some(assoc_item) => item_for_path_search_assoc(db, assoc_item)?,
541 None => item,
542 },
543 ItemInNs::Macros(_) => item,
544 })
545}
546
547fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Option<ItemInNs> {
548 Some(match assoc_item.container(db) {
549 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
550 AssocItemContainer::Impl(impl_) => {
551 ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
552 }
553 })
554}
555
556fn trait_applicable_items<'db>(
557 db: &'db RootDatabase,
558 current_crate: Crate,
559 scope: &SemanticsScope<'db>,
560 trait_candidate: &TraitImportCandidate<'db>,
561 trait_assoc_item: bool,
562 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
563 scope_filter: impl Fn(hir::Trait) -> bool,
564) -> FxIndexSet<LocatedImport> {
565 let _p = tracing::info_span!("ImportAssets::trait_applicable_items").entered();
566
567 let inherent_traits = trait_candidate.receiver_ty.applicable_inherent_traits(db);
568 let env_traits = trait_candidate.receiver_ty.env_traits(db);
569 let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>();
570
571 let mut required_assoc_items = FxHashMap::default();
572 let mut trait_candidates: FxHashSet<_> = items_locator::items_with_name(
573 db,
574 current_crate,
575 trait_candidate.assoc_item_name.clone(),
576 AssocSearchMode::AssocItemsOnly,
577 )
578 .filter_map(|(input, do_not_complete)| Some((item_as_assoc(db, input)?, do_not_complete)))
579 .filter_map(|(assoc, do_not_complete)| {
580 if !trait_assoc_item && matches!(assoc, AssocItem::Const(_) | AssocItem::TypeAlias(_)) {
581 return None;
582 }
583
584 let assoc_item_trait = assoc.container_trait(db)?;
585 if related_traits.contains(&assoc_item_trait) {
586 return None;
587 }
588 required_assoc_items
589 .insert(assoc, CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport));
590 Some(assoc_item_trait.into())
591 })
592 .collect();
593
594 let autoderef_method_receiver = {
595 let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::<Vec<_>>();
596 if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) {
598 let slice = Type::new_slice(ty);
599 deref_chain.push(slice);
600 }
601 deref_chain
602 .into_iter()
603 .filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?)))
604 .unique()
605 .collect::<Vec<_>>()
606 };
607
608 if autoderef_method_receiver.is_empty() {
610 return Default::default();
611 }
612
613 if !autoderef_method_receiver
617 .iter()
618 .any(|(_, fingerprint)| matches!(fingerprint, hir::SimplifiedType::Placeholder))
619 {
620 trait_candidates.retain(|&candidate_trait_id| {
621 let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
629
630 let trait_impls_in_crate =
631 hir::TraitImpls::for_crate(db, defining_crate_for_trait.into());
632 let definitions_exist_in_trait_crate =
633 autoderef_method_receiver.iter().any(|(_, fingerprint)| {
634 trait_impls_in_crate
635 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
636 });
637 let definitions_exist_in_receiver_crate = || {
640 autoderef_method_receiver.iter().any(|(krate, fingerprint)| {
641 hir::TraitImpls::for_crate(db, *krate)
642 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
643 })
644 };
645
646 definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
647 });
648 }
649
650 let mut located_imports = FxIndexSet::default();
651 let mut trait_import_paths = FxHashMap::default();
652
653 if trait_assoc_item {
654 trait_candidate.receiver_ty.iterate_path_candidates(
655 db,
656 scope,
657 &trait_candidates,
658 None,
659 |assoc| {
660 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
661 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
662 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
663 let import_path = trait_import_paths
664 .entry(trait_item)
665 .or_insert_with(|| mod_path(trait_item))
666 .clone()?;
667 located_imports.insert(LocatedImport::new(
668 import_path,
669 trait_item,
670 assoc_to_item(assoc),
671 complete_in_flyimport,
672 ));
673 }
674 None::<()>
675 },
676 )
677 } else {
678 trait_candidate.receiver_ty.iterate_method_candidates_with_traits(
679 db,
680 scope,
681 &trait_candidates,
682 None,
683 |function| {
684 let assoc = function.as_assoc_item(db)?;
685 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
686 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
687 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
688 let import_path = trait_import_paths
689 .entry(trait_item)
690 .or_insert_with(|| mod_path(trait_item))
691 .clone()?;
692 located_imports.insert(LocatedImport::new(
693 import_path,
694 trait_item,
695 assoc_to_item(assoc),
696 complete_in_flyimport,
697 ));
698 }
699 None::<()>
700 },
701 )
702 };
703
704 located_imports
705}
706
707fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
708 match assoc {
709 AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
710 AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
711 AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
712 }
713}
714
715#[tracing::instrument(skip_all)]
716fn get_mod_path(
717 db: &RootDatabase,
718 item_to_search: ItemInNs,
719 module_with_candidate: &Module,
720 prefixed: Option<PrefixKind>,
721 cfg: FindPathConfig,
722) -> Option<ModPath> {
723 if let Some(prefix_kind) = prefixed {
724 module_with_candidate.find_use_path(db, item_to_search, prefix_kind, cfg)
725 } else {
726 module_with_candidate.find_path(db, item_to_search, cfg)
727 }
728}
729
730impl<'db> ImportCandidate<'db> {
731 fn for_method_call(
732 sema: &Semantics<'db, RootDatabase>,
733 method_call: &ast::MethodCallExpr,
734 ) -> Option<Self> {
735 match sema.resolve_method_call(method_call) {
736 Some(_) => None,
737 None => Some(Self::TraitMethod(TraitImportCandidate {
738 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?.adjusted(),
739 assoc_item_name: NameToImport::exact_case_sensitive(
740 method_call.name_ref()?.to_string(),
741 ),
742 })),
743 }
744 }
745
746 fn for_regular_path(sema: &Semantics<'db, RootDatabase>, path: &ast::Path) -> Option<Self> {
747 if sema.resolve_path(path).is_some() {
748 return None;
749 }
750 path_import_candidate(
751 sema,
752 path.qualifier(),
753 NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()),
754 )
755 }
756
757 fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option<Self> {
758 if sema
759 .scope(name.syntax())?
760 .speculative_resolve(&make::ext::ident_path(&name.text()))
761 .is_some()
762 {
763 return None;
764 }
765 Some(ImportCandidate::Path(PathImportCandidate {
766 qualifier: vec![],
767 name: NameToImport::exact_case_sensitive(name.to_string()),
768 }))
769 }
770
771 fn for_fuzzy_path(
772 qualifier: Option<ast::Path>,
773 fuzzy_name: String,
774 sema: &Semantics<'db, RootDatabase>,
775 ) -> Option<Self> {
776 path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name))
777 }
778}
779
780fn path_import_candidate<'db>(
781 sema: &Semantics<'db, RootDatabase>,
782 qualifier: Option<ast::Path>,
783 name: NameToImport,
784) -> Option<ImportCandidate<'db>> {
785 Some(match qualifier {
786 Some(qualifier) => match sema.resolve_path(&qualifier) {
787 Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => {
788 if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
789 let qualifier = qualifier
790 .segments()
791 .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
792 .collect::<Option<Vec<_>>>()?;
793 ImportCandidate::Path(PathImportCandidate { qualifier, name })
794 } else {
795 return None;
796 }
797 }
798 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
799 ImportCandidate::TraitAssocItem(TraitImportCandidate {
800 receiver_ty: assoc_item_path.ty(sema.db),
801 assoc_item_name: name,
802 })
803 }
804 Some(PathResolution::Def(ModuleDef::TypeAlias(alias))) => {
805 let ty = alias.ty(sema.db);
806 if ty.as_adt().is_some() {
807 ImportCandidate::TraitAssocItem(TraitImportCandidate {
808 receiver_ty: ty,
809 assoc_item_name: name,
810 })
811 } else {
812 return None;
813 }
814 }
815 Some(_) => return None,
816 },
817 None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name }),
818 })
819}
820
821fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
822 item.into_module_def().as_assoc_item(db)
823}