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)
178 fn find_trait_method(
181 trait_method_name: &ast::NameRef,
182 ) -> Option<hir::Function> {
183 if let Some(hir::AssocItem::Function(method)) =
184 trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
186 .map(|name| name.to_string() == trait_method_name.to_string())
196 fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
197 let item_module_def = item.as_module_def()?;
199 match item_module_def {
200 hir::ModuleDef::Trait(trait_) => Some(trait_),
201 _ => item_module_def.as_assoc_item(db)?.containing_trait(db),
205 fn group_label(candidate: &ImportCandidate) -> GroupLabel {
206 let name = match candidate {
207 ImportCandidate::Path(it) => &it.name,
208 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
213 GroupLabel(format!("Qualify {}", name))
216 fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
218 ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
219 format!("Qualify as `{}`", import.import_path)
221 _ => format!("Qualify with `{}`", import.import_path),
227 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
232 fn applicable_when_found_an_import_partial() {
233 cov_mark::check!(qualify_path_unqualified_name);
239 pub struct Formatter;
250 pub struct Formatter;
262 fn applicable_when_found_an_import() {
269 pub struct PubStruct;
276 pub struct PubStruct;
283 fn applicable_in_macros() {
288 ($i:ident) => { fn foo(a: $i) {} }
293 pub struct PubStruct;
298 ($i:ident) => { fn foo(a: $i) {} }
300 foo!(PubMod::PubStruct);
303 pub struct PubStruct;
310 fn applicable_when_found_multiple_imports() {
317 pub struct PubStruct;
320 pub struct PubStruct;
323 pub struct PubStruct;
330 pub struct PubStruct;
333 pub struct PubStruct;
336 pub struct PubStruct;
343 fn not_applicable_for_already_imported_types() {
344 check_assist_not_applicable(
347 use PubMod::PubStruct;
352 pub struct PubStruct;
359 fn not_applicable_for_types_with_private_paths() {
360 check_assist_not_applicable(
366 struct PrivateStruct;
373 fn not_applicable_when_no_imports_found() {
374 check_assist_not_applicable(qualify_path, r#"PubStruct$0"#);
378 fn not_applicable_in_import_statements() {
379 check_assist_not_applicable(
385 pub struct PubStruct;
392 fn qualify_function() {
399 pub fn test_function() {};
403 PubMod::test_function
406 pub fn test_function() {};
417 //- /lib.rs crate:crate_with_macro
423 //- /main.rs crate:main deps:crate_with_macro
430 crate_with_macro::foo
437 fn qualify_path_target() {
442 group_label: Option<$0GroupLabel>,
445 mod m { pub struct GroupLabel; }
452 fn not_applicable_when_path_start_is_imported() {
453 check_assist_not_applicable(
459 pub struct TestStruct;
466 mod2::mod3::TestStruct$0
473 fn not_applicable_for_imported_function() {
474 check_assist_not_applicable(
478 pub fn test_function() {}
481 use test_mod::test_function;
490 fn associated_struct_function() {
495 pub struct TestStruct {}
497 pub fn test_function() {}
502 TestStruct::test_function$0
507 pub struct TestStruct {}
509 pub fn test_function() {}
514 test_mod::TestStruct::test_function
521 fn associated_struct_const() {
522 cov_mark::check!(qualify_path_qualifier_start);
527 pub struct TestStruct {}
529 const TEST_CONST: u8 = 42;
534 TestStruct::TEST_CONST$0
539 pub struct TestStruct {}
541 const TEST_CONST: u8 = 42;
546 test_mod::TestStruct::TEST_CONST
553 fn associated_struct_const_unqualified() {
554 // FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details
555 check_assist_not_applicable(
559 pub struct TestStruct {}
561 const TEST_CONST: u8 = 42;
573 fn associated_trait_function() {
578 pub trait TestTrait {
581 pub struct TestStruct {}
582 impl TestTrait for TestStruct {
583 fn test_function() {}
588 test_mod::TestStruct::test_function$0
593 pub trait TestTrait {
596 pub struct TestStruct {}
597 impl TestTrait for TestStruct {
598 fn test_function() {}
603 <test_mod::TestStruct as test_mod::TestTrait>::test_function
610 fn not_applicable_for_imported_trait_for_function() {
611 check_assist_not_applicable(
615 pub trait TestTrait {
618 pub trait TestTrait2 {
625 impl TestTrait2 for TestEnum {
626 fn test_function() {}
628 impl TestTrait for TestEnum {
629 fn test_function() {}
633 use test_mod::TestTrait2;
635 test_mod::TestEnum::test_function$0;
642 fn associated_trait_const() {
643 cov_mark::check!(qualify_path_trait_assoc_item);
648 pub trait TestTrait {
649 const TEST_CONST: u8;
651 pub struct TestStruct {}
652 impl TestTrait for TestStruct {
653 const TEST_CONST: u8 = 42;
658 test_mod::TestStruct::TEST_CONST$0
663 pub trait TestTrait {
664 const TEST_CONST: u8;
666 pub struct TestStruct {}
667 impl TestTrait for TestStruct {
668 const TEST_CONST: u8 = 42;
673 <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST
680 fn not_applicable_for_imported_trait_for_const() {
681 check_assist_not_applicable(
685 pub trait TestTrait {
686 const TEST_CONST: u8;
688 pub trait TestTrait2 {
689 const TEST_CONST: f64;
695 impl TestTrait2 for TestEnum {
696 const TEST_CONST: f64 = 42.0;
698 impl TestTrait for TestEnum {
699 const TEST_CONST: u8 = 42;
703 use test_mod::TestTrait2;
705 test_mod::TestEnum::TEST_CONST$0;
713 cov_mark::check!(qualify_path_trait_method);
718 pub trait TestTrait {
719 fn test_method(&self);
721 pub struct TestStruct {}
722 impl TestTrait for TestStruct {
723 fn test_method(&self) {}
728 let test_struct = test_mod::TestStruct {};
729 test_struct.test_meth$0od()
734 pub trait TestTrait {
735 fn test_method(&self);
737 pub struct TestStruct {}
738 impl TestTrait for TestStruct {
739 fn test_method(&self) {}
744 let test_struct = test_mod::TestStruct {};
745 test_mod::TestTrait::test_method(&test_struct)
752 fn trait_method_multi_params() {
757 pub trait TestTrait {
758 fn test_method(&self, test: i32);
760 pub struct TestStruct {}
761 impl TestTrait for TestStruct {
762 fn test_method(&self, test: i32) {}
767 let test_struct = test_mod::TestStruct {};
768 test_struct.test_meth$0od(42)
773 pub trait TestTrait {
774 fn test_method(&self, test: i32);
776 pub struct TestStruct {}
777 impl TestTrait for TestStruct {
778 fn test_method(&self, test: i32) {}
783 let test_struct = test_mod::TestStruct {};
784 test_mod::TestTrait::test_method(&test_struct, 42)
791 fn trait_method_consume() {
796 pub trait TestTrait {
797 fn test_method(self);
799 pub struct TestStruct {}
800 impl TestTrait for TestStruct {
801 fn test_method(self) {}
806 let test_struct = test_mod::TestStruct {};
807 test_struct.test_meth$0od()
812 pub trait TestTrait {
813 fn test_method(self);
815 pub struct TestStruct {}
816 impl TestTrait for TestStruct {
817 fn test_method(self) {}
822 let test_struct = test_mod::TestStruct {};
823 test_mod::TestTrait::test_method(test_struct)
830 fn trait_method_cross_crate() {
834 //- /main.rs crate:main deps:dep
836 let test_struct = dep::test_mod::TestStruct {};
837 test_struct.test_meth$0od()
839 //- /dep.rs crate:dep
841 pub trait TestTrait {
842 fn test_method(&self);
844 pub struct TestStruct {}
845 impl TestTrait for TestStruct {
846 fn test_method(&self) {}
852 let test_struct = dep::test_mod::TestStruct {};
853 dep::test_mod::TestTrait::test_method(&test_struct)
860 fn assoc_fn_cross_crate() {
864 //- /main.rs crate:main deps:dep
866 dep::test_mod::TestStruct::test_func$0tion
868 //- /dep.rs crate:dep
870 pub trait TestTrait {
873 pub struct TestStruct {}
874 impl TestTrait for TestStruct {
875 fn test_function() {}
881 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function
888 fn assoc_const_cross_crate() {
892 //- /main.rs crate:main deps:dep
894 dep::test_mod::TestStruct::CONST$0
896 //- /dep.rs crate:dep
898 pub trait TestTrait {
901 pub struct TestStruct {}
902 impl TestTrait for TestStruct {
903 const CONST: bool = true;
909 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST
916 fn assoc_fn_as_method_cross_crate() {
917 check_assist_not_applicable(
920 //- /main.rs crate:main deps:dep
922 let test_struct = dep::test_mod::TestStruct {};
923 test_struct.test_func$0tion()
925 //- /dep.rs crate:dep
927 pub trait TestTrait {
930 pub struct TestStruct {}
931 impl TestTrait for TestStruct {
932 fn test_function() {}
940 fn private_trait_cross_crate() {
941 check_assist_not_applicable(
944 //- /main.rs crate:main deps:dep
946 let test_struct = dep::test_mod::TestStruct {};
947 test_struct.test_meth$0od()
949 //- /dep.rs crate:dep
952 fn test_method(&self);
954 pub struct TestStruct {}
955 impl TestTrait for TestStruct {
956 fn test_method(&self) {}
964 fn not_applicable_for_imported_trait_for_method() {
965 check_assist_not_applicable(
969 pub trait TestTrait {
970 fn test_method(&self);
972 pub trait TestTrait2 {
973 fn test_method(&self);
979 impl TestTrait2 for TestEnum {
980 fn test_method(&self) {}
982 impl TestTrait for TestEnum {
983 fn test_method(&self) {}
987 use test_mod::TestTrait2;
989 let one = test_mod::TestEnum::One;
1001 //- /lib.rs crate:dep
1004 //- /main.rs crate:main deps:dep
1018 fn whole_segment() {
1019 // Tests that only imports whose last segment matches the identifier get suggested.
1023 //- /lib.rs crate:dep
1025 pub trait Display {}
1028 pub fn panic_fmt() {}
1030 //- /main.rs crate:main deps:dep
1033 impl f$0mt::Display for S {}
1038 impl dep::fmt::Display for S {}
1044 fn macro_generated() {
1045 // Tests that macro-generated items are suggested from external crates.
1049 //- /lib.rs crate:dep
1058 //- /main.rs crate:main deps:dep
1073 // Tests that differently cased names don't interfere and we only suggest the matching one.
1077 //- /lib.rs crate:dep
1081 //- /main.rs crate:main deps:dep
1095 fn keep_generic_annotations() {
1099 //- /lib.rs crate:dep
1100 pub mod generic { pub struct Thing<'a, T>(&'a T); }
1102 //- /main.rs crate:main deps:dep
1103 fn foo() -> Thin$0g<'static, ()> {}
1108 fn foo() -> dep::generic::Thing<'static, ()> {}
1116 fn keep_generic_annotations_leading_colon() {
1120 //- /lib.rs crate:dep
1121 pub mod generic { pub struct Thing<'a, T>(&'a T); }
1123 //- /main.rs crate:main deps:dep
1124 fn foo() -> Thin$0g::<'static, ()> {}
1129 fn foo() -> dep::generic::Thing::<'static, ()> {}
1137 fn associated_struct_const_generic() {
1142 pub struct TestStruct<T> {}
1143 impl<T> TestStruct<T> {
1144 const TEST_CONST: u8 = 42;
1149 TestStruct::<()>::TEST_CONST$0
1154 pub struct TestStruct<T> {}
1155 impl<T> TestStruct<T> {
1156 const TEST_CONST: u8 = 42;
1161 test_mod::TestStruct::<()>::TEST_CONST
1168 fn associated_trait_const_generic() {
1173 pub trait TestTrait {
1174 const TEST_CONST: u8;
1176 pub struct TestStruct<T> {}
1177 impl<T> TestTrait for TestStruct<T> {
1178 const TEST_CONST: u8 = 42;
1183 test_mod::TestStruct::<()>::TEST_CONST$0
1188 pub trait TestTrait {
1189 const TEST_CONST: u8;
1191 pub struct TestStruct<T> {}
1192 impl<T> TestTrait for TestStruct<T> {
1193 const TEST_CONST: u8 = 42;
1198 <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
1205 fn trait_method_generic() {
1210 pub trait TestTrait {
1211 fn test_method<T>(&self);
1213 pub struct TestStruct {}
1214 impl TestTrait for TestStruct {
1215 fn test_method<T>(&self) {}
1220 let test_struct = test_mod::TestStruct {};
1221 test_struct.test_meth$0od::<()>()
1226 pub trait TestTrait {
1227 fn test_method<T>(&self);
1229 pub struct TestStruct {}
1230 impl TestTrait for TestStruct {
1231 fn test_method<T>(&self) {}
1236 let test_struct = test_mod::TestStruct {};
1237 test_mod::TestTrait::test_method::<()>(&test_struct)