Skip to content

Commit

Permalink
Auto merge of #15416 - oxalica:fix/hover-assoc-type, r=lowr
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
bors committed Aug 8, 2023
2 parents af4ba46 + 3bfe1d5 commit 6918eb6
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 7 deletions.
42 changes: 35 additions & 7 deletions crates/hir-ty/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, "::")?;
Expand Down Expand Up @@ -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, ", ")?;
}
Expand All @@ -1857,6 +1873,7 @@ impl HirDisplay for Path {
for binding in generic_args.bindings.iter() {
if first {
first = false;
write!(f, "<")?;
} else {
write!(f, ", ")?;
}
Expand All @@ -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(())
}
}
Expand Down
43 changes: 43 additions & 0 deletions crates/ide/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 6918eb6

Please sign in to comment.