5 import_assets::{ImportCandidate, LocatedImport},
8 use ide_db::RootDatabase;
11 ast::{make, HasArgList},
16 assist_context::{AssistContext, Assists},
17 handlers::auto_import::find_importable_node,
18 AssistId, AssistKind, GroupLabel,
21 // Assist: qualify_path
23 // If the name is unresolved, provides all possible qualified paths for it.
27 // let map = HashMap$0::new();
29 // # pub mod std { pub mod collections { pub struct HashMap { } } }
34 // let map = std::collections::HashMap::new();
36 // # pub mod std { pub mod collections { pub struct HashMap { } } }
38 pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
40 let mut proposed_imports = import_assets.search_for_relative_paths(&ctx.sema);
41 if proposed_imports.is_empty() {
45 let range = ctx.sema.original_range(&syntax_under_caret).range;
46 let candidate = import_assets.import_candidate();
47 let qualify_candidate = match candidate {
48 ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
49 cov_mark::hit!(qualify_path_qualifier_start);
50 let path = ast::Path::cast(syntax_under_caret)?;
51 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
52 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
54 ImportCandidate::Path(_) => {
55 cov_mark::hit!(qualify_path_unqualified_name);
56 let path = ast::Path::cast(syntax_under_caret)?;
57 let generics = path.segment()?.generic_arg_list();
58 QualifyCandidate::UnqualifiedName(generics)
60 ImportCandidate::TraitAssocItem(_) => {
61 cov_mark::hit!(qualify_path_trait_assoc_item);
62 let path = ast::Path::cast(syntax_under_caret)?;
63 let (qualifier, segment) = (path.qualifier()?, path.segment()?);
64 QualifyCandidate::TraitAssocItem(qualifier, segment)
66 ImportCandidate::TraitMethod(_) => {
67 cov_mark::hit!(qualify_path_trait_method);
68 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
69 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
73 // we aren't interested in different namespaces
74 proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
76 let group_label = group_label(candidate);
77 for import in proposed_imports {
80 AssistId("qualify_path", AssistKind::QuickFix),
81 label(candidate, &import),
84 qualify_candidate.qualify(
85 |replace_with: String| builder.replace(range, replace_with),
87 import.item_to_import,
94 pub(crate) enum QualifyCandidate<'db> {
95 QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
96 UnqualifiedName(Option<ast::GenericArgList>),
97 TraitAssocItem(ast::Path, ast::PathSegment),
98 TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
99 ImplMethod(&'db RootDatabase, ast::MethodCallExpr, hir::Function),
102 impl QualifyCandidate<'_> {
103 pub(crate) fn qualify(
105 mut replacer: impl FnMut(String),
106 import: &hir::ModPath,
109 let import = mod_path_to_ast(import);
111 QualifyCandidate::QualifierStart(segment, generics) => {
112 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
113 replacer(format!("{}{}::{}", import, generics, segment));
115 QualifyCandidate::UnqualifiedName(generics) => {
116 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
117 replacer(format!("{}{}", import.to_string(), generics));
119 QualifyCandidate::TraitAssocItem(qualifier, segment) => {
120 replacer(format!("<{} as {}>::{}", qualifier, import, segment));
122 QualifyCandidate::TraitMethod(db, mcall_expr) => {
123 Self::qualify_trait_method(db, mcall_expr, replacer, import, item);
125 QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => {
126 Self::qualify_fn_call(db, mcall_expr, replacer, import, hir_fn);
133 mcall_expr: &ast::MethodCallExpr,
134 mut replacer: impl FnMut(String),
136 hir_fn: &hir::Function,
138 let receiver = mcall_expr.receiver()?;
139 let method_name = mcall_expr.name_ref()?;
141 mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
142 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
144 if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) {
145 let receiver = match self_access {
146 hir::Access::Shared => make::expr_ref(receiver, false),
147 hir::Access::Exclusive => make::expr_ref(receiver, true),
148 hir::Access::Owned => receiver,
156 Some(args) => make::arg_list(iter::once(receiver).chain(args)),
157 None => make::arg_list(iter::once(receiver)),
164 fn qualify_trait_method(
166 mcall_expr: &ast::MethodCallExpr,
167 replacer: impl FnMut(String),
171 let trait_method_name = mcall_expr.name_ref()?;
172 let trait_ = item_as_trait(db, item)?;
173 let method = find_trait_method(db, trait_, &trait_method_name)?;
174 Self::qualify_fn_call(db, mcall_expr, replacer, import, &method);
179 fn find_trait_method(
182 trait_method_name: &ast::NameRef,
183 ) -> Option<hir::Function> {
184 if let Some(hir::AssocItem::Function(method)) =
185 trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
187 .map(|name| name.to_string() == trait_method_name.to_string())
197 fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
198 let item_module_def = item.as_module_def()?;
200 match item_module_def {
201 hir::ModuleDef::Trait(trait_) => Some(trait_),
202 _ => item_module_def.as_assoc_item(db)?.containing_trait(db),
206 fn group_label(candidate: &ImportCandidate) -> GroupLabel {
207 let name = match candidate {
208 ImportCandidate::Path(it) => &it.name,
209 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
214 GroupLabel(format!("Qualify {}", name))
217 fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
219 ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
220 format!("Qualify as `{}`", import.import_path)
222 _ => format!("Qualify with `{}`", import.import_path),
228 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
233 fn applicable_when_found_an_import_partial() {
234 cov_mark::check!(qualify_path_unqualified_name);
240 pub struct Formatter;
251 pub struct Formatter;
263 fn applicable_when_found_an_import() {
270 pub struct PubStruct;
277 pub struct PubStruct;
284 fn applicable_in_macros() {
289 ($i:ident) => { fn foo(a: $i) {} }
294 pub struct PubStruct;
299 ($i:ident) => { fn foo(a: $i) {} }
301 foo!(PubMod::PubStruct);
304 pub struct PubStruct;
311 fn applicable_when_found_multiple_imports() {
318 pub struct PubStruct;
321 pub struct PubStruct;
324 pub struct PubStruct;
331 pub struct PubStruct;
334 pub struct PubStruct;
337 pub struct PubStruct;
344 fn not_applicable_for_already_imported_types() {
345 check_assist_not_applicable(
348 use PubMod::PubStruct;
353 pub struct PubStruct;
360 fn not_applicable_for_types_with_private_paths() {
361 check_assist_not_applicable(
367 struct PrivateStruct;
374 fn not_applicable_when_no_imports_found() {
375 check_assist_not_applicable(qualify_path, r#"PubStruct$0"#);
379 fn not_applicable_in_import_statements() {
380 check_assist_not_applicable(
386 pub struct PubStruct;
393 fn qualify_function() {
400 pub fn test_function() {};
404 PubMod::test_function
407 pub fn test_function() {};
418 //- /lib.rs crate:crate_with_macro
424 //- /main.rs crate:main deps:crate_with_macro
431 crate_with_macro::foo
438 fn qualify_path_target() {
443 group_label: Option<$0GroupLabel>,
446 mod m { pub struct GroupLabel; }
453 fn not_applicable_when_path_start_is_imported() {
454 check_assist_not_applicable(
460 pub struct TestStruct;
467 mod2::mod3::TestStruct$0
474 fn not_applicable_for_imported_function() {
475 check_assist_not_applicable(
479 pub fn test_function() {}
482 use test_mod::test_function;
491 fn associated_struct_function() {
496 pub struct TestStruct {}
498 pub fn test_function() {}
503 TestStruct::test_function$0
508 pub struct TestStruct {}
510 pub fn test_function() {}
515 test_mod::TestStruct::test_function
522 fn associated_struct_const() {
523 cov_mark::check!(qualify_path_qualifier_start);
528 pub struct TestStruct {}
530 const TEST_CONST: u8 = 42;
535 TestStruct::TEST_CONST$0
540 pub struct TestStruct {}
542 const TEST_CONST: u8 = 42;
547 test_mod::TestStruct::TEST_CONST
554 fn associated_struct_const_unqualified() {
555 // FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details
556 check_assist_not_applicable(
560 pub struct TestStruct {}
562 const TEST_CONST: u8 = 42;
574 fn associated_trait_function() {
579 pub trait TestTrait {
582 pub struct TestStruct {}
583 impl TestTrait for TestStruct {
584 fn test_function() {}
589 test_mod::TestStruct::test_function$0
594 pub trait TestTrait {
597 pub struct TestStruct {}
598 impl TestTrait for TestStruct {
599 fn test_function() {}
604 <test_mod::TestStruct as test_mod::TestTrait>::test_function
611 fn not_applicable_for_imported_trait_for_function() {
612 check_assist_not_applicable(
616 pub trait TestTrait {
619 pub trait TestTrait2 {
626 impl TestTrait2 for TestEnum {
627 fn test_function() {}
629 impl TestTrait for TestEnum {
630 fn test_function() {}
634 use test_mod::TestTrait2;
636 test_mod::TestEnum::test_function$0;
643 fn associated_trait_const() {
644 cov_mark::check!(qualify_path_trait_assoc_item);
649 pub trait TestTrait {
650 const TEST_CONST: u8;
652 pub struct TestStruct {}
653 impl TestTrait for TestStruct {
654 const TEST_CONST: u8 = 42;
659 test_mod::TestStruct::TEST_CONST$0
664 pub trait TestTrait {
665 const TEST_CONST: u8;
667 pub struct TestStruct {}
668 impl TestTrait for TestStruct {
669 const TEST_CONST: u8 = 42;
674 <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST
681 fn not_applicable_for_imported_trait_for_const() {
682 check_assist_not_applicable(
686 pub trait TestTrait {
687 const TEST_CONST: u8;
689 pub trait TestTrait2 {
690 const TEST_CONST: f64;
696 impl TestTrait2 for TestEnum {
697 const TEST_CONST: f64 = 42.0;
699 impl TestTrait for TestEnum {
700 const TEST_CONST: u8 = 42;
704 use test_mod::TestTrait2;
706 test_mod::TestEnum::TEST_CONST$0;
714 cov_mark::check!(qualify_path_trait_method);
719 pub trait TestTrait {
720 fn test_method(&self);
722 pub struct TestStruct {}
723 impl TestTrait for TestStruct {
724 fn test_method(&self) {}
729 let test_struct = test_mod::TestStruct {};
730 test_struct.test_meth$0od()
735 pub trait TestTrait {
736 fn test_method(&self);
738 pub struct TestStruct {}
739 impl TestTrait for TestStruct {
740 fn test_method(&self) {}
745 let test_struct = test_mod::TestStruct {};
746 test_mod::TestTrait::test_method(&test_struct)
753 fn trait_method_multi_params() {
758 pub trait TestTrait {
759 fn test_method(&self, test: i32);
761 pub struct TestStruct {}
762 impl TestTrait for TestStruct {
763 fn test_method(&self, test: i32) {}
768 let test_struct = test_mod::TestStruct {};
769 test_struct.test_meth$0od(42)
774 pub trait TestTrait {
775 fn test_method(&self, test: i32);
777 pub struct TestStruct {}
778 impl TestTrait for TestStruct {
779 fn test_method(&self, test: i32) {}
784 let test_struct = test_mod::TestStruct {};
785 test_mod::TestTrait::test_method(&test_struct, 42)
792 fn trait_method_consume() {
797 pub trait TestTrait {
798 fn test_method(self);
800 pub struct TestStruct {}
801 impl TestTrait for TestStruct {
802 fn test_method(self) {}
807 let test_struct = test_mod::TestStruct {};
808 test_struct.test_meth$0od()
813 pub trait TestTrait {
814 fn test_method(self);
816 pub struct TestStruct {}
817 impl TestTrait for TestStruct {
818 fn test_method(self) {}
823 let test_struct = test_mod::TestStruct {};
824 test_mod::TestTrait::test_method(test_struct)
831 fn trait_method_cross_crate() {
835 //- /main.rs crate:main deps:dep
837 let test_struct = dep::test_mod::TestStruct {};
838 test_struct.test_meth$0od()
840 //- /dep.rs crate:dep
842 pub trait TestTrait {
843 fn test_method(&self);
845 pub struct TestStruct {}
846 impl TestTrait for TestStruct {
847 fn test_method(&self) {}
853 let test_struct = dep::test_mod::TestStruct {};
854 dep::test_mod::TestTrait::test_method(&test_struct)
861 fn assoc_fn_cross_crate() {
865 //- /main.rs crate:main deps:dep
867 dep::test_mod::TestStruct::test_func$0tion
869 //- /dep.rs crate:dep
871 pub trait TestTrait {
874 pub struct TestStruct {}
875 impl TestTrait for TestStruct {
876 fn test_function() {}
882 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function
889 fn assoc_const_cross_crate() {
893 //- /main.rs crate:main deps:dep
895 dep::test_mod::TestStruct::CONST$0
897 //- /dep.rs crate:dep
899 pub trait TestTrait {
902 pub struct TestStruct {}
903 impl TestTrait for TestStruct {
904 const CONST: bool = true;
910 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST
917 fn assoc_fn_as_method_cross_crate() {
918 check_assist_not_applicable(
921 //- /main.rs crate:main deps:dep
923 let test_struct = dep::test_mod::TestStruct {};
924 test_struct.test_func$0tion()
926 //- /dep.rs crate:dep
928 pub trait TestTrait {
931 pub struct TestStruct {}
932 impl TestTrait for TestStruct {
933 fn test_function() {}
941 fn private_trait_cross_crate() {
942 check_assist_not_applicable(
945 //- /main.rs crate:main deps:dep
947 let test_struct = dep::test_mod::TestStruct {};
948 test_struct.test_meth$0od()
950 //- /dep.rs crate:dep
953 fn test_method(&self);
955 pub struct TestStruct {}
956 impl TestTrait for TestStruct {
957 fn test_method(&self) {}
965 fn not_applicable_for_imported_trait_for_method() {
966 check_assist_not_applicable(
970 pub trait TestTrait {
971 fn test_method(&self);
973 pub trait TestTrait2 {
974 fn test_method(&self);
980 impl TestTrait2 for TestEnum {
981 fn test_method(&self) {}
983 impl TestTrait for TestEnum {
984 fn test_method(&self) {}
988 use test_mod::TestTrait2;
990 let one = test_mod::TestEnum::One;
1002 //- /lib.rs crate:dep
1005 //- /main.rs crate:main deps:dep
1019 fn whole_segment() {
1020 // Tests that only imports whose last segment matches the identifier get suggested.
1024 //- /lib.rs crate:dep
1026 pub trait Display {}
1029 pub fn panic_fmt() {}
1031 //- /main.rs crate:main deps:dep
1034 impl f$0mt::Display for S {}
1039 impl dep::fmt::Display for S {}
1045 fn macro_generated() {
1046 // Tests that macro-generated items are suggested from external crates.
1050 //- /lib.rs crate:dep
1059 //- /main.rs crate:main deps:dep
1074 // Tests that differently cased names don't interfere and we only suggest the matching one.
1078 //- /lib.rs crate:dep
1082 //- /main.rs crate:main deps:dep
1096 fn keep_generic_annotations() {
1100 //- /lib.rs crate:dep
1101 pub mod generic { pub struct Thing<'a, T>(&'a T); }
1103 //- /main.rs crate:main deps:dep
1104 fn foo() -> Thin$0g<'static, ()> {}
1109 fn foo() -> dep::generic::Thing<'static, ()> {}
1117 fn keep_generic_annotations_leading_colon() {
1121 //- /lib.rs crate:dep
1122 pub mod generic { pub struct Thing<'a, T>(&'a T); }
1124 //- /main.rs crate:main deps:dep
1125 fn foo() -> Thin$0g::<'static, ()> {}
1130 fn foo() -> dep::generic::Thing::<'static, ()> {}
1138 fn associated_struct_const_generic() {
1143 pub struct TestStruct<T> {}
1144 impl<T> TestStruct<T> {
1145 const TEST_CONST: u8 = 42;
1150 TestStruct::<()>::TEST_CONST$0
1155 pub struct TestStruct<T> {}
1156 impl<T> TestStruct<T> {
1157 const TEST_CONST: u8 = 42;
1162 test_mod::TestStruct::<()>::TEST_CONST
1169 fn associated_trait_const_generic() {
1174 pub trait TestTrait {
1175 const TEST_CONST: u8;
1177 pub struct TestStruct<T> {}
1178 impl<T> TestTrait for TestStruct<T> {
1179 const TEST_CONST: u8 = 42;
1184 test_mod::TestStruct::<()>::TEST_CONST$0
1189 pub trait TestTrait {
1190 const TEST_CONST: u8;
1192 pub struct TestStruct<T> {}
1193 impl<T> TestTrait for TestStruct<T> {
1194 const TEST_CONST: u8 = 42;
1199 <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
1206 fn trait_method_generic() {
1211 pub trait TestTrait {
1212 fn test_method<T>(&self);
1214 pub struct TestStruct {}
1215 impl TestTrait for TestStruct {
1216 fn test_method<T>(&self) {}
1221 let test_struct = test_mod::TestStruct {};
1222 test_struct.test_meth$0od::<()>()
1227 pub trait TestTrait {
1228 fn test_method<T>(&self);
1230 pub struct TestStruct {}
1231 impl TestTrait for TestStruct {
1232 fn test_method<T>(&self) {}
1237 let test_struct = test_mod::TestStruct {};
1238 test_mod::TestTrait::test_method::<()>(&test_struct)