aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-06-18 17:47:58 +0100
committerGitHub <[email protected]>2021-06-18 17:47:58 +0100
commit0657812bc2f95037b5e2efbe8fbfc80fb28492c1 (patch)
tree1e2df163514b45efc2af9b159bd44f62be0cf9a6 /crates/ide_completion
parent7eb843b218b5e10b39a5171f5b9689ec6c2d71c3 (diff)
parentb3e5c648e0def7a359a75b2b5023c18aceda83d3 (diff)
Merge #9321
9321: Inline generics in const and function trait completions r=Veykril a=RDambrosio016 This PR does a couple of things: - moves path_transform from ide_assists to ide_db to be shared by both assists and completions - when completing a const or a function for a trait, it will "inline" any generics in those associated items instead of leaving the generic's name. For example: ```rust trait Foo<T> { const BAR: T; fn foo() -> T; } struct Bar; impl Foo<u32> for Bar { // autocompletes to this fn foo() -> u32; // and not this (old) fn foo() -> T; // also works for associated consts and where clauses const BAR: u32 = /* */ } ``` Currently this does not work for const generics, because `PathTransform` does not seem to account for them. If this should work on const generics too, `PathTransform` will need to be changed. However, it is uncommon to implement a trait only for a single const value, so this isnt a huge concern. Co-authored-by: rdambrosio <[email protected]>
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs309
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
34use hir::{self, HasAttrs, HasSource}; 34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind}; 35use ide_db::{path_transform::PathTransform, traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use 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
53pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 53pub(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.
180fn 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
167fn add_type_alias_impl( 202fn 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#"
832trait Foo<T> {
833 fn function() -> T;
834}
835struct Bar;
836
837impl Foo<u32> for Bar {
838 fn f$0
839}
840"#,
841 r#"
842trait Foo<T> {
843 fn function() -> T;
844}
845struct Bar;
846
847impl 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#"
861trait Foo<T> {
862 fn function(bar: T);
863}
864struct Bar;
865
866impl Foo<u32> for Bar {
867 fn f$0
868}
869"#,
870 r#"
871trait Foo<T> {
872 fn function(bar: T);
873}
874struct Bar;
875
876impl 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#"
890trait Foo<T> {
891 fn function(bar: Vec<T>);
892}
893struct Bar;
894
895impl Foo<u32> for Bar {
896 fn f$0
897}
898"#,
899 r#"
900trait Foo<T> {
901 fn function(bar: Vec<T>);
902}
903struct Bar;
904
905impl 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#"
919trait Foo<T, U, V> {
920 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
921}
922struct Bar;
923
924impl Foo<u32, Vec<usize>, u8> for Bar {
925 fn f$0
926}
927"#,
928 r#"
929trait Foo<T, U, V> {
930 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
931}
932struct Bar;
933
934impl 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#"
948trait Foo<T> {
949 const BAR: T;
950}
951struct Bar;
952
953impl Foo<u32> for Bar {
954 const B$0;
955}
956"#,
957 r#"
958trait Foo<T> {
959 const BAR: T;
960}
961struct Bar;
962
963impl 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#"
975trait SomeTrait<T> {}
976
977trait Foo<T> {
978 fn function()
979 where Self: SomeTrait<T>;
980}
981struct Bar;
982
983impl Foo<u32> for Bar {
984 fn f$0
985}
986"#,
987 r#"
988trait SomeTrait<T> {}
989
990trait Foo<T> {
991 fn function()
992 where Self: SomeTrait<T>;
993}
994struct Bar;
995
996impl Foo<u32> for Bar {
997 fn function()
998where Self: SomeTrait<u32> {
999 $0
1000}
1001}
1002"#,
1003 )
1004 }
782} 1005}