From b35832e92b62321f2ab97e90d523c9fd8dc6adae Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 1 Nov 2025 23:53:21 -0400 Subject: [PATCH] Implement `implied_bounds` functionality for generic type parameters. --- src/librustdoc/clean/auto_trait.rs | 1 + src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 600 ++++++++++++++++-- src/librustdoc/clean/types.rs | 85 ++- src/librustdoc/clean/utils.rs | 2 +- src/librustdoc/core.rs | 6 +- src/librustdoc/fold.rs | 4 +- src/librustdoc/formats/cache.rs | 10 +- src/librustdoc/formats/item_type.rs | 4 +- src/librustdoc/html/format.rs | 4 +- src/librustdoc/html/render/mod.rs | 24 +- src/librustdoc/html/render/search_index.rs | 7 +- src/librustdoc/html/render/sidebar.rs | 6 +- src/librustdoc/json/conversions.rs | 24 +- src/librustdoc/json/mod.rs | 2 +- .../passes/check_doc_test_visibility.rs | 4 +- src/librustdoc/passes/collect_trait_impls.rs | 2 +- src/librustdoc/passes/propagate_stability.rs | 4 +- src/librustdoc/passes/stripper.rs | 4 +- src/librustdoc/visit.rs | 4 +- src/rustdoc-json-types/lib.rs | 36 +- src/tools/jsondoclint/src/validator.rs | 16 +- tests/rustdoc-json/explicit-sized-bound.rs | 30 + tests/rustdoc-json/fns/async_return.rs | 12 +- tests/rustdoc-json/fns/generic_args.rs | 27 +- tests/rustdoc-json/fns/generic_returns.rs | 4 +- tests/rustdoc-json/fns/generics.rs | 2 +- tests/rustdoc-json/generic-args.rs | 2 +- .../rustdoc-json/impl-trait-in-assoc-type.rs | 10 +- .../impl-trait-precise-capturing.rs | 6 +- .../rustdoc-json/implied-bounds-assoc-type.rs | 22 + .../implied-bounds-impl-trait-args.rs | 44 ++ .../implied-bounds-impl-trait-return.rs | 23 + .../implied-bounds-multi-lifetimes.rs | 33 + .../implied-bounds-needing-elaboration.rs | 23 + .../implied-bounds-no-duplicates.rs | 141 ++++ .../implied-bounds-redundant-sized.rs | 18 + tests/rustdoc-json/implied-bounds-refs.rs | 45 ++ tests/rustdoc-json/implied-bounds-simple.rs | 52 ++ .../rustdoc-json/implied-bounds-trait-rpit.rs | 22 + tests/rustdoc-json/implied-bounds-where.rs | 17 + tests/rustdoc-json/non_lifetime_binders.rs | 2 +- tests/rustdoc-json/trait_alias.rs | 4 +- tests/rustdoc-json/traits/trait_alias.rs | 2 +- 44 files changed, 1222 insertions(+), 170 deletions(-) create mode 100644 tests/rustdoc-json/explicit-sized-bound.rs create mode 100644 tests/rustdoc-json/implied-bounds-assoc-type.rs create mode 100644 tests/rustdoc-json/implied-bounds-impl-trait-args.rs create mode 100644 tests/rustdoc-json/implied-bounds-impl-trait-return.rs create mode 100644 tests/rustdoc-json/implied-bounds-multi-lifetimes.rs create mode 100644 tests/rustdoc-json/implied-bounds-needing-elaboration.rs create mode 100644 tests/rustdoc-json/implied-bounds-no-duplicates.rs create mode 100644 tests/rustdoc-json/implied-bounds-redundant-sized.rs create mode 100644 tests/rustdoc-json/implied-bounds-refs.rs create mode 100644 tests/rustdoc-json/implied-bounds-simple.rs create mode 100644 tests/rustdoc-json/implied-bounds-trait-rpit.rs create mode 100644 tests/rustdoc-json/implied-bounds-where.rs diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 6c67916571a40..04e1dbdac5d68 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -103,6 +103,7 @@ fn synthesize_auto_trait_impl<'tcx>( // regardless of the choice of `T`. let mut generics = clean_ty_generics_inner( cx, + item_def_id, tcx.generics_of(item_def_id), ty::GenericPredicates::default(), ); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index b470af50f68fe..ffef4a4f91e09 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -536,7 +536,7 @@ pub(crate) fn build_impl( }) .map(|item| clean_impl_item(item, cx)) .collect::>(), - clean_generics(impl_.generics, cx), + clean_generics(impl_.generics, did, cx), ), None => ( tcx.associated_items(did) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 725869c6caf5d..7c64f5e725d3b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -38,6 +38,7 @@ use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::thin_vec::ThinVec; +use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{FatalError, struct_span_code_err}; use rustc_hir::attrs::AttributeKind; @@ -46,13 +47,19 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId}; use rustc_hir::{LangItem, PredicateOrigin, find_attr}; use rustc_hir_analysis::hir_ty_lowering::FeedConstTy; use rustc_hir_analysis::{lower_const_arg_for_rustdoc, lower_ty}; +use rustc_infer::infer::region_constraints::GenericKind; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; -use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{ + self, AdtKind, AliasTy, GenericArgsRef, ParamTy, Ty, TyCtxt, TypeVisitableExt, TypingMode, +}; use rustc_middle::{bug, span_bug}; use rustc_span::ExpnKind; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_trait_selection::infer::outlives::env::OutlivesEnvironment; +use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; use rustc_trait_selection::traits::wf::object_region_bounds; use tracing::{debug, instrument}; use utils::*; @@ -437,6 +444,374 @@ fn clean_type_outlives_predicate<'tcx>( } } +fn clause_targets_ty<'tcx>(clause: ty::Clause<'tcx>, target: Ty<'tcx>) -> bool { + if let Some(trait_clause) = clause.as_trait_clause() { + return trait_clause.self_ty().skip_binder() == target; + } + if let Some(type_outlives) = clause.as_type_outlives_clause() { + return type_outlives.skip_binder().0 == target; + } + false +} + +fn clause_to_generic_bound<'tcx>( + cx: &mut DocContext<'tcx>, + clause: ty::Clause<'tcx>, +) -> Option { + if let Some(trait_clause) = clause.as_trait_clause() { + let def_id = trait_clause.def_id(); + + match cx.tcx.as_lang_item(def_id) { + None => { + // Not a lang item trait, so we'll output it. + } + Some( + LangItem::Sized + | LangItem::Clone + | LangItem::Copy + | LangItem::Sync + | LangItem::Drop + | LangItem::Add + | LangItem::Sub + | LangItem::Mul + | LangItem::Div + | LangItem::Rem + | LangItem::Neg + | LangItem::Not + | LangItem::BitXor + | LangItem::BitAnd + | LangItem::BitOr + | LangItem::Shl + | LangItem::Shr + | LangItem::AddAssign + | LangItem::SubAssign + | LangItem::MulAssign + | LangItem::DivAssign + | LangItem::RemAssign + | LangItem::BitXorAssign + | LangItem::BitAndAssign + | LangItem::BitOrAssign + | LangItem::ShlAssign + | LangItem::ShrAssign + | LangItem::Index + | LangItem::IndexMut + | LangItem::Deref + | LangItem::DerefMut + | LangItem::Fn + | LangItem::FnMut + | LangItem::FnOnce + | LangItem::AsyncFn + | LangItem::AsyncFnMut + | LangItem::AsyncFnOnce + | LangItem::Iterator + | LangItem::FusedIterator + | LangItem::Future + | LangItem::Unpin + | LangItem::PartialEq + | LangItem::PartialOrd, + ) => { + // Stable lang item trait. Output it. + } + Some(_) => { + // Not a stable lang item trait. Suppress it. + return None; + } + } + + let bound = clean_poly_trait_ref_with_constraints( + cx, + trait_clause.map_bound(|pred| pred.trait_ref), + ThinVec::new(), + ); + return Some(bound); + } + + if let Some(type_outlives) = clause.as_type_outlives_clause() { + let ty::OutlivesPredicate(_, region) = type_outlives.skip_binder(); + return clean_middle_region(region, cx).map(GenericBound::Outlives); + } + + None +} + +fn populate_implied_bounds_for_ty<'tcx, B>( + cx: &mut DocContext<'tcx>, + target_ty: Ty<'tcx>, + clauses: &[ty::Clause<'tcx>], + implicitly_sized: bool, + explicit_bounds: &[GenericBound], + implied_bounds: &mut B, +) where + B: Extend, + for<'a> &'a B: IntoIterator, +{ + if !cx.is_json_output() { + return; + } + + let explicit_bounds: UnordSet<_> = explicit_bounds.iter().collect(); + let deduplicated_implied_bounds: UnordSet<_> = + (&*implied_bounds).into_iter().cloned().collect(); + + let mut added_sized_bound = false; + for clause in clauses { + if !clause_targets_ty(*clause, target_ty) { + continue; + } + + if let Some(bound) = clause_to_generic_bound(cx, *clause) { + if bound.is_sized_bound(cx) { + added_sized_bound = true; + } + + if explicit_bounds.contains(&bound) { + continue; + } + if !deduplicated_implied_bounds.contains(&bound) { + implied_bounds.extend([bound]); + } + } + } + + if !added_sized_bound && implicitly_sized { + let sized_bound = GenericBound::sized(cx); + implied_bounds.extend([sized_bound]); + } +} + +fn param_ty_for_param<'tcx>( + tcx: TyCtxt<'tcx>, + owner_def_id: DefId, + param_def_id: DefId, +) -> Option> { + let generics = tcx.generics_of(owner_def_id); + let index = generics.param_def_id_to_index(tcx, param_def_id)?; + let param_def = generics.param_at(index as usize, tcx); + match param_def.kind { + ty::GenericParamDefKind::Type { .. } => Some(ParamTy::for_def(param_def).to_ty(tcx)), + _ => None, + } +} + +fn populate_implied_bounds_for_type_param<'tcx, B>( + cx: &mut DocContext<'tcx>, + owner_def_id: DefId, + param_def_id: DefId, + explicit_bounds: &[GenericBound], + implied_bounds: &mut B, +) where + B: Extend, + for<'a> &'a B: IntoIterator, +{ + if !cx.is_json_output() { + return; + } + + let Some(target_ty) = param_ty_for_param(cx.tcx, owner_def_id, param_def_id) else { + // TODO: Presumably unreachable? + return; + }; + let clauses = cx.tcx.param_env(owner_def_id).caller_bounds().as_slice(); + + let allows_unsized = explicit_bounds.iter().any(|bound| bound.is_maybe_sized_bound(cx)); + let implicitly_sized = !allows_unsized || fn_param_used_directly(cx, owner_def_id, target_ty); + + populate_implied_bounds_for_ty( + cx, + target_ty, + clauses, + implicitly_sized, + explicit_bounds, + implied_bounds, + ); + + let ty::Param(param_ty) = target_ty.kind() else { + // TODO: Presumably unreachable? + return; + }; + + let extra_bounds = implied_outlives_bounds_for_param(cx, owner_def_id, *param_ty); + if !extra_bounds.is_empty() { + let explicit_set: UnordSet<_> = explicit_bounds.iter().collect(); + let mut implied_set: UnordSet<_> = (&*implied_bounds).into_iter().cloned().collect(); + + for bound in extra_bounds { + if explicit_set.contains(&bound) || implied_set.contains(&bound) { + continue; + } + implied_bounds.extend([bound.clone()]); + implied_set.insert(bound); + } + } +} + +fn fn_param_used_directly<'tcx>( + cx: &DocContext<'tcx>, + owner_def_id: DefId, + target_ty: Ty<'tcx>, +) -> bool { + let is_function = matches!(cx.tcx.def_kind(owner_def_id), DefKind::Fn | DefKind::AssocFn); + if !is_function { + return false; + } + + let sig = cx.tcx.fn_sig(owner_def_id).instantiate_identity(); + let sig = sig.skip_binder(); + sig.inputs().iter().any(|ty| *ty == target_ty) || sig.output() == target_ty +} + +fn implied_outlives_bounds_for_param<'tcx>( + cx: &mut DocContext<'tcx>, + owner_def_id: DefId, + param_ty: ParamTy, +) -> Vec { + let Some(local_def_id) = owner_def_id.as_local() else { + return Vec::new(); + }; + + let assumed_wf = cx.tcx.assumed_wf_types(local_def_id); + if assumed_wf.is_empty() { + return Vec::new(); + } + + let param_env = cx.tcx.param_env(owner_def_id); + let infcx = cx.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let env = OutlivesEnvironment::new( + &infcx, + local_def_id, + param_env, + assumed_wf.iter().map(|(ty, _)| *ty), + ); + + env.region_bound_pairs() + .iter() + .filter_map(|predicate| match predicate { + ty::OutlivesPredicate(GenericKind::Param(param), region) + if param.index == param_ty.index => + { + clean_middle_region(*region, cx).map(GenericBound::Outlives) + } + _ => None, + }) + .collect() +} + +fn populate_implied_bounds_for_generics<'tcx>( + cx: &mut DocContext<'tcx>, + owner_def_id: DefId, + generics: &mut Generics, +) { + if !cx.is_json_output() { + return; + } + + let mut param_by_name = FxHashMap::default(); + let mut explicit_bounds_by_param = FxHashMap::default(); + + for param in &generics.params { + if let GenericParamDefKind::Type { bounds, synthetic: false, .. } = ¶m.kind { + param_by_name.insert(param.name, param.def_id); + explicit_bounds_by_param + .entry(param.def_id) + .or_insert_with(Vec::new) + .extend(bounds.iter().cloned()); + } + } + + for predicate in &generics.where_predicates { + if let WherePredicate::BoundPredicate { ty: Type::Generic(name), bounds, .. } = predicate + && let Some(def_id) = param_by_name.get(name) + { + explicit_bounds_by_param + .entry(*def_id) + .or_insert_with(Vec::new) + .extend(bounds.iter().cloned()); + } + } + + for param in &mut generics.params { + if let GenericParamDefKind::Type { bounds, implied_bounds, synthetic: false, .. } = + &mut param.kind + { + let mut explicit_bounds_for_param = + explicit_bounds_by_param.remove(¶m.def_id).unwrap_or_default(); + explicit_bounds_for_param.extend(bounds.iter().cloned()); + + populate_implied_bounds_for_type_param( + cx, + owner_def_id, + param.def_id, + explicit_bounds_for_param.as_slice(), + implied_bounds, + ); + } + } +} + +fn compute_implied_bounds_from_item_bounds<'tcx>( + cx: &mut DocContext<'tcx>, + item_def_id: DefId, + args: GenericArgsRef<'tcx>, + target_ty: Ty<'tcx>, + explicit_bounds: &[GenericBound], + implicitly_sized: bool, +) -> Vec { + if !cx.is_json_output() { + return Vec::new(); + } + + let clauses = cx.tcx.item_bounds(item_def_id).instantiate(cx.tcx, args); + + let mut implied_bounds = Vec::new(); + populate_implied_bounds_for_ty( + cx, + target_ty, + clauses, + implicitly_sized, + explicit_bounds, + &mut implied_bounds, + ); + + implied_bounds +} + +fn compute_implied_bounds_for_assoc_item<'tcx>( + cx: &mut DocContext<'tcx>, + assoc_def_id: DefId, + explicit_bounds: &[GenericBound], +) -> Vec { + let args = ty::GenericArgs::identity_for_item(cx.tcx, assoc_def_id); + let target_ty = + Ty::new_alias(cx.tcx, ty::Projection, AliasTy::new_from_args(cx.tcx, assoc_def_id, args)); + compute_implied_bounds_from_item_bounds( + cx, + assoc_def_id, + args, + target_ty, + explicit_bounds, + true, + ) +} + +fn compute_implied_bounds_for_opaque<'tcx>( + cx: &mut DocContext<'tcx>, + opaque_def_id: DefId, + args: GenericArgsRef<'tcx>, + explicit_bounds: &[GenericBound], + implicitly_sized: bool, +) -> Vec { + let target_ty = + Ty::new_alias(cx.tcx, ty::Opaque, AliasTy::new_from_args(cx.tcx, opaque_def_id, args)); + compute_implied_bounds_from_item_bounds( + cx, + opaque_def_id, + args, + target_ty, + explicit_bounds, + implicitly_sized, + ) +} + fn clean_middle_term<'tcx>( term: ty::Binder<'tcx, ty::Term<'tcx>>, cx: &mut DocContext<'tcx>, @@ -544,7 +919,8 @@ fn clean_generic_param_def( ( def.name, GenericParamDefKind::Type { - bounds: ThinVec::new(), // These are filled in from the where-clauses. + bounds: ThinVec::new(), // These are filled in from the where-clauses. + implied_bounds: ThinVec::new(), // These are filled in based on `bounds`. default: default.map(Box::new), synthetic, }, @@ -619,12 +995,14 @@ fn clean_generic_param<'tcx>( } else { ThinVec::new() }; + let implied_bounds = ThinVec::new(); // Filled later by `populate_implied_bounds_for_type_param`. ( param.name.ident().name, GenericParamDefKind::Type { bounds, default: default.map(|t| clean_ty(t, cx)).map(Box::new), synthetic, + implied_bounds, }, ) } @@ -664,6 +1042,7 @@ fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { pub(crate) fn clean_generics<'tcx>( gens: &hir::Generics<'tcx>, + owner_def_id: DefId, cx: &mut DocContext<'tcx>, ) -> Generics { let impl_trait_params = gens @@ -671,11 +1050,21 @@ pub(crate) fn clean_generics<'tcx>( .iter() .filter(|param| is_impl_trait(param)) .map(|param| { - let param = clean_generic_param(cx, Some(gens), param); - match param.kind { + let mut param = clean_generic_param(cx, Some(gens), param); + match &mut param.kind { GenericParamDefKind::Lifetime { .. } => unreachable!(), - GenericParamDefKind::Type { ref bounds, .. } => { - cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec()); + GenericParamDefKind::Type { bounds, implied_bounds, synthetic, .. } => { + debug_assert!(*synthetic); + let param_def_id = param.def_id; + populate_implied_bounds_for_type_param( + cx, + owner_def_id, + param_def_id, + bounds.as_slice(), + &mut *implied_bounds, + ); + cx.impl_trait_bounds + .insert(param_def_id.into(), (bounds.to_vec(), implied_bounds.to_vec())); } GenericParamDefKind::Const { .. } => unreachable!(), } @@ -747,15 +1136,28 @@ pub(crate) fn clean_generics<'tcx>( } } } - GenericParamDefKind::Type { bounds, synthetic: false, .. } => { - if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) { - // We merge bounds in the `where` clause. - for bound in bounds.drain(..) { - if !bound_pred.0.contains(&bound) { - bound_pred.0.push(bound); + GenericParamDefKind::Type { bounds, implied_bounds, synthetic: false, .. } => { + let explicit_bounds = + if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) { + // We merge bounds in the `where` clause. + for bound in bounds.drain(..) { + if !bound_pred.0.contains(&bound) { + bound_pred.0.push(bound); + } } - } - } + + bound_pred.0.as_slice() + } else { + bounds.as_slice() + }; + + populate_implied_bounds_for_type_param( + cx, + owner_def_id, + p.def_id, + explicit_bounds, + implied_bounds, + ); } GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { // nothing to do here. @@ -771,7 +1173,7 @@ pub(crate) fn clean_generics<'tcx>( .into_iter() .map(|(ty, (bounds, bound_params))| WherePredicate::BoundPredicate { ty, - bounds, + bounds: bounds.into_iter().collect(), bound_params, }) .chain( @@ -785,17 +1187,23 @@ pub(crate) fn clean_generics<'tcx>( } fn clean_ty_generics<'tcx>(cx: &mut DocContext<'tcx>, def_id: DefId) -> Generics { - clean_ty_generics_inner(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)) + clean_ty_generics_inner( + cx, + def_id, + cx.tcx.generics_of(def_id), + cx.tcx.explicit_predicates_of(def_id), + ) } fn clean_ty_generics_inner<'tcx>( cx: &mut DocContext<'tcx>, + owner_def_id: DefId, gens: &ty::Generics, preds: ty::GenericPredicates<'tcx>, ) -> Generics { // Don't populate `cx.impl_trait_bounds` before cleaning where clauses, // since `clean_predicate` would consume them. - let mut impl_trait = BTreeMap::>::default(); + let mut impl_trait = BTreeMap::)>::default(); let params: ThinVec<_> = gens .own_params @@ -808,7 +1216,7 @@ fn clean_ty_generics_inner<'tcx>( return false; } if synthetic { - impl_trait.insert(param.index, vec![]); + impl_trait.insert(param.index, (param.def_id, vec![])); return false; } true @@ -849,7 +1257,7 @@ fn clean_ty_generics_inner<'tcx>( }; if let Some(param_idx) = param_idx - && let Some(bounds) = impl_trait.get_mut(¶m_idx) + && let Some((_, bounds)) = impl_trait.get_mut(¶m_idx) { let pred = clean_predicate(*pred, cx)?; @@ -871,7 +1279,7 @@ fn clean_ty_generics_inner<'tcx>( }) .collect::>(); - for (idx, mut bounds) in impl_trait { + for (idx, (param_def_id, mut bounds)) in impl_trait { let mut has_sized = false; bounds.retain(|b| { if b.is_sized_bound(cx) { @@ -905,7 +1313,16 @@ fn clean_ty_generics_inner<'tcx>( } } - cx.impl_trait_bounds.insert(idx.into(), bounds); + let mut implied_bounds = Vec::new(); + populate_implied_bounds_for_type_param( + cx, + owner_def_id, + param_def_id, + &bounds, + &mut implied_bounds, + ); + + cx.impl_trait_bounds.insert(idx.into(), (bounds, implied_bounds)); } // Now that `cx.impl_trait_bounds` is populated, we can process @@ -916,6 +1333,7 @@ fn clean_ty_generics_inner<'tcx>( let mut generics = Generics { params, where_predicates }; simplify::sized_bounds(cx, &mut generics); generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + populate_implied_bounds_for_generics(cx, owner_def_id, &mut generics); generics } @@ -1015,7 +1433,13 @@ fn clean_fn_or_proc_macro<'tcx>( match macro_kind { Some(kind) => clean_proc_macro(item, name, kind, cx), None => { - let mut func = clean_function(cx, sig, generics, ParamsSrc::Body(body_id)); + let mut func = clean_function( + cx, + sig, + generics, + item.owner_id.to_def_id(), + ParamsSrc::Body(body_id), + ); clean_fn_decl_legacy_const_generics(&mut func, attrs); FunctionItem(func) } @@ -1059,11 +1483,12 @@ fn clean_function<'tcx>( cx: &mut DocContext<'tcx>, sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'tcx>, + owner_def_id: DefId, params: ParamsSrc<'tcx>, ) -> Box { let (generics, decl) = enter_impl_trait(cx, |cx| { // NOTE: Generics must be cleaned before params. - let generics = clean_generics(generics, cx); + let generics = clean_generics(generics, owner_def_id, cx); let params = match params { ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id), // Let's not perpetuate anon params from Rust 2015; use `_` for them. @@ -1139,7 +1564,7 @@ fn clean_poly_fn_sig<'tcx>( // function isn't async without needing to execute the query `asyncness` at // all which gives us a noticeable performance boost. if let Some(did) = did - && let Type::ImplTrait(_) = output + && let Type::ImplTrait { .. } = output && cx.tcx.asyncness(did).is_async() { output = output.sugared_async_return_type(); @@ -1193,42 +1618,59 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext let inner = match trait_item.kind { hir::TraitItemKind::Const(ty, Some(default)) => { ProvidedAssocConstItem(Box::new(Constant { - generics: enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)), + generics: enter_impl_trait(cx, |cx| { + clean_generics(trait_item.generics, local_did, cx) + }), kind: ConstantKind::Local { def_id: local_did, body: default }, type_: clean_ty(ty, cx), })) } hir::TraitItemKind::Const(ty, None) => { - let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + let generics = + enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, local_did, cx)); RequiredAssocConstItem(generics, Box::new(clean_ty(ty, cx))) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body)); + let m = + clean_function(cx, sig, trait_item.generics, local_did, ParamsSrc::Body(body)); MethodItem(m, None) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { - let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents)); + let m = clean_function( + cx, + sig, + trait_item.generics, + local_did, + ParamsSrc::Idents(idents), + ); RequiredMethodItem(m) } 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 generics = + enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, local_did, cx)); + let bounds: Vec<_> = + bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); let item_type = clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, default)), cx, None, None); - AssocTypeItem( - Box::new(TypeAlias { + let implied_bounds = compute_implied_bounds_for_assoc_item(cx, local_did, &bounds); + AssocTypeItem { + ty: Box::new(TypeAlias { type_: clean_ty(default, cx), generics, inner_type: None, item_type: Some(item_type), }), bounds, - ) + implied_bounds, + } } hir::TraitItemKind::Type(bounds, None) => { - 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(); - RequiredAssocTypeItem(generics, bounds) + let generics = + enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, local_did, cx)); + let bounds: Vec<_> = + bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); + let implied_bounds = compute_implied_bounds_for_assoc_item(cx, local_did, &bounds); + RequiredAssocTypeItem { generics, bounds, implied_bounds } } }; Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx) @@ -1243,12 +1685,12 @@ pub(crate) fn clean_impl_item<'tcx>( cx.with_param_env(local_did, |cx| { let inner = match impl_.kind { hir::ImplItemKind::Const(ty, expr) => ImplAssocConstItem(Box::new(Constant { - generics: clean_generics(impl_.generics, cx), + generics: clean_generics(impl_.generics, local_did, cx), kind: ConstantKind::Local { def_id: local_did, body: expr }, type_: clean_ty(ty, cx), })), hir::ImplItemKind::Fn(ref sig, body) => { - let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body)); + let m = clean_function(cx, sig, impl_.generics, local_did, ParamsSrc::Body(body)); let defaultness = match impl_.impl_kind { hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final, hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness, @@ -1257,18 +1699,20 @@ 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 generics = clean_generics(impl_.generics, local_did, cx); let item_type = clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); - AssocTypeItem( - Box::new(TypeAlias { + AssocTypeItem { + ty: Box::new(TypeAlias { type_, generics, inner_type: None, item_type: Some(item_type), }), - Vec::new(), - ) + // Associated types inside `impl` blocks are not allowed to have bounds. + bounds: Vec::new(), + implied_bounds: Vec::new(), + } } }; @@ -1379,6 +1823,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } let mut generics = clean_ty_generics_inner( cx, + assoc_item.def_id, tcx.generics_of(assoc_item.def_id), ty::GenericPredicates { parent: None, predicates }, ); @@ -1455,9 +1900,11 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo None => bounds.push(GenericBound::maybe_sized(cx)), } + let implied_bounds = + compute_implied_bounds_for_assoc_item(cx, assoc_item.def_id, &bounds); if tcx.defaultness(assoc_item.def_id).has_value() { - AssocTypeItem( - Box::new(TypeAlias { + AssocTypeItem { + ty: Box::new(TypeAlias { type_: clean_middle_ty( ty::Binder::dummy( tcx.type_of(assoc_item.def_id).instantiate_identity(), @@ -1471,13 +1918,14 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo item_type: None, }), bounds, - ) + implied_bounds, + } } else { - RequiredAssocTypeItem(generics, bounds) + RequiredAssocTypeItem { generics, bounds, implied_bounds } } } else { - AssocTypeItem( - Box::new(TypeAlias { + AssocTypeItem { + ty: Box::new(TypeAlias { type_: clean_middle_ty( ty::Binder::dummy( tcx.type_of(assoc_item.def_id).instantiate_identity(), @@ -1492,8 +1940,9 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo }), // Associated types inside trait or inherent impls are not allowed to have // item bounds. Thus we don't attempt to move any bounds there. - Vec::new(), - ) + bounds: Vec::new(), + implied_bounds: Vec::new(), + } } } }; @@ -1630,8 +2079,8 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type if let Some(new_ty) = cx.args.get(&did).and_then(|p| p.as_ty()).cloned() { return new_ty; } - if let Some(bounds) = cx.impl_trait_bounds.remove(&did.into()) { - return ImplTrait(bounds); + if let Some((bounds, implied_bounds)) = cx.impl_trait_bounds.remove(&did.into()) { + return ImplTrait { bounds, implied_bounds }; } } @@ -1815,7 +2264,22 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()), TyKind::OpaqueDef(ty) => { - ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) + let bounds = + ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect::>(); + let args = ty::GenericArgs::identity_for_item(cx.tcx, ty.def_id.to_def_id()); + let implicitly_sized = match &ty.origin { + OpaqueTyOrigin::FnReturn { .. } => true, + OpaqueTyOrigin::AsyncFn { .. } => true, + OpaqueTyOrigin::TyAlias { .. } => false, + }; + let implied_bounds = compute_implied_bounds_for_opaque( + cx, + ty.def_id.to_def_id(), + args, + &bounds, + implicitly_sized, + ); + ImplTrait { bounds, implied_bounds } } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, lifetime) => { @@ -2204,8 +2668,8 @@ pub(crate) fn clean_middle_ty<'tcx>( } ty::Param(ref p) => { - if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { - ImplTrait(bounds) + if let Some((bounds, implied_bounds)) = cx.impl_trait_bounds.remove(&p.index.into()) { + ImplTrait { bounds, implied_bounds } } else if p.name == kw::SelfUpper { SelfTy } else { @@ -2341,7 +2805,9 @@ fn clean_middle_opaque_bounds<'tcx>( )); } - ImplTrait(bounds) + let implied_bounds = + compute_implied_bounds_for_opaque(cx, impl_trait_def_id, args, &bounds, true); + ImplTrait { bounds, implied_bounds } } pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { @@ -2786,7 +3252,7 @@ fn clean_maybe_renamed_item<'tcx>( expr: Some(body_id), }), ItemKind::Const(_, generics, ty, body_id) => ConstantItem(Box::new(Constant { - generics: clean_generics(generics, cx), + generics: clean_generics(generics, def_id, cx), type_: clean_ty(ty, cx), kind: ConstantKind::Local { body: body_id, def_id }, })), @@ -2795,7 +3261,7 @@ fn clean_maybe_renamed_item<'tcx>( let rustdoc_ty = clean_ty(ty, cx); let type_ = clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, ty)), cx, None, None); - let generics = clean_generics(generics, cx); + let generics = clean_generics(generics, def_id, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; if *count == 0 { @@ -2825,19 +3291,19 @@ fn clean_maybe_renamed_item<'tcx>( } ItemKind::Enum(_, generics, def) => EnumItem(Enum { variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), - generics: clean_generics(generics, cx), + generics: clean_generics(generics, def_id, cx), }), ItemKind::TraitAlias(_, _, generics, bounds) => TraitAliasItem(TraitAlias { - generics: clean_generics(generics, cx), + generics: clean_generics(generics, def_id, cx), bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }), ItemKind::Union(_, generics, variant_data) => UnionItem(Union { - generics: clean_generics(generics, cx), + generics: clean_generics(generics, def_id, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), ItemKind::Struct(_, generics, variant_data) => StructItem(Struct { ctor_kind: variant_data.ctor_kind(), - generics: clean_generics(generics, cx), + generics: clean_generics(generics, def_id, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), // FIXME: handle attributes and derives that aren't proc macros, and macros with @@ -2866,7 +3332,7 @@ fn clean_maybe_renamed_item<'tcx>( TraitItem(Box::new(Trait { def_id, items, - generics: clean_generics(generics, cx), + generics: clean_generics(generics, def_id, cx), bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), })) } @@ -2929,7 +3395,7 @@ fn clean_impl<'tcx>( Some(of_trait) => of_trait.safety, None => hir::Safety::Safe, }, - generics: clean_generics(impl_.generics, cx), + generics: clean_generics(impl_.generics, def_id.to_def_id(), cx), trait_, for_, items, @@ -3144,7 +3610,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>( cx.with_param_env(def_id, |cx| { let kind = match item.kind { hir::ForeignItemKind::Fn(sig, idents, generics) => ForeignFunctionItem( - clean_function(cx, &sig, generics, ParamsSrc::Idents(idents)), + clean_function(cx, &sig, generics, def_id, ParamsSrc::Idents(idents)), sig.header.safety(), ), hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( @@ -3207,6 +3673,8 @@ fn clean_bound_vars<'tcx>( def_id, kind: GenericParamDefKind::Type { bounds: ThinVec::new(), + // Higher-ranked parameters do not participate in implied bound calculation. + implied_bounds: ThinVec::new(), default: None, synthetic: false, }, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 73d0f40275404..66785475f68e9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -573,10 +573,10 @@ impl Item { self.type_() == ItemType::Variant } pub(crate) fn is_associated_type(&self) -> bool { - matches!(self.kind, AssocTypeItem(..) | StrippedItem(box AssocTypeItem(..))) + matches!(self.kind, AssocTypeItem { .. } | StrippedItem(box AssocTypeItem { .. })) } pub(crate) fn is_required_associated_type(&self) -> bool { - matches!(self.kind, RequiredAssocTypeItem(..) | StrippedItem(box RequiredAssocTypeItem(..))) + matches!(self.kind, RequiredAssocTypeItem { .. } | StrippedItem(box RequiredAssocTypeItem { .. })) } pub(crate) fn is_associated_const(&self) -> bool { matches!(self.kind, ProvidedAssocConstItem(..) | ImplAssocConstItem(..) | StrippedItem(box (ProvidedAssocConstItem(..) | ImplAssocConstItem(..)))) @@ -772,8 +772,8 @@ impl Item { RequiredAssocConstItem(..) | ProvidedAssocConstItem(..) | ImplAssocConstItem(..) - | AssocTypeItem(..) - | RequiredAssocTypeItem(..) + | AssocTypeItem { .. } + | RequiredAssocTypeItem { .. } | RequiredMethodItem(..) | MethodItem(..) => { match tcx.associated_item(def_id).container { @@ -847,9 +847,17 @@ pub(crate) enum ItemKind { /// A required associated type in a trait declaration. /// /// The bounds may be non-empty if there is a `where` clause. - RequiredAssocTypeItem(Generics, Vec), + RequiredAssocTypeItem { + generics: Generics, + bounds: Vec, + implied_bounds: Vec, + }, /// An associated type in a trait impl or a provided one in a trait declaration. - AssocTypeItem(Box, Vec), + AssocTypeItem { + ty: Box, + bounds: Vec, + implied_bounds: Vec, + }, /// An item that has been stripped by a rustdoc pass StrippedItem(Box), /// This item represents a module with a `#[doc(keyword = "...")]` attribute which is used @@ -895,8 +903,8 @@ impl ItemKind { | RequiredAssocConstItem(..) | ProvidedAssocConstItem(..) | ImplAssocConstItem(..) - | RequiredAssocTypeItem(..) - | AssocTypeItem(..) + | RequiredAssocTypeItem { .. } + | AssocTypeItem { .. } | StrippedItem(_) | KeywordItem | AttributeItem => [].iter(), @@ -1113,6 +1121,18 @@ impl GenericBound { self.is_bounded_by_lang_item(cx, LangItem::MetaSized) } + pub(crate) fn is_maybe_sized_bound(&self, cx: &DocContext<'_>) -> bool { + if let GenericBound::TraitBound( + PolyTrait { ref trait_, .. }, + rustc_hir::TraitBoundModifiers { polarity: rustc_hir::BoundPolarity::Maybe(_), .. }, + ) = *self + && cx.tcx.is_lang_item(trait_.def_id(), LangItem::Sized) + { + return true; + } + false + } + fn is_bounded_by_lang_item(&self, cx: &DocContext<'_>, lang_item: LangItem) -> bool { if let GenericBound::TraitBound( PolyTrait { ref trait_, .. }, @@ -1181,10 +1201,20 @@ impl WherePredicate { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericParamDefKind { - Lifetime { outlives: ThinVec }, - Type { bounds: ThinVec, default: Option>, synthetic: bool }, + Lifetime { + outlives: ThinVec, + }, + Type { + bounds: ThinVec, + implied_bounds: ThinVec, + default: Option>, + synthetic: bool, + }, // Option> makes this type smaller than `Option` would. - Const { ty: Box, default: Option> }, + Const { + ty: Box, + default: Option>, + }, } impl GenericParamDefKind { @@ -1353,7 +1383,22 @@ pub(crate) enum Type { Infer, /// An `impl Trait`: `impl TraitA + TraitB + ...` - ImplTrait(Vec), + ImplTrait { + /// The bounds that are syntactically present: + /// ```rust + /// impl TraitA + TraitB + /// // ^^^^^^ ^^^^^^ + /// ``` + bounds: Vec, + /// Additional bounds implied by the syntactically present bounds: + /// ```rust + /// trait SizedAndStatic: Sized + 'static {} + /// + /// fn example(_: impl SizedAndStatic) {} + /// // ^^^^ + Sized + 'static (implied) + /// ``` + implied_bounds: Vec, + }, UnsafeBinder(Box), } @@ -1481,8 +1526,8 @@ impl Type { /// This function will panic if the return type does not match the expected sugaring for async /// functions. pub(crate) fn sugared_async_return_type(self) -> Type { - if let Type::ImplTrait(mut v) = self - && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _)) = v.pop() + if let Type::ImplTrait { mut bounds, .. } = self + && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _)) = bounds.pop() && let Some(segment) = trait_.segments.pop() && let GenericArgs::AngleBracketed { mut constraints, .. } = segment.args && let Some(constraint) = constraints.pop() @@ -1552,7 +1597,7 @@ impl Type { Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), - Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, + Generic(_) | SelfTy | Infer | ImplTrait { .. } | UnsafeBinder(_) => return None, }; Primitive(t).def_id(cache) } @@ -2407,14 +2452,14 @@ mod size_asserts { // tidy-alphabetical-start static_assert_size!(Crate, 16); // frequently moved by-value static_assert_size!(DocFragment, 32); - static_assert_size!(GenericArg, 32); + static_assert_size!(GenericArg, 48); static_assert_size!(GenericArgs, 24); - static_assert_size!(GenericParamDef, 40); + static_assert_size!(GenericParamDef, 48); static_assert_size!(Generics, 16); static_assert_size!(Item, 8); - static_assert_size!(ItemInner, 144); - static_assert_size!(ItemKind, 48); + static_assert_size!(ItemInner, 160); + static_assert_size!(ItemKind, 64); static_assert_size!(PathSegment, 32); - static_assert_size!(Type, 32); + static_assert_size!(Type, 48); // tidy-alphabetical-end } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 23aaa6cf98604..dccb68d71d1f7 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -267,7 +267,7 @@ pub(crate) fn build_deref_target_impls( for item in items { let target = match item.kind { - ItemKind::AssocTypeItem(ref t, _) => &t.type_, + ItemKind::AssocTypeItem { ref ty, .. } => &ty.type_, _ => continue, }; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 44bac8197539b..03acff0463fc7 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -55,8 +55,10 @@ pub(crate) struct DocContext<'tcx> { // therefore wouldn't use the corresp. generic arg anyway. Add support for them. pub(crate) args: DefIdMap, pub(crate) current_type_aliases: DefIdMap, - /// Table synthetic type parameter for `impl Trait` in argument position -> bounds - pub(crate) impl_trait_bounds: FxHashMap>, + /// Table of synthetic type parameter + /// for `impl Trait` in argument position -> (bounds, implied_bounds) + pub(crate) impl_trait_bounds: + FxHashMap, Vec)>, /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. pub(crate) generated_synthetics: FxHashSet<(Ty<'tcx>, DefId)>, diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index ee5f260615db5..d5380478d9232 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -94,8 +94,8 @@ pub(crate) trait DocFolder: Sized { | RequiredAssocConstItem(..) | ProvidedAssocConstItem(..) | ImplAssocConstItem(..) - | RequiredAssocTypeItem(..) - | AssocTypeItem(..) + | RequiredAssocTypeItem { .. } + | AssocTypeItem { .. } | KeywordItem | AttributeItem => kind, } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 5e5592269af60..2a400e64a9d94 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -375,8 +375,8 @@ impl DocFolder for CacheBuilder<'_, '_> { | clean::RequiredAssocConstItem(..) | clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) - | clean::RequiredAssocTypeItem(..) - | clean::AssocTypeItem(..) + | clean::RequiredAssocTypeItem { .. } + | clean::AssocTypeItem { .. } | clean::StrippedItem(..) | clean::KeywordItem | clean::AttributeItem => { @@ -483,7 +483,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It clean::StrippedItem(..) => return, clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) - | clean::AssocTypeItem(..) + | clean::AssocTypeItem { .. } if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) => { // skip associated items in trait impls @@ -491,7 +491,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It } clean::RequiredMethodItem(..) | clean::RequiredAssocConstItem(..) - | clean::RequiredAssocTypeItem(..) + | clean::RequiredAssocTypeItem { .. } | clean::StructFieldItem(..) | clean::VariantItem(..) => { // Don't index if containing module is stripped (i.e., private), @@ -510,7 +510,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It clean::MethodItem(..) | clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) - | clean::AssocTypeItem(..) => { + | clean::AssocTypeItem { .. } => { let last = cache.parent_stack.last().expect("parent_stack is empty 2"); let parent_did = match last { // impl Trait for &T { fn method(self); } diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index ab40c01cb369d..97c4a0f3a8bc5 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -132,7 +132,9 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::RequiredAssocConstItem(..) | clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) => ItemType::AssocConst, - clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => ItemType::AssocType, + clean::RequiredAssocTypeItem { .. } | clean::AssocTypeItem { .. } => { + ItemType::AssocType + } clean::ForeignTypeItem => ItemType::ForeignType, clean::KeywordItem => ItemType::Keyword, clean::AttributeItem => ItemType::Attribute, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 856e637a4587b..d62ee82ea4f0a 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -977,7 +977,7 @@ fn fmt_type( { true } - clean::ImplTrait(ref bounds) if bounds.len() > 1 => true, + clean::ImplTrait { ref bounds, implied_bounds: _ } if bounds.len() > 1 => true, _ => false, }; Wrapped::with_parens() @@ -985,7 +985,7 @@ fn fmt_type( .wrap_fn(|f| fmt_type(ty, f, use_absolute, cx)) .fmt(f) } - clean::ImplTrait(bounds) => { + clean::ImplTrait { bounds, implied_bounds: _ } => { f.write_str("impl ")?; print_generic_bounds(bounds, cx).fmt(f) } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 13178ee4e9934..f7cad94032be0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1293,7 +1293,7 @@ fn render_assoc_item( cx, ) .fmt(f), - clean::RequiredAssocTypeItem(generics, bounds) => assoc_type( + clean::RequiredAssocTypeItem { generics, bounds, .. } => assoc_type( item, generics, bounds, @@ -1303,7 +1303,7 @@ fn render_assoc_item( cx, ) .fmt(f), - clean::AssocTypeItem(ty, bounds) => assoc_type( + clean::AssocTypeItem { ty, bounds, .. } => assoc_type( item, &ty.generics, bounds, @@ -1553,9 +1553,9 @@ fn render_deref_methods( .items .iter() .find_map(|item| match item.kind { - clean::AssocTypeItem(box ref t, _) => Some(match *t { - clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), + clean::AssocTypeItem { box ref ty, .. } => Some(match *ty { + clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &ty.type_), + _ => (&ty.type_, &ty.type_), }), _ => None, }) @@ -1693,7 +1693,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { for (impl_, trait_did) in notable_impls { write!(f, "
{}
", impl_.print(false, cx))?; for it in &impl_.items { - let clean::AssocTypeItem(tydef, ..) = &it.kind else { + let clean::AssocTypeItem { ty: tydef, .. } = &it.kind else { continue; }; @@ -1940,7 +1940,7 @@ fn render_impl( ), )?; } - clean::RequiredAssocTypeItem(generics, bounds) => { + clean::RequiredAssocTypeItem { generics, bounds, .. } => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); write!( @@ -1967,7 +1967,7 @@ fn render_impl( ), )?; } - clean::AssocTypeItem(tydef, _bounds) => { + clean::AssocTypeItem { ty: tydef, .. } => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); write!( @@ -2027,7 +2027,7 @@ fn render_impl( clean::MethodItem(..) | clean::RequiredMethodItem(_) => { methods.push(trait_item) } - clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => { + clean::RequiredAssocTypeItem { .. } | clean::AssocTypeItem { .. } => { assoc_types.push(trait_item) } clean::RequiredAssocConstItem(..) @@ -2287,15 +2287,15 @@ fn render_impl_summary( write!(w, "{}", inner_impl.print(use_absolute, cx))?; if show_def_docs { for it in &inner_impl.items { - if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { + if let clean::AssocTypeItem { ref ty, .. } = it.kind { write!( w, "
{};
", assoc_type( it, - &tydef.generics, + &ty.generics, &[], // intentionally leaving out bounds - Some(&tydef.type_), + Some(&ty.type_), AssocItemLink::Anchor(None), 0, cx, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 96e941b598ad1..ffd467ec88f95 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -2022,7 +2022,7 @@ fn get_index_type_id( clean::Type::Pat(..) | clean::Generic(_) | clean::SelfTy - | clean::ImplTrait(_) + | clean::ImplTrait { .. } | clean::Infer | clean::UnsafeBinder(_) => None, } @@ -2138,7 +2138,7 @@ fn simplify_fn_type<'a, 'tcx>( }); } } - Type::ImplTrait(ref bounds) => { + Type::ImplTrait { ref bounds, .. } => { let mut type_bounds = Vec::new(); for bound in bounds { if let Some(path) = bound.get_trait_path() { @@ -2335,8 +2335,7 @@ fn simplify_fn_type<'a, 'tcx>( && trait_.items.iter().any(|at| at.is_required_associated_type()) { for assoc_ty in &trait_.items { - if let clean::ItemKind::RequiredAssocTypeItem(_generics, bounds) = - &assoc_ty.kind + if let clean::ItemKind::RequiredAssocTypeItem { bounds, .. } = &assoc_ty.kind && let Some(name) = assoc_ty.name { let idx = -isize::try_from(rgen.len() + 1).unwrap(); diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index b9f5ada417c7f..5139d0dcca0e1 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -512,9 +512,9 @@ fn sidebar_deref_methods<'a>( debug!("found Deref: {impl_:?}"); if let Some((target, real_target)) = impl_.inner_impl().items.iter().find_map(|item| match item.kind { - clean::AssocTypeItem(box ref t, _) => Some(match *t { - clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), + clean::AssocTypeItem { box ref ty, .. } => Some(match *ty { + clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &ty.type_), + _ => (&ty.type_, &ty.type_), }), _ => None, }) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 909262d563e9f..ad5655b60296e 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -325,15 +325,17 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum type_: ci.type_.into_json(renderer), value: Some(ci.kind.expr(renderer.tcx)), }, - RequiredAssocTypeItem(g, b) => ItemEnum::AssocType { - generics: g.into_json(renderer), - bounds: b.into_json(renderer), + RequiredAssocTypeItem { generics, bounds, implied_bounds } => ItemEnum::AssocType { + generics: generics.into_json(renderer), + bounds: bounds.into_json(renderer), + implied_bounds: implied_bounds.into_json(renderer), type_: None, }, - AssocTypeItem(t, b) => ItemEnum::AssocType { - generics: t.generics.into_json(renderer), - bounds: b.into_json(renderer), - type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)), + AssocTypeItem { ty, bounds, implied_bounds } => ItemEnum::AssocType { + generics: ty.generics.into_json(renderer), + bounds: bounds.into_json(renderer), + implied_bounds: implied_bounds.into_json(renderer), + type_: Some(ty.item_type.as_ref().unwrap_or(&ty.type_).into_json(renderer)), }, // `convert_item` early returns `None` for stripped items, keywords and attributes. KeywordItem | AttributeItem => unreachable!(), @@ -461,8 +463,9 @@ impl FromClean for GenericParamDefKind { Lifetime { outlives } => { GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) } } - Type { bounds, default, synthetic } => GenericParamDefKind::Type { + Type { bounds, implied_bounds, default, synthetic } => GenericParamDefKind::Type { bounds: bounds.into_json(renderer), + implied_bounds: implied_bounds.into_json(renderer), default: default.into_json(renderer), is_synthetic: *synthetic, }, @@ -578,7 +581,10 @@ impl FromClean for Type { type_: Box::new(t.into_json(renderer)), __pat_unstable_do_not_use: p.to_string(), }, - ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), + ImplTrait { bounds, implied_bounds } => Type::ImplTrait { + bounds: bounds.into_json(renderer), + implied_bounds: implied_bounds.into_json(renderer), + }, Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { is_mutable: *mutability == ast::Mutability::Mut, diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index b724d7e866a05..8cb1de35202f2 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -397,7 +397,7 @@ mod size_asserts { static_assert_size!(GenericArg, 80); static_assert_size!(GenericArgs, 104); static_assert_size!(GenericBound, 72); - static_assert_size!(GenericParamDef, 136); + static_assert_size!(GenericParamDef, 160); static_assert_size!(Impl, 304); // `Item` contains a `PathBuf`, which is different sizes on different OSes. static_assert_size!(Item, 528 + size_of::()); diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index e0ea760cf3ba5..211a7446c31a0 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -73,11 +73,11 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | clean::ForeignTypeItem - | clean::AssocTypeItem(..) + | clean::AssocTypeItem { .. } | clean::RequiredAssocConstItem(..) | clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) - | clean::RequiredAssocTypeItem(..) + | clean::RequiredAssocTypeItem { .. } // check for trait impl | clean::ImplItem(box clean::Impl { trait_: Some(_), .. }) ) diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 2339a6b69cd8a..b200be5a54d8a 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -167,7 +167,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> let target = items .iter() .find_map(|item| match item.kind { - AssocTypeItem(ref t, _) => Some(&t.type_), + AssocTypeItem { ref ty, .. } => Some(&ty.type_), _ => None, }) .expect("Deref impl without Target type"); diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index 5139ca301dd3d..97eb798f407b4 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -103,8 +103,8 @@ impl DocFolder for StabilityPropagator<'_, '_> { | ItemKind::RequiredAssocConstItem(..) | ItemKind::ProvidedAssocConstItem(..) | ItemKind::ImplAssocConstItem(..) - | ItemKind::RequiredAssocTypeItem(..) - | ItemKind::AssocTypeItem(..) + | ItemKind::RequiredAssocTypeItem { .. } + | ItemKind::AssocTypeItem { .. } | ItemKind::PrimitiveItem(..) | ItemKind::KeywordItem | ItemKind::AttributeItem => own_stability, diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 99d22526f85b7..8c8ed9fa77433 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -82,7 +82,7 @@ impl DocFolder for Stripper<'_, '_> { clean::MethodItem(..) | clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) - | clean::AssocTypeItem(..) => { + | clean::AssocTypeItem { .. } => { let item_id = i.item_id; if item_id.is_local() && !self.effective_visibilities.is_reachable(self.tcx, item_id.expect_def_id()) @@ -123,7 +123,7 @@ impl DocFolder for Stripper<'_, '_> { // tymethods etc. have no control over privacy clean::RequiredMethodItem(..) | clean::RequiredAssocConstItem(..) - | clean::RequiredAssocTypeItem(..) => {} + | clean::RequiredAssocTypeItem { .. } => {} // Proc-macros are always public clean::ProcMacroItem(..) => {} diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 4d31409afe825..57f29630e15be 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -47,8 +47,8 @@ pub(crate) trait DocVisitor<'a>: Sized { | RequiredAssocConstItem(..) | ProvidedAssocConstItem(..) | ImplAssocConstItem(..) - | RequiredAssocTypeItem(..) - | AssocTypeItem(..) + | RequiredAssocTypeItem { .. } + | AssocTypeItem { .. } | KeywordItem | AttributeItem => {} } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 658d3791d2578..a2d80c8d7e211 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -37,8 +37,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Add `ItemKind::Attribute`. -pub const FORMAT_VERSION: u32 = 56; +// Latest feature: Add `implied_bounds`. +pub const FORMAT_VERSION: u32 = 57; /// The root of the emitted JSON blob. /// @@ -663,6 +663,18 @@ pub enum ItemEnum { /// } /// ``` bounds: Vec, + /// Additional bounds that are implied by the bounds that were syntactically present. + /// + /// For example: + /// ```rust + /// trait SizedAndStatic: Sized + 'static {} + /// + /// trait Example { + /// type Item: SizedAndStatic; + /// // Item: Sized and Item: 'static are implied bounds + /// } + /// ``` + implied_bounds: Vec, /// Inside a trait declaration, this is the default for the associated type, if provided. /// Inside an impl block, this is the type assigned to the associated type, and will always /// be present. @@ -951,6 +963,19 @@ pub enum GenericParamDefKind { /// // ^^^^^^^ /// ``` bounds: Vec, + /// Additional bounds that are implied by other requirements. + /// + /// For example: + /// ```rust + /// trait SizedAndStatic: Sized + 'static {} + /// + /// fn f(x: T) {} + /// // T: Sized and T: 'static are implied bounds + /// ``` + /// + /// All such bounds for this type parameter appear here, regardless of whether + /// the bound was implied by a `where` clause or by a direct type bound. + implied_bounds: Vec, /// The default type for this parameter, if provided, e.g. /// /// ```rust @@ -1170,7 +1195,12 @@ pub enum Type { __pat_unstable_do_not_use: String, }, /// An opaque type that satisfies a set of bounds, `impl TraitA + TraitB + ...` - ImplTrait(Vec), + ImplTrait { + /// The syntactic bounds specified on the `impl Trait`. + bounds: Vec, + /// Additional bounds implied by the syntactic bounds, such as `Sized` or `'static`. + implied_bounds: Vec, + }, /// A type that's left to be inferred, `_` Infer, /// A raw pointer type, e.g. `*mut u32`, `*const u8`, etc. diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 0a4051fcbe8cd..f3a76abffbd07 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -115,9 +115,10 @@ impl<'a> Validator<'a> { // FIXME: Why don't these have their own structs? ItemEnum::ExternCrate { .. } => {} ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_), - ItemEnum::AssocType { generics, bounds, type_ } => { + ItemEnum::AssocType { generics, bounds, implied_bounds, type_ } => { self.check_generics(generics); bounds.iter().for_each(|b| self.check_generic_bound(b)); + implied_bounds.iter().for_each(|b| self.check_generic_bound(b)); if let Some(ty) = type_ { self.check_type(ty); } @@ -266,7 +267,10 @@ impl<'a> Validator<'a> { Type::Tuple(tys) => tys.iter().for_each(|ty| self.check_type(ty)), Type::Slice(inner) => self.check_type(&**inner), Type::Array { type_, len: _ } => self.check_type(&**type_), - Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)), + Type::ImplTrait { bounds, implied_bounds } => { + bounds.iter().for_each(|b| self.check_generic_bound(b)); + implied_bounds.iter().for_each(|b| self.check_generic_bound(b)); + } Type::Infer => {} Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_), Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_), @@ -332,8 +336,14 @@ impl<'a> Validator<'a> { fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) { match &gpd.kind { rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {} - rustdoc_json_types::GenericParamDefKind::Type { bounds, default, is_synthetic: _ } => { + rustdoc_json_types::GenericParamDefKind::Type { + bounds, + implied_bounds, + default, + is_synthetic: _, + } => { bounds.iter().for_each(|b| self.check_generic_bound(b)); + implied_bounds.iter().for_each(|b| self.check_generic_bound(b)); if let Some(ty) = default { self.check_type(ty); } diff --git a/tests/rustdoc-json/explicit-sized-bound.rs b/tests/rustdoc-json/explicit-sized-bound.rs new file mode 100644 index 0000000000000..0664784acd556 --- /dev/null +++ b/tests/rustdoc-json/explicit-sized-bound.rs @@ -0,0 +1,30 @@ +//! When a `Sized` bound is explicitly given, it appears in rustdoc JSON too. + +//@ has "$.index[?(@.name=='explicitly_sized_generic')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='explicitly_sized_generic')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn explicitly_sized_generic(value: T) -> T { + value +} + +//@ has "$.index[?(@.name=='explicitly_sized_generic_where_clause')].inner.function.generics.where_predicates[0].bound_predicate.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='explicitly_sized_generic_where_clause')].inner.function.generics.where_predicates[0].bound_predicate.bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn explicitly_sized_generic_where_clause(value: T) -> T +where + T: Sized, +{ + value +} + +//@ has "$.index[?(@.name=='explicitly_sized_impl_trait')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='explicitly_sized_impl_trait')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='explicitly_sized_impl_trait')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='explicitly_sized_impl_trait')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn explicitly_sized_impl_trait(value: impl Sized) -> impl Sized { + value +} + +pub trait Example { + //@ has "$.index[?(@.name=='Explicit')].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ !has "$.index[?(@.name=='Explicit')].inner.assoc_type.bounds[?(@.trait_bound.modifier=='maybe')]" + type Explicit: Sized; +} diff --git a/tests/rustdoc-json/fns/async_return.rs b/tests/rustdoc-json/fns/async_return.rs index e7c6a2981aceb..64892755f4187 100644 --- a/tests/rustdoc-json/fns/async_return.rs +++ b/tests/rustdoc-json/fns/async_return.rs @@ -16,17 +16,17 @@ pub async fn get_int_async() -> i32 { 42 } -//@ is "$.index[?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.path" '"Future"' -//@ is "$.index[?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"' -//@ is "$.index[?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\" +//@ is "$.index[?(@.name=='get_int_future')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.path" '"Future"' +//@ is "$.index[?(@.name=='get_int_future')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"' +//@ is "$.index[?(@.name=='get_int_future')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\" //@ is "$.index[?(@.name=='get_int_future')].inner.function.header.is_async" false pub fn get_int_future() -> impl Future { async { 42 } } -//@ is "$.index[?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.path" '"Future"' -//@ is "$.index[?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"' -//@ is "$.index[?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\" +//@ is "$.index[?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.path" '"Future"' +//@ is "$.index[?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"' +//@ is "$.index[?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\" //@ is "$.index[?(@.name=='get_int_future_async')].inner.function.header.is_async" true pub async fn get_int_future_async() -> impl Future { async { 42 } diff --git a/tests/rustdoc-json/fns/generic_args.rs b/tests/rustdoc-json/fns/generic_args.rs index 2ea68173d68d8..18fca2496afe6 100644 --- a/tests/rustdoc-json/fns/generic_args.rs +++ b/tests/rustdoc-json/fns/generic_args.rs @@ -10,6 +10,8 @@ pub trait GenericFoo<'a> {} //@ is "$.index[?(@.name=='generics')].inner.function.generics.params[0].kind.type.default" 'null' //@ count "$.index[?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[*]" 1 //@ is "$.index[?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" '$foo' +//@ has "$.index[?(@.name=='generics')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='generics')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" //@ count "$.index[?(@.name=='generics')].inner.function.sig.inputs[*]" 1 //@ is "$.index[?(@.name=='generics')].inner.function.sig.inputs[0][0]" '"f"' //@ is "$.index[?(@.name=='generics')].inner.function.sig.inputs[0][1].generic" '"F"' @@ -19,15 +21,33 @@ pub fn generics(f: F) {} //@ count "$.index[?(@.name=='impl_trait')].inner.function.generics.params[*]" 1 //@ is "$.index[?(@.name=='impl_trait')].inner.function.generics.params[0].name" '"impl Foo"' //@ is "$.index[?(@.name=='impl_trait')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $foo +//@ has "$.index[?(@.name=='impl_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='impl_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" //@ count "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[*]" 1 //@ is "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][0]" '"f"' -//@ count "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait[*]" 1 -//@ is "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.id" $foo +//@ count "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait.bounds[*]" 1 +//@ is "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait.bounds[0].trait_bound.trait.id" $foo +//@ has "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Foo')]" pub fn impl_trait(f: impl Foo) {} //@ count "$.index[?(@.name=='where_clase')].inner.function.generics.params[*]" 3 //@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].name" '"F"' -//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "is_synthetic": false}}' +//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind.type.bounds" "[]" +//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind.type.default" 'null' +//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind.type.is_synthetic" 'false' +//@ !has "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Foo')]" +//@ has "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[1].name" '"G"' +//@ !has "$.index[?(@.name=='where_clase')].inner.function.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='GenericFoo')]" +//@ has "$.index[?(@.name=='where_clase')].inner.function.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='where_clase')].inner.function.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[2].name" '"H"' +//@ !has "$.index[?(@.name=='where_clase')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Foo')]" +//@ has "$.index[?(@.name=='where_clase')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='where_clase')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" //@ count "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[*]" 3 //@ is "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[0][0]" '"f"' //@ is "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[0][1].generic" '"F"' @@ -36,7 +56,6 @@ pub fn impl_trait(f: impl Foo) {} //@ is "$.index[?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.type.generic" \"F\" //@ count "$.index[?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.bounds[*]" 1 //@ is "$.index[?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.id" $foo - //@ is "$.index[?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.type.generic" \"G\" //@ count "$.index[?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.bounds[*]" 1 //@ is "$.index[?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.trait.id" $generic_foo diff --git a/tests/rustdoc-json/fns/generic_returns.rs b/tests/rustdoc-json/fns/generic_returns.rs index a0d4c745599cb..b460d5ed627a4 100644 --- a/tests/rustdoc-json/fns/generic_returns.rs +++ b/tests/rustdoc-json/fns/generic_returns.rs @@ -4,8 +4,8 @@ pub trait Foo {} //@ is "$.index[?(@.name=='get_foo')].inner.function.sig.inputs" [] -//@ count "$.index[?(@.name=='get_foo')].inner.function.sig.output.impl_trait[*]" 1 -//@ is "$.index[?(@.name=='get_foo')].inner.function.sig.output.impl_trait[0].trait_bound.trait.id" $foo +//@ count "$.index[?(@.name=='get_foo')].inner.function.sig.output.impl_trait.bounds[*]" 1 +//@ is "$.index[?(@.name=='get_foo')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.id" $foo pub fn get_foo() -> impl Foo { Fooer {} } diff --git a/tests/rustdoc-json/fns/generics.rs b/tests/rustdoc-json/fns/generics.rs index 3efd917309a0e..74bb5f4e9a888 100644 --- a/tests/rustdoc-json/fns/generics.rs +++ b/tests/rustdoc-json/fns/generics.rs @@ -16,5 +16,5 @@ pub fn one_generic_param_fn(w: T) {} //@ is "$.index[?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id //@ count "$.index[?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[*]" 1 //@ is "$.index[?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[0][0]" '"w"' -//@ is "$.index[?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.id" $wham_id +//@ is "$.index[?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[0][1].impl_trait.bounds[0].trait_bound.trait.id" $wham_id pub fn one_synthetic_generic_param_fn(w: impl Wham) {} diff --git a/tests/rustdoc-json/generic-args.rs b/tests/rustdoc-json/generic-args.rs index b4a73a046b506..c3642ba7685fa 100644 --- a/tests/rustdoc-json/generic-args.rs +++ b/tests/rustdoc-json/generic-args.rs @@ -17,7 +17,7 @@ pub fn my_fn1(_: ::MyType) {} //@ is "$.index[?(@.name=='my_fn2')].inner.function.sig.inputs[0][1].dyn_trait.traits[0].trait.args.angle_bracketed.constraints[0].args" null pub fn my_fn2(_: IntoIterator) {} -//@ is "$.index[?(@.name=='my_fn3')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.args.parenthesized.inputs" [] +//@ is "$.index[?(@.name=='my_fn3')].inner.function.sig.inputs[0][1].impl_trait.bounds[0].trait_bound.trait.args.parenthesized.inputs" [] pub fn my_fn3(f: impl FnMut()) {} fn main() {} diff --git a/tests/rustdoc-json/impl-trait-in-assoc-type.rs b/tests/rustdoc-json/impl-trait-in-assoc-type.rs index 742a46e896746..4ee76e7bd61e2 100644 --- a/tests/rustdoc-json/impl-trait-in-assoc-type.rs +++ b/tests/rustdoc-json/impl-trait-in-assoc-type.rs @@ -8,11 +8,11 @@ impl IntoIterator for AlwaysTrue { /// type Item type Item = bool; - //@ count '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[*]' 1 - //@ is '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.path' '"Iterator"' - //@ count '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[*]' 1 - //@ is '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name' '"Item"' - //@ is '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive' '"bool"' + //@ count '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait.bounds[*]' 1 + //@ is '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait.bounds[0].trait_bound.trait.path' '"Iterator"' + //@ count '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[*]' 1 + //@ is '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[0].name' '"Item"' + //@ is '$.index[?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive' '"bool"' //@ set IntoIter = '$.index[?(@.docs=="type IntoIter")].id' /// type IntoIter diff --git a/tests/rustdoc-json/impl-trait-precise-capturing.rs b/tests/rustdoc-json/impl-trait-precise-capturing.rs index 37adb514f55fb..cab7ca0aac0e0 100644 --- a/tests/rustdoc-json/impl-trait-precise-capturing.rs +++ b/tests/rustdoc-json/impl-trait-precise-capturing.rs @@ -1,4 +1,4 @@ -//@ is "$.index[?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[0].lifetime" \"\'a\" -//@ is "$.index[?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[1].param" \"T\" -//@ is "$.index[?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[2].param" \"N\" +//@ is "$.index[?(@.name=='hello')].inner.function.sig.output.impl_trait.bounds[1].use[0].lifetime" \"\'a\" +//@ is "$.index[?(@.name=='hello')].inner.function.sig.output.impl_trait.bounds[1].use[1].param" \"T\" +//@ is "$.index[?(@.name=='hello')].inner.function.sig.output.impl_trait.bounds[1].use[2].param" \"N\" pub fn hello<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {} diff --git a/tests/rustdoc-json/implied-bounds-assoc-type.rs b/tests/rustdoc-json/implied-bounds-assoc-type.rs new file mode 100644 index 0000000000000..c7e63af4f4c88 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-assoc-type.rs @@ -0,0 +1,22 @@ +pub trait SizedOnly: Sized {} +impl SizedOnly for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +//@ has "$.index[?(@.name=='Item')].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='Item')].inner.assoc_type.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='Item')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='Item')].inner.assoc_type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='Item')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +pub trait Container { + type Item: SizedOnly + ?Sized; +} + +//@ has "$.index[?(@.name=='Output')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='Output')].inner.assoc_type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='Output')].inner.assoc_type.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='Output')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub trait StaticContainer { + type Output: StaticOnly; +} diff --git a/tests/rustdoc-json/implied-bounds-impl-trait-args.rs b/tests/rustdoc-json/implied-bounds-impl-trait-args.rs new file mode 100644 index 0000000000000..bd5385cc9dfad --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-impl-trait-args.rs @@ -0,0 +1,44 @@ +use std::fmt::Debug; + +pub trait SizedOnly: Sized {} +impl SizedOnly for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +//@ is "$.index[?(@.name=='claimed_unsized_value_arg')].inner.function.sig.inputs[0][0]" '"arg"' +//@ has "$.index[?(@.name=='claimed_unsized_value_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='claimed_unsized_value_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='claimed_unsized_value_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='claimed_unsized_value_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='claimed_unsized_value_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='claimed_unsized_value_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +pub fn claimed_unsized_value_arg(arg: impl SizedOnly + ?Sized) {} + +//@ is "$.index[?(@.name=='claimed_unsized_ref_arg')].inner.function.sig.inputs[0][0]" '"arg"' +//@ has "$.index[?(@.name=='claimed_unsized_ref_arg')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='claimed_unsized_ref_arg')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='claimed_unsized_ref_arg')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='claimed_unsized_ref_arg')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +pub fn claimed_unsized_ref_arg(arg: &(impl SizedOnly + ?Sized)) {} + +//@ is "$.index[?(@.name=='implicitly_static_value_arg')].inner.function.sig.inputs[0][0]" '"arg"' +//@ has "$.index[?(@.name=='implicitly_static_value_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='implicitly_static_value_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub fn implicitly_static_value_arg(arg: impl StaticOnly) {} + +//@ is "$.index[?(@.name=='implicitly_sized_because_fn_arg')].inner.function.sig.inputs[0][0]" '"arg"' +//@ has "$.index[?(@.name=='implicitly_sized_because_fn_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='implicitly_sized_because_fn_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='implicitly_sized_because_fn_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" +pub fn implicitly_sized_because_fn_arg(arg: impl Debug + ?Sized) {} + +//@ has "$.index[?(@.name=='sized_only_ref')].inner.function.sig.inputs[0][0]" '"arg"' +//@ has "$.index[?(@.name=='sized_only_ref')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='sized_only_ref')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='sized_only_ref')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='sized_only_ref')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='sized_only_ref')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn sized_only_ref(arg: &(impl SizedOnly + ?Sized)) { + let _ = arg; +} diff --git a/tests/rustdoc-json/implied-bounds-impl-trait-return.rs b/tests/rustdoc-json/implied-bounds-impl-trait-return.rs new file mode 100644 index 0000000000000..52f8313fcfd2b --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-impl-trait-return.rs @@ -0,0 +1,23 @@ +use std::fmt::Debug; + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +//@ has "$.index[?(@.name=='returns_static')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='returns_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='returns_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='returns_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='returns_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub fn returns_static() -> impl StaticOnly { + 0u8 +} + +//@ has "$.index[?(@.name=='returns_maybe_unsized_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='returns_maybe_unsized_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='returns_maybe_unsized_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='returns_maybe_unsized_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='returns_maybe_unsized_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_maybe_unsized_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" +pub fn returns_maybe_unsized_ref() -> &'static (impl Debug + ?Sized) { + Box::leak(Box::new("hello world".to_string())) +} diff --git a/tests/rustdoc-json/implied-bounds-multi-lifetimes.rs b/tests/rustdoc-json/implied-bounds-multi-lifetimes.rs new file mode 100644 index 0000000000000..100ad5ab9ac68 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-multi-lifetimes.rs @@ -0,0 +1,33 @@ +//@ is "$.index[?(@.name=='Pair')].inner.struct.generics.params[2].name" '"T"' +//@ has "$.index[?(@.name=='Pair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='Pair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'b\")]" +//@ has "$.index[?(@.name=='Pair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='Pair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub struct Pair<'a, 'b, T>(&'a T, &'b T); + +//@ is "$.index[?(@.name=='require_pair')].inner.function.generics.params[2].name" '"T"' +//@ has "$.index[?(@.name=='require_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='require_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'b\")]" +//@ has "$.index[?(@.name=='require_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='require_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn require_pair<'a, 'b, T>(_: &'a &'b T) -> Pair<'a, 'b, T> { + todo!() +} + +//@ is "$.index[?(@.name=='OutlivePair')].inner.struct.generics.params[2].name" '"T"' +//@ has "$.index[?(@.name=='OutlivePair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='OutlivePair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'b\")]" +//@ has "$.index[?(@.name=='OutlivePair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='OutlivePair')].inner.struct.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub struct OutlivePair<'a, 'b: 'a, T>(&'a T, &'b T); + +//@ is "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[1].name" \"\'b\" +//@ is "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[2].name" '"T"' +// TODO: enable this: //@ is "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[1].kind.lifetime.implicitly_outlives" '["\'a"]' +//@ has "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.outlives==\"'b\")]" +//@ has "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[2].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn require_outlive_pair<'a, 'b, T>(x: &'a T, y: &'b T) -> OutlivePair<'a, 'b, T> { + todo!() +} diff --git a/tests/rustdoc-json/implied-bounds-needing-elaboration.rs b/tests/rustdoc-json/implied-bounds-needing-elaboration.rs new file mode 100644 index 0000000000000..3afb4826ad7d5 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-needing-elaboration.rs @@ -0,0 +1,23 @@ +pub trait NeedsSized: Sized {} +pub trait IndirectSized: NeedsSized {} + +pub trait NeedsStatic: 'static {} +pub trait IndirectStatic: NeedsStatic {} + +// - `IndirectStatic` and `IndirectSized` are explicit bounds, only appearing in `bounds`. +// - `NeedsSized`, `NeedsStatic`, `Sized`, and `'static` are implied bounds, +// only appearing in `implied_bounds`. +//@ has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='IndirectStatic')]" +//@ has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='IndirectSized')]" +//@ !has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='NeedsStatic')]" +//@ !has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ !has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='IndirectStatic')]" +//@ !has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='IndirectSized')]" +//@ has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsStatic')]" +//@ has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='example')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.outlives==\"'static\")]" +pub fn example(value: &T) {} diff --git a/tests/rustdoc-json/implied-bounds-no-duplicates.rs b/tests/rustdoc-json/implied-bounds-no-duplicates.rs new file mode 100644 index 0000000000000..c7d9f1eee68eb --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-no-duplicates.rs @@ -0,0 +1,141 @@ +pub trait SizedOnly: Sized {} +impl SizedOnly for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +pub trait OtherSized: Sized {} +impl OtherSized for T {} + +pub trait OtherStatic: 'static {} +impl OtherStatic for T {} + +//@ has "$.index[?(@.name=='duplicate_generic_sized')]" +//@ count "$.index[?(@.name=='duplicate_generic_sized')].inner.function.generics.params[0].kind.type.bounds[*]" 2 +//@ has "$.index[?(@.name=='duplicate_generic_sized')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='duplicate_generic_sized')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='duplicate_generic_sized')].inner.function.generics.params[0].kind.type.implied_bounds[*]" +pub fn duplicate_generic_sized(_t: T) {} + +//@ has "$.index[?(@.name=='duplicate_generic_static')]" +//@ count "$.index[?(@.name=='duplicate_generic_static')].inner.function.generics.params[0].kind.type.bounds[*]" 2 +//@ has "$.index[?(@.name=='duplicate_generic_static')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='duplicate_generic_static')].inner.function.generics.params[0].kind.type.bounds[?(@.outlives==\"'static\")]" +//@ count "$.index[?(@.name=='duplicate_generic_static')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='duplicate_generic_static')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='duplicate_generic_static')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ !has "$.index[?(@.name=='duplicate_generic_static')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.outlives==\"'static\")]" +pub fn duplicate_generic_static(_t: T) {} + +//@ count "$.index[?(@.name=='SizedItem')].inner.assoc_type.bounds[*]" 2 +//@ has "$.index[?(@.name=='SizedItem')].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='SizedItem')].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='SizedItem')].inner.assoc_type.implied_bounds[*]" +pub trait WithSizedAssoc { + type SizedItem: SizedOnly + Sized; +} + +//@ count "$.index[?(@.name=='StaticItem')].inner.assoc_type.bounds[*]" 2 +//@ has "$.index[?(@.name=='StaticItem')].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='StaticItem')].inner.assoc_type.bounds[?(@.outlives==\"'static\")]" +//@ count "$.index[?(@.name=='StaticItem')].inner.assoc_type.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='StaticItem')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='StaticItem')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ !has "$.index[?(@.name=='StaticItem')].inner.assoc_type.implied_bounds[?(@.outlives==\"'static\")]" +pub trait WithStaticAssoc { + type StaticItem: StaticOnly + 'static; +} + +//@ has "$.index[?(@.name=='sized_not_implied')]" +//@ count "$.index[?(@.name=='sized_not_implied')].inner.function.sig.inputs[0][1].impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='sized_not_implied')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='sized_not_implied')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ count "$.index[?(@.name=='sized_not_implied')].inner.function.generics.params[0].kind.type.bounds[*]" 2 +//@ has "$.index[?(@.name=='sized_not_implied')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='sized_not_implied')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='sized_not_implied')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[*]" +//@ !has "$.index[?(@.name=='sized_not_implied')].inner.function.generics.params[0].kind.type.implied_bounds[*]" +pub fn sized_not_implied(arg: impl SizedOnly + Sized) { + let _ = arg; +} + +//@ has "$.index[?(@.name=='static_not_implied')]" +//@ count "$.index[?(@.name=='static_not_implied')].inner.function.sig.inputs[0][1].impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='static_not_implied')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='static_not_implied')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.outlives==\"'static\")]" +//@ count "$.index[?(@.name=='static_not_implied')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='static_not_implied')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ count "$.index[?(@.name=='static_not_implied')].inner.function.generics.params[0].kind.type.bounds[*]" 2 +//@ has "$.index[?(@.name=='static_not_implied')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='static_not_implied')].inner.function.generics.params[0].kind.type.bounds[?(@.outlives==\"'static\")]" +//@ count "$.index[?(@.name=='static_not_implied')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='static_not_implied')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='static_not_implied')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ !has "$.index[?(@.name=='static_not_implied')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub fn static_not_implied(arg: impl StaticOnly + 'static) { + let _ = arg; +} + +//@ has "$.index[?(@.name=='sized_return_not_implied')]" +//@ count "$.index[?(@.name=='sized_return_not_implied')].inner.function.sig.output.impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='sized_return_not_implied')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='sized_return_not_implied')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='sized_return_not_implied')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ !has "$.index[?(@.name=='sized_return_not_implied')].inner.function.sig.output.impl_trait.implied_bounds[*]" +pub fn sized_return_not_implied() -> impl SizedOnly + Sized { + () +} + +//@ has "$.index[?(@.name=='static_return_not_implied')]" +//@ count "$.index[?(@.name=='static_return_not_implied')].inner.function.sig.output.impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='static_return_not_implied')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='static_return_not_implied')].inner.function.sig.output.impl_trait.bounds[?(@.outlives==\"'static\")]" +//@ count "$.index[?(@.name=='static_return_not_implied')].inner.function.sig.output.impl_trait.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='static_return_not_implied')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='static_return_not_implied')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ !has "$.index[?(@.name=='static_return_not_implied')].inner.function.sig.output.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub fn static_return_not_implied() -> impl StaticOnly + 'static { + () +} + +//@ has "$.index[?(@.name=='diamond_implied_sized_arg')]" +//@ count "$.index[?(@.name=='diamond_implied_sized_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='diamond_implied_sized_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='diamond_implied_sized_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='OtherSized')]" +//@ count "$.index[?(@.name=='diamond_implied_sized_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='diamond_implied_sized_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn diamond_implied_sized_arg(arg: impl SizedOnly + OtherSized) { + let _ = arg; +} + +//@ has "$.index[?(@.name=='diamond_implied_static_arg')]" +//@ count "$.index[?(@.name=='diamond_implied_static_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='diamond_implied_static_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='diamond_implied_static_arg')].inner.function.sig.inputs[0][1].impl_trait.bounds[?(@.trait_bound.trait.path=='OtherStatic')]" +//@ count "$.index[?(@.name=='diamond_implied_static_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[*]" 2 +//@ has "$.index[?(@.name=='diamond_implied_static_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='diamond_implied_static_arg')].inner.function.sig.inputs[0][1].impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub fn diamond_implied_static_arg(arg: impl StaticOnly + OtherStatic) { + let _ = arg; +} + +//@ has "$.index[?(@.name=='diamond_implied_sized_return')]" +//@ count "$.index[?(@.name=='diamond_implied_sized_return')].inner.function.sig.output.impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='diamond_implied_sized_return')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" +//@ has "$.index[?(@.name=='diamond_implied_sized_return')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='OtherSized')]" +//@ count "$.index[?(@.name=='diamond_implied_sized_return')].inner.function.sig.output.impl_trait.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='diamond_implied_sized_return')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn diamond_implied_sized_return() -> impl SizedOnly + OtherSized { + () +} + +//@ has "$.index[?(@.name=='diamond_implied_static_return')]" +//@ count "$.index[?(@.name=='diamond_implied_static_return')].inner.function.sig.output.impl_trait.bounds[*]" 2 +//@ has "$.index[?(@.name=='diamond_implied_static_return')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='diamond_implied_static_return')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='OtherStatic')]" +//@ count "$.index[?(@.name=='diamond_implied_static_return')].inner.function.sig.output.impl_trait.implied_bounds[*]" 2 +//@ has "$.index[?(@.name=='diamond_implied_static_return')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='diamond_implied_static_return')].inner.function.sig.output.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub fn diamond_implied_static_return() -> impl StaticOnly + OtherStatic { + () +} diff --git a/tests/rustdoc-json/implied-bounds-redundant-sized.rs b/tests/rustdoc-json/implied-bounds-redundant-sized.rs new file mode 100644 index 0000000000000..32aefe959d4db --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-redundant-sized.rs @@ -0,0 +1,18 @@ +//@ has "$.index[?(@.name=='redundant_at_def')]" +//@ has "$.index[?(@.name=='redundant_where')]" + +//@ count "$.index[?(@.name=='redundant_at_def')].inner.function.generics.where_predicates[*]" 0 +//@ has "$.index[?(@.name=='redundant_at_def')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='redundant_at_def')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ count "$.index[?(@.name=='redundant_at_def')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 0 +pub fn redundant_at_def(_: T) {} + +//@ is "$.index[?(@.name=='redundant_where')].inner.function.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.path" '"Sized"' +//@ is "$.index[?(@.name=='redundant_where')].inner.function.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.modifier" '"none"' +//@ count "$.index[?(@.name=='redundant_where')].inner.function.generics.params[0].kind.type.bounds[*]" 0 +//@ count "$.index[?(@.name=='redundant_where')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 0 +pub fn redundant_where(_: T) +where + T: Sized, +{ +} diff --git a/tests/rustdoc-json/implied-bounds-refs.rs b/tests/rustdoc-json/implied-bounds-refs.rs new file mode 100644 index 0000000000000..48772c4c023e6 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-refs.rs @@ -0,0 +1,45 @@ +use std::fmt::Debug; + +//@ has "$.index[?(@.name=='ref_maybe_unsized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='ref_maybe_unsized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='ref_maybe_unsized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ count "$.index[?(@.name=='ref_maybe_unsized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[*]" 0 +pub fn ref_maybe_unsized(arg: &(impl Debug + ?Sized)) { + let _ = arg; +} + +//@ has "$.index[?(@.name=='ref_sized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ count "$.index[?(@.name=='ref_sized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='ref_sized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='ref_sized')].inner.function.sig.inputs[0][1].borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn ref_sized(arg: &impl Debug) { + let _ = arg; +} + +//@ has "$.index[?(@.name=='ptr_maybe_unsized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='ptr_maybe_unsized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='ptr_maybe_unsized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ count "$.index[?(@.name=='ptr_maybe_unsized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.implied_bounds[*]" 0 +pub fn ptr_maybe_unsized(arg: *const (impl Debug + ?Sized)) { + let _ = arg; +} + +//@ has "$.index[?(@.name=='ptr_sized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ count "$.index[?(@.name=='ptr_sized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='ptr_sized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='ptr_sized')].inner.function.sig.inputs[0][1].raw_pointer.type.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn ptr_sized(arg: *const impl Debug) { + let _ = arg; +} + +//@ count "$.index[?(@.name=='nested_ref_maybe_unsized')].inner.function.sig.inputs[0][1].borrowed_ref.type.borrowed_ref.type.impl_trait.implied_bounds[*]" 0 +pub fn nested_ref_maybe_unsized(arg: &&(impl Debug + ?Sized)) { + let _ = arg; +} + +//@ count "$.index[?(@.name=='nested_ref_sized')].inner.function.sig.inputs[0][1].borrowed_ref.type.borrowed_ref.type.impl_trait.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='nested_ref_sized')].inner.function.sig.inputs[0][1].borrowed_ref.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='nested_ref_sized')].inner.function.sig.inputs[0][1].borrowed_ref.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn nested_ref_sized(arg: &&impl Debug) { + let _ = arg; +} diff --git a/tests/rustdoc-json/implied-bounds-simple.rs b/tests/rustdoc-json/implied-bounds-simple.rs new file mode 100644 index 0000000000000..ca0d2b47928ea --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-simple.rs @@ -0,0 +1,52 @@ +//@ has "$.index[?(@.name=='unsized_ok')]" +//@ has "$.index[?(@.name=='sized_through_trait')]" +//@ has "$.index[?(@.name=='static_via_trait')]" +//@ has "$.index[?(@.name=='carries_inner_unsized')]" + +pub trait NeedsSized: Sized {} + +//@ count "$.index[?(@.name=='unsized_ok')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 0 +pub fn unsized_ok(_value: &T) {} + +//@ has "$.index[?(@.name=='sized_through_trait')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='sized_through_trait')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='sized_through_trait')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='sized_through_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='sized_through_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='sized_through_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub fn sized_through_trait(_value: &T) {} + +pub trait NeedsStatic: 'static {} +impl NeedsStatic for T {} + +//@ has "$.index[?(@.name=='static_via_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='static_via_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='static_via_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsStatic')]" +//@ has "$.index[?(@.name=='static_via_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='static_via_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn static_via_trait(_value: T) {} + +//@ count "$.index[?(@.name=='explicit_sized')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 0 +pub fn explicit_sized(_value: T) {} + +//@ count "$.index[?(@.name=='explicit_sized_ref')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 0 +pub fn explicit_sized_ref(_value: &T) {} + +//@ has "$.index[?(@.name=='Inner')].inner.struct.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='Inner')].inner.struct.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='Inner')].inner.struct.generics.params[1].kind.type.implied_bounds[?(@.outlives==\"'a\")]" +pub struct Inner<'a, T>(&'a T); + +//@ has "$.index[?(@.name=='InnerUnsized')].inner.struct.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='InnerUnsized')].inner.struct.generics.params[1].kind.type.bounds[?(@.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='InnerUnsized')].inner.struct.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='InnerUnsized')].inner.struct.generics.params[1].kind.type.implied_bounds[?(@.outlives==\"'a\")]" +pub struct InnerUnsized<'a, T: ?Sized>(&'a T); + +//@ has "$.index[?(@.name=='carries_inner_unsized')].inner.function.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='carries_inner_unsized')].inner.function.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn carries_inner_unsized<'a, T>(lt: &'a i64, ty: T) -> InnerUnsized<'a, T> { + let _ = lt; + let _ = ty; + todo!() +} diff --git a/tests/rustdoc-json/implied-bounds-trait-rpit.rs b/tests/rustdoc-json/implied-bounds-trait-rpit.rs new file mode 100644 index 0000000000000..920ffcace5edc --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-trait-rpit.rs @@ -0,0 +1,22 @@ +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +pub trait SizedOnly: Sized {} +impl SizedOnly for T {} + +pub trait Provider { + //@ has "$.index[?(@.name=='provides_static')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='provides_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ has "$.index[?(@.name=='provides_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ !has "$.index[?(@.name=='provides_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ !has "$.index[?(@.name=='provides_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" + fn provides_static(&self) -> impl StaticOnly; + + //@ has "$.index[?(@.name=='provides_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" + //@ has "$.index[?(@.name=='provides_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ has "$.index[?(@.name=='provides_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='provides_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ !has "$.index[?(@.name=='provides_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" + //@ !has "$.index[?(@.name=='provides_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='SizedOnly')]" + fn provides_ref(&self) -> &(impl SizedOnly + ?Sized); +} diff --git a/tests/rustdoc-json/implied-bounds-where.rs b/tests/rustdoc-json/implied-bounds-where.rs new file mode 100644 index 0000000000000..3b8d1ab8cbc48 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds-where.rs @@ -0,0 +1,17 @@ +//@ count "$.index[?(@.name=='where_sized')].inner.function.generics.params[0].kind.type.bounds[*]" 0 +//@ is "$.index[?(@.name=='where_sized')].inner.function.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.path" '"Sized"' +//@ !has "$.index[?(@.name=='where_sized')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn where_sized(value: T) +where + T: Sized, +{ + let _ = value; +} + +//@ count "$.index[?(@.name=='where_unsized')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 0 +pub fn where_unsized(value: &T) +where + T: ?Sized, +{ + let _ = value; +} diff --git a/tests/rustdoc-json/non_lifetime_binders.rs b/tests/rustdoc-json/non_lifetime_binders.rs index 84318821c5084..9f5a9e2c606cd 100644 --- a/tests/rustdoc-json/non_lifetime_binders.rs +++ b/tests/rustdoc-json/non_lifetime_binders.rs @@ -9,7 +9,7 @@ pub struct Wrapper(std::marker::PhantomData); //@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].name" \"\'a\" //@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' //@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].name" \"T\" -//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "is_synthetic": false } }' +//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "implied_bounds": [], "default": null, "is_synthetic": false } }' pub fn foo() where for<'a, T> &'a Wrapper: Trait, diff --git a/tests/rustdoc-json/trait_alias.rs b/tests/rustdoc-json/trait_alias.rs index e7a586ee95a3e..e6bb1aa4321a1 100644 --- a/tests/rustdoc-json/trait_alias.rs +++ b/tests/rustdoc-json/trait_alias.rs @@ -6,12 +6,12 @@ //@ is "$.index[?(@.name=='StrLike')].span.filename" $FILE pub trait StrLike = AsRef; -//@ is "$.index[?(@.name=='f')].inner.function.sig.output.impl_trait[0].trait_bound.trait.id" $StrLike +//@ is "$.index[?(@.name=='f')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.id" $StrLike pub fn f() -> impl StrLike { "heya" } -//@ !is "$.index[?(@.name=='g')].inner.function.sig.output.impl_trait[0].trait_bound.trait.id" $StrLike +//@ !is "$.index[?(@.name=='g')].inner.function.sig.output.impl_trait.bounds[0].trait_bound.trait.id" $StrLike pub fn g() -> impl AsRef { "heya" } diff --git a/tests/rustdoc-json/traits/trait_alias.rs b/tests/rustdoc-json/traits/trait_alias.rs index 497930a67c832..49f7c65bc07be 100644 --- a/tests/rustdoc-json/traits/trait_alias.rs +++ b/tests/rustdoc-json/traits/trait_alias.rs @@ -19,7 +19,7 @@ pub struct Struct; impl Orig for Struct {} //@ has "$.index[?(@.name=='takes_alias')].inner.function.sig.inputs[0][1].impl_trait" -//@ is "$.index[?(@.name=='takes_alias')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.id" $Alias +//@ is "$.index[?(@.name=='takes_alias')].inner.function.sig.inputs[0][1].impl_trait.bounds[0].trait_bound.trait.id" $Alias //@ is "$.index[?(@.name=='takes_alias')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $Alias pub fn takes_alias(_: impl Alias) {} // FIXME: Should the trait be mentioned in both the decl and generics?