diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions/trait_impl.rs | 309 |
1 files changed, 266 insertions, 43 deletions
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs index dc1d198cc..65f0f3843 100644 --- a/crates/ide_completion/src/completions/trait_impl.rs +++ b/crates/ide_completion/src/completions/trait_impl.rs | |||
@@ -32,7 +32,7 @@ | |||
32 | //! ``` | 32 | //! ``` |
33 | 33 | ||
34 | use hir::{self, HasAttrs, HasSource}; | 34 | use hir::{self, HasAttrs, HasSource}; |
35 | use ide_db::{traits::get_missing_assoc_items, SymbolKind}; | 35 | use ide_db::{path_transform::PathTransform, traits::get_missing_assoc_items, SymbolKind}; |
36 | use syntax::{ | 36 | use syntax::{ |
37 | ast::{self, edit}, | 37 | ast::{self, edit}, |
38 | display::function_declaration, | 38 | display::function_declaration, |
@@ -52,24 +52,26 @@ enum ImplCompletionKind { | |||
52 | 52 | ||
53 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 53 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { |
54 | if let Some((kind, trigger, impl_def)) = completion_match(ctx.token.clone()) { | 54 | if let Some((kind, trigger, impl_def)) = completion_match(ctx.token.clone()) { |
55 | get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { | 55 | if let Some(hir_impl) = ctx.sema.to_def(&impl_def) { |
56 | hir::AssocItem::Function(fn_item) | 56 | get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { |
57 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => | 57 | hir::AssocItem::Function(fn_item) |
58 | { | 58 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => |
59 | add_function_impl(&trigger, acc, ctx, fn_item) | 59 | { |
60 | } | 60 | add_function_impl(&trigger, acc, ctx, fn_item, hir_impl) |
61 | hir::AssocItem::TypeAlias(type_item) | 61 | } |
62 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias => | 62 | hir::AssocItem::TypeAlias(type_item) |
63 | { | 63 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias => |
64 | add_type_alias_impl(&trigger, acc, ctx, type_item) | 64 | { |
65 | } | 65 | add_type_alias_impl(&trigger, acc, ctx, type_item) |
66 | hir::AssocItem::Const(const_item) | 66 | } |
67 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const => | 67 | hir::AssocItem::Const(const_item) |
68 | { | 68 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const => |
69 | add_const_impl(&trigger, acc, ctx, const_item) | 69 | { |
70 | } | 70 | add_const_impl(&trigger, acc, ctx, const_item, hir_impl) |
71 | _ => {} | 71 | } |
72 | }); | 72 | _ => {} |
73 | }); | ||
74 | } | ||
73 | } | 75 | } |
74 | } | 76 | } |
75 | 77 | ||
@@ -129,6 +131,7 @@ fn add_function_impl( | |||
129 | acc: &mut Completions, | 131 | acc: &mut Completions, |
130 | ctx: &CompletionContext, | 132 | ctx: &CompletionContext, |
131 | func: hir::Function, | 133 | func: hir::Function, |
134 | impl_def: hir::Impl, | ||
132 | ) { | 135 | ) { |
133 | let fn_name = func.name(ctx.db).to_string(); | 136 | let fn_name = func.name(ctx.db).to_string(); |
134 | 137 | ||
@@ -147,23 +150,55 @@ fn add_function_impl( | |||
147 | CompletionItemKind::SymbolKind(SymbolKind::Function) | 150 | CompletionItemKind::SymbolKind(SymbolKind::Function) |
148 | }; | 151 | }; |
149 | let range = replacement_range(ctx, fn_def_node); | 152 | let range = replacement_range(ctx, fn_def_node); |
150 | if let Some(src) = func.source(ctx.db) { | 153 | |
151 | let function_decl = function_declaration(&src.value); | 154 | if let Some(source) = func.source(ctx.db) { |
152 | match ctx.config.snippet_cap { | 155 | let assoc_item = ast::AssocItem::Fn(source.value); |
153 | Some(cap) => { | 156 | if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { |
154 | let snippet = format!("{} {{\n $0\n}}", function_decl); | 157 | let transformed_fn = match transformed_item { |
155 | item.snippet_edit(cap, TextEdit::replace(range, snippet)); | 158 | ast::AssocItem::Fn(func) => func, |
156 | } | 159 | _ => unreachable!(), |
157 | None => { | 160 | }; |
158 | let header = format!("{} {{", function_decl); | 161 | |
159 | item.text_edit(TextEdit::replace(range, header)); | 162 | let function_decl = function_declaration(&transformed_fn); |
160 | } | 163 | match ctx.config.snippet_cap { |
161 | }; | 164 | Some(cap) => { |
162 | item.kind(completion_kind); | 165 | let snippet = format!("{} {{\n $0\n}}", function_decl); |
163 | item.add_to(acc); | 166 | item.snippet_edit(cap, TextEdit::replace(range, snippet)); |
167 | } | ||
168 | None => { | ||
169 | let header = format!("{} {{", function_decl); | ||
170 | item.text_edit(TextEdit::replace(range, header)); | ||
171 | } | ||
172 | }; | ||
173 | item.kind(completion_kind); | ||
174 | item.add_to(acc); | ||
175 | } | ||
164 | } | 176 | } |
165 | } | 177 | } |
166 | 178 | ||
179 | /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. | ||
180 | fn get_transformed_assoc_item( | ||
181 | ctx: &CompletionContext, | ||
182 | assoc_item: ast::AssocItem, | ||
183 | impl_def: hir::Impl, | ||
184 | ) -> Option<ast::AssocItem> { | ||
185 | let assoc_item = assoc_item.clone_for_update(); | ||
186 | let trait_ = impl_def.trait_(ctx.db)?; | ||
187 | let source_scope = &ctx.sema.scope_for_def(trait_); | ||
188 | let target_scope = &ctx.sema.scope(impl_def.source(ctx.db)?.syntax().value); | ||
189 | let transform = PathTransform { | ||
190 | subst: (trait_, impl_def.source(ctx.db)?.value), | ||
191 | source_scope, | ||
192 | target_scope, | ||
193 | }; | ||
194 | |||
195 | transform.apply(assoc_item.clone()); | ||
196 | Some(match assoc_item { | ||
197 | ast::AssocItem::Fn(func) => ast::AssocItem::Fn(edit::remove_attrs_and_docs(&func)), | ||
198 | _ => assoc_item, | ||
199 | }) | ||
200 | } | ||
201 | |||
167 | fn add_type_alias_impl( | 202 | fn add_type_alias_impl( |
168 | type_def_node: &SyntaxNode, | 203 | type_def_node: &SyntaxNode, |
169 | acc: &mut Completions, | 204 | acc: &mut Completions, |
@@ -188,21 +223,30 @@ fn add_const_impl( | |||
188 | acc: &mut Completions, | 223 | acc: &mut Completions, |
189 | ctx: &CompletionContext, | 224 | ctx: &CompletionContext, |
190 | const_: hir::Const, | 225 | const_: hir::Const, |
226 | impl_def: hir::Impl, | ||
191 | ) { | 227 | ) { |
192 | let const_name = const_.name(ctx.db).map(|n| n.to_string()); | 228 | let const_name = const_.name(ctx.db).map(|n| n.to_string()); |
193 | 229 | ||
194 | if let Some(const_name) = const_name { | 230 | if let Some(const_name) = const_name { |
195 | if let Some(source) = const_.source(ctx.db) { | 231 | if let Some(source) = const_.source(ctx.db) { |
196 | let snippet = make_const_compl_syntax(&source.value); | 232 | let assoc_item = ast::AssocItem::Const(source.value); |
197 | 233 | if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { | |
198 | let range = replacement_range(ctx, const_def_node); | 234 | let transformed_const = match transformed_item { |
199 | let mut item = | 235 | ast::AssocItem::Const(const_) => const_, |
200 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); | 236 | _ => unreachable!(), |
201 | item.text_edit(TextEdit::replace(range, snippet)) | 237 | }; |
202 | .lookup_by(const_name) | 238 | |
203 | .kind(SymbolKind::Const) | 239 | let snippet = make_const_compl_syntax(&transformed_const); |
204 | .set_documentation(const_.docs(ctx.db)); | 240 | |
205 | item.add_to(acc); | 241 | let range = replacement_range(ctx, const_def_node); |
242 | let mut item = | ||
243 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); | ||
244 | item.text_edit(TextEdit::replace(range, snippet)) | ||
245 | .lookup_by(const_name) | ||
246 | .kind(SymbolKind::Const) | ||
247 | .set_documentation(const_.docs(ctx.db)); | ||
248 | item.add_to(acc); | ||
249 | } | ||
206 | } | 250 | } |
207 | } | 251 | } |
208 | } | 252 | } |
@@ -779,4 +823,183 @@ impl Foo for T {{ | |||
779 | test("Type", "type T$0", "type Type = "); | 823 | test("Type", "type T$0", "type Type = "); |
780 | test("CONST", "const C$0", "const CONST: i32 = "); | 824 | test("CONST", "const C$0", "const CONST: i32 = "); |
781 | } | 825 | } |
826 | |||
827 | #[test] | ||
828 | fn generics_are_inlined_in_return_type() { | ||
829 | check_edit( | ||
830 | "function", | ||
831 | r#" | ||
832 | trait Foo<T> { | ||
833 | fn function() -> T; | ||
834 | } | ||
835 | struct Bar; | ||
836 | |||
837 | impl Foo<u32> for Bar { | ||
838 | fn f$0 | ||
839 | } | ||
840 | "#, | ||
841 | r#" | ||
842 | trait Foo<T> { | ||
843 | fn function() -> T; | ||
844 | } | ||
845 | struct Bar; | ||
846 | |||
847 | impl Foo<u32> for Bar { | ||
848 | fn function() -> u32 { | ||
849 | $0 | ||
850 | } | ||
851 | } | ||
852 | "#, | ||
853 | ) | ||
854 | } | ||
855 | |||
856 | #[test] | ||
857 | fn generics_are_inlined_in_parameter() { | ||
858 | check_edit( | ||
859 | "function", | ||
860 | r#" | ||
861 | trait Foo<T> { | ||
862 | fn function(bar: T); | ||
863 | } | ||
864 | struct Bar; | ||
865 | |||
866 | impl Foo<u32> for Bar { | ||
867 | fn f$0 | ||
868 | } | ||
869 | "#, | ||
870 | r#" | ||
871 | trait Foo<T> { | ||
872 | fn function(bar: T); | ||
873 | } | ||
874 | struct Bar; | ||
875 | |||
876 | impl Foo<u32> for Bar { | ||
877 | fn function(bar: u32) { | ||
878 | $0 | ||
879 | } | ||
880 | } | ||
881 | "#, | ||
882 | ) | ||
883 | } | ||
884 | |||
885 | #[test] | ||
886 | fn generics_are_inlined_when_part_of_other_types() { | ||
887 | check_edit( | ||
888 | "function", | ||
889 | r#" | ||
890 | trait Foo<T> { | ||
891 | fn function(bar: Vec<T>); | ||
892 | } | ||
893 | struct Bar; | ||
894 | |||
895 | impl Foo<u32> for Bar { | ||
896 | fn f$0 | ||
897 | } | ||
898 | "#, | ||
899 | r#" | ||
900 | trait Foo<T> { | ||
901 | fn function(bar: Vec<T>); | ||
902 | } | ||
903 | struct Bar; | ||
904 | |||
905 | impl Foo<u32> for Bar { | ||
906 | fn function(bar: Vec<u32>) { | ||
907 | $0 | ||
908 | } | ||
909 | } | ||
910 | "#, | ||
911 | ) | ||
912 | } | ||
913 | |||
914 | #[test] | ||
915 | fn generics_are_inlined_complex() { | ||
916 | check_edit( | ||
917 | "function", | ||
918 | r#" | ||
919 | trait Foo<T, U, V> { | ||
920 | fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>; | ||
921 | } | ||
922 | struct Bar; | ||
923 | |||
924 | impl Foo<u32, Vec<usize>, u8> for Bar { | ||
925 | fn f$0 | ||
926 | } | ||
927 | "#, | ||
928 | r#" | ||
929 | trait Foo<T, U, V> { | ||
930 | fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>; | ||
931 | } | ||
932 | struct Bar; | ||
933 | |||
934 | impl Foo<u32, Vec<usize>, u8> for Bar { | ||
935 | fn function(bar: Vec<u32>, baz: Vec<usize>) -> Arc<Vec<u8>> { | ||
936 | $0 | ||
937 | } | ||
938 | } | ||
939 | "#, | ||
940 | ) | ||
941 | } | ||
942 | |||
943 | #[test] | ||
944 | fn generics_are_inlined_in_associated_const() { | ||
945 | check_edit( | ||
946 | "BAR", | ||
947 | r#" | ||
948 | trait Foo<T> { | ||
949 | const BAR: T; | ||
950 | } | ||
951 | struct Bar; | ||
952 | |||
953 | impl Foo<u32> for Bar { | ||
954 | const B$0; | ||
955 | } | ||
956 | "#, | ||
957 | r#" | ||
958 | trait Foo<T> { | ||
959 | const BAR: T; | ||
960 | } | ||
961 | struct Bar; | ||
962 | |||
963 | impl Foo<u32> for Bar { | ||
964 | const BAR: u32 = ; | ||
965 | } | ||
966 | "#, | ||
967 | ) | ||
968 | } | ||
969 | |||
970 | #[test] | ||
971 | fn generics_are_inlined_in_where_clause() { | ||
972 | check_edit( | ||
973 | "function", | ||
974 | r#" | ||
975 | trait SomeTrait<T> {} | ||
976 | |||
977 | trait Foo<T> { | ||
978 | fn function() | ||
979 | where Self: SomeTrait<T>; | ||
980 | } | ||
981 | struct Bar; | ||
982 | |||
983 | impl Foo<u32> for Bar { | ||
984 | fn f$0 | ||
985 | } | ||
986 | "#, | ||
987 | r#" | ||
988 | trait SomeTrait<T> {} | ||
989 | |||
990 | trait Foo<T> { | ||
991 | fn function() | ||
992 | where Self: SomeTrait<T>; | ||
993 | } | ||
994 | struct Bar; | ||
995 | |||
996 | impl Foo<u32> for Bar { | ||
997 | fn function() | ||
998 | where Self: SomeTrait<u32> { | ||
999 | $0 | ||
1000 | } | ||
1001 | } | ||
1002 | "#, | ||
1003 | ) | ||
1004 | } | ||
782 | } | 1005 | } |