Skip to content

Commit 8e08b86

Browse files
committed
Feat: inline generics in const and func trait completions
1 parent 0d863cc commit 8e08b86

File tree

5 files changed

+261
-38
lines changed

5 files changed

+261
-38
lines changed

crates/ide_assists/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ mod assist_context;
1515
#[cfg(test)]
1616
mod tests;
1717
pub mod utils;
18-
pub mod path_transform;
1918

2019
use hir::Semantics;
2120
use ide_db::{base_db::FileRange, RootDatabase};

crates/ide_assists/src/utils.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use ast::TypeBoundsOwner;
88
use hir::{Adt, HasSource, Semantics};
99
use ide_db::{
1010
helpers::{FamousDefs, SnippetCap},
11+
path_transform::PathTransform,
1112
RootDatabase,
1213
};
1314
use itertools::Itertools;
@@ -22,10 +23,7 @@ use syntax::{
2223
SyntaxNode, TextSize, T,
2324
};
2425

25-
use crate::{
26-
assist_context::{AssistBuilder, AssistContext},
27-
path_transform::PathTransform,
28-
};
26+
use crate::assist_context::{AssistBuilder, AssistContext};
2927

3028
pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
3129
extract_trivial_expression(&block)

crates/ide_completion/src/completions/trait_impl.rs

Lines changed: 252 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
//! ```
3333
3434
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};
3636
use syntax::{
3737
ast::{self, edit},
3838
display::function_declaration,
@@ -56,7 +56,9 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
5656
hir::AssocItem::Function(fn_item)
5757
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
5858
{
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+
}
6062
}
6163
hir::AssocItem::TypeAlias(type_item)
6264
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
@@ -66,7 +68,9 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
6668
hir::AssocItem::Const(const_item)
6769
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
6870
{
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+
}
7074
}
7175
_ => {}
7276
});
@@ -129,6 +133,7 @@ fn add_function_impl(
129133
acc: &mut Completions,
130134
ctx: &CompletionContext,
131135
func: hir::Function,
136+
impl_def: hir::Impl,
132137
) {
133138
let fn_name = func.name(ctx.db).to_string();
134139

@@ -147,23 +152,55 @@ fn add_function_impl(
147152
CompletionItemKind::SymbolKind(SymbolKind::Function)
148153
};
149154
let range = replacement_range(ctx, fn_def_node);
150-
if let Some(src) = func.source(ctx.db) {
151-
let function_decl = function_declaration(&src.value);
152-
match ctx.config.snippet_cap {
153-
Some(cap) => {
154-
let snippet = format!("{} {{\n $0\n}}", function_decl);
155-
item.snippet_edit(cap, TextEdit::replace(range, snippet));
156-
}
157-
None => {
158-
let header = format!("{} {{", function_decl);
159-
item.text_edit(TextEdit::replace(range, header));
160-
}
161-
};
162-
item.kind(completion_kind);
163-
item.add_to(acc);
155+
156+
if let Some(source) = func.source(ctx.db) {
157+
let assoc_item = ast::AssocItem::Fn(source.value);
158+
if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
159+
let transformed_fn = match transformed_item {
160+
ast::AssocItem::Fn(func) => func,
161+
_ => unreachable!(),
162+
};
163+
164+
let function_decl = function_declaration(&transformed_fn);
165+
match ctx.config.snippet_cap {
166+
Some(cap) => {
167+
let snippet = format!("{} {{\n $0\n}}", function_decl);
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+
}
164178
}
165179
}
166180

181+
/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
182+
fn 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+
167204
fn add_type_alias_impl(
168205
type_def_node: &SyntaxNode,
169206
acc: &mut Completions,
@@ -188,21 +225,30 @@ fn add_const_impl(
188225
acc: &mut Completions,
189226
ctx: &CompletionContext,
190227
const_: hir::Const,
228+
impl_def: hir::Impl,
191229
) {
192230
let const_name = const_.name(ctx.db).map(|n| n.to_string());
193231

194232
if let Some(const_name) = const_name {
195233
if let Some(source) = const_.source(ctx.db) {
196-
let snippet = make_const_compl_syntax(&source.value);
197-
198-
let range = replacement_range(ctx, const_def_node);
199-
let mut item =
200-
CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
201-
item.text_edit(TextEdit::replace(range, snippet))
202-
.lookup_by(const_name)
203-
.kind(SymbolKind::Const)
204-
.set_documentation(const_.docs(ctx.db));
205-
item.add_to(acc);
234+
let assoc_item = ast::AssocItem::Const(source.value);
235+
if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
236+
let transformed_const = match transformed_item {
237+
ast::AssocItem::Const(const_) => const_,
238+
_ => unreachable!(),
239+
};
240+
241+
let snippet = make_const_compl_syntax(&transformed_const);
242+
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+
}
206252
}
207253
}
208254
}
@@ -779,4 +825,183 @@ impl Foo for T {{
779825
test("Type", "type T$0", "type Type = ");
780826
test("CONST", "const C$0", "const CONST: i32 = ");
781827
}
828+
829+
#[test]
830+
fn generics_are_inlined_in_return_type() {
831+
check_edit(
832+
"function",
833+
r#"
834+
trait Foo<T> {
835+
fn function() -> T;
836+
}
837+
struct Bar;
838+
839+
impl Foo<u32> for Bar {
840+
fn f$0
841+
}
842+
"#,
843+
r#"
844+
trait Foo<T> {
845+
fn function() -> T;
846+
}
847+
struct Bar;
848+
849+
impl 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#"
863+
trait Foo<T> {
864+
fn function(bar: T);
865+
}
866+
struct Bar;
867+
868+
impl Foo<u32> for Bar {
869+
fn f$0
870+
}
871+
"#,
872+
r#"
873+
trait Foo<T> {
874+
fn function(bar: T);
875+
}
876+
struct Bar;
877+
878+
impl 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#"
892+
trait Foo<T> {
893+
fn function(bar: Vec<T>);
894+
}
895+
struct Bar;
896+
897+
impl Foo<u32> for Bar {
898+
fn f$0
899+
}
900+
"#,
901+
r#"
902+
trait Foo<T> {
903+
fn function(bar: Vec<T>);
904+
}
905+
struct Bar;
906+
907+
impl 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#"
921+
trait Foo<T, U, V> {
922+
fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
923+
}
924+
struct Bar;
925+
926+
impl Foo<u32, Vec<usize>, u8> for Bar {
927+
fn f$0
928+
}
929+
"#,
930+
r#"
931+
trait Foo<T, U, V> {
932+
fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
933+
}
934+
struct Bar;
935+
936+
impl 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#"
950+
trait Foo<T> {
951+
const BAR: T;
952+
}
953+
struct Bar;
954+
955+
impl Foo<u32> for Bar {
956+
const B$0
957+
}
958+
"#,
959+
r#"
960+
trait Foo<T> {
961+
const BAR: T;
962+
}
963+
struct Bar;
964+
965+
impl 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#"
977+
trait SomeTrait<T> {}
978+
979+
trait Foo<T> {
980+
fn function()
981+
where Self: SomeTrait<T>;
982+
}
983+
struct Bar;
984+
985+
impl Foo<u32> for Bar {
986+
fn f$0
987+
}
988+
"#,
989+
r#"
990+
trait SomeTrait<T> {}
991+
992+
trait Foo<T> {
993+
fn function()
994+
where Self: SomeTrait<T>;
995+
}
996+
struct Bar;
997+
998+
impl Foo<u32> for Bar {
999+
fn function()
1000+
where Self: SomeTrait<u32> {
1001+
$0
1002+
}
1003+
}
1004+
"#,
1005+
)
1006+
}
7821007
}

crates/ide_db/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod ty_filter;
1414
pub mod traits;
1515
pub mod call_info;
1616
pub mod helpers;
17+
pub mod path_transform;
1718

1819
pub mod search;
1920
pub mod rename;

crates/ide_assists/src/path_transform.rs renamed to crates/ide_db/src/path_transform.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! See [`PathTransform`].
22
3+
use crate::helpers::mod_path_to_ast;
34
use hir::{HirDisplay, SemanticsScope};
4-
use ide_db::helpers::mod_path_to_ast;
55
use rustc_hash::FxHashMap;
66
use syntax::{
77
ast::{self, AstNode},
@@ -31,14 +31,14 @@ use syntax::{
3131
/// }
3232
/// }
3333
/// ```
34-
pub(crate) struct PathTransform<'a> {
35-
pub(crate) subst: (hir::Trait, ast::Impl),
36-
pub(crate) target_scope: &'a SemanticsScope<'a>,
37-
pub(crate) source_scope: &'a SemanticsScope<'a>,
34+
pub struct PathTransform<'a> {
35+
pub subst: (hir::Trait, ast::Impl),
36+
pub target_scope: &'a SemanticsScope<'a>,
37+
pub source_scope: &'a SemanticsScope<'a>,
3838
}
3939

4040
impl<'a> PathTransform<'a> {
41-
pub(crate) fn apply(&self, item: ast::AssocItem) {
41+
pub fn apply(&self, item: ast::AssocItem) {
4242
if let Some(ctx) = self.build_ctx() {
4343
ctx.apply(item)
4444
}

0 commit comments

Comments
 (0)