diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-06-18 17:47:58 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-06-18 17:47:58 +0100 |
commit | 0657812bc2f95037b5e2efbe8fbfc80fb28492c1 (patch) | |
tree | 1e2df163514b45efc2af9b159bd44f62be0cf9a6 /crates | |
parent | 7eb843b218b5e10b39a5171f5b9689ec6c2d71c3 (diff) | |
parent | b3e5c648e0def7a359a75b2b5023c18aceda83d3 (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')
-rw-r--r-- | crates/ide_assists/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ide_assists/src/utils.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/trait_impl.rs | 309 | ||||
-rw-r--r-- | crates/ide_db/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ide_db/src/path_transform.rs (renamed from crates/ide_assists/src/path_transform.rs) | 12 |
5 files changed, 275 insertions, 54 deletions
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index fa378a622..86a57ce5d 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -15,7 +15,6 @@ mod assist_context; | |||
15 | #[cfg(test)] | 15 | #[cfg(test)] |
16 | mod tests; | 16 | mod tests; |
17 | pub mod utils; | 17 | pub mod utils; |
18 | pub mod path_transform; | ||
19 | 18 | ||
20 | use hir::Semantics; | 19 | use hir::Semantics; |
21 | use ide_db::{base_db::FileRange, RootDatabase}; | 20 | use ide_db::{base_db::FileRange, RootDatabase}; |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 068df005b..0ec236aa0 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -8,6 +8,7 @@ use ast::TypeBoundsOwner; | |||
8 | use hir::{Adt, HasSource, Semantics}; | 8 | use hir::{Adt, HasSource, Semantics}; |
9 | use ide_db::{ | 9 | use ide_db::{ |
10 | helpers::{FamousDefs, SnippetCap}, | 10 | helpers::{FamousDefs, SnippetCap}, |
11 | path_transform::PathTransform, | ||
11 | RootDatabase, | 12 | RootDatabase, |
12 | }; | 13 | }; |
13 | use itertools::Itertools; | 14 | use itertools::Itertools; |
@@ -22,10 +23,7 @@ use syntax::{ | |||
22 | SyntaxNode, TextSize, T, | 23 | SyntaxNode, TextSize, T, |
23 | }; | 24 | }; |
24 | 25 | ||
25 | use crate::{ | 26 | use crate::assist_context::{AssistBuilder, AssistContext}; |
26 | assist_context::{AssistBuilder, AssistContext}, | ||
27 | path_transform::PathTransform, | ||
28 | }; | ||
29 | 27 | ||
30 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { | 28 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { |
31 | extract_trivial_expression(&block) | 29 | extract_trivial_expression(&block) |
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 | } |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 7bbd08d6f..bde8767dd 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -14,6 +14,7 @@ pub mod ty_filter; | |||
14 | pub mod traits; | 14 | pub mod traits; |
15 | pub mod call_info; | 15 | pub mod call_info; |
16 | pub mod helpers; | 16 | pub mod helpers; |
17 | pub mod path_transform; | ||
17 | 18 | ||
18 | pub mod search; | 19 | pub mod search; |
19 | pub mod rename; | 20 | pub mod rename; |
diff --git a/crates/ide_assists/src/path_transform.rs b/crates/ide_db/src/path_transform.rs index 48a7fa06a..f3d7aa920 100644 --- a/crates/ide_assists/src/path_transform.rs +++ b/crates/ide_db/src/path_transform.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! See [`PathTransform`]. | 1 | //! See [`PathTransform`]. |
2 | 2 | ||
3 | use crate::helpers::mod_path_to_ast; | ||
3 | use hir::{HirDisplay, SemanticsScope}; | 4 | use hir::{HirDisplay, SemanticsScope}; |
4 | use ide_db::helpers::mod_path_to_ast; | ||
5 | use rustc_hash::FxHashMap; | 5 | use rustc_hash::FxHashMap; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
@@ -31,14 +31,14 @@ use syntax::{ | |||
31 | /// } | 31 | /// } |
32 | /// } | 32 | /// } |
33 | /// ``` | 33 | /// ``` |
34 | pub(crate) struct PathTransform<'a> { | 34 | pub struct PathTransform<'a> { |
35 | pub(crate) subst: (hir::Trait, ast::Impl), | 35 | pub subst: (hir::Trait, ast::Impl), |
36 | pub(crate) target_scope: &'a SemanticsScope<'a>, | 36 | pub target_scope: &'a SemanticsScope<'a>, |
37 | pub(crate) source_scope: &'a SemanticsScope<'a>, | 37 | pub source_scope: &'a SemanticsScope<'a>, |
38 | } | 38 | } |
39 | 39 | ||
40 | impl<'a> PathTransform<'a> { | 40 | impl<'a> PathTransform<'a> { |
41 | pub(crate) fn apply(&self, item: ast::AssocItem) { | 41 | pub fn apply(&self, item: ast::AssocItem) { |
42 | if let Some(ctx) = self.build_ctx() { | 42 | if let Some(ctx) = self.build_ctx() { |
43 | ctx.apply(item) | 43 | ctx.apply(item) |
44 | } | 44 | } |