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 parser::SyntaxKind;
12use rustc_hash::{FxHashMap, FxHashSet};
13use smallvec::{SmallVec, smallvec};
14use stdx::never;
15use syntax::{
16 AstNode, SyntaxNode,
17 ast::{self, HasName, make},
18};
19
20use crate::{
21 FxIndexSet, RootDatabase,
22 items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT},
23};
24
25#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
26pub struct ImportPathConfig {
27 pub prefer_no_std: bool,
30 pub prefer_prelude: bool,
32 pub prefer_absolute: bool,
34}
35
36#[derive(Debug)]
42pub enum ImportCandidate<'db> {
43 Path(PathImportCandidate),
45 TraitAssocItem(TraitImportCandidate<'db>),
49 TraitMethod(TraitImportCandidate<'db>),
53}
54
55#[derive(Debug)]
59pub struct TraitImportCandidate<'db> {
60 pub receiver_ty: Type<'db>,
62 pub assoc_item_name: NameToImport,
64}
65
66#[derive(Debug)]
67struct PathDefinitionKinds {
68 modules: bool,
69 bang_macros: bool,
70 attr_macros: bool,
72 value_namespace: bool,
73 type_namespace: bool,
74 records: bool,
77 tuple_structs: bool,
80 structs_and_consts: bool,
82}
83
84impl PathDefinitionKinds {
85 const ALL_DISABLED: Self = Self {
86 modules: false,
87 bang_macros: false,
88 attr_macros: false,
89 value_namespace: false,
90 type_namespace: false,
91 records: false,
92 tuple_structs: false,
93 structs_and_consts: false,
94 };
95 const ALL_ENABLED: Self = Self {
96 modules: true,
97 bang_macros: true,
98 attr_macros: true,
99 value_namespace: true,
100 type_namespace: true,
101 records: true,
102 tuple_structs: true,
103 structs_and_consts: true,
104 };
105 const PATH_PAT_KINDS: PathDefinitionKinds =
107 Self { structs_and_consts: true, bang_macros: true, ..Self::ALL_DISABLED };
108
109 fn deduce_from_path(path: &ast::Path, exact: bool) -> Self {
110 let Some(parent) = path.syntax().parent() else {
111 return Self::ALL_ENABLED;
112 };
113 let mut result = match parent.kind() {
114 SyntaxKind::PATH => Self { modules: true, type_namespace: true, ..Self::ALL_DISABLED },
119 SyntaxKind::MACRO_CALL => Self { bang_macros: true, ..Self::ALL_DISABLED },
120 SyntaxKind::PATH_META | SyntaxKind::KEY_VALUE_META | SyntaxKind::TOKEN_TREE_META => {
121 Self { attr_macros: true, ..Self::ALL_DISABLED }
122 }
123 SyntaxKind::USE_TREE => {
124 if ast::UseTree::cast(parent).unwrap().use_tree_list().is_some() {
125 Self { modules: true, ..Self::ALL_DISABLED }
126 } else {
127 Self::ALL_ENABLED
128 }
129 }
130 SyntaxKind::VISIBILITY => Self { modules: true, ..Self::ALL_DISABLED },
131 SyntaxKind::ASM_SYM => Self { value_namespace: true, ..Self::ALL_DISABLED },
132 SyntaxKind::PATH_EXPR => Self {
135 value_namespace: true,
136 bang_macros: true,
137 type_namespace: true,
138 ..Self::ALL_DISABLED
139 },
140 SyntaxKind::PATH_PAT => Self::PATH_PAT_KINDS,
141 SyntaxKind::TUPLE_STRUCT_PAT => {
142 Self { tuple_structs: true, bang_macros: true, ..Self::ALL_DISABLED }
143 }
144 SyntaxKind::RECORD_EXPR | SyntaxKind::RECORD_PAT => {
145 Self { records: true, bang_macros: true, ..Self::ALL_DISABLED }
146 }
147 SyntaxKind::PATH_TYPE => {
148 Self { type_namespace: true, bang_macros: true, ..Self::ALL_DISABLED }
149 }
150 SyntaxKind::ERROR => Self::ALL_ENABLED,
151 _ => {
152 never!("this match should cover all possible parents of paths\nparent={parent:#?}");
153 Self::ALL_ENABLED
154 }
155 };
156 if !exact {
157 result.modules = true;
159 result.type_namespace = true;
160 }
161 result
162 }
163}
164
165#[derive(Debug)]
167pub struct PathImportCandidate {
168 pub qualifier: Vec<Name>,
170 pub name: NameToImport,
172 pub after: Vec<Name>,
174 definition_kinds: PathDefinitionKinds,
176}
177
178#[derive(Debug, Clone)]
180pub enum NameToImport {
181 Exact(String, bool),
183 Prefix(String, bool),
185 Fuzzy(String, bool),
188}
189
190impl NameToImport {
191 pub fn exact_case_sensitive(s: String) -> NameToImport {
192 let s = match s.strip_prefix("r#") {
193 Some(s) => s.to_owned(),
194 None => s,
195 };
196 NameToImport::Exact(s, true)
197 }
198
199 pub fn fuzzy(s: String) -> NameToImport {
200 let s = match s.strip_prefix("r#") {
201 Some(s) => s.to_owned(),
202 None => s,
203 };
204 let case_sensitive = s.chars().any(|c| c.is_uppercase());
206 NameToImport::Fuzzy(s, case_sensitive)
207 }
208
209 pub fn text(&self) -> &str {
210 match self {
211 NameToImport::Prefix(text, _)
212 | NameToImport::Exact(text, _)
213 | NameToImport::Fuzzy(text, _) => text.as_str(),
214 }
215 }
216}
217
218#[derive(Debug)]
220pub struct ImportAssets<'db> {
221 import_candidate: ImportCandidate<'db>,
222 candidate_node: SyntaxNode,
223 module_with_candidate: Module,
224}
225
226impl<'db> ImportAssets<'db> {
227 pub fn for_method_call(
228 method_call: &ast::MethodCallExpr,
229 sema: &Semantics<'db, RootDatabase>,
230 ) -> Option<Self> {
231 let candidate_node = method_call.syntax().clone();
232 Some(Self {
233 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
234 module_with_candidate: sema.scope(&candidate_node)?.module(),
235 candidate_node,
236 })
237 }
238
239 pub fn for_exact_path(
240 fully_qualified_path: &ast::Path,
241 sema: &Semantics<'db, RootDatabase>,
242 ) -> Option<Self> {
243 let candidate_node = fully_qualified_path.syntax().clone();
244 if let Some(use_tree) = candidate_node.ancestors().find_map(ast::UseTree::cast) {
245 if use_tree.syntax().parent().and_then(ast::Use::cast).is_none()
247 || fully_qualified_path.qualifier().is_some()
248 {
249 return None;
250 }
251 }
252 Some(Self {
253 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
254 module_with_candidate: sema.scope(&candidate_node)?.module(),
255 candidate_node,
256 })
257 }
258
259 pub fn for_ident_pat(sema: &Semantics<'db, RootDatabase>, pat: &ast::IdentPat) -> Option<Self> {
260 if !pat.is_simple_ident() {
261 return None;
262 }
263 let name = pat.name()?;
264 let candidate_node = pat.syntax().clone();
265 Some(Self {
266 import_candidate: ImportCandidate::for_name(sema, &name)?,
267 module_with_candidate: sema.scope(&candidate_node)?.module(),
268 candidate_node,
269 })
270 }
271
272 pub fn for_fuzzy_path(
273 module_with_candidate: Module,
274 path: Option<&ast::Path>,
275 qualifier: Option<ast::Path>,
276 fuzzy_name: String,
277 sema: &Semantics<'db, RootDatabase>,
278 candidate_node: SyntaxNode,
279 ) -> Option<Self> {
280 Some(Self {
281 import_candidate: ImportCandidate::for_fuzzy_path(path, qualifier, fuzzy_name, sema)?,
282 module_with_candidate,
283 candidate_node,
284 })
285 }
286
287 pub fn for_fuzzy_method_call(
288 module_with_method_call: Module,
289 receiver_ty: Type<'db>,
290 fuzzy_method_name: String,
291 candidate_node: SyntaxNode,
292 ) -> Option<Self> {
293 Some(Self {
294 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
295 receiver_ty,
296 assoc_item_name: NameToImport::fuzzy(fuzzy_method_name),
297 }),
298 module_with_candidate: module_with_method_call,
299 candidate_node,
300 })
301 }
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
305pub struct CompleteInFlyimport(pub bool);
306
307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
310pub struct LocatedImport {
311 pub import_path: ModPath,
313 pub item_to_import: ItemInNs,
315 pub original_item: ItemInNs,
322 pub complete_in_flyimport: CompleteInFlyimport,
324}
325
326impl LocatedImport {
327 pub fn new(
328 import_path: ModPath,
329 item_to_import: ItemInNs,
330 original_item: ItemInNs,
331 complete_in_flyimport: CompleteInFlyimport,
332 ) -> Self {
333 Self { import_path, item_to_import, original_item, complete_in_flyimport }
334 }
335
336 pub fn new_no_completion(
337 import_path: ModPath,
338 item_to_import: ItemInNs,
339 original_item: ItemInNs,
340 ) -> Self {
341 Self {
342 import_path,
343 item_to_import,
344 original_item,
345 complete_in_flyimport: CompleteInFlyimport(true),
346 }
347 }
348}
349
350impl<'db> ImportAssets<'db> {
351 pub fn import_candidate(&self) -> &ImportCandidate<'db> {
352 &self.import_candidate
353 }
354
355 pub fn search_for_imports(
356 &self,
357 sema: &Semantics<'db, RootDatabase>,
358 cfg: ImportPathConfig,
359 prefix_kind: PrefixKind,
360 ) -> impl Iterator<Item = LocatedImport> {
361 let _p = tracing::info_span!("ImportAssets::search_for_imports").entered();
362 self.search_for(sema, Some(prefix_kind), cfg)
363 }
364
365 pub fn search_for_relative_paths(
367 &self,
368 sema: &Semantics<'db, RootDatabase>,
369 cfg: ImportPathConfig,
370 ) -> impl Iterator<Item = LocatedImport> {
371 let _p = tracing::info_span!("ImportAssets::search_for_relative_paths").entered();
372 self.search_for(sema, None, cfg)
373 }
374
375 pub fn path_fuzzy_name_to_prefix(&mut self) {
377 if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
378 &mut self.import_candidate
379 {
380 let (name, case_sensitive) = match to_import {
381 NameToImport::Fuzzy(name, case_sensitive) => {
382 (std::mem::take(name), *case_sensitive)
383 }
384 _ => return,
385 };
386 *to_import = NameToImport::Prefix(name, case_sensitive);
387 }
388 }
389
390 pub fn path_fuzzy_name_to_exact(&mut self) {
392 if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
393 &mut self.import_candidate
394 {
395 let (name, case_sensitive) = match to_import {
396 NameToImport::Fuzzy(name, case_sensitive) => {
397 (std::mem::take(name), *case_sensitive)
398 }
399 _ => return,
400 };
401 *to_import = NameToImport::Exact(name, case_sensitive);
402 }
403 }
404
405 fn search_for(
406 &self,
407 sema: &Semantics<'db, RootDatabase>,
408 prefixed: Option<PrefixKind>,
409 cfg: ImportPathConfig,
410 ) -> impl Iterator<Item = LocatedImport> {
411 let _p = tracing::info_span!("ImportAssets::search_for").entered();
412
413 let scope = match sema.scope(&self.candidate_node) {
414 Some(it) => it,
415 None => return <FxIndexSet<_>>::default().into_iter(),
416 };
417 let cfg = FindPathConfig {
418 prefer_no_std: cfg.prefer_no_std,
419 prefer_prelude: cfg.prefer_prelude,
420 prefer_absolute: cfg.prefer_absolute,
421 allow_unstable: sema.is_nightly(scope.krate()),
422 };
423 let db = sema.db;
424 let krate = self.module_with_candidate.krate(sema.db);
425 let scope_definitions = self.scope_definitions(sema);
426 let mod_path = |item| {
427 get_mod_path(
428 db,
429 item_for_path_search(db, item)?,
430 &self.module_with_candidate,
431 prefixed,
432 cfg,
433 )
434 .filter(|path| path.len() > 1)
435 };
436
437 match &self.import_candidate {
438 ImportCandidate::Path(path_candidate) => path_applicable_imports(
439 db,
440 &scope,
441 krate,
442 path_candidate,
443 mod_path,
444 |item_to_import| !scope_definitions.contains(&ScopeDef::from(item_to_import)),
445 ),
446 ImportCandidate::TraitAssocItem(trait_candidate)
447 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
448 db,
449 krate,
450 &scope,
451 trait_candidate,
452 matches!(self.import_candidate, ImportCandidate::TraitAssocItem(_)),
453 mod_path,
454 |trait_to_import| {
455 !scope_definitions
456 .contains(&ScopeDef::ModuleDef(ModuleDef::Trait(trait_to_import)))
457 && !scope.can_use_trait_methods(trait_to_import)
458 },
459 ),
460 }
461 .into_iter()
462 }
463
464 fn scope_definitions(&self, sema: &Semantics<'_, RootDatabase>) -> FxHashSet<ScopeDef> {
465 let _p = tracing::info_span!("ImportAssets::scope_definitions").entered();
466 let mut scope_definitions = FxHashSet::default();
467 if let Some(scope) = sema.scope(&self.candidate_node) {
468 scope.process_all_names(&mut |_, scope_def| {
469 scope_definitions.insert(scope_def);
470 });
471 }
472 scope_definitions
473 }
474}
475
476fn path_applicable_imports(
477 db: &RootDatabase,
478 scope: &SemanticsScope<'_>,
479 current_crate: Crate,
480 path_candidate: &PathImportCandidate,
481 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
482 scope_filter: impl Fn(ItemInNs) -> bool + Copy,
483) -> FxIndexSet<LocatedImport> {
484 let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered();
485
486 let mut result = match &*path_candidate.qualifier {
487 [] => {
488 items_locator::items_with_name(
489 db,
490 current_crate,
491 path_candidate.name.clone(),
492 AssocSearchMode::Exclude,
501 )
502 .filter(|(item, _)| {
503 filter_by_definition_kind(db, *item, &path_candidate.definition_kinds)
504 })
505 .filter_map(|(item, do_not_complete)| {
506 if !scope_filter(item) {
507 return None;
508 }
509 let mod_path = mod_path(item)?;
510 Some(LocatedImport::new(
511 mod_path,
512 item,
513 item,
514 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
515 ))
516 })
517 .take(DEFAULT_QUERY_SEARCH_LIMIT)
518 .collect()
519 }
520 [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
525 db,
526 current_crate,
527 NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
528 AssocSearchMode::Exclude,
529 )
530 .flat_map(|(item, do_not_complete)| {
531 validate_resolvable(
534 db,
535 scope,
536 mod_path,
537 scope_filter,
538 &path_candidate.name,
539 item,
540 qualifier_rest,
541 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
542 )
543 })
544 .take(DEFAULT_QUERY_SEARCH_LIMIT)
545 .collect(),
546 };
547
548 filter_candidates_by_after_path(db, scope, path_candidate, &mut result);
549
550 result
551}
552
553fn filter_by_definition_kind(
554 db: &RootDatabase,
555 item: ItemInNs,
556 allowed: &PathDefinitionKinds,
557) -> bool {
558 let item = item.into_module_def();
559 let struct_per_kind = |struct_kind| {
560 allowed.structs_and_consts
561 || match struct_kind {
562 hir::StructKind::Record => allowed.records,
563 hir::StructKind::Tuple => allowed.value_namespace || allowed.tuple_structs,
564 hir::StructKind::Unit => allowed.value_namespace,
565 }
566 };
567 match item {
568 ModuleDef::Module(_) => allowed.modules,
569 ModuleDef::Function(_) => allowed.value_namespace,
570 ModuleDef::Adt(hir::Adt::Struct(item)) => {
571 allowed.type_namespace || struct_per_kind(item.kind(db))
572 }
573 ModuleDef::Adt(hir::Adt::Enum(_)) => allowed.type_namespace,
574 ModuleDef::Adt(hir::Adt::Union(_)) => {
575 allowed.type_namespace || allowed.records || allowed.structs_and_consts
576 }
577 ModuleDef::EnumVariant(item) => struct_per_kind(item.kind(db)),
578 ModuleDef::Const(_) => allowed.value_namespace || allowed.structs_and_consts,
579 ModuleDef::Static(_) => allowed.value_namespace,
580 ModuleDef::Trait(_) => allowed.type_namespace,
581 ModuleDef::TypeAlias(_) => allowed.type_namespace,
582 ModuleDef::BuiltinType(_) => allowed.type_namespace,
583 ModuleDef::Macro(item) => {
584 if item.is_fn_like(db) {
585 allowed.bang_macros
586 } else {
587 allowed.attr_macros
588 }
589 }
590 }
591}
592
593fn filter_candidates_by_after_path(
594 db: &RootDatabase,
595 scope: &SemanticsScope<'_>,
596 path_candidate: &PathImportCandidate,
597 imports: &mut FxIndexSet<LocatedImport>,
598) {
599 if imports.len() <= 1 {
600 return;
602 }
603
604 let Some((last_after, after_except_last)) = path_candidate.after.split_last() else {
605 return;
606 };
607
608 let original_imports = imports.clone();
609
610 let traits_in_scope = scope.visible_traits();
611 imports.retain(|import| {
612 let items = if after_except_last.is_empty() {
613 smallvec![import.original_item]
614 } else {
615 let ItemInNs::Types(ModuleDef::Module(item)) = import.original_item else {
616 return false;
617 };
618 item.resolve_mod_path(db, after_except_last.iter().cloned())
620 .into_iter()
621 .flatten()
622 .collect::<SmallVec<[_; 3]>>()
623 };
624 items.into_iter().any(|item| {
625 let has_last_method = |ty: hir::Type<'_>| {
626 ty.iterate_path_candidates(db, scope, &traits_in_scope, Some(last_after), |_| {
627 Some(())
628 })
629 .is_some()
630 };
631 match item {
633 ItemInNs::Types(ModuleDef::Module(module)) => module
635 .resolve_mod_path(db, [last_after.clone()])
636 .is_some_and(|mut it| it.any(|_| true)),
637 ItemInNs::Types(ModuleDef::Adt(it)) => has_last_method(it.ty(db)),
639 ItemInNs::Types(ModuleDef::BuiltinType(it)) => has_last_method(it.ty(db)),
640 ItemInNs::Types(ModuleDef::TypeAlias(it)) => has_last_method(it.ty(db)),
641 ItemInNs::Types(ModuleDef::Trait(it)) => it
643 .items(db)
644 .into_iter()
645 .any(|assoc_item| assoc_item.name(db) == Some(last_after.clone())),
646 _ => false,
648 }
649 })
650 });
651
652 if imports.is_empty() {
653 *imports = original_imports;
655 }
656}
657
658fn validate_resolvable(
661 db: &RootDatabase,
662 scope: &SemanticsScope<'_>,
663 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
664 scope_filter: impl Fn(ItemInNs) -> bool,
665 candidate: &NameToImport,
666 resolved_qualifier: ItemInNs,
667 unresolved_qualifier: &[Name],
668 complete_in_flyimport: CompleteInFlyimport,
669) -> SmallVec<[LocatedImport; 1]> {
670 let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
671
672 let qualifier = (|| {
673 let mut adjusted_resolved_qualifier = resolved_qualifier;
674 if !unresolved_qualifier.is_empty() {
675 match resolved_qualifier {
676 ItemInNs::Types(ModuleDef::Module(module)) => {
677 adjusted_resolved_qualifier = module
678 .resolve_mod_path(db, unresolved_qualifier.iter().cloned())?
679 .next()?;
680 }
681 _ => return None,
683 }
684 }
685
686 match adjusted_resolved_qualifier {
687 ItemInNs::Types(def) => Some(def),
688 _ => None,
689 }
690 })();
691 let Some(qualifier) = qualifier else { return SmallVec::new() };
692 let Some(import_path_candidate) = mod_path(resolved_qualifier) else { return SmallVec::new() };
693 let mut result = SmallVec::new();
694 let ty = match qualifier {
695 ModuleDef::Module(module) => {
696 items_locator::items_with_name_in_module::<Infallible>(
697 db,
698 module,
699 candidate.clone(),
700 AssocSearchMode::Exclude,
701 |item| {
702 if scope_filter(item) {
703 result.push(LocatedImport::new(
704 import_path_candidate.clone(),
705 resolved_qualifier,
706 item,
707 complete_in_flyimport,
708 ));
709 }
710 ControlFlow::Continue(())
711 },
712 );
713 return result;
714 }
715 ModuleDef::Trait(_) => return SmallVec::new(),
717 ModuleDef::TypeAlias(alias) => alias.ty(db),
718 ModuleDef::BuiltinType(builtin) => builtin.ty(db),
719 ModuleDef::Adt(adt) => adt.ty(db),
720 _ => return SmallVec::new(),
721 };
722 ty.iterate_path_candidates::<Infallible>(db, scope, &FxHashSet::default(), None, |assoc| {
723 if assoc.container_or_implemented_trait(db).is_some() {
725 return None;
726 }
727 let name = assoc.name(db)?;
728 let is_match = match candidate {
729 NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
730 NameToImport::Prefix(text, false) => {
731 name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
732 name_char.eq_ignore_ascii_case(&candidate_char)
733 })
734 }
735 NameToImport::Exact(text, true) => name.as_str() == text,
736 NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
737 NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
738 NameToImport::Fuzzy(text, false) => text
739 .chars()
740 .all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
741 };
742 if !is_match {
743 return None;
744 }
745 result.push(LocatedImport::new(
746 import_path_candidate.clone(),
747 resolved_qualifier,
748 assoc_to_item(assoc),
749 complete_in_flyimport,
750 ));
751 None
752 });
753 result
754}
755
756pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
757 Some(match item {
758 ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
759 Some(assoc_item) => item_for_path_search_assoc(db, assoc_item)?,
760 None => item,
761 },
762 ItemInNs::Macros(_) => item,
763 })
764}
765
766fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Option<ItemInNs> {
767 Some(match assoc_item.container(db) {
768 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
769 AssocItemContainer::Impl(impl_) => {
770 ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
771 }
772 })
773}
774
775fn trait_applicable_items<'db>(
776 db: &'db RootDatabase,
777 current_crate: Crate,
778 scope: &SemanticsScope<'db>,
779 trait_candidate: &TraitImportCandidate<'db>,
780 trait_assoc_item: bool,
781 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
782 scope_filter: impl Fn(hir::Trait) -> bool,
783) -> FxIndexSet<LocatedImport> {
784 let _p = tracing::info_span!("ImportAssets::trait_applicable_items").entered();
785
786 let inherent_traits = trait_candidate.receiver_ty.applicable_inherent_traits(db);
787 let env_traits = trait_candidate.receiver_ty.env_traits(db);
788 let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>();
789
790 let mut required_assoc_items = FxHashMap::default();
791 let mut trait_candidates: FxHashSet<_> = items_locator::items_with_name(
792 db,
793 current_crate,
794 trait_candidate.assoc_item_name.clone(),
795 AssocSearchMode::AssocItemsOnly,
796 )
797 .filter_map(|(input, do_not_complete)| Some((item_as_assoc(db, input)?, do_not_complete)))
798 .filter_map(|(assoc, do_not_complete)| {
799 if !trait_assoc_item && matches!(assoc, AssocItem::Const(_) | AssocItem::TypeAlias(_)) {
800 return None;
801 }
802
803 let assoc_item_trait = assoc.container_trait(db)?;
804 if related_traits.contains(&assoc_item_trait) {
805 return None;
806 }
807 required_assoc_items
808 .insert(assoc, CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport));
809 Some(assoc_item_trait.into())
810 })
811 .collect();
812
813 let autoderef_method_receiver = {
814 let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::<Vec<_>>();
815 if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) {
817 let slice = Type::new_slice(ty);
818 deref_chain.push(slice);
819 }
820 deref_chain
821 .into_iter()
822 .flat_map(|ty| {
823 let fingerprint = ty.fingerprint_for_trait_impl()?;
824 let mut crates = vec![];
825
826 if let Some(adt) = ty.as_adt() {
827 crates.push((adt.krate(db).into(), fingerprint));
829 }
830 crates.push((ty.krate(db).into(), fingerprint));
832 Some(crates)
833 })
834 .flatten()
835 .unique()
836 .collect::<Vec<_>>()
837 };
838
839 if autoderef_method_receiver.is_empty() {
841 return Default::default();
842 }
843
844 if !autoderef_method_receiver
848 .iter()
849 .any(|(_, fingerprint)| matches!(fingerprint, hir::SimplifiedType::Placeholder))
850 {
851 trait_candidates.retain(|&candidate_trait_id| {
852 let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
860
861 let trait_impls_in_crate =
862 hir::TraitImpls::for_crate(db, defining_crate_for_trait.into());
863 let definitions_exist_in_trait_crate =
864 autoderef_method_receiver.iter().any(|(_, fingerprint)| {
865 trait_impls_in_crate
866 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
867 });
868 let definitions_exist_in_receiver_crate = || {
871 autoderef_method_receiver.iter().any(|(krate, fingerprint)| {
872 hir::TraitImpls::for_crate(db, *krate)
873 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
874 })
875 };
876
877 definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
878 });
879 }
880
881 let mut located_imports = FxIndexSet::default();
882 let mut trait_import_paths = FxHashMap::default();
883
884 if trait_assoc_item {
885 trait_candidate.receiver_ty.iterate_path_candidates(
886 db,
887 scope,
888 &trait_candidates,
889 None,
890 |assoc| {
891 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
892 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
893 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
894 let import_path = trait_import_paths
895 .entry(trait_item)
896 .or_insert_with(|| mod_path(trait_item))
897 .clone()?;
898 located_imports.insert(LocatedImport::new(
899 import_path,
900 trait_item,
901 assoc_to_item(assoc),
902 complete_in_flyimport,
903 ));
904 }
905 None::<()>
906 },
907 )
908 } else {
909 trait_candidate.receiver_ty.iterate_method_candidates_with_traits(
910 db,
911 scope,
912 &trait_candidates,
913 None,
914 |function| {
915 let assoc = function.as_assoc_item(db)?;
916 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
917 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
918 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
919 let import_path = trait_import_paths
920 .entry(trait_item)
921 .or_insert_with(|| mod_path(trait_item))
922 .clone()?;
923 located_imports.insert(LocatedImport::new(
924 import_path,
925 trait_item,
926 assoc_to_item(assoc),
927 complete_in_flyimport,
928 ));
929 }
930 None::<()>
931 },
932 )
933 };
934
935 located_imports
936}
937
938fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
939 match assoc {
940 AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
941 AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
942 AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
943 }
944}
945
946#[tracing::instrument(skip_all)]
947fn get_mod_path(
948 db: &RootDatabase,
949 item_to_search: ItemInNs,
950 module_with_candidate: &Module,
951 prefixed: Option<PrefixKind>,
952 cfg: FindPathConfig,
953) -> Option<ModPath> {
954 if let Some(prefix_kind) = prefixed {
955 module_with_candidate.find_use_path(db, item_to_search, prefix_kind, cfg)
956 } else {
957 module_with_candidate.find_path(db, item_to_search, cfg)
958 }
959}
960
961impl<'db> ImportCandidate<'db> {
962 fn for_method_call(
963 sema: &Semantics<'db, RootDatabase>,
964 method_call: &ast::MethodCallExpr,
965 ) -> Option<Self> {
966 match sema.resolve_method_call(method_call) {
967 Some(_) => None,
968 None => Some(Self::TraitMethod(TraitImportCandidate {
969 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?.adjusted(),
970 assoc_item_name: NameToImport::exact_case_sensitive(
971 method_call.name_ref()?.to_string(),
972 ),
973 })),
974 }
975 }
976
977 fn for_regular_path(sema: &Semantics<'db, RootDatabase>, path: &ast::Path) -> Option<Self> {
978 if sema.resolve_path(path).is_some() {
979 return None;
980 }
981 let after = std::iter::successors(path.parent_path(), |it| it.parent_path())
982 .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text())))
983 .collect::<Option<_>>()?;
984 path_import_candidate(
985 sema,
986 Some(path),
987 path.qualifier(),
988 NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()),
989 after,
990 )
991 }
992
993 fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option<Self> {
994 if sema
995 .scope(name.syntax())?
996 .speculative_resolve(&make::ext::ident_path(&name.text()))
997 .is_some()
998 {
999 return None;
1000 }
1001 Some(ImportCandidate::Path(PathImportCandidate {
1002 qualifier: vec![],
1003 name: NameToImport::exact_case_sensitive(name.to_string()),
1004 after: vec![],
1005 definition_kinds: PathDefinitionKinds::PATH_PAT_KINDS,
1006 }))
1007 }
1008
1009 fn for_fuzzy_path(
1010 path: Option<&ast::Path>,
1011 qualifier: Option<ast::Path>,
1012 fuzzy_name: String,
1013 sema: &Semantics<'db, RootDatabase>,
1014 ) -> Option<Self> {
1015 path_import_candidate(sema, path, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new())
1017 }
1018}
1019
1020fn path_import_candidate<'db>(
1021 sema: &Semantics<'db, RootDatabase>,
1022 path: Option<&ast::Path>,
1023 qualifier: Option<ast::Path>,
1024 name: NameToImport,
1025 after: Vec<Name>,
1026) -> Option<ImportCandidate<'db>> {
1027 let definition_kinds = path.map_or(PathDefinitionKinds::ALL_ENABLED, |path| {
1028 PathDefinitionKinds::deduce_from_path(path, matches!(name, NameToImport::Exact(..)))
1029 });
1030 Some(match qualifier {
1031 Some(qualifier) => match sema.resolve_path(&qualifier) {
1032 Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => {
1033 if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
1034 let qualifier = qualifier
1035 .segments()
1036 .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
1037 .collect::<Option<Vec<_>>>()?;
1038 ImportCandidate::Path(PathImportCandidate {
1039 qualifier,
1040 name,
1041 after,
1042 definition_kinds,
1043 })
1044 } else {
1045 return None;
1046 }
1047 }
1048 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
1049 ImportCandidate::TraitAssocItem(TraitImportCandidate {
1050 receiver_ty: assoc_item_path.ty(sema.db),
1051 assoc_item_name: name,
1052 })
1053 }
1054 Some(PathResolution::Def(ModuleDef::TypeAlias(alias))) => {
1055 let ty = alias.ty(sema.db);
1056 if ty.as_adt().is_some() {
1057 ImportCandidate::TraitAssocItem(TraitImportCandidate {
1058 receiver_ty: ty,
1059 assoc_item_name: name,
1060 })
1061 } else {
1062 return None;
1063 }
1064 }
1065 Some(_) => return None,
1066 },
1067 None => ImportCandidate::Path(PathImportCandidate {
1068 qualifier: vec![],
1069 name,
1070 after,
1071 definition_kinds,
1072 }),
1073 })
1074}
1075
1076fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
1077 item.into_module_def().as_assoc_item(db)
1078}