From e0162a8a56d1c59e185e293f33c38d94a5a2d462 Mon Sep 17 00:00:00 2001 From: Justus K Date: Sun, 2 May 2021 14:48:28 +0200 Subject: [PATCH 1/3] rustdoc: Render `for<'_>` lifetimes in front of where bound --- src/librustdoc/clean/auto_trait.rs | 8 +++- src/librustdoc/clean/inline.rs | 11 +++-- src/librustdoc/clean/mod.rs | 11 ++++- src/librustdoc/clean/simplify.rs | 28 +++++++++---- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/format.rs | 22 ++++++++-- src/librustdoc/json/conversions.rs | 3 +- .../rustdoc/higher-ranked-trait-bounds.rs | 41 +++++++++++++++++++ 8 files changed, 104 insertions(+), 22 deletions(-) create mode 100644 src/test/rustdoc/higher-ranked-trait-bounds.rs diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index a3f63ea1046e3..0828f0b3e518c 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -414,7 +414,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { let mut bounds_vec = bounds.into_iter().collect(); self.sort_where_bounds(&mut bounds_vec); - Some(WherePredicate::BoundPredicate { ty, bounds: bounds_vec }) + Some(WherePredicate::BoundPredicate { + ty, + bounds: bounds_vec, + bound_params: Vec::new(), + }) }) .chain( lifetime_to_bounds.into_iter().filter(|&(_, ref bounds)| !bounds.is_empty()).map( @@ -492,7 +496,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } let p = p.unwrap(); match p { - WherePredicate::BoundPredicate { ty, mut bounds } => { + WherePredicate::BoundPredicate { ty, mut bounds, .. } => { // Writing a projection trait bound of the form // ::Name : ?Sized // is illegal, because ?Sized bounds can only diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 111827aacdff8..e8f0960da79cb 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -566,9 +566,11 @@ fn build_macro(cx: &mut DocContext<'_>, did: DefId, name: Symbol) -> clean::Item fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics { for pred in &mut g.where_predicates { match *pred { - clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref mut bounds } - if *s == kw::SelfUpper => - { + clean::WherePredicate::BoundPredicate { + ty: clean::Generic(ref s), + ref mut bounds, + .. + } if *s == kw::SelfUpper => { bounds.retain(|bound| match *bound { clean::GenericBound::TraitBound( clean::PolyTrait { trait_: clean::ResolvedPath { did, .. }, .. }, @@ -591,6 +593,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: .. }, ref bounds, + .. } => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did), _ => true, }); @@ -605,7 +608,7 @@ fn separate_supertrait_bounds( ) -> (clean::Generics, Vec) { let mut ty_bounds = Vec::new(); g.where_predicates.retain(|pred| match *pred { - clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds } + clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds, .. } if *s == kw::SelfUpper => { ty_bounds.extend(bounds.iter().cloned()); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bc04480ab7c51..634a8dae76382 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -330,6 +330,7 @@ impl Clean for hir::WherePredicate<'_> { hir::WherePredicate::BoundPredicate(ref wbp) => WherePredicate::BoundPredicate { ty: wbp.bounded_ty.clean(cx), bounds: wbp.bounds.clean(cx), + bound_params: wbp.bound_generic_params.into_iter().map(|x| x.clean(cx)).collect(), }, hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { @@ -370,6 +371,7 @@ impl<'a> Clean for ty::PolyTraitPredicate<'a> { WherePredicate::BoundPredicate { ty: poly_trait_ref.skip_binder().self_ty().clean(cx), bounds: vec![poly_trait_ref.clean(cx)], + bound_params: Vec::new(), } } } @@ -402,6 +404,7 @@ impl<'tcx> Clean> for ty::OutlivesPredicate, ty: Some(WherePredicate::BoundPredicate { ty: ty.clean(cx), bounds: vec![GenericBound::Outlives(lt.clean(cx).expect("failed to clean lifetimes"))], + bound_params: Vec::new(), }) } } @@ -567,7 +570,9 @@ impl Clean for hir::Generics<'_> { // to where predicates when such cases occur. for where_pred in &mut generics.where_predicates { match *where_pred { - WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds } => { + WherePredicate::BoundPredicate { + ty: Generic(ref name), ref mut bounds, .. + } => { if bounds.is_empty() { for param in &mut generics.params { match param.kind { @@ -721,7 +726,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx // handled in cleaning associated types let mut sized_params = FxHashSet::default(); where_predicates.retain(|pred| match *pred { - WP::BoundPredicate { ty: Generic(ref g), ref bounds } => { + WP::BoundPredicate { ty: Generic(ref g), ref bounds, .. } => { if bounds.iter().any(|b| b.is_sized_bound(cx)) { sized_params.insert(*g); false @@ -741,6 +746,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx where_predicates.push(WP::BoundPredicate { ty: Type::Generic(tp.name), bounds: vec![GenericBound::maybe_sized(cx)], + bound_params: Vec::new(), }) } } @@ -1117,6 +1123,7 @@ impl Clean for ty::AssocItem { WherePredicate::BoundPredicate { ty: QPath { ref name, ref self_type, ref trait_, .. }, ref bounds, + .. } => (name, self_type, trait_, bounds), _ => return None, }; diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index d4d0a8ce24c7b..3ec0a22a2c09a 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -24,16 +24,20 @@ use crate::core::DocContext; crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { // First, partition the where clause into its separate components - let mut params: BTreeMap<_, Vec<_>> = BTreeMap::new(); + let mut params: BTreeMap<_, (Vec<_>, Vec<_>)> = BTreeMap::new(); let mut lifetimes = Vec::new(); let mut equalities = Vec::new(); let mut tybounds = Vec::new(); for clause in clauses { match clause { - WP::BoundPredicate { ty, bounds } => match ty { - clean::Generic(s) => params.entry(s).or_default().extend(bounds), - t => tybounds.push((t, bounds)), + WP::BoundPredicate { ty, bounds, bound_params } => match ty { + clean::Generic(s) => { + let (b, p) = params.entry(s).or_default(); + b.extend(bounds); + p.extend(bound_params); + } + t => tybounds.push((t, (bounds, bound_params))), }, WP::RegionPredicate { lifetime, bounds } => { lifetimes.push((lifetime, bounds)); @@ -54,7 +58,7 @@ crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { clean::Generic(s) => s, _ => return true, }; - let bounds = match params.get_mut(generic) { + let (bounds, _) = match params.get_mut(generic) { Some(bound) => bound, None => return true, }; @@ -67,10 +71,16 @@ crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { clauses.extend( lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }), ); - clauses.extend( - params.into_iter().map(|(k, v)| WP::BoundPredicate { ty: clean::Generic(k), bounds: v }), - ); - clauses.extend(tybounds.into_iter().map(|(ty, bounds)| WP::BoundPredicate { ty, bounds })); + clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate { + ty: clean::Generic(k), + bounds, + bound_params: params, + })); + clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate { + ty, + bounds, + bound_params, + })); clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs })); clauses } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6a7c3f8caa49f..5d036d4d35b9e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1193,7 +1193,7 @@ impl Lifetime { #[derive(Clone, Debug)] crate enum WherePredicate { - BoundPredicate { ty: Type, bounds: Vec }, + BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, EqPredicate { lhs: Type, rhs: Type }, } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 918a5cb509430..a424932d83fb7 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -249,17 +249,33 @@ crate fn print_where_clause<'a, 'tcx: 'a>( } match pred { - clean::WherePredicate::BoundPredicate { ty, bounds } => { + clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { let bounds = bounds; + let for_prefix = match bound_params.len() { + 0 => String::new(), + _ if f.alternate() => { + format!( + "for<{:#}> ", + comma_sep(bound_params.iter().map(|lt| lt.print())) + ) + } + _ => format!( + "for<{}> ", + comma_sep(bound_params.iter().map(|lt| lt.print())) + ), + }; + if f.alternate() { clause.push_str(&format!( - "{:#}: {:#}", + "{}{:#}: {:#}", + for_prefix, ty.print(cx), print_generic_bounds(bounds, cx) )); } else { clause.push_str(&format!( - "{}: {}", + "{}{}: {}", + for_prefix, ty.print(cx), print_generic_bounds(bounds, cx) )); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 7086dd8c4d258..c844d91096a73 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -328,9 +328,10 @@ impl FromWithTcx for WherePredicate { fn from_tcx(predicate: clean::WherePredicate, tcx: TyCtxt<'_>) -> Self { use clean::WherePredicate::*; match predicate { - BoundPredicate { ty, bounds } => WherePredicate::BoundPredicate { + BoundPredicate { ty, bounds, .. } => WherePredicate::BoundPredicate { ty: ty.into_tcx(tcx), bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(), + // FIXME: add `bound_params` to rustdoc-json-params? }, RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate { lifetime: lifetime.0.to_string(), diff --git a/src/test/rustdoc/higher-ranked-trait-bounds.rs b/src/test/rustdoc/higher-ranked-trait-bounds.rs new file mode 100644 index 0000000000000..b5c55df287b14 --- /dev/null +++ b/src/test/rustdoc/higher-ranked-trait-bounds.rs @@ -0,0 +1,41 @@ +#![crate_name = "foo"] + +trait A<'x> {} + +// @has foo/fn.test1.html +// @has - '//pre' "pub fn test1() where for<'a> &'a T: Iterator," +pub fn test1() +where + for<'a> &'a T: Iterator, +{ +} + +// @has foo/fn.test2.html +// @has - '//pre' "pub fn test2() where for<'a, 'b> &'a T: A<'b>," +pub fn test2() +where + for<'a, 'b> &'a T: A<'b>, +{ +} + +// @has foo/fn.test3.html +// @has - '//pre' "pub fn test3() where F: for<'a, 'b> Fn(&'a u8, &'b u8)," +pub fn test3() +where + F: for<'a, 'b> Fn(&'a u8, &'b u8), +{ +} + +// @has foo/struct.Foo.html +pub struct Foo<'a> { + _x: &'a u8, +} + +impl<'a> Foo<'a> { + // @has - '//code' "pub fn bar() where T: A<'a>," + pub fn bar() + where + T: A<'a>, + { + } +} From 1f65f56461fa72df809fff43975a7e72f08fda44 Mon Sep 17 00:00:00 2001 From: Justus K Date: Tue, 18 May 2021 10:06:24 +0200 Subject: [PATCH 2/3] rustdoc: Render `for<'_>` lifetimes in trait objects --- src/librustdoc/clean/mod.rs | 21 ++++++++++---- src/librustdoc/clean/types.rs | 4 ++- src/librustdoc/html/format.rs | 29 +++++++++++++++---- src/librustdoc/json/conversions.rs | 2 +- src/test/rustdoc/for-lifetime.rs | 12 -------- .../rustdoc/higher-ranked-trait-bounds.rs | 22 ++++++++++---- 6 files changed, 61 insertions(+), 29 deletions(-) delete mode 100644 src/test/rustdoc/for-lifetime.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 634a8dae76382..889e645309c15 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1378,8 +1378,9 @@ impl Clean for hir::Ty<'_> { } TyKind::Path(_) => clean_qpath(&self, cx), TyKind::TraitObject(ref bounds, ref lifetime, _) => { - match bounds[0].clean(cx).trait_ { - ResolvedPath { path, param_names: None, did, is_generic } => { + let cleaned = bounds[0].clean(cx); + match cleaned.trait_ { + ResolvedPath { path, param_names: None, did, is_generic, .. } => { let mut bounds: Vec = bounds[1..] .iter() .map(|bound| { @@ -1392,7 +1393,12 @@ impl Clean for hir::Ty<'_> { if !lifetime.is_elided() { bounds.push(self::GenericBound::Outlives(lifetime.clean(cx))); } - ResolvedPath { path, param_names: Some(bounds), did, is_generic } + ResolvedPath { + path, + param_names: Some((bounds, cleaned.generic_params)), + did, + is_generic, + } } _ => Infer, // shouldn't happen } @@ -1542,7 +1548,12 @@ impl<'tcx> Clean for Ty<'tcx> { let path = external_path(cx, cx.tcx.item_name(did), Some(did), false, bindings, substs); - ResolvedPath { path, param_names: Some(param_names), did, is_generic: false } + ResolvedPath { + path, + param_names: Some((param_names, vec![])), + did, + is_generic: false, + } } ty::Tuple(ref t) => { Tuple(t.iter().map(|t| t.expect_ty()).collect::>().clean(cx)) @@ -2248,7 +2259,7 @@ impl From for SimpleBound { GenericBound::TraitBound(t, mod_) => match t.trait_ { Type::ResolvedPath { path, param_names, .. } => SimpleBound::TraitBound( path.segments, - param_names.map_or_else(Vec::new, |v| { + param_names.map_or_else(Vec::new, |(v, _)| { v.iter().map(|p| SimpleBound::from(p.clone())).collect() }), t.generic_params, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 5d036d4d35b9e..4c94501b80be0 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1407,7 +1407,9 @@ crate enum Type { /// Structs/enums/traits (most that would be an `hir::TyKind::Path`). ResolvedPath { path: Path, - param_names: Option>, + /// If `param_names` is `Some`, this path is a trait object and the Vecs repsresent + /// `(generic bounds, generic parameters)` + param_names: Option<(Vec, Vec)>, did: DefId, /// `true` if is a `T::Name` path for associated types. is_generic: bool, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index a424932d83fb7..e4fb7384affa2 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -646,11 +646,11 @@ fn primitive_link( /// Helper to render type parameters fn tybounds<'a, 'tcx: 'a>( - param_names: &'a Option>, + param_names: Option<&'a Vec>, cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { - display_fn(move |f| match *param_names { - Some(ref params) => { + display_fn(move |f| match param_names { + Some(params) => { for param in params { write!(f, " + ")?; fmt::Display::fmt(¶m.print(cx), f)?; @@ -695,8 +695,27 @@ fn fmt_type<'cx>( match *t { clean::Generic(name) => write!(f, "{}", name), clean::ResolvedPath { did, ref param_names, ref path, is_generic } => { - if param_names.is_some() { + let generic_params = param_names.as_ref().map(|(_, x)| x); + let param_names = param_names.as_ref().map(|(x, _)| x); + + if let Some(generic_params) = generic_params { f.write_str("dyn ")?; + + if !generic_params.is_empty() { + if f.alternate() { + write!( + f, + "for<{:#}> ", + comma_sep(generic_params.iter().map(|g| g.print(cx))) + )?; + } else { + write!( + f, + "for<{}> ", + comma_sep(generic_params.iter().map(|g| g.print(cx))) + )?; + } + } } // Paths like `T::Output` and `Self::Output` should be rendered with all segments. resolved_path(f, did, path, is_generic, use_absolute, cx)?; @@ -835,7 +854,7 @@ fn fmt_type<'cx>( } } } - clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => { + clean::ResolvedPath { param_names: Some((ref v, _)), .. } if !v.is_empty() => { write!(f, "{}{}{}(", amp, lt, m)?; fmt_type(&ty, f, use_absolute, cx)?; write!(f, ")") diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index c844d91096a73..643c0f82ae9f8 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -378,7 +378,7 @@ impl FromWithTcx for Type { id: from_def_id(did.into()), args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), param_names: param_names - .map(|v| v.into_iter().map(|x| x.into_tcx(tcx)).collect()) + .map(|(v, _)| v.into_iter().map(|x| x.into_tcx(tcx)).collect()) .unwrap_or_default(), }, Generic(s) => Type::Generic(s.to_string()), diff --git a/src/test/rustdoc/for-lifetime.rs b/src/test/rustdoc/for-lifetime.rs deleted file mode 100644 index 34a7eae31c790..0000000000000 --- a/src/test/rustdoc/for-lifetime.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![crate_name = "foo"] -#![crate_type = "lib"] - -pub struct Foo { - pub some_func: for<'a> fn(val: &'a i32) -> i32, - pub some_trait: dyn for<'a> Trait<'a>, -} - -// @has foo/struct.Foo.html '//span[@id="structfield.some_func"]' "some_func: for<'a> fn(val: &'a i32) -> i32" -// @has foo/struct.Foo.html '//span[@id="structfield.some_trait"]' "some_trait: dyn Trait<'a>" - -pub trait Trait<'a> {} diff --git a/src/test/rustdoc/higher-ranked-trait-bounds.rs b/src/test/rustdoc/higher-ranked-trait-bounds.rs index b5c55df287b14..492a743fbf29d 100644 --- a/src/test/rustdoc/higher-ranked-trait-bounds.rs +++ b/src/test/rustdoc/higher-ranked-trait-bounds.rs @@ -1,6 +1,7 @@ #![crate_name = "foo"] -trait A<'x> {} +// @has foo/trait.Trait.html +pub trait Trait<'x> {} // @has foo/fn.test1.html // @has - '//pre' "pub fn test1() where for<'a> &'a T: Iterator," @@ -11,10 +12,10 @@ where } // @has foo/fn.test2.html -// @has - '//pre' "pub fn test2() where for<'a, 'b> &'a T: A<'b>," +// @has - '//pre' "pub fn test2() where for<'a, 'b> &'a T: Trait<'b>," pub fn test2() where - for<'a, 'b> &'a T: A<'b>, + for<'a, 'b> &'a T: Trait<'b>, { } @@ -29,13 +30,24 @@ where // @has foo/struct.Foo.html pub struct Foo<'a> { _x: &'a u8, + pub some_trait: &'a dyn for<'b> Trait<'b>, + pub some_func: for<'c> fn(val: &'c i32) -> i32, } +// @has - '//span[@id="structfield.some_func"]' "some_func: for<'c> fn(val: &'c i32) -> i32" +// @has - '//span[@id="structfield.some_trait"]' "some_trait: &'a dyn for<'b> Trait<'b>" + impl<'a> Foo<'a> { - // @has - '//code' "pub fn bar() where T: A<'a>," + // @has - '//code' "pub fn bar() where T: Trait<'a>," pub fn bar() where - T: A<'a>, + T: Trait<'a>, { } } + +// @has foo/trait.B.html +pub trait B<'x> {} + +// @has - '//code[@class="in-band"]' "impl<'a> B<'a> for dyn for<'b> Trait<'b>" +impl<'a> B<'a> for dyn for<'b> Trait<'b> {} From 4ea27484c9582389cded2301d1f0dd4f421c8c35 Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 18 Jun 2021 21:47:42 +0200 Subject: [PATCH 3/3] rustdoc: Introduce new `DynTrait` type for better representation of trait objects --- src/librustdoc/clean/auto_trait.rs | 10 +-- src/librustdoc/clean/mod.rs | 84 ++++++------------- src/librustdoc/clean/types.rs | 8 +- src/librustdoc/clean/utils.rs | 18 +++- src/librustdoc/formats/cache.rs | 15 ++++ src/librustdoc/html/format.rs | 60 +++++-------- src/librustdoc/html/render/cache.rs | 1 + src/librustdoc/json/conversions.rs | 29 ++++++- .../rustdoc/higher-ranked-trait-bounds.rs | 8 ++ 9 files changed, 117 insertions(+), 116 deletions(-) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 0828f0b3e518c..ebab3add6c55d 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -353,12 +353,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { let (poly_trait, output) = (data.0.as_ref().expect("as_ref failed").clone(), data.1.as_ref().cloned()); let new_ty = match poly_trait.trait_ { - Type::ResolvedPath { - ref path, - ref param_names, - ref did, - ref is_generic, - } => { + Type::ResolvedPath { ref path, ref did, ref is_generic } => { let mut new_path = path.clone(); let last_segment = new_path.segments.pop().expect("segments were empty"); @@ -395,7 +390,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { Type::ResolvedPath { path: new_path, - param_names: param_names.clone(), did: *did, is_generic: *is_generic, } @@ -570,7 +564,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { match **trait_ { Type::ResolvedPath { path: ref trait_path, - ref param_names, ref did, ref is_generic, } => { @@ -617,7 +610,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { PolyTrait { trait_: Type::ResolvedPath { path: new_trait_path, - param_names: param_names.clone(), did: *did, is_generic: *is_generic, }, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 889e645309c15..fea09b383c0b1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -180,7 +180,7 @@ impl Clean for (ty::TraitRef<'_>, &[TypeBinding]) { debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs); - ResolvedPath { path, param_names: None, did: trait_ref.def_id, is_generic: false } + ResolvedPath { path, did: trait_ref.def_id, is_generic: false } } } @@ -1378,30 +1378,9 @@ impl Clean for hir::Ty<'_> { } TyKind::Path(_) => clean_qpath(&self, cx), TyKind::TraitObject(ref bounds, ref lifetime, _) => { - let cleaned = bounds[0].clean(cx); - match cleaned.trait_ { - ResolvedPath { path, param_names: None, did, is_generic, .. } => { - let mut bounds: Vec = bounds[1..] - .iter() - .map(|bound| { - self::GenericBound::TraitBound( - bound.clean(cx), - hir::TraitBoundModifier::None, - ) - }) - .collect(); - if !lifetime.is_elided() { - bounds.push(self::GenericBound::Outlives(lifetime.clean(cx))); - } - ResolvedPath { - path, - param_names: Some((bounds, cleaned.generic_params)), - did, - is_generic, - } - } - _ => Infer, // shouldn't happen - } + let bounds = bounds.iter().map(|bound| bound.clean(cx)).collect(); + let lifetime = if !lifetime.is_elided() { Some(lifetime.clean(cx)) } else { None }; + DynTrait(bounds, lifetime) } TyKind::BareFn(ref barefn) => BareFunction(box barefn.clean(cx)), TyKind::Infer | TyKind::Err => Infer, @@ -1484,7 +1463,7 @@ impl<'tcx> Clean for Ty<'tcx> { }; inline::record_extern_fqn(cx, did, kind); let path = external_path(cx, cx.tcx.item_name(did), None, false, vec![], substs); - ResolvedPath { path, param_names: None, did, is_generic: false } + ResolvedPath { path, did, is_generic: false } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); @@ -1496,7 +1475,7 @@ impl<'tcx> Clean for Ty<'tcx> { vec![], InternalSubsts::empty(), ); - ResolvedPath { path, param_names: None, did, is_generic: false } + ResolvedPath { path, did, is_generic: false } } ty::Dynamic(ref obj, ref reg) => { // HACK: pick the first `did` as the `did` of the trait object. Someone @@ -1514,28 +1493,19 @@ impl<'tcx> Clean for Ty<'tcx> { inline::record_extern_fqn(cx, did, ItemType::Trait); - let mut param_names = vec![]; - if let Some(b) = reg.clean(cx) { - param_names.push(GenericBound::Outlives(b)); - } + let lifetime = reg.clean(cx); + let mut bounds = vec![]; + for did in dids { let empty = cx.tcx.intern_substs(&[]); let path = external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty); inline::record_extern_fqn(cx, did, ItemType::Trait); - let bound = GenericBound::TraitBound( - PolyTrait { - trait_: ResolvedPath { - path, - param_names: None, - did, - is_generic: false, - }, - generic_params: Vec::new(), - }, - hir::TraitBoundModifier::None, - ); - param_names.push(bound); + let bound = PolyTrait { + trait_: ResolvedPath { path, did, is_generic: false }, + generic_params: Vec::new(), + }; + bounds.push(bound); } let mut bindings = vec![]; @@ -1548,12 +1518,15 @@ impl<'tcx> Clean for Ty<'tcx> { let path = external_path(cx, cx.tcx.item_name(did), Some(did), false, bindings, substs); - ResolvedPath { - path, - param_names: Some((param_names, vec![])), - did, - is_generic: false, - } + bounds.insert( + 0, + PolyTrait { + trait_: ResolvedPath { path, did, is_generic: false }, + generic_params: Vec::new(), + }, + ); + + DynTrait(bounds, lifetime) } ty::Tuple(ref t) => { Tuple(t.iter().map(|t| t.expect_ty()).collect::>().clean(cx)) @@ -2257,14 +2230,9 @@ impl From for SimpleBound { match bound.clone() { GenericBound::Outlives(l) => SimpleBound::Outlives(l), GenericBound::TraitBound(t, mod_) => match t.trait_ { - Type::ResolvedPath { path, param_names, .. } => SimpleBound::TraitBound( - path.segments, - param_names.map_or_else(Vec::new, |(v, _)| { - v.iter().map(|p| SimpleBound::from(p.clone())).collect() - }), - t.generic_params, - mod_, - ), + Type::ResolvedPath { path, .. } => { + SimpleBound::TraitBound(path.segments, Vec::new(), t.generic_params, mod_) + } _ => panic!("Unexpected bound {:?}", bound), }, } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 4c94501b80be0..b78cabddef6b9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1141,7 +1141,7 @@ impl GenericBound { inline::record_extern_fqn(cx, did, ItemType::Trait); GenericBound::TraitBound( PolyTrait { - trait_: ResolvedPath { path, param_names: None, did, is_generic: false }, + trait_: ResolvedPath { path, did, is_generic: false }, generic_params: Vec::new(), }, hir::TraitBoundModifier::Maybe, @@ -1407,13 +1407,12 @@ crate enum Type { /// Structs/enums/traits (most that would be an `hir::TyKind::Path`). ResolvedPath { path: Path, - /// If `param_names` is `Some`, this path is a trait object and the Vecs repsresent - /// `(generic bounds, generic parameters)` - param_names: Option<(Vec, Vec)>, did: DefId, /// `true` if is a `T::Name` path for associated types. is_generic: bool, }, + /// `dyn for<'a> Trait<'a> + Send + 'static` + DynTrait(Vec, Option), /// For parameterized types, so the consumer of the JSON don't go /// looking for types which don't exist anywhere. Generic(Symbol), @@ -1600,6 +1599,7 @@ impl Type { fn inner_def_id(&self, cache: Option<&Cache>) -> Option { let t: PrimitiveType = match *self { ResolvedPath { did, .. } => return Some(did.into()), + DynTrait(ref bounds, _) => return bounds[0].trait_.inner_def_id(cache), Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache), diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 706a56fbcbfe6..3d056979780f4 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -2,7 +2,7 @@ use crate::clean::auto_trait::AutoTraitFinder; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::{ inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, - Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding, + Path, PathSegment, PolyTrait, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -163,8 +163,18 @@ pub(super) fn external_path( crate fn strip_type(ty: Type) -> Type { match ty { - Type::ResolvedPath { path, param_names, did, is_generic } => { - Type::ResolvedPath { path: strip_path(&path), param_names, did, is_generic } + Type::ResolvedPath { path, did, is_generic } => { + Type::ResolvedPath { path: strip_path(&path), did, is_generic } + } + Type::DynTrait(mut bounds, lt) => { + let first = bounds.remove(0); + let stripped_trait = strip_type(first.trait_); + + bounds.insert( + 0, + PolyTrait { trait_: stripped_trait, generic_params: first.generic_params }, + ); + Type::DynTrait(bounds, lt) } Type::Tuple(inner_tys) => { Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect()) @@ -431,7 +441,7 @@ crate fn resolve_type(cx: &mut DocContext<'_>, path: Path, id: hir::HirId) -> Ty _ => false, }; let did = register_res(cx, path.res); - ResolvedPath { path, param_names: None, did, is_generic } + ResolvedPath { path, did, is_generic } } crate fn get_auto_trait_and_blanket_impls( diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 5734a4a98e2b5..fb6ecca4c8928 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -402,6 +402,15 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { self.cache.parent_stack.push(did); true } + clean::DynTrait(ref bounds, _) + | clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => { + if let Some(did) = bounds[0].trait_.def_id() { + self.cache.parent_stack.push(did); + true + } else { + false + } + } ref t => { let prim_did = t .primitive_type() @@ -432,6 +441,12 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { | clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => { dids.insert(did); } + clean::DynTrait(ref bounds, _) + | clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => { + if let Some(did) = bounds[0].trait_.def_id() { + dids.insert(did); + } + } ref t => { let did = t .primitive_type() diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index e4fb7384affa2..404af8f1aed5e 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -646,18 +646,24 @@ fn primitive_link( /// Helper to render type parameters fn tybounds<'a, 'tcx: 'a>( - param_names: Option<&'a Vec>, + bounds: &'a Vec, + lt: &'a Option, cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { - display_fn(move |f| match param_names { - Some(params) => { - for param in params { + display_fn(move |f| { + for (i, bound) in bounds.iter().enumerate() { + if i > 0 { write!(f, " + ")?; - fmt::Display::fmt(¶m.print(cx), f)?; } - Ok(()) + + fmt::Display::fmt(&bound.print(cx), f)?; } - None => Ok(()), + + if let Some(lt) = lt { + write!(f, " + ")?; + fmt::Display::fmt(<.print(), f)?; + } + Ok(()) }) } @@ -694,32 +700,13 @@ fn fmt_type<'cx>( match *t { clean::Generic(name) => write!(f, "{}", name), - clean::ResolvedPath { did, ref param_names, ref path, is_generic } => { - let generic_params = param_names.as_ref().map(|(_, x)| x); - let param_names = param_names.as_ref().map(|(x, _)| x); - - if let Some(generic_params) = generic_params { - f.write_str("dyn ")?; - - if !generic_params.is_empty() { - if f.alternate() { - write!( - f, - "for<{:#}> ", - comma_sep(generic_params.iter().map(|g| g.print(cx))) - )?; - } else { - write!( - f, - "for<{}> ", - comma_sep(generic_params.iter().map(|g| g.print(cx))) - )?; - } - } - } + clean::ResolvedPath { did, ref path, is_generic } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. - resolved_path(f, did, path, is_generic, use_absolute, cx)?; - fmt::Display::fmt(&tybounds(param_names, cx), f) + resolved_path(f, did, path, is_generic, use_absolute, cx) + } + clean::DynTrait(ref bounds, ref lt) => { + f.write_str("dyn ")?; + fmt::Display::fmt(&tybounds(bounds, lt, cx), f) } clean::Infer => write!(f, "_"), clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx), @@ -854,7 +841,9 @@ fn fmt_type<'cx>( } } } - clean::ResolvedPath { param_names: Some((ref v, _)), .. } if !v.is_empty() => { + clean::DynTrait(ref bounds, ref trait_lt) + if bounds.len() > 1 || trait_lt.is_some() => + { write!(f, "{}{}{}(", amp, lt, m)?; fmt_type(&ty, f, use_absolute, cx)?; write!(f, ")") @@ -915,7 +904,7 @@ fn fmt_type<'cx>( // the ugliness comes from inlining across crates where // everything comes in as a fully resolved QPath (hard to // look at). - box clean::ResolvedPath { did, ref param_names, .. } => { + box clean::ResolvedPath { did, .. } => { match href(did.into(), cx) { Some((ref url, _, ref path)) if !f.alternate() => { write!( @@ -930,9 +919,6 @@ fn fmt_type<'cx>( } _ => write!(f, "{}", name)?, } - - // FIXME: `param_names` are not rendered, and this seems bad? - drop(param_names); Ok(()) } _ => write!(f, "{}", name), diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index 3e056c4b67a70..78b2f1e251309 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -233,6 +233,7 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option }); Some(path_segment.name) } + clean::DynTrait(ref bounds, _) => get_index_type_name(&bounds[0].trait_, accept_generic), clean::Generic(s) if accept_generic => Some(s), clean::Primitive(ref p) => Some(p.as_sym()), clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic), diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 643c0f82ae9f8..b89a266a695e9 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -373,14 +373,35 @@ impl FromWithTcx for Type { fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self { use clean::Type::*; match ty { - ResolvedPath { path, param_names, did, is_generic: _ } => Type::ResolvedPath { + ResolvedPath { path, did, is_generic: _ } => Type::ResolvedPath { name: path.whole_name(), id: from_def_id(did.into()), args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), - param_names: param_names - .map(|(v, _)| v.into_iter().map(|x| x.into_tcx(tcx)).collect()) - .unwrap_or_default(), + param_names: Vec::new(), }, + DynTrait(mut bounds, lt) => { + let (path, id) = match bounds.remove(0).trait_ { + ResolvedPath { path, did, .. } => (path, did), + _ => unreachable!(), + }; + + Type::ResolvedPath { + name: path.whole_name(), + id: from_def_id(id.into()), + args: path + .segments + .last() + .map(|args| Box::new(args.clone().args.into_tcx(tcx))), + param_names: bounds + .into_iter() + .map(|t| { + clean::GenericBound::TraitBound(t, rustc_hir::TraitBoundModifier::None) + }) + .chain(lt.into_iter().map(|lt| clean::GenericBound::Outlives(lt))) + .map(|bound| bound.into_tcx(tcx)) + .collect(), + } + } Generic(s) => Type::Generic(s.to_string()), Primitive(p) => Type::Primitive(p.as_sym().to_string()), BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))), diff --git a/src/test/rustdoc/higher-ranked-trait-bounds.rs b/src/test/rustdoc/higher-ranked-trait-bounds.rs index 492a743fbf29d..41940b0884e43 100644 --- a/src/test/rustdoc/higher-ranked-trait-bounds.rs +++ b/src/test/rustdoc/higher-ranked-trait-bounds.rs @@ -51,3 +51,11 @@ pub trait B<'x> {} // @has - '//code[@class="in-band"]' "impl<'a> B<'a> for dyn for<'b> Trait<'b>" impl<'a> B<'a> for dyn for<'b> Trait<'b> {} + +// @has foo/struct.Bar.html +// @has - '//span[@id="structfield.bar"]' "bar: &'a (dyn for<'b> Trait<'b> + Unpin)" +// @has - '//span[@id="structfield.baz"]' "baz: &'a (dyn Unpin + for<'b> Trait<'b>)" +pub struct Bar<'a> { + pub bar: &'a (dyn for<'b> Trait<'b> + Unpin), + pub baz: &'a (dyn Unpin + for<'b> Trait<'b>), +}