From 3bfe1d5d78e09aa1ba59b9d5740b211e7e0ce1a5 Mon Sep 17 00:00:00 2001 From: oxalica Date: Wed, 9 Aug 2023 00:04:55 +0800 Subject: [PATCH] Display fully qualified associated types correctly Currently they are formatted in the internal `Trait::Assoc` forms where `hir_ty::TypeRef` is formatted, like hover. --- crates/hir-ty/src/display.rs | 42 ++++++++++++++++++++++++++++------ crates/ide/src/hover/tests.rs | 43 +++++++++++++++++++++++++++++++++++ 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 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { } } + // 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::Assoc` + // => + // `>::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 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { 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 `` form. - write!(f, "Self = ")?; - } + write!(f, "<")?; } else { write!(f, ", ")?; } @@ -1857,6 +1873,7 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { for binding in generic_args.bindings.iter() { if first { first = false; + write!(f, "<")?; } else { write!(f, ", ")?; } @@ -1872,9 +1889,20 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { } } } - 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 `::Assoc`. + if !first { + write!(f, ">")?; + } + + // Current position: `|` + 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 @@ -1556,6 +1556,49 @@ fn foo(a: i32, b: i32) -> i32 ); } +#[test] +fn test_hover_function_associated_type_params() { + check( + r#" +trait Foo { type Bar; } +impl Foo for i32 { type Bar = i64; } +fn foo(arg: ::Bar) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: ::Bar) + ``` + "#]], + ); + + check( + r#" +trait Foo { type Bar; } +impl Foo for i32 { type Bar = i32; } +fn foo(arg: <>::Bar as Foo>::Bar) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: <>::Bar as Foo>::Bar) + ``` + "#]], + ); +} + #[test] fn test_hover_function_pointer_show_identifiers() { check(