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.
This commit is contained in:
oxalica 2023-08-09 00:04:55 +08:00
parent af4ba46b40
commit 3bfe1d5d78
No known key found for this signature in database
GPG key ID: D425CB23CADE82D9
2 changed files with 78 additions and 7 deletions

View file

@ -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(())
}
}

View file

@ -1556,6 +1556,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(