From ec9ef9c28325ca8d2c520d4316e2d82281c0cbf1 Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukastw97@gmail.com>
Date: Fri, 11 Jun 2021 23:12:30 +0200
Subject: Complete associated types in dyn and impl trait

---
 crates/ide_completion/src/completions.rs           | 10 ++++++-
 .../src/completions/unqualified_path.rs            | 34 +++++++++++++++++++++-
 crates/ide_completion/src/patterns.rs              |  7 ++++-
 crates/ide_completion/src/render/type_alias.rs     | 23 ++++++++++-----
 4 files changed, 64 insertions(+), 10 deletions(-)

(limited to 'crates/ide_completion/src')

diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index fbd499900..bd90cefb2 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 b1e6b2b77..952f052a1 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<B$0>) {}
+"#,
+            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 ee87bf461..81d7a1a1d 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 e47b4c745..e0234171a 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<CompletionItem> {
-    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<CompletionItem> {
+    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<CompletionItem> {
-        let name = self.name()?;
+    fn render(self, with_eq: bool) -> Option<CompletionItem> {
+        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<String> {
-        self.ast_node.name().map(|name| name.text().to_string())
-    }
-
     fn detail(&self) -> String {
         type_label(&self.ast_node)
     }
-- 
cgit v1.2.3