diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index fbd499900a9a..bd90cefb2a8b 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs @@ -29,7 +29,7 @@ use crate::{ macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, render_field, render_resolution, render_tuple_field, - type_alias::render_type_alias, + type_alias::{render_type_alias, render_type_alias_with_eq}, RenderContext, }, CompletionContext, CompletionItem, CompletionItemKind, @@ -188,6 +188,14 @@ impl Completions { self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); } + pub(crate) fn add_type_alias_with_eq( + &mut self, + ctx: &CompletionContext, + type_alias: hir::TypeAlias, + ) { + self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); + } + pub(crate) fn add_qualified_enum_variant( &mut self, ctx: &CompletionContext, diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index b1e6b2b775ec..952f052a18ac 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -1,8 +1,9 @@ //! Completion of names from the current scope, e.g. locals and imported items. use hir::ScopeDef; +use syntax::{ast, AstNode}; -use crate::{CompletionContext, Completions}; +use crate::{patterns::ImmediateLocation, CompletionContext, Completions}; pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { if ctx.is_path_disallowed() || !ctx.is_trivial_path() { @@ -43,6 +44,20 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC }); } + if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location { + if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) { + if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) = + ctx.sema.resolve_path(&path_seg.parent_path()) + { + trait_.items(ctx.sema.db).into_iter().for_each(|it| { + if let hir::AssocItem::TypeAlias(alias) = it { + acc.add_type_alias_with_eq(ctx, alias) + } + }); + } + } + } + ctx.scope.process_all_names(&mut |name, res| { if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { cov_mark::hit!(skip_lifetime_completion); @@ -777,4 +792,21 @@ $0 "#]], ) } + + #[test] + fn completes_assoc_types_in_dynimpl_trait() { + check( + r#" +trait Foo { + type Bar; +} + +fn foo(_: impl Foo) {} +"#, + expect![[r#" + ta Bar = type Bar; + tt Foo + "#]], + ); + } } diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index ee87bf461444..81d7a1a1d471 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs @@ -47,6 +47,9 @@ pub(crate) enum ImmediateLocation { receiver_is_ambiguous_float_literal: bool, }, // Original file ast node + // Only set from a type arg + GenericArgList(ast::GenericArgList), + // Original file ast node /// The record expr of the field name we are completing RecordExpr(ast::RecordExpr), // Original file ast node @@ -159,7 +162,6 @@ pub(crate) fn determine_location( } } }; - let res = match_ast! { match parent { ast::IdentPat(_it) => ImmediateLocation::IdentPat, @@ -174,6 +176,9 @@ pub(crate) fn determine_location( Some(TRAIT) => ImmediateLocation::Trait, _ => return None, }, + ast::GenericArgList(_it) => sema + .find_node_at_offset_with_macros(original_file, offset) + .map(ImmediateLocation::GenericArgList)?, ast::Module(it) => { if it.item_list().is_none() { ImmediateLocation::ModDeclaration(it) diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs index e47b4c745a8c..e0234171ac19 100644 --- a/crates/ide_completion/src/render/type_alias.rs +++ b/crates/ide_completion/src/render/type_alias.rs @@ -16,7 +16,14 @@ pub(crate) fn render_type_alias<'a>( ctx: RenderContext<'a>, type_alias: hir::TypeAlias, ) -> Option { - TypeAliasRender::new(ctx, type_alias)?.render() + TypeAliasRender::new(ctx, type_alias)?.render(false) +} + +pub(crate) fn render_type_alias_with_eq<'a>( + ctx: RenderContext<'a>, + type_alias: hir::TypeAlias, +) -> Option { + TypeAliasRender::new(ctx, type_alias)?.render(true) } #[derive(Debug)] @@ -32,8 +39,14 @@ impl<'a> TypeAliasRender<'a> { Some(TypeAliasRender { ctx, type_alias, ast_node }) } - fn render(self) -> Option { - let name = self.name()?; + fn render(self, with_eq: bool) -> Option { + let name = self.ast_node.name().map(|name| { + if with_eq { + format!("{} = ", name.text()) + } else { + name.text().to_string() + } + })?; let detail = self.detail(); let mut item = @@ -49,10 +62,6 @@ impl<'a> TypeAliasRender<'a> { Some(item.build()) } - fn name(&self) -> Option { - self.ast_node.name().map(|name| name.text().to_string()) - } - fn detail(&self) -> String { type_label(&self.ast_node) }