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