diff options
| author | bors <bors@rust-lang.org> | 2023-08-08 16:42:39 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-08-08 16:42:39 +0000 |
| commit | 6918eb6ebd4e31dbba317f6e8e659fbc4c64ebaa (patch) | |
| tree | 1b521da8047ba2ad1f033765f7f69c4a67d4651a | |
| parent | af4ba46b4038776ead0953bfbbe0b5fafc916786 (diff) | |
| parent | 3bfe1d5d78e09aa1ba59b9d5740b211e7e0ce1a5 (diff) | |
| download | rust-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.rs | 42 | ||||
| -rw-r--r-- | 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 @@ 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;"#, |
