diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index fa378a622dc..86a57ce5dca 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs @@ -15,7 +15,6 @@ mod assist_context; #[cfg(test)] mod tests; pub mod utils; -pub mod path_transform; use hir::Semantics; use ide_db::{base_db::FileRange, RootDatabase}; diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 068df005bfa..0ec236aa0c7 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs @@ -8,6 +8,7 @@ use ast::TypeBoundsOwner; use hir::{Adt, HasSource, Semantics}; use ide_db::{ helpers::{FamousDefs, SnippetCap}, + path_transform::PathTransform, RootDatabase, }; use itertools::Itertools; @@ -22,10 +23,7 @@ use syntax::{ SyntaxNode, TextSize, T, }; -use crate::{ - assist_context::{AssistBuilder, AssistContext}, - path_transform::PathTransform, -}; +use crate::assist_context::{AssistBuilder, AssistContext}; pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { 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 dc1d198cc23..1f6b959af77 100644 --- a/crates/ide_completion/src/completions/trait_impl.rs +++ b/crates/ide_completion/src/completions/trait_impl.rs @@ -32,7 +32,7 @@ //! ``` use hir::{self, HasAttrs, HasSource}; -use ide_db::{traits::get_missing_assoc_items, SymbolKind}; +use ide_db::{path_transform::PathTransform, traits::get_missing_assoc_items, SymbolKind}; use syntax::{ ast::{self, edit}, display::function_declaration, @@ -56,7 +56,9 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext hir::AssocItem::Function(fn_item) if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => { - add_function_impl(&trigger, acc, ctx, fn_item) + if let Some(impl_def) = ctx.sema.to_def(&impl_def) { + add_function_impl(&trigger, acc, ctx, fn_item, impl_def) + } } hir::AssocItem::TypeAlias(type_item) if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias => @@ -66,7 +68,9 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext hir::AssocItem::Const(const_item) if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const => { - add_const_impl(&trigger, acc, ctx, const_item) + if let Some(impl_def) = ctx.sema.to_def(&impl_def) { + add_const_impl(&trigger, acc, ctx, const_item, impl_def) + } } _ => {} }); @@ -129,6 +133,7 @@ fn add_function_impl( acc: &mut Completions, ctx: &CompletionContext, func: hir::Function, + impl_def: hir::Impl, ) { let fn_name = func.name(ctx.db).to_string(); @@ -147,23 +152,55 @@ fn add_function_impl( CompletionItemKind::SymbolKind(SymbolKind::Function) }; let range = replacement_range(ctx, fn_def_node); - if let Some(src) = func.source(ctx.db) { - let function_decl = function_declaration(&src.value); - match ctx.config.snippet_cap { - Some(cap) => { - let snippet = format!("{} {{\n $0\n}}", function_decl); - item.snippet_edit(cap, TextEdit::replace(range, snippet)); - } - None => { - let header = format!("{} {{", function_decl); - item.text_edit(TextEdit::replace(range, header)); - } - }; - item.kind(completion_kind); - item.add_to(acc); + + if let Some(source) = func.source(ctx.db) { + let assoc_item = ast::AssocItem::Fn(source.value); + if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + let transformed_fn = match transformed_item { + ast::AssocItem::Fn(func) => func, + _ => unreachable!(), + }; + + let function_decl = function_declaration(&transformed_fn); + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = format!("{} {{\n $0\n}}", function_decl); + item.snippet_edit(cap, TextEdit::replace(range, snippet)); + } + None => { + let header = format!("{} {{", function_decl); + item.text_edit(TextEdit::replace(range, header)); + } + }; + item.kind(completion_kind); + item.add_to(acc); + } } } +/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. +fn get_transformed_assoc_item( + ctx: &CompletionContext, + assoc_item: ast::AssocItem, + impl_def: hir::Impl, +) -> Option { + let assoc_item = assoc_item.clone_for_update(); + let trait_ = impl_def.trait_(ctx.db)?; + let source_scope = &ctx.sema.scope_for_def(trait_); + let target_scope = &ctx.sema.scope(impl_def.source(ctx.db)?.syntax().value); + let transform = PathTransform { + subst: (trait_, impl_def.source(ctx.db)?.value), + source_scope, + target_scope, + }; + + transform.apply(assoc_item.clone()); + Some(match assoc_item { + ast::AssocItem::Fn(func) => ast::AssocItem::Fn(edit::remove_attrs_and_docs(&func)), + _ => assoc_item, + }) +} + fn add_type_alias_impl( type_def_node: &SyntaxNode, acc: &mut Completions, @@ -188,21 +225,30 @@ fn add_const_impl( acc: &mut Completions, ctx: &CompletionContext, const_: hir::Const, + impl_def: hir::Impl, ) { let const_name = const_.name(ctx.db).map(|n| n.to_string()); if let Some(const_name) = const_name { if let Some(source) = const_.source(ctx.db) { - let snippet = make_const_compl_syntax(&source.value); + let assoc_item = ast::AssocItem::Const(source.value); + if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + let transformed_const = match transformed_item { + ast::AssocItem::Const(const_) => const_, + _ => unreachable!(), + }; - let range = replacement_range(ctx, const_def_node); - let mut item = - CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); - item.text_edit(TextEdit::replace(range, snippet)) - .lookup_by(const_name) - .kind(SymbolKind::Const) - .set_documentation(const_.docs(ctx.db)); - item.add_to(acc); + let snippet = make_const_compl_syntax(&transformed_const); + + let range = replacement_range(ctx, const_def_node); + let mut item = + CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); + item.text_edit(TextEdit::replace(range, snippet)) + .lookup_by(const_name) + .kind(SymbolKind::Const) + .set_documentation(const_.docs(ctx.db)); + item.add_to(acc); + } } } } @@ -779,4 +825,183 @@ impl Foo for T {{ test("Type", "type T$0", "type Type = "); test("CONST", "const C$0", "const CONST: i32 = "); } + + #[test] + fn generics_are_inlined_in_return_type() { + check_edit( + "function", + r#" +trait Foo { + fn function() -> T; +} +struct Bar; + +impl Foo for Bar { + fn f$0 +} +"#, + r#" +trait Foo { + fn function() -> T; +} +struct Bar; + +impl Foo for Bar { + fn function() -> u32 { + $0 +} +} +"#, + ) + } + + #[test] + fn generics_are_inlined_in_parameter() { + check_edit( + "function", + r#" +trait Foo { + fn function(bar: T); +} +struct Bar; + +impl Foo for Bar { + fn f$0 +} +"#, + r#" +trait Foo { + fn function(bar: T); +} +struct Bar; + +impl Foo for Bar { + fn function(bar: u32) { + $0 +} +} +"#, + ) + } + + #[test] + fn generics_are_inlined_when_part_of_other_types() { + check_edit( + "function", + r#" +trait Foo { + fn function(bar: Vec); +} +struct Bar; + +impl Foo for Bar { + fn f$0 +} +"#, + r#" +trait Foo { + fn function(bar: Vec); +} +struct Bar; + +impl Foo for Bar { + fn function(bar: Vec) { + $0 +} +} +"#, + ) + } + + #[test] + fn generics_are_inlined_complex() { + check_edit( + "function", + r#" +trait Foo { + fn function(bar: Vec, baz: U) -> Arc>; +} +struct Bar; + +impl Foo, u8> for Bar { + fn f$0 +} +"#, + r#" +trait Foo { + fn function(bar: Vec, baz: U) -> Arc>; +} +struct Bar; + +impl Foo, u8> for Bar { + fn function(bar: Vec, baz: Vec) -> Arc> { + $0 +} +} +"#, + ) + } + + #[test] + fn generics_are_inlined_in_associated_const() { + check_edit( + "BAR", + r#" +trait Foo { + const BAR: T; +} +struct Bar; + +impl Foo for Bar { + const B$0 +} +"#, + r#" +trait Foo { + const BAR: T; +} +struct Bar; + +impl Foo for Bar { + const BAR: u32 = +} +"#, + ) + } + + #[test] + fn generics_are_inlined_in_where_clause() { + check_edit( + "function", + r#" +trait SomeTrait {} + +trait Foo { + fn function() + where Self: SomeTrait; +} +struct Bar; + +impl Foo for Bar { + fn f$0 +} +"#, + r#" +trait SomeTrait {} + +trait Foo { + fn function() + where Self: SomeTrait; +} +struct Bar; + +impl Foo for Bar { + fn function() +where Self: SomeTrait { + $0 +} +} +"#, + ) + } } diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 7bbd08d6f17..bde8767dd68 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -14,6 +14,7 @@ pub mod ty_filter; pub mod traits; pub mod call_info; pub mod helpers; +pub mod path_transform; pub mod search; pub mod rename; diff --git a/crates/ide_assists/src/path_transform.rs b/crates/ide_db/src/path_transform.rs similarity index 95% rename from crates/ide_assists/src/path_transform.rs rename to crates/ide_db/src/path_transform.rs index 48a7fa06a22..f3d7aa920c0 100644 --- a/crates/ide_assists/src/path_transform.rs +++ b/crates/ide_db/src/path_transform.rs @@ -1,7 +1,7 @@ //! See [`PathTransform`]. +use crate::helpers::mod_path_to_ast; use hir::{HirDisplay, SemanticsScope}; -use ide_db::helpers::mod_path_to_ast; use rustc_hash::FxHashMap; use syntax::{ ast::{self, AstNode}, @@ -31,14 +31,14 @@ use syntax::{ /// } /// } /// ``` -pub(crate) struct PathTransform<'a> { - pub(crate) subst: (hir::Trait, ast::Impl), - pub(crate) target_scope: &'a SemanticsScope<'a>, - pub(crate) source_scope: &'a SemanticsScope<'a>, +pub struct PathTransform<'a> { + pub subst: (hir::Trait, ast::Impl), + pub target_scope: &'a SemanticsScope<'a>, + pub source_scope: &'a SemanticsScope<'a>, } impl<'a> PathTransform<'a> { - pub(crate) fn apply(&self, item: ast::AssocItem) { + pub fn apply(&self, item: ast::AssocItem) { if let Some(ctx) = self.build_ctx() { ctx.apply(item) }