Skip to content

Commit 0657812

Browse files
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]>
2 parents 7eb843b + b3e5c64 commit 0657812

File tree

5 files changed

+275
-54
lines changed

5 files changed

+275
-54
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: 266 additions & 43 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,
@@ -52,24 +52,26 @@ enum ImplCompletionKind {
5252

5353
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
5454
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 {
56-
hir::AssocItem::Function(fn_item)
57-
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
58-
{
59-
add_function_impl(&trigger, acc, ctx, fn_item)
60-
}
61-
hir::AssocItem::TypeAlias(type_item)
62-
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
63-
{
64-
add_type_alias_impl(&trigger, acc, ctx, type_item)
65-
}
66-
hir::AssocItem::Const(const_item)
67-
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
68-
{
69-
add_const_impl(&trigger, acc, ctx, const_item)
70-
}
71-
_ => {}
72-
});
55+
if let Some(hir_impl) = ctx.sema.to_def(&impl_def) {
56+
get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
57+
hir::AssocItem::Function(fn_item)
58+
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
59+
{
60+
add_function_impl(&trigger, acc, ctx, fn_item, hir_impl)
61+
}
62+
hir::AssocItem::TypeAlias(type_item)
63+
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
64+
{
65+
add_type_alias_impl(&trigger, acc, ctx, type_item)
66+
}
67+
hir::AssocItem::Const(const_item)
68+
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
69+
{
70+
add_const_impl(&trigger, acc, ctx, const_item, hir_impl)
71+
}
72+
_ => {}
73+
});
74+
}
7375
}
7476
}
7577

@@ -129,6 +131,7 @@ fn add_function_impl(
129131
acc: &mut Completions,
130132
ctx: &CompletionContext,
131133
func: hir::Function,
134+
impl_def: hir::Impl,
132135
) {
133136
let fn_name = func.name(ctx.db).to_string();
134137

@@ -147,23 +150,55 @@ fn add_function_impl(
147150
CompletionItemKind::SymbolKind(SymbolKind::Function)
148151
};
149152
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);
153+
154+
if let Some(source) = func.source(ctx.db) {
155+
let assoc_item = ast::AssocItem::Fn(source.value);
156+
if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
157+
let transformed_fn = match transformed_item {
158+
ast::AssocItem::Fn(func) => func,
159+
_ => unreachable!(),
160+
};
161+
162+
let function_decl = function_declaration(&transformed_fn);
163+
match ctx.config.snippet_cap {
164+
Some(cap) => {
165+
let snippet = format!("{} {{\n $0\n}}", function_decl);
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+
}
164176
}
165177
}
166178

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+
167202
fn add_type_alias_impl(
168203
type_def_node: &SyntaxNode,
169204
acc: &mut Completions,
@@ -188,21 +223,30 @@ fn add_const_impl(
188223
acc: &mut Completions,
189224
ctx: &CompletionContext,
190225
const_: hir::Const,
226+
impl_def: hir::Impl,
191227
) {
192228
let const_name = const_.name(ctx.db).map(|n| n.to_string());
193229

194230
if let Some(const_name) = const_name {
195231
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);
232+
let assoc_item = ast::AssocItem::Const(source.value);
233+
if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
234+
let transformed_const = match transformed_item {
235+
ast::AssocItem::Const(const_) => const_,
236+
_ => unreachable!(),
237+
};
238+
239+
let snippet = make_const_compl_syntax(&transformed_const);
240+
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+
}
206250
}
207251
}
208252
}
@@ -779,4 +823,183 @@ impl Foo for T {{
779823
test("Type", "type T$0", "type Type = ");
780824
test("CONST", "const C$0", "const CONST: i32 = ");
781825
}
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+
}
7821005
}

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;

0 commit comments

Comments
 (0)