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 },
458 ),
459 }
460 .into_iter()
461 }
462
463 fn scope_definitions(&self, sema: &Semantics<'_, RootDatabase>) -> FxHashSet<ScopeDef> {
464 let _p = tracing::info_span!("ImportAssets::scope_definitions").entered();
465 let mut scope_definitions = FxHashSet::default();
466 if let Some(scope) = sema.scope(&self.candidate_node) {
467 scope.process_all_names(&mut |_, scope_def| {
468 scope_definitions.insert(scope_def);
469 });
470 }
471 scope_definitions
472 }
473}
474
475fn path_applicable_imports(
476 db: &RootDatabase,
477 scope: &SemanticsScope<'_>,
478 current_crate: Crate,
479 path_candidate: &PathImportCandidate,
480 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
481 scope_filter: impl Fn(ItemInNs) -> bool + Copy,
482) -> FxIndexSet<LocatedImport> {
483 let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered();
484
485 let mut result = match &*path_candidate.qualifier {
486 [] => {
487 items_locator::items_with_name(
488 db,
489 current_crate,
490 path_candidate.name.clone(),
491 AssocSearchMode::Exclude,
500 )
501 .filter(|(item, _)| {
502 filter_by_definition_kind(db, *item, &path_candidate.definition_kinds)
503 })
504 .filter_map(|(item, do_not_complete)| {
505 if !scope_filter(item) {
506 return None;
507 }
508 let mod_path = mod_path(item)?;
509 Some(LocatedImport::new(
510 mod_path,
511 item,
512 item,
513 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
514 ))
515 })
516 .take(DEFAULT_QUERY_SEARCH_LIMIT)
517 .collect()
518 }
519 [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
524 db,
525 current_crate,
526 NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
527 AssocSearchMode::Exclude,
528 )
529 .flat_map(|(item, do_not_complete)| {
530 validate_resolvable(
533 db,
534 scope,
535 mod_path,
536 scope_filter,
537 &path_candidate.name,
538 item,
539 qualifier_rest,
540 CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport),
541 )
542 })
543 .take(DEFAULT_QUERY_SEARCH_LIMIT)
544 .collect(),
545 };
546
547 filter_candidates_by_after_path(db, scope, path_candidate, &mut result);
548
549 result
550}
551
552fn filter_by_definition_kind(
553 db: &RootDatabase,
554 item: ItemInNs,
555 allowed: &PathDefinitionKinds,
556) -> bool {
557 let item = item.into_module_def();
558 let struct_per_kind = |struct_kind| {
559 allowed.structs_and_consts
560 || match struct_kind {
561 hir::StructKind::Record => allowed.records,
562 hir::StructKind::Tuple => allowed.value_namespace || allowed.tuple_structs,
563 hir::StructKind::Unit => allowed.value_namespace,
564 }
565 };
566 match item {
567 ModuleDef::Module(_) => allowed.modules,
568 ModuleDef::Function(_) => allowed.value_namespace,
569 ModuleDef::Adt(hir::Adt::Struct(item)) => {
570 allowed.type_namespace || struct_per_kind(item.kind(db))
571 }
572 ModuleDef::Adt(hir::Adt::Enum(_)) => allowed.type_namespace,
573 ModuleDef::Adt(hir::Adt::Union(_)) => {
574 allowed.type_namespace || allowed.records || allowed.structs_and_consts
575 }
576 ModuleDef::EnumVariant(item) => struct_per_kind(item.kind(db)),
577 ModuleDef::Const(_) => allowed.value_namespace || allowed.structs_and_consts,
578 ModuleDef::Static(_) => allowed.value_namespace,
579 ModuleDef::Trait(_) => allowed.type_namespace,
580 ModuleDef::TypeAlias(_) => allowed.type_namespace,
581 ModuleDef::BuiltinType(_) => allowed.type_namespace,
582 ModuleDef::Macro(item) => {
583 if item.is_fn_like(db) {
584 allowed.bang_macros
585 } else {
586 allowed.attr_macros
587 }
588 }
589 }
590}
591
592fn filter_candidates_by_after_path(
593 db: &RootDatabase,
594 scope: &SemanticsScope<'_>,
595 path_candidate: &PathImportCandidate,
596 imports: &mut FxIndexSet<LocatedImport>,
597) {
598 if imports.len() <= 1 {
599 return;
601 }
602
603 let Some((last_after, after_except_last)) = path_candidate.after.split_last() else {
604 return;
605 };
606
607 let original_imports = imports.clone();
608
609 let traits_in_scope = scope.visible_traits();
610 imports.retain(|import| {
611 let items = if after_except_last.is_empty() {
612 smallvec![import.original_item]
613 } else {
614 let ItemInNs::Types(ModuleDef::Module(item)) = import.original_item else {
615 return false;
616 };
617 item.resolve_mod_path(db, after_except_last.iter().cloned())
619 .into_iter()
620 .flatten()
621 .collect::<SmallVec<[_; 3]>>()
622 };
623 items.into_iter().any(|item| {
624 let has_last_method = |ty: hir::Type<'_>| {
625 ty.iterate_path_candidates(db, scope, &traits_in_scope, Some(last_after), |_| {
626 Some(())
627 })
628 .is_some()
629 };
630 match item {
632 ItemInNs::Types(ModuleDef::Module(module)) => module
634 .resolve_mod_path(db, [last_after.clone()])
635 .is_some_and(|mut it| it.any(|_| true)),
636 ItemInNs::Types(ModuleDef::Adt(it)) => has_last_method(it.ty(db)),
638 ItemInNs::Types(ModuleDef::BuiltinType(it)) => has_last_method(it.ty(db)),
639 ItemInNs::Types(ModuleDef::TypeAlias(it)) => has_last_method(it.ty(db)),
640 ItemInNs::Types(ModuleDef::Trait(it)) => it
642 .items(db)
643 .into_iter()
644 .any(|assoc_item| assoc_item.name(db) == Some(last_after.clone())),
645 _ => false,
647 }
648 })
649 });
650
651 if imports.is_empty() {
652 *imports = original_imports;
654 }
655}
656
657fn validate_resolvable(
660 db: &RootDatabase,
661 scope: &SemanticsScope<'_>,
662 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
663 scope_filter: impl Fn(ItemInNs) -> bool,
664 candidate: &NameToImport,
665 resolved_qualifier: ItemInNs,
666 unresolved_qualifier: &[Name],
667 complete_in_flyimport: CompleteInFlyimport,
668) -> SmallVec<[LocatedImport; 1]> {
669 let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
670
671 let qualifier = (|| {
672 let mut adjusted_resolved_qualifier = resolved_qualifier;
673 if !unresolved_qualifier.is_empty() {
674 match resolved_qualifier {
675 ItemInNs::Types(ModuleDef::Module(module)) => {
676 adjusted_resolved_qualifier = module
677 .resolve_mod_path(db, unresolved_qualifier.iter().cloned())?
678 .next()?;
679 }
680 _ => return None,
682 }
683 }
684
685 match adjusted_resolved_qualifier {
686 ItemInNs::Types(def) => Some(def),
687 _ => None,
688 }
689 })();
690 let Some(qualifier) = qualifier else { return SmallVec::new() };
691 let Some(import_path_candidate) = mod_path(resolved_qualifier) else { return SmallVec::new() };
692 let mut result = SmallVec::new();
693 let ty = match qualifier {
694 ModuleDef::Module(module) => {
695 items_locator::items_with_name_in_module::<Infallible>(
696 db,
697 module,
698 candidate.clone(),
699 AssocSearchMode::Exclude,
700 |item| {
701 if scope_filter(item) {
702 result.push(LocatedImport::new(
703 import_path_candidate.clone(),
704 resolved_qualifier,
705 item,
706 complete_in_flyimport,
707 ));
708 }
709 ControlFlow::Continue(())
710 },
711 );
712 return result;
713 }
714 ModuleDef::Trait(_) => return SmallVec::new(),
716 ModuleDef::TypeAlias(alias) => alias.ty(db),
717 ModuleDef::BuiltinType(builtin) => builtin.ty(db),
718 ModuleDef::Adt(adt) => adt.ty(db),
719 _ => return SmallVec::new(),
720 };
721 ty.iterate_path_candidates::<Infallible>(db, scope, &FxHashSet::default(), None, |assoc| {
722 if assoc.container_or_implemented_trait(db).is_some() {
724 return None;
725 }
726 let name = assoc.name(db)?;
727 let is_match = match candidate {
728 NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
729 NameToImport::Prefix(text, false) => {
730 name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
731 name_char.eq_ignore_ascii_case(&candidate_char)
732 })
733 }
734 NameToImport::Exact(text, true) => name.as_str() == text,
735 NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
736 NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
737 NameToImport::Fuzzy(text, false) => text
738 .chars()
739 .all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
740 };
741 if !is_match {
742 return None;
743 }
744 result.push(LocatedImport::new(
745 import_path_candidate.clone(),
746 resolved_qualifier,
747 assoc_to_item(assoc),
748 complete_in_flyimport,
749 ));
750 None
751 });
752 result
753}
754
755pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
756 Some(match item {
757 ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
758 Some(assoc_item) => item_for_path_search_assoc(db, assoc_item)?,
759 None => item,
760 },
761 ItemInNs::Macros(_) => item,
762 })
763}
764
765fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Option<ItemInNs> {
766 Some(match assoc_item.container(db) {
767 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
768 AssocItemContainer::Impl(impl_) => {
769 ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
770 }
771 })
772}
773
774fn trait_applicable_items<'db>(
775 db: &'db RootDatabase,
776 current_crate: Crate,
777 scope: &SemanticsScope<'db>,
778 trait_candidate: &TraitImportCandidate<'db>,
779 trait_assoc_item: bool,
780 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
781 scope_filter: impl Fn(hir::Trait) -> bool,
782) -> FxIndexSet<LocatedImport> {
783 let _p = tracing::info_span!("ImportAssets::trait_applicable_items").entered();
784
785 let inherent_traits = trait_candidate.receiver_ty.applicable_inherent_traits(db);
786 let env_traits = trait_candidate.receiver_ty.env_traits(db);
787 let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>();
788
789 let mut required_assoc_items = FxHashMap::default();
790 let mut trait_candidates: FxHashSet<_> = items_locator::items_with_name(
791 db,
792 current_crate,
793 trait_candidate.assoc_item_name.clone(),
794 AssocSearchMode::AssocItemsOnly,
795 )
796 .filter_map(|(input, do_not_complete)| Some((item_as_assoc(db, input)?, do_not_complete)))
797 .filter_map(|(assoc, do_not_complete)| {
798 if !trait_assoc_item && matches!(assoc, AssocItem::Const(_) | AssocItem::TypeAlias(_)) {
799 return None;
800 }
801
802 let assoc_item_trait = assoc.container_trait(db)?;
803 if related_traits.contains(&assoc_item_trait) {
804 return None;
805 }
806 required_assoc_items
807 .insert(assoc, CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport));
808 Some(assoc_item_trait.into())
809 })
810 .collect();
811
812 let autoderef_method_receiver = {
813 let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::<Vec<_>>();
814 if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) {
816 let slice = Type::new_slice(ty);
817 deref_chain.push(slice);
818 }
819 deref_chain
820 .into_iter()
821 .flat_map(|ty| {
822 let fingerprint = ty.fingerprint_for_trait_impl()?;
823 let mut crates = vec![];
824
825 if let Some(adt) = ty.as_adt() {
826 crates.push((adt.krate(db).into(), fingerprint));
828 }
829 crates.push((ty.krate(db).into(), fingerprint));
831 Some(crates)
832 })
833 .flatten()
834 .unique()
835 .collect::<Vec<_>>()
836 };
837
838 if autoderef_method_receiver.is_empty() {
840 return Default::default();
841 }
842
843 if !autoderef_method_receiver
847 .iter()
848 .any(|(_, fingerprint)| matches!(fingerprint, hir::SimplifiedType::Placeholder))
849 {
850 trait_candidates.retain(|&candidate_trait_id| {
851 let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
859
860 let trait_impls_in_crate =
861 hir::TraitImpls::for_crate(db, defining_crate_for_trait.into());
862 let definitions_exist_in_trait_crate =
863 autoderef_method_receiver.iter().any(|(_, fingerprint)| {
864 trait_impls_in_crate
865 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
866 });
867 let definitions_exist_in_receiver_crate = || {
870 autoderef_method_receiver.iter().any(|(krate, fingerprint)| {
871 hir::TraitImpls::for_crate(db, *krate)
872 .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
873 })
874 };
875
876 definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
877 });
878 }
879
880 let mut located_imports = FxIndexSet::default();
881 let mut trait_import_paths = FxHashMap::default();
882
883 if trait_assoc_item {
884 trait_candidate.receiver_ty.iterate_path_candidates(
885 db,
886 scope,
887 &trait_candidates,
888 None,
889 |assoc| {
890 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
891 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
892 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
893 let import_path = trait_import_paths
894 .entry(trait_item)
895 .or_insert_with(|| mod_path(trait_item))
896 .clone()?;
897 located_imports.insert(LocatedImport::new(
898 import_path,
899 trait_item,
900 assoc_to_item(assoc),
901 complete_in_flyimport,
902 ));
903 }
904 None::<()>
905 },
906 )
907 } else {
908 trait_candidate.receiver_ty.iterate_method_candidates_with_traits(
909 db,
910 scope,
911 &trait_candidates,
912 None,
913 |function| {
914 let assoc = function.as_assoc_item(db)?;
915 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
916 let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
917 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
918 let import_path = trait_import_paths
919 .entry(trait_item)
920 .or_insert_with(|| mod_path(trait_item))
921 .clone()?;
922 located_imports.insert(LocatedImport::new(
923 import_path,
924 trait_item,
925 assoc_to_item(assoc),
926 complete_in_flyimport,
927 ));
928 }
929 None::<()>
930 },
931 )
932 };
933
934 located_imports
935}
936
937fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
938 match assoc {
939 AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
940 AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
941 AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
942 }
943}
944
945#[tracing::instrument(skip_all)]
946fn get_mod_path(
947 db: &RootDatabase,
948 item_to_search: ItemInNs,
949 module_with_candidate: &Module,
950 prefixed: Option<PrefixKind>,
951 cfg: FindPathConfig,
952) -> Option<ModPath> {
953 if let Some(prefix_kind) = prefixed {
954 module_with_candidate.find_use_path(db, item_to_search, prefix_kind, cfg)
955 } else {
956 module_with_candidate.find_path(db, item_to_search, cfg)
957 }
958}
959
960impl<'db> ImportCandidate<'db> {
961 fn for_method_call(
962 sema: &Semantics<'db, RootDatabase>,
963 method_call: &ast::MethodCallExpr,
964 ) -> Option<Self> {
965 match sema.resolve_method_call(method_call) {
966 Some(_) => None,
967 None => Some(Self::TraitMethod(TraitImportCandidate {
968 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?.adjusted(),
969 assoc_item_name: NameToImport::exact_case_sensitive(
970 method_call.name_ref()?.to_string(),
971 ),
972 })),
973 }
974 }
975
976 fn for_regular_path(sema: &Semantics<'db, RootDatabase>, path: &ast::Path) -> Option<Self> {
977 if sema.resolve_path(path).is_some() {
978 return None;
979 }
980 let after = std::iter::successors(path.parent_path(), |it| it.parent_path())
981 .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text())))
982 .collect::<Option<_>>()?;
983 path_import_candidate(
984 sema,
985 Some(path),
986 path.qualifier(),
987 NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()),
988 after,
989 )
990 }
991
992 fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option<Self> {
993 if sema
994 .scope(name.syntax())?
995 .speculative_resolve(&make::ext::ident_path(&name.text()))
996 .is_some()
997 {
998 return None;
999 }
1000 Some(ImportCandidate::Path(PathImportCandidate {
1001 qualifier: vec![],
1002 name: NameToImport::exact_case_sensitive(name.to_string()),
1003 after: vec![],
1004 definition_kinds: PathDefinitionKinds::PATH_PAT_KINDS,
1005 }))
1006 }
1007
1008 fn for_fuzzy_path(
1009 path: Option<&ast::Path>,
1010 qualifier: Option<ast::Path>,
1011 fuzzy_name: String,
1012 sema: &Semantics<'db, RootDatabase>,
1013 ) -> Option<Self> {
1014 path_import_candidate(sema, path, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new())
1016 }
1017}
1018
1019fn path_import_candidate<'db>(
1020 sema: &Semantics<'db, RootDatabase>,
1021 path: Option<&ast::Path>,
1022 qualifier: Option<ast::Path>,
1023 name: NameToImport,
1024 after: Vec<Name>,
1025) -> Option<ImportCandidate<'db>> {
1026 let definition_kinds = path.map_or(PathDefinitionKinds::ALL_ENABLED, |path| {
1027 PathDefinitionKinds::deduce_from_path(path, matches!(name, NameToImport::Exact(..)))
1028 });
1029 Some(match qualifier {
1030 Some(qualifier) => match sema.resolve_path(&qualifier) {
1031 Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => {
1032 if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
1033 let qualifier = qualifier
1034 .segments()
1035 .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
1036 .collect::<Option<Vec<_>>>()?;
1037 ImportCandidate::Path(PathImportCandidate {
1038 qualifier,
1039 name,
1040 after,
1041 definition_kinds,
1042 })
1043 } else {
1044 return None;
1045 }
1046 }
1047 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
1048 ImportCandidate::TraitAssocItem(TraitImportCandidate {
1049 receiver_ty: assoc_item_path.ty(sema.db),
1050 assoc_item_name: name,
1051 })
1052 }
1053 Some(PathResolution::Def(ModuleDef::TypeAlias(alias))) => {
1054 let ty = alias.ty(sema.db);
1055 if ty.as_adt().is_some() {
1056 ImportCandidate::TraitAssocItem(TraitImportCandidate {
1057 receiver_ty: ty,
1058 assoc_item_name: name,
1059 })
1060 } else {
1061 return None;
1062 }
1063 }
1064 Some(_) => return None,
1065 },
1066 None => ImportCandidate::Path(PathImportCandidate {
1067 qualifier: vec![],
1068 name,
1069 after,
1070 definition_kinds,
1071 }),
1072 })
1073}
1074
1075fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
1076 item.into_module_def().as_assoc_item(db)
1077}