about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-08 16:42:39 +0000
committerbors <bors@rust-lang.org>2023-08-08 16:42:39 +0000
commit6918eb6ebd4e31dbba317f6e8e659fbc4c64ebaa (patch)
tree1b521da8047ba2ad1f033765f7f69c4a67d4651a
parentaf4ba46b4038776ead0953bfbbe0b5fafc916786 (diff)
parent3bfe1d5d78e09aa1ba59b9d5740b211e7e0ce1a5 (diff)
downloadrust-6918eb6ebd4e31dbba317f6e8e659fbc4c64ebaa.tar.gz
rust-6918eb6ebd4e31dbba317f6e8e659fbc4c64ebaa.zip
Auto merge of #15416 - oxalica:fix/hover-assoc-type, r=lowr
Display fully qualified associated types correctly

Currently they are formatted in the internal `Trait<Self = Type>::Assoc` forms where `hir_ty::TypeRef` is formatted, like hover.

There is no test of `TypeRef::hir_fmt` in crate `hir-ty` (verified by replacing it with a `panic!()`), most tests are about inference and printing the real `Ty` instead of `TypeRef`. So I added the test in `ide::hover`.
-rw-r--r--crates/hir-ty/src/display.rs42
-rw-r--r--crates/ide/src/hover/tests.rs43
2 files changed, 78 insertions, 7 deletions
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 1b4ee4613d6..f6d6b00d740 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -1809,6 +1809,25 @@ impl HirDisplay for Path {
             }
         }
 
+        // Convert trait's `Self` bound back to the surface syntax. Note there is no associated
+        // trait, so there can only be one path segment that `has_self_type`. The `Self` type
+        // itself can contain further qualified path through, which will be handled by recursive
+        // `hir_fmt`s.
+        //
+        // `trait_mod::Trait<Self = type_mod::Type, Args>::Assoc`
+        // =>
+        // `<type_mod::Type as trait_mod::Trait<Args>>::Assoc`
+        let trait_self_ty = self.segments().iter().find_map(|seg| {
+            let generic_args = seg.args_and_bindings?;
+            generic_args.has_self_type.then(|| &generic_args.args[0])
+        });
+        if let Some(ty) = trait_self_ty {
+            write!(f, "<")?;
+            ty.hir_fmt(f)?;
+            write!(f, " as ")?;
+            // Now format the path of the trait...
+        }
+
         for (seg_idx, segment) in self.segments().iter().enumerate() {
             if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
                 write!(f, "::")?;
@@ -1840,15 +1859,12 @@ impl HirDisplay for Path {
                     return Ok(());
                 }
 
-                write!(f, "<")?;
                 let mut first = true;
-                for arg in generic_args.args.iter() {
+                // Skip the `Self` bound if exists. It's handled outside the loop.
+                for arg in &generic_args.args[generic_args.has_self_type as usize..] {
                     if first {
                         first = false;
-                        if generic_args.has_self_type {
-                            // FIXME: Convert to `<Ty as Trait>` form.
-                            write!(f, "Self = ")?;
-                        }
+                        write!(f, "<")?;
                     } else {
                         write!(f, ", ")?;
                     }
@@ -1857,6 +1873,7 @@ impl HirDisplay for Path {
                 for binding in generic_args.bindings.iter() {
                     if first {
                         first = false;
+                        write!(f, "<")?;
                     } else {
                         write!(f, ", ")?;
                     }
@@ -1872,9 +1889,20 @@ impl HirDisplay for Path {
                         }
                     }
                 }
-                write!(f, ">")?;
+
+                // There may be no generic arguments to print, in case of a trait having only a
+                // single `Self` bound which is converted to `<Ty as Trait>::Assoc`.
+                if !first {
+                    write!(f, ">")?;
+                }
+
+                // Current position: `<Ty as Trait<Args>|`
+                if generic_args.has_self_type {
+                    write!(f, ">")?;
+                }
             }
         }
+
         Ok(())
     }
 }
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 133a488e1de..541cce8c5bf 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1557,6 +1557,49 @@ fn test_hover_function_show_types() {
 }
 
 #[test]
+fn test_hover_function_associated_type_params() {
+    check(
+        r#"
+trait Foo { type Bar; }
+impl Foo for i32 { type Bar = i64; }
+fn foo(arg: <i32 as Foo>::Bar) {}
+fn main() { foo$0; }
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            fn foo(arg: <i32 as Foo>::Bar)
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+trait Foo<T> { type Bar<U>; }
+impl Foo<i64> for i32 { type Bar<U> = i32; }
+fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) {}
+fn main() { foo$0; }
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>)
+            ```
+        "#]],
+    );
+}
+
+#[test]
 fn test_hover_function_pointer_show_identifiers() {
     check(
         r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,