diff options
| author | bors <bors@rust-lang.org> | 2023-06-10 18:28:14 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-06-10 18:28:14 +0000 |
| commit | 7820972f866ca2cea281ccc39201c0cd27087110 (patch) | |
| tree | df3ecf5581de552cb863e001ca6ee552dcea2e2a /src | |
| parent | ef8ee73fc4ff70a426e71f900441f040db252dae (diff) | |
| parent | 5b5d84fd6a36a8ea4f39e3b730262321cc8f8622 (diff) | |
Auto merge of #107637 - fmease:rustdoc-reelide-x-crate-def-tr-obj-lt-bnds, r=notriddle,cgillot,GuillaumeGomez
rustdoc: re-elide cross-crate default trait-object lifetime bounds Hide trait-object lifetime bounds (re-exported from an external crate) if they coincide with [their default](https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes). Partially addresses #44306. Follow-up to #103885. [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/clean_middle_ty.3A.20I.20need.20to.20add.20a.20parameter/near/307143097). Most notably, if `std` exported something from `core` containing a type like `Box<dyn Fn()>`, then it would now be rendered as `Box<dyn Fn(), Global>` instead of `Box<dyn Fn() + 'static, Global>` (hiding `+ 'static` as it is the default in this case). Showing `Global` here is a separate issue, #80379, which is on my agenda. Note that I am not really fond of the fact that I had to add a parameter to such a widely used function (30+ call sites) to address such a niche bug. CC `@GuillaumeGomez` Requesting a review from a compiler contributor or team member as recommended on Zulip. r? compiler --- `@rustbot` label T-compiler T-rustdoc A-cross-crate-reexports
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustdoc/clean/auto_trait.rs | 2 | ||||
| -rw-r--r-- | src/librustdoc/clean/blanket_impl.rs | 8 | ||||
| -rw-r--r-- | src/librustdoc/clean/inline.rs | 19 | ||||
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 212 | ||||
| -rw-r--r-- | src/librustdoc/clean/utils.rs | 43 |
5 files changed, 235 insertions, 49 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index baf2b0a8585..c8a40e01501 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -124,7 +124,7 @@ where unsafety: hir::Unsafety::Normal, generics: new_generics, trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None), + for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None), items: Vec::new(), polarity, kind: ImplKind::Auto, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 9183fdaa087..a3604158851 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -107,7 +107,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { ty::Binder::dummy(trait_ref.subst_identity()), ThinVec::new(), )), - for_: clean_middle_ty(ty::Binder::dummy(ty.subst_identity()), cx, None), + for_: clean_middle_ty( + ty::Binder::dummy(ty.subst_identity()), + cx, + None, + None, + ), items: cx .tcx .associated_items(impl_def_id) @@ -119,6 +124,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { ty::Binder::dummy(trait_ref.subst_identity().self_ty()), cx, None, + None, ))), }))), cfg: None, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 7dc08b3b1ff..3aa98da1c80 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -278,8 +278,12 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::Typedef> { let predicates = cx.tcx.explicit_predicates_of(did); - let type_ = - clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did)); + let type_ = clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), + cx, + Some(did), + None, + ); Box::new(clean::Typedef { type_, @@ -386,9 +390,12 @@ pub(crate) fn build_impl( let for_ = match &impl_item { Some(impl_) => clean_ty(impl_.self_ty, cx), - None => { - clean_middle_ty(ty::Binder::dummy(tcx.type_of(did).subst_identity()), cx, Some(did)) - } + None => clean_middle_ty( + ty::Binder::dummy(tcx.type_of(did).subst_identity()), + cx, + Some(did), + None, + ), }; // Only inline impl if the implementing type is @@ -630,6 +637,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), cx, Some(def_id), + None, ), kind: clean::ConstantKind::Extern { def_id }, } @@ -641,6 +649,7 @@ fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::St ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did), + None, ), mutability: if mutable { Mutability::Mut } else { Mutability::Not }, expr: None, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b56b8127996..0c7950bfd3d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -31,6 +31,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, ExpnKind}; +use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -253,6 +254,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), cx, Some(def_id), + None, ), kind: ConstantKind::Anonymous { body: constant.value.body }, } @@ -264,7 +266,7 @@ pub(crate) fn clean_middle_const<'tcx>( ) -> Constant { // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { - type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None), + type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None), kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, } } @@ -370,7 +372,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None), + ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None, None), bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) @@ -396,7 +398,7 @@ fn clean_type_outlives_predicate<'tcx>( let ty::OutlivesPredicate(ty, lt) = pred.skip_binder(); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(pred.rebind(ty), cx, None), + ty: clean_middle_ty(pred.rebind(ty), cx, None, None), bounds: vec![GenericBound::Outlives( clean_middle_region(lt).expect("failed to clean lifetimes"), )], @@ -409,7 +411,7 @@ fn clean_middle_term<'tcx>( cx: &mut DocContext<'tcx>, ) -> Term { match term.skip_binder().unpack() { - ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None)), + ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None, None)), ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)), } } @@ -462,7 +464,7 @@ fn clean_projection<'tcx>( let trait_ = clean_trait_ref_with_bindings(cx, ty.map_bound(|ty| ty.trait_ref(cx.tcx)), ThinVec::new()); - let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None); + let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) } else { @@ -493,8 +495,13 @@ fn projection_to_path_segment<'tcx>( PathSegment { name: item.name, args: GenericArgs::AngleBracketed { - args: substs_to_args(cx, ty.map_bound(|ty| &ty.substs[generics.parent_count..]), false) - .into(), + args: substs_to_args( + cx, + ty.map_bound(|ty| &ty.substs[generics.parent_count..]), + false, + None, + ) + .into(), bindings: Default::default(), }, } @@ -514,6 +521,7 @@ fn clean_generic_param_def<'tcx>( ty::Binder::dummy(cx.tcx.type_of(def.def_id).subst_identity()), cx, Some(def.def_id), + None, )) } else { None @@ -540,6 +548,7 @@ fn clean_generic_param_def<'tcx>( ), cx, Some(def.def_id), + None, )), default: match has_default { true => Some(Box::new( @@ -871,7 +880,7 @@ fn clean_ty_generics<'tcx>( let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() }; if let Some(proj) = impl_trait_proj.remove(&idx) { for (trait_did, name, rhs, bound_params) in proj { - let rhs = clean_middle_ty(rhs, cx, None); + let rhs = clean_middle_ty(rhs, cx, None, None); simplify::merge_bounds( cx, &mut bounds, @@ -1126,7 +1135,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, // but shouldn't change any code meaning. - let output = clean_middle_ty(sig.output(), cx, None); + let output = clean_middle_ty(sig.output(), cx, None, None); FnDecl { output, @@ -1136,7 +1145,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( .inputs() .iter() .map(|t| Argument { - type_: clean_middle_ty(t.map_bound(|t| *t), cx, None), + type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None), name: names .next() .map(|i| i.name) @@ -1190,8 +1199,12 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); - let item_type = - clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), cx, None); + let item_type = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), + cx, + None, + None, + ); AssocTypeItem( Box::new(Typedef { type_: clean_ty(default, cx), @@ -1230,8 +1243,12 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); let generics = clean_generics(impl_.generics, cx); - let item_type = - clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); + let item_type = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); AssocTypeItem( Box::new(Typedef { type_, generics, item_type: Some(item_type) }), Vec::new(), @@ -1254,6 +1271,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ); let provided = match assoc_item.container { @@ -1447,6 +1465,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ), generics, // FIXME: should we obtain the Type from HIR and pass it on here? @@ -1465,6 +1484,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ), generics: Generics { params: ThinVec::new(), @@ -1510,7 +1530,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type if !ty.has_escaping_bound_vars() && let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) { - return clean_middle_ty(normalized_value, cx, None); + return clean_middle_ty(normalized_value, cx, None, None); } let trait_segments = &p.segments[..p.segments.len() - 1]; @@ -1738,11 +1758,140 @@ fn normalize<'tcx>( } } +fn clean_trait_object_lifetime_bound<'tcx>( + region: ty::Region<'tcx>, + container: Option<ContainerTy<'tcx>>, + preds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + tcx: TyCtxt<'tcx>, +) -> Option<Lifetime> { + if can_elide_trait_object_lifetime_bound(region, container, preds, tcx) { + return None; + } + + // Since there is a semantic difference between an implicitly elided (i.e. "defaulted") object + // lifetime and an explicitly elided object lifetime (`'_`), we intentionally don't hide the + // latter contrary to `clean_middle_region`. + match *region { + ty::ReStatic => Some(Lifetime::statik()), + ty::ReEarlyBound(region) if region.name != kw::Empty => Some(Lifetime(region.name)), + ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) + if name != kw::Empty => + { + Some(Lifetime(name)) + } + ty::ReEarlyBound(_) + | ty::ReLateBound(..) + | ty::ReFree(_) + | ty::ReVar(_) + | ty::RePlaceholder(_) + | ty::ReErased + | ty::ReError(_) => None, + } +} + +fn can_elide_trait_object_lifetime_bound<'tcx>( + region: ty::Region<'tcx>, + container: Option<ContainerTy<'tcx>>, + preds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + tcx: TyCtxt<'tcx>, +) -> bool { + // Below we quote extracts from https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes + + // > If the trait object is used as a type argument of a generic type then the containing type is + // > first used to try to infer a bound. + let default = container + .map_or(ObjectLifetimeDefault::Empty, |container| container.object_lifetime_default(tcx)); + + // > If there is a unique bound from the containing type then that is the default + // If there is a default object lifetime and the given region is lexically equal to it, elide it. + match default { + ObjectLifetimeDefault::Static => return *region == ty::ReStatic, + // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. + ObjectLifetimeDefault::Arg(default) => return region.get_name() == default.get_name(), + // > If there is more than one bound from the containing type then an explicit bound must be specified + // Due to ambiguity there is no default trait-object lifetime and thus elision is impossible. + // Don't elide the lifetime. + ObjectLifetimeDefault::Ambiguous => return false, + // There is no meaningful bound. Further processing is needed... + ObjectLifetimeDefault::Empty => {} + } + + // > If neither of those rules apply, then the bounds on the trait are used: + match *object_region_bounds(tcx, preds) { + // > If the trait has no lifetime bounds, then the lifetime is inferred in expressions + // > and is 'static outside of expressions. + // FIXME: If we are in an expression context (i.e. fn bodies and const exprs) then the default is + // `'_` and not `'static`. Only if we are in a non-expression one, the default is `'static`. + // Note however that at the time of this writing it should be fine to disregard this subtlety + // as we neither render const exprs faithfully anyway (hiding them in some places or using `_` instead) + // nor show the contents of fn bodies. + [] => *region == ty::ReStatic, + // > If the trait is defined with a single lifetime bound then that bound is used. + // > If 'static is used for any lifetime bound then 'static is used. + // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. + [object_region] => object_region.get_name() == region.get_name(), + // There are several distinct trait regions and none are `'static`. + // Due to ambiguity there is no default trait-object lifetime and thus elision is impossible. + // Don't elide the lifetime. + _ => false, + } +} + +#[derive(Debug)] +pub(crate) enum ContainerTy<'tcx> { + Ref(ty::Region<'tcx>), + Regular { ty: DefId, substs: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, arg: usize }, +} + +impl<'tcx> ContainerTy<'tcx> { + fn object_lifetime_default(self, tcx: TyCtxt<'tcx>) -> ObjectLifetimeDefault<'tcx> { + match self { + Self::Ref(region) => ObjectLifetimeDefault::Arg(region), + Self::Regular { ty: container, substs, arg: index } => { + let (DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::TyAlias + | DefKind::Trait + | DefKind::AssocTy + | DefKind::Variant) = tcx.def_kind(container) + else { + return ObjectLifetimeDefault::Empty; + }; + + let generics = tcx.generics_of(container); + let param = generics.params[index].def_id; + let default = tcx.object_lifetime_default(param); + + match default { + rbv::ObjectLifetimeDefault::Param(lifetime) => { + let index = generics.param_def_id_to_index[&lifetime]; + let arg = substs.skip_binder()[index as usize].expect_region(); + ObjectLifetimeDefault::Arg(arg) + } + rbv::ObjectLifetimeDefault::Empty => ObjectLifetimeDefault::Empty, + rbv::ObjectLifetimeDefault::Static => ObjectLifetimeDefault::Static, + rbv::ObjectLifetimeDefault::Ambiguous => ObjectLifetimeDefault::Ambiguous, + } + } + } + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum ObjectLifetimeDefault<'tcx> { + Empty, + Static, + Ambiguous, + Arg(ty::Region<'tcx>), +} + #[instrument(level = "trace", skip(cx), ret)] pub(crate) fn clean_middle_ty<'tcx>( bound_ty: ty::Binder<'tcx, Ty<'tcx>>, cx: &mut DocContext<'tcx>, parent_def_id: Option<DefId>, + container: Option<ContainerTy<'tcx>>, ) -> Type { let bound_ty = normalize(cx, bound_ty).unwrap_or(bound_ty); match *bound_ty.skip_binder().kind() { @@ -1753,19 +1902,24 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Uint(uint_ty) => Primitive(uint_ty.into()), ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), - ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None))), + ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))), ty::Array(ty, mut n) => { n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); - Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), n.into()) + Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } ty::RawPtr(mt) => { - RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None))) + RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None, None))) } ty::Ref(r, ty, mutbl) => BorrowedRef { lifetime: clean_middle_region(r), mutability: mutbl, - type_: Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), + type_: Box::new(clean_middle_ty( + bound_ty.rebind(ty), + cx, + None, + Some(ContainerTy::Ref(r)), + )), }, ty::FnDef(..) | ty::FnPtr(_) => { // FIXME: should we merge the outer and inner binders somehow? @@ -1817,10 +1971,8 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); - // FIXME(fmease): Hide the trait-object lifetime bound if it coincides with its default - // to partially address #44306. Follow the rules outlined at - // https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes - let lifetime = clean_middle_region(*reg); + let lifetime = clean_trait_object_lifetime_bound(*reg, container, obj, cx.tcx); + let mut bounds = dids .map(|did| { let empty = ty::Binder::dummy(InternalSubsts::empty()); @@ -1869,7 +2021,7 @@ pub(crate) fn clean_middle_ty<'tcx>( DynTrait(bounds, lifetime) } ty::Tuple(t) => { - Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None)).collect()) + Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect()) } ty::Alias(ty::Projection, ref data) => { @@ -1878,7 +2030,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Alias(ty::Inherent, alias_ty) => { let alias_ty = bound_ty.rebind(alias_ty); - let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None); + let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None); Type::QPath(Box::new(QPathData { assoc: PathSegment { @@ -1888,6 +2040,7 @@ pub(crate) fn clean_middle_ty<'tcx>( cx, alias_ty.map_bound(|ty| ty.substs.as_slice()), true, + None, ) .into(), bindings: Default::default(), @@ -2023,6 +2176,7 @@ pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext ty::Binder::dummy(cx.tcx.type_of(field.did).subst_identity()), cx, Some(field.did), + None, ), cx, ) @@ -2314,7 +2468,12 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); - let ty = clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); + let ty = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); let generics = clean_generics(generics, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; @@ -2418,6 +2577,7 @@ fn clean_impl<'tcx>( ty::Binder::dummy(tcx.type_of(def_id).subst_identity()), cx, Some(def_id.to_def_id()), + None, )), _ => None, }); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 366f9395296..294de12cea8 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -73,8 +73,9 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { pub(crate) fn substs_to_args<'tcx>( cx: &mut DocContext<'tcx>, - substs: ty::Binder<'tcx, &[ty::subst::GenericArg<'tcx>]>, + substs: ty::Binder<'tcx, &'tcx [ty::subst::GenericArg<'tcx>]>, mut skip_first: bool, + container: Option<DefId>, ) -> Vec<GenericArg> { let mut ret_val = Vec::with_capacity(substs.skip_binder().len().saturating_sub(if skip_first { @@ -82,19 +83,29 @@ pub(crate) fn substs_to_args<'tcx>( } else { 0 })); - ret_val.extend(substs.iter().filter_map(|kind| match kind.skip_binder().unpack() { - GenericArgKind::Lifetime(lt) => { - Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) - } - GenericArgKind::Type(_) if skip_first => { - skip_first = false; - None - } - GenericArgKind::Type(ty) => { - Some(GenericArg::Type(clean_middle_ty(kind.rebind(ty), cx, None))) - } - GenericArgKind::Const(ct) => { - Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) + + ret_val.extend(substs.iter().enumerate().filter_map(|(index, kind)| { + match kind.skip_binder().unpack() { + GenericArgKind::Lifetime(lt) => { + Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) + } + GenericArgKind::Type(_) if skip_first => { + skip_first = false; + None + } + GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty( + kind.rebind(ty), + cx, + None, + container.map(|container| crate::clean::ContainerTy::Regular { + ty: container, + substs, + arg: index, + }), + ))), + GenericArgKind::Const(ct) => { + Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) + } } })); ret_val @@ -107,7 +118,7 @@ fn external_generic_args<'tcx>( bindings: ThinVec<TypeBinding>, substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> GenericArgs { - let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self); + let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self, Some(did)); if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { let ty = substs @@ -118,7 +129,7 @@ fn external_generic_args<'tcx>( let inputs = // The trait's first substitution is the one after self, if there is one. match ty.skip_binder().kind() { - ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None)).collect::<Vec<_>>().into(), + ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None, None)).collect::<Vec<_>>().into(), _ => return GenericArgs::AngleBracketed { args: args.into(), bindings }, }; let output = bindings.into_iter().next().and_then(|binding| match binding.kind { |
