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