From 15dfeabb96b370e9fab1ddbb8b39a9dcefc3764b Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 15 Nov 2022 12:05:11 +0100 Subject: [PATCH] Fix GAT completion not including generic parameters --- .../src/completions/item_list/trait_impl.rs | 77 +++++++++++++++---- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index e82cbfdcb84..b612cdc4a17 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -157,7 +157,7 @@ fn complete_trait_impl( add_function_impl(acc, ctx, replacement_range, func, hir_impl) } (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - add_type_alias_impl(acc, ctx, replacement_range, type_alias) + add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl) } (hir::AssocItem::Const(const_), All | Const) => { add_const_impl(acc, ctx, replacement_range, const_, hir_impl) @@ -247,24 +247,50 @@ fn add_type_alias_impl( ctx: &CompletionContext<'_>, replacement_range: TextRange, type_alias: hir::TypeAlias, + impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db); - let (alias_name, escaped_name) = - (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str()); + let alias_name = type_alias.name(ctx.db).unescaped().to_smol_str(); let label = format!("type {} =", alias_name); - let replacement = format!("type {} = ", escaped_name); let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label); item.lookup_by(format!("type {}", alias_name)) .set_documentation(type_alias.docs(ctx.db)) .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); - match ctx.config.snippet_cap { - Some(cap) => item - .snippet_edit(cap, TextEdit::replace(replacement_range, format!("{}$0;", replacement))), - None => item.text_edit(TextEdit::replace(replacement_range, replacement)), - }; - item.add_to(acc); + + if let Some(source) = ctx.sema.source(type_alias) { + let assoc_item = ast::AssocItem::TypeAlias(source.value); + if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + let transformed_ty = match transformed_item { + ast::AssocItem::TypeAlias(ty) => ty, + _ => unreachable!(), + }; + + let start = transformed_ty.syntax().text_range().start(); + let Some(end) = transformed_ty + .eq_token() + .map(|tok| tok.text_range().start()) + .or(transformed_ty.semicolon_token().map(|tok| tok.text_range().start())) else { return }; + + let len = end - start; + let mut decl = transformed_ty.syntax().text().slice(..len).to_string(); + if !decl.ends_with(' ') { + decl.push(' '); + } + decl.push_str("= "); + + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = format!("{}$0;", decl); + item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); + } + None => { + item.text_edit(TextEdit::replace(replacement_range, decl)); + } + }; + item.add_to(acc); + } + } } fn add_const_impl( @@ -350,9 +376,7 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String { .map_or(end, |f| f.text_range().start()); let len = end - start; - let range = TextRange::new(0.into(), len); - - let syntax = node.text().slice(range).to_string(); + let syntax = node.text().slice(..len).to_string(); syntax.trim_end().to_owned() } @@ -1160,6 +1184,31 @@ impl Foo for Test { $0 } } +"#, + ); + } + + #[test] + fn includes_gat_generics() { + check_edit( + "type Ty", + r#" +trait Tr<'b> { + type Ty<'a: 'b, T: Copy, const C: usize>; +} + +impl<'b> Tr<'b> for () { + $0 +} +"#, + r#" +trait Tr<'b> { + type Ty<'a: 'b, T: Copy, const C: usize>; +} + +impl<'b> Tr<'b> for () { + type Ty<'a: 'b, T: Copy, const C: usize> = $0; +} "#, ); }