From c4fcfa2b0d516b9790fa8abdf96bb2308657d60a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 25 Aug 2019 13:57:03 +0200 Subject: Properly format `impl Trait` types It's a bit complicated because we basically have to 'undo' the desugaring, and the result is very dependent on the specifics of the desugaring and will probably produce weird results otherwise. --- crates/ra_hir/src/ty.rs | 112 +++++++++++++++++++++++++++++++++++------- crates/ra_hir/src/ty/tests.rs | 8 +-- 2 files changed, 97 insertions(+), 23 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 035491bff..c0c609d78 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -121,6 +121,16 @@ pub struct ProjectionTy { } impl ProjectionTy { + pub fn trait_ref(&self, db: &impl HirDatabase) -> TraitRef { + TraitRef { + trait_: self + .associated_ty + .parent_trait(db) + .expect("projection ty without parent trait"), + substs: self.parameters.clone(), + } + } + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { self.parameters.walk(f); } @@ -341,6 +351,21 @@ impl GenericPredicate { } } + pub fn is_implemented(&self) -> bool { + match self { + GenericPredicate::Implemented(_) => true, + _ => false, + } + } + + pub fn trait_ref(&self, db: &impl HirDatabase) -> Option { + match self { + GenericPredicate::Implemented(tr) => Some(tr.clone()), + GenericPredicate::Projection(proj) => Some(proj.projection_ty.trait_ref(db)), + GenericPredicate::Error => None, + } + } + pub fn subst(self, substs: &Substs) -> GenericPredicate { match self { GenericPredicate::Implemented(trait_ref) => { @@ -769,23 +794,66 @@ impl HirDisplay for Ty { Ty::Opaque(_) => write!(f, "impl ")?, _ => unreachable!(), }; - // looping by hand here just to format the bounds in a slightly nicer way + // Note: This code is written to produce nice results (i.e. + // corresponding to surface Rust) for types that can occur in + // actual Rust. It will have weird results if the predicates + // aren't as expected (i.e. self types = $0, projection + // predicates for a certain trait come after the Implemented + // predicate for that trait). let mut first = true; + let mut angle_open = false; for p in predicates.iter() { - if !first { - write!(f, " + ")?; - } - first = false; match p { - // don't show the $0 self type GenericPredicate::Implemented(trait_ref) => { - trait_ref.hir_fmt_ext(f, false)? + if angle_open { + write!(f, ">")?; + } + if !first { + write!(f, " + ")?; + } + // We assume that the self type is $0 (i.e. the + // existential) here, which is the only thing that's + // possible in actual Rust, and hence don't print it + write!( + f, + "{}", + trait_ref.trait_.name(f.db).unwrap_or_else(Name::missing) + )?; + if trait_ref.substs.len() > 1 { + write!(f, "<")?; + f.write_joined(&trait_ref.substs[1..], ", ")?; + // there might be assoc type bindings, so we leave the angle brackets open + angle_open = true; + } } - GenericPredicate::Projection(_projection_pred) => { - // TODO show something + GenericPredicate::Projection(projection_pred) => { + // in types in actual Rust, these will always come + // after the corresponding Implemented predicate + if angle_open { + write!(f, ", ")?; + } else { + write!(f, "<")?; + angle_open = true; + } + let name = projection_pred.projection_ty.associated_ty.name(f.db); + write!(f, "{} = ", name)?; + projection_pred.ty.hir_fmt(f)?; + } + GenericPredicate::Error => { + if angle_open { + // impl Trait + write!(f, ", ")?; + } else if !first { + // impl Trait + {error} + write!(f, " + ")?; + } + p.hir_fmt(f)?; } - GenericPredicate::Error => p.hir_fmt(f)?, } + first = false; + } + if angle_open { + write!(f, ">")?; } } Ty::Unknown => write!(f, "{{unknown}}")?, @@ -796,13 +864,12 @@ impl HirDisplay for Ty { } impl TraitRef { - fn hir_fmt_ext( - &self, - f: &mut HirFormatter, - with_self_ty: bool, - ) -> fmt::Result { - if with_self_ty { - write!(f, "{}: ", self.substs[0].display(f.db),)?; + fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { + self.substs[0].hir_fmt(f)?; + if use_as { + write!(f, " as ")?; + } else { + write!(f, ": ")?; } write!(f, "{}", self.trait_.name(f.db).unwrap_or_else(Name::missing))?; if self.substs.len() > 1 { @@ -816,7 +883,7 @@ impl TraitRef { impl HirDisplay for TraitRef { fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - self.hir_fmt_ext(f, true) + self.hir_fmt_ext(f, false) } } @@ -831,7 +898,14 @@ impl HirDisplay for GenericPredicate { match self { GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, GenericPredicate::Projection(projection_pred) => { - // TODO print something + write!(f, "<")?; + projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; + write!( + f, + ">::{} = {}", + projection_pred.projection_ty.associated_ty.name(f.db), + projection_pred.ty.display(f.db) + )?; } GenericPredicate::Error => write!(f, "{{error}}")?, } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3e743ef58..d92d4659b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3586,7 +3586,7 @@ fn test>(x: T, y: impl Trait) { [166; 169) '{t}': T [167; 168) 't': T [257; 258) 'x': T - [263; 264) 'y': impl Trait + + [263; 264) 'y': impl Trait [290; 398) '{ ...r>); }': () [296; 299) 'get': fn get(T) -> ::Type [296; 302) 'get(x)': {unknown} @@ -3594,12 +3594,12 @@ fn test>(x: T, y: impl Trait) { [308; 312) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [308; 315) 'get2(x)': {unknown} [313; 314) 'x': T - [321; 324) 'get': fn get(T) -> ::Type + [321; 324) 'get': fn get>(T) -> ::Type [321; 327) 'get(y)': {unknown} - [325; 326) 'y': impl Trait + + [325; 326) 'y': impl Trait [333; 337) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [333; 340) 'get2(y)': {unknown} - [338; 339) 'y': impl Trait + + [338; 339) 'y': impl Trait [346; 349) 'get': fn get>(T) -> ::Type [346; 357) 'get(set(S))': u64 [350; 353) 'set': fn set>(T) -> T -- cgit v1.2.3