diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c09e17d3787e1..972bacff54a0f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -264,7 +264,7 @@ pub(crate) fn clean_trait_ref_with_constraints<'tcx>( path } -fn clean_poly_trait_ref_with_constraints<'tcx>( +pub(crate) fn clean_poly_trait_ref_with_constraints<'tcx>( cx: &mut DocContext<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, constraints: ThinVec, @@ -458,7 +458,7 @@ fn clean_type_outlives_predicate<'tcx>( } } -fn clean_middle_term<'tcx>( +pub(crate) fn clean_middle_term<'tcx>( term: ty::Binder<'tcx, ty::Term<'tcx>>, cx: &mut DocContext<'tcx>, ) -> Term { @@ -526,7 +526,7 @@ fn should_fully_qualify_path(self_def_id: Option, trait_: &Path, self_typ .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_) } -fn projection_to_path_segment<'tcx>( +pub(crate) fn projection_to_path_segment<'tcx>( proj: ty::Binder<'tcx, ty::AliasTerm<'tcx>>, cx: &mut DocContext<'tcx>, ) -> PathSegment { @@ -698,8 +698,13 @@ pub(crate) fn clean_generics<'tcx>( let param = clean_generic_param(cx, Some(gens), param); match param.kind { GenericParamDefKind::Lifetime { .. } => unreachable!(), - GenericParamDefKind::Type { ref bounds, .. } => { - cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec()); + GenericParamDefKind::Type { ref bounds, ref synthetic, .. } => { + debug_assert!(*synthetic, "non-synthetic generic for impl trait: {param:?}"); + let param_def_id = param.def_id; + cx.impl_trait_bounds.insert( + param_def_id.into(), + (bounds.to_vec(), ImplTraitOrigin::Param { def_id: param_def_id }), + ); } GenericParamDefKind::Const { .. } => unreachable!(), } @@ -819,7 +824,7 @@ fn clean_ty_generics_inner<'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 @@ -832,7 +837,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 @@ -873,7 +878,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)?; @@ -895,7 +900,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) { @@ -929,7 +934,8 @@ fn clean_ty_generics_inner<'tcx>( } } - cx.impl_trait_bounds.insert(idx.into(), bounds); + cx.impl_trait_bounds + .insert(idx.into(), (bounds, ImplTraitOrigin::Param { def_id: param_def_id })); } // Now that `cx.impl_trait_bounds` is populated, we can process @@ -1157,7 +1163,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(); @@ -1648,8 +1654,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, origin)) = cx.impl_trait_bounds.remove(&did.into()) { + return ImplTrait { bounds, origin }; } } @@ -1838,7 +1844,9 @@ 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::>(); + ImplTrait { bounds, origin: ImplTraitOrigin::Opaque { def_id: ty.def_id.to_def_id() } } } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, lifetime) => { @@ -2226,8 +2234,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, origin)) = cx.impl_trait_bounds.remove(&p.index.into()) { + ImplTrait { bounds, origin } } else if p.name == kw::SelfUpper { SelfTy } else { @@ -2363,7 +2371,7 @@ fn clean_middle_opaque_bounds<'tcx>( )); } - ImplTrait(bounds) + ImplTrait { bounds, origin: ImplTraitOrigin::Opaque { def_id: impl_trait_def_id } } } pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { @@ -3177,7 +3185,7 @@ fn clean_assoc_item_constraint<'tcx>( } } -fn clean_bound_vars<'tcx>( +pub(crate) fn clean_bound_vars<'tcx>( bound_vars: &ty::List>, cx: &mut DocContext<'tcx>, ) -> Vec { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c145929534d97..3db13a9dbcaff 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1292,6 +1292,15 @@ pub(crate) struct PolyTrait { pub(crate) generic_params: Vec, } +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub(crate) enum ImplTraitOrigin { + /// Synthetic type parameter for `impl Trait` in argument position. + Param { def_id: DefId }, + + /// Opaque type backing `impl Trait`, such as in RPIT or TAIT. + Opaque { def_id: DefId }, +} + /// Rustdoc's representation of types, mostly based on the [`hir::Ty`]. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum Type { @@ -1337,7 +1346,25 @@ pub(crate) enum Type { Infer, /// An `impl Trait`: `impl TraitA + TraitB + ...` - ImplTrait(Vec), + ImplTrait { + /// The bounds that are syntactically present: + /// ```rust + /// # trait TraitA {} + /// # trait TraitB {} + /// # struct Both; + /// # impl TraitA for Both {} + /// # impl TraitB for Both {} + /// fn example() -> impl TraitA + TraitB { + /// // ^^^^^^ ^^^^^^ + /// // ... + /// # Both + /// } + /// ``` + bounds: Vec, + /// Whether this `impl Trait` is syntactic sugar for an anonymous generic parameter, + /// or represents an opaque type. + origin: ImplTraitOrigin, + }, UnsafeBinder(Box), } @@ -1465,8 +1492,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() @@ -1536,7 +1563,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) } @@ -2392,7 +2419,7 @@ mod size_asserts { // tidy-alphabetical-start static_assert_size!(Crate, 16); // frequently moved by-value static_assert_size!(DocFragment, 48); - static_assert_size!(GenericArg, 32); + static_assert_size!(GenericArg, 40); static_assert_size!(GenericArgs, 24); static_assert_size!(GenericParamDef, 40); static_assert_size!(Generics, 16); @@ -2400,6 +2427,6 @@ mod size_asserts { static_assert_size!(ItemInner, 144); static_assert_size!(ItemKind, 48); static_assert_size!(PathSegment, 32); - static_assert_size!(Type, 32); + static_assert_size!(Type, 40); // tidy-alphabetical-end } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7629425ffb559..1cd7dde130163 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -54,8 +54,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, trait origin) + pub(crate) impl_trait_bounds: + FxHashMap, clean::ImplTraitOrigin)>, /// 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/html/format.rs b/src/librustdoc/html/format.rs index f38a21bd1ff36..4166a7a81ccbb 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1015,7 +1015,7 @@ fn fmt_type( { true } - clean::ImplTrait(ref bounds) if bounds.len() > 1 => true, + clean::ImplTrait { ref bounds, .. } if bounds.len() > 1 => true, _ => false, }; Wrapped::with_parens() @@ -1023,7 +1023,7 @@ fn fmt_type( .wrap_fn(|f| fmt_type(ty, f, use_absolute, cx)) .fmt(f) } - clean::ImplTrait(bounds) => { + clean::ImplTrait { bounds, .. } => { f.write_str("impl ")?; print_generic_bounds(bounds, cx).fmt(f) } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 30b534003da17..0003885bb8d3b 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -2043,7 +2043,7 @@ fn get_index_type_id( clean::Type::Pat(..) | clean::Generic(_) | clean::SelfTy - | clean::ImplTrait(_) + | clean::ImplTrait { .. } | clean::Infer | clean::UnsafeBinder(_) => None, } @@ -2141,7 +2141,7 @@ fn simplify_fn_type<'a, 'tcx>( RenderType { id: Some(RenderTypeId::Index(idx)), generics: None, bindings: None } }) } - Type::ImplTrait(ref bounds) => { + Type::ImplTrait { ref bounds, .. } => { let type_bounds = bounds .iter() .filter_map(|bound| bound.get_trait_path()) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 2edf7891be400..6ef0f273a6ef0 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -4,7 +4,7 @@ use rustc_abi::ExternAbi; use rustc_ast::ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::attrs::{self, DeprecatedSince, DocAttribute, DocInline, HideOrShow}; @@ -20,6 +20,9 @@ use rustdoc_json_types::*; use crate::clean::{self, ItemId}; use crate::formats::item_type::ItemType; use crate::json::JsonRenderer; +use crate::json::implied_bounds::{ + implied_bounds_for_assoc_type, implied_bounds_for_impl_trait, implied_bounds_for_type_param, +}; use crate::passes::collect_intra_doc_links::UrlFragment; impl JsonRenderer<'_> { @@ -274,36 +277,59 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum let name = item.name; let is_crate = item.is_crate(); let header = item.fn_header(renderer.tcx); + let owner_def_id = match item.item_id { + ItemId::DefId(did) => did, + ItemId::Auto { for_, .. } => for_, + ItemId::Blanket { impl_id, .. } => impl_id, + }; match &item.inner.kind { ModuleItem(m) => { ItemEnum::Module(Module { is_crate, items: renderer.ids(&m.items), is_stripped: false }) } ImportItem(i) => ItemEnum::Use(i.into_json(renderer)), - StructItem(s) => ItemEnum::Struct(s.into_json(renderer)), - UnionItem(u) => ItemEnum::Union(u.into_json(renderer)), + StructItem(s) => ItemEnum::Struct(from_clean_struct(s, owner_def_id, renderer)), + UnionItem(u) => ItemEnum::Union(from_clean_union(u, owner_def_id, renderer)), StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)), - EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), + EnumItem(e) => ItemEnum::Enum(from_clean_enum(e, owner_def_id, renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), - FunctionItem(f) => { - ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer)) - } - ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer)) - } + FunctionItem(f) => ItemEnum::Function(from_clean_function( + f, + owner_def_id, + true, + header.unwrap(), + renderer, + )), + ForeignFunctionItem(f, _) => ItemEnum::Function(from_clean_function( + f, + owner_def_id, + false, + header.unwrap(), + renderer, + )), TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)), - TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) + TraitAliasItem(t) => { + ItemEnum::TraitAlias(from_clean_trait_alias(t, owner_def_id, renderer)) } - RequiredMethodItem(m) => { - ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) - } - ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), + MethodItem(m, _) => ItemEnum::Function(from_clean_function( + m, + owner_def_id, + true, + header.unwrap(), + renderer, + )), + RequiredMethodItem(m) => ItemEnum::Function(from_clean_function( + m, + owner_def_id, + false, + header.unwrap(), + renderer, + )), + ImplItem(i) => ItemEnum::Impl(from_clean_impl(i, owner_def_id, renderer)), StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)), ForeignStaticItem(s, safety) => ItemEnum::Static(from_clean_static(s, *safety, renderer)), ForeignTypeItem => ItemEnum::ExternType, - TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)), + TypeAliasItem(t) => ItemEnum::TypeAlias(from_clean_type_alias(t, owner_def_id, renderer)), // FIXME(generic_const_items): Add support for generic free consts ConstantItem(ci) => ItemEnum::Constant { type_: ci.type_.into_json(renderer), @@ -326,16 +352,24 @@ 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), - 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)), - }, + RequiredAssocTypeItem(generics, bounds) => { + let bounds_json: Vec = bounds.into_json(renderer); + ItemEnum::AssocType { + generics: from_clean_generics(generics, owner_def_id, renderer), + bounds: bounds_json.clone(), + implied_bounds: implied_bounds_for_assoc_type(owner_def_id, &bounds_json, renderer), + type_: None, + } + } + AssocTypeItem(ty, bounds) => { + let bounds_json: Vec = bounds.into_json(renderer); + ItemEnum::AssocType { + generics: from_clean_generics(&ty.generics, owner_def_id, renderer), + bounds: bounds_json.clone(), + implied_bounds: implied_bounds_for_assoc_type(owner_def_id, &bounds_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!(), StrippedItem(inner) => { @@ -356,41 +390,6 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum } } -impl FromClean for Struct { - fn from_clean(struct_: &clean::Struct, renderer: &JsonRenderer<'_>) -> Self { - let has_stripped_fields = struct_.has_stripped_entries(); - let clean::Struct { ctor_kind, generics, fields } = struct_; - - let kind = match ctor_kind { - Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)), - Some(CtorKind::Const) => { - assert!(fields.is_empty()); - StructKind::Unit - } - None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields }, - }; - - Struct { - kind, - generics: generics.into_json(renderer), - impls: Vec::new(), // Added in JsonRenderer::item - } - } -} - -impl FromClean for Union { - fn from_clean(union_: &clean::Union, renderer: &JsonRenderer<'_>) -> Self { - let has_stripped_fields = union_.has_stripped_entries(); - let clean::Union { generics, fields } = union_; - Union { - generics: generics.into_json(renderer), - has_stripped_fields, - fields: renderer.ids(fields), - impls: Vec::new(), // Added in JsonRenderer::item - } - } -} - impl FromClean for FunctionHeader { fn from_clean(header: &rustc_hir::FnHeader, renderer: &JsonRenderer<'_>) -> Self { let is_unsafe = match header.safety { @@ -437,15 +436,6 @@ impl FromClean for String { } } -impl FromClean for Generics { - fn from_clean(generics: &clean::Generics, renderer: &JsonRenderer<'_>) -> Self { - Generics { - params: generics.params.into_json(renderer), - where_predicates: generics.where_predicates.into_json(renderer), - } - } -} - impl FromClean for GenericParamDef { fn from_clean(generic_param: &clean::GenericParamDef, renderer: &JsonRenderer<'_>) -> Self { GenericParamDef { @@ -464,6 +454,13 @@ impl FromClean for GenericParamDefKind { } Type { bounds, default, synthetic } => GenericParamDefKind::Type { bounds: bounds.into_json(renderer), + // This path is only hit when we aren't going through `from_clean_generics()`, + // i.e. we're processing cases like HRTB binders or function-pointer generic params. + // + // Non-lifetime binders don't currently allow bounds on type parameters, and + // late-bound type params are rejected on function pointers. That leaves no + // place for implied bounds to come from, as of today's version of Rust. + implied_bounds: Vec::new(), default: default.into_json(renderer), is_synthetic: *synthetic, }, @@ -579,7 +576,11 @@ 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, origin } => { + let bounds_json: Vec = bounds.into_json(renderer); + let implied_bounds = implied_bounds_for_impl_trait(origin, &bounds_json, renderer); + Type::ImplTrait { bounds: bounds_json, implied_bounds } + } Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { is_mutable: *mutability == ast::Mutability::Mut, @@ -677,19 +678,187 @@ impl FromClean for FunctionSignature { } } +fn from_clean_generics( + generics: &clean::Generics, + owner_def_id: DefId, + renderer: &JsonRenderer<'_>, +) -> Generics { + let mut param_by_name = FxHashMap::default(); + let mut param_bounds: FxHashMap<_, Vec> = FxHashMap::default(); + let mut explicit_bounds: FxHashMap<_, Vec> = FxHashMap::default(); + + for param in &generics.params { + if let clean::GenericParamDefKind::Type { bounds, .. } = ¶m.kind { + let bounds_json: Vec = bounds.into_json(renderer); + param_by_name.insert(param.name, param.def_id); + explicit_bounds.entry(param.def_id).or_default().extend(bounds_json.clone()); + param_bounds.insert(param.def_id, bounds_json); + } + } + + for predicate in &generics.where_predicates { + if let clean::WherePredicate::BoundPredicate { + ty: clean::Type::Generic(name), bounds, .. + } = predicate + && let Some(def_id) = param_by_name.get(name) + { + let where_bounds: Vec = bounds.into_json(renderer); + explicit_bounds.entry(*def_id).or_default().extend(where_bounds); + } + } + + let params = generics + .params + .iter() + .map(|param| match ¶m.kind { + clean::GenericParamDefKind::Lifetime { outlives } => GenericParamDef { + name: param.name.to_string(), + kind: GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) }, + }, + clean::GenericParamDefKind::Type { bounds, default, synthetic } => { + let bounds_json = param_bounds + .remove(¶m.def_id) + .unwrap_or_else(|| bounds.into_json(renderer)); + let explicit_bounds = + explicit_bounds.remove(¶m.def_id).unwrap_or_else(|| bounds_json.clone()); + let implied_bounds = implied_bounds_for_type_param( + owner_def_id, + param.def_id, + &explicit_bounds, + renderer, + ); + GenericParamDef { + name: param.name.to_string(), + kind: GenericParamDefKind::Type { + bounds: bounds_json, + implied_bounds, + default: default.into_json(renderer), + is_synthetic: *synthetic, + }, + } + } + clean::GenericParamDefKind::Const { ty, default } => GenericParamDef { + name: param.name.to_string(), + kind: GenericParamDefKind::Const { + type_: ty.into_json(renderer), + default: default.as_ref().map(|x| x.as_ref().clone()), + }, + }, + }) + .collect(); + + Generics { params, where_predicates: generics.where_predicates.into_json(renderer) } +} + +fn from_clean_impl(impl_: &clean::Impl, owner_def_id: DefId, renderer: &JsonRenderer<'_>) -> Impl { + let provided_trait_methods = impl_.provided_trait_methods(renderer.tcx); + let clean::Impl { safety, generics, trait_, for_, items, polarity, kind, .. } = impl_; + // FIXME: use something like ImplKind in JSON? + let (is_synthetic, blanket_impl) = match kind { + clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None), + clean::ImplKind::Auto => (true, None), + clean::ImplKind::Blanket(ty) => (false, Some(ty)), + }; + let is_negative = matches!(polarity, ty::ImplPolarity::Negative); + Impl { + is_unsafe: safety.is_unsafe(), + generics: from_clean_generics(generics, owner_def_id, renderer), + provided_trait_methods: provided_trait_methods.into_iter().map(|x| x.to_string()).collect(), + trait_: trait_.into_json(renderer), + for_: for_.into_json(renderer), + items: renderer.ids(items), + is_negative, + is_synthetic, + blanket_impl: blanket_impl.map(|x| x.into_json(renderer)), + } +} + +fn from_clean_struct( + struct_: &clean::Struct, + owner_def_id: DefId, + renderer: &JsonRenderer<'_>, +) -> Struct { + let has_stripped_fields = struct_.has_stripped_entries(); + let clean::Struct { ctor_kind, generics, fields } = struct_; + + let kind = match ctor_kind { + Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)), + Some(CtorKind::Const) => { + assert!(fields.is_empty()); + StructKind::Unit + } + None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields }, + }; + + Struct { + kind, + generics: from_clean_generics(generics, owner_def_id, renderer), + impls: Vec::new(), // Added in JsonRenderer::item + } +} + +fn from_clean_union( + union_: &clean::Union, + owner_def_id: DefId, + renderer: &JsonRenderer<'_>, +) -> Union { + let has_stripped_fields = union_.has_stripped_entries(); + let clean::Union { generics, fields } = union_; + Union { + generics: from_clean_generics(generics, owner_def_id, renderer), + has_stripped_fields, + fields: renderer.ids(fields), + impls: Vec::new(), // Added in JsonRenderer::item + } +} + +fn from_clean_enum(enum_: &clean::Enum, owner_def_id: DefId, renderer: &JsonRenderer<'_>) -> Enum { + let has_stripped_variants = enum_.has_stripped_entries(); + let clean::Enum { variants, generics } = enum_; + Enum { + generics: from_clean_generics(generics, owner_def_id, renderer), + has_stripped_variants, + variants: renderer.ids(&variants.as_slice().raw), + impls: Vec::new(), // Added in JsonRenderer::item + } +} + +fn from_clean_type_alias( + type_alias: &clean::TypeAlias, + owner_def_id: DefId, + renderer: &JsonRenderer<'_>, +) -> TypeAlias { + let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = type_alias; + TypeAlias { + type_: type_.into_json(renderer), + generics: from_clean_generics(generics, owner_def_id, renderer), + } +} + +fn from_clean_trait_alias( + alias: &clean::TraitAlias, + owner_def_id: DefId, + renderer: &JsonRenderer<'_>, +) -> TraitAlias { + TraitAlias { + generics: from_clean_generics(&alias.generics, owner_def_id, renderer), + params: alias.bounds.into_json(renderer), + } +} + impl FromClean for Trait { fn from_clean(trait_: &clean::Trait, renderer: &JsonRenderer<'_>) -> Self { let tcx = renderer.tcx; let is_auto = trait_.is_auto(tcx); let is_unsafe = trait_.safety(tcx).is_unsafe(); let is_dyn_compatible = trait_.is_dyn_compatible(tcx); - let clean::Trait { items, generics, bounds, .. } = trait_; + let clean::Trait { items, generics, bounds, def_id, .. } = trait_; Trait { is_auto, is_unsafe, is_dyn_compatible, items: renderer.ids(items), - generics: generics.into_json(renderer), + generics: from_clean_generics(generics, *def_id, renderer), bounds: bounds.into_json(renderer), implementations: Vec::new(), // Added in JsonRenderer::item } @@ -708,65 +877,21 @@ impl FromClean for PolyTrait { } } -impl FromClean for Impl { - fn from_clean(impl_: &clean::Impl, renderer: &JsonRenderer<'_>) -> Self { - let provided_trait_methods = impl_.provided_trait_methods(renderer.tcx); - let clean::Impl { safety, generics, trait_, for_, items, polarity, kind, is_deprecated: _ } = - impl_; - // FIXME: use something like ImplKind in JSON? - let (is_synthetic, blanket_impl) = match kind { - clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None), - clean::ImplKind::Auto => (true, None), - clean::ImplKind::Blanket(ty) => (false, Some(ty)), - }; - let is_negative = match polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => false, - ty::ImplPolarity::Negative => true, - }; - Impl { - is_unsafe: safety.is_unsafe(), - generics: generics.into_json(renderer), - provided_trait_methods: provided_trait_methods - .into_iter() - .map(|x| x.to_string()) - .collect(), - trait_: trait_.into_json(renderer), - for_: for_.into_json(renderer), - items: renderer.ids(items), - is_negative, - is_synthetic, - blanket_impl: blanket_impl.map(|x| x.into_json(renderer)), - } - } -} - pub(crate) fn from_clean_function( clean::Function { decl, generics }: &clean::Function, + owner_def_id: DefId, has_body: bool, header: rustc_hir::FnHeader, renderer: &JsonRenderer<'_>, ) -> Function { Function { sig: decl.into_json(renderer), - generics: generics.into_json(renderer), + generics: from_clean_generics(generics, owner_def_id, renderer), header: header.into_json(renderer), has_body, } } -impl FromClean for Enum { - fn from_clean(enum_: &clean::Enum, renderer: &JsonRenderer<'_>) -> Self { - let has_stripped_variants = enum_.has_stripped_entries(); - let clean::Enum { variants, generics } = enum_; - Enum { - generics: generics.into_json(renderer), - has_stripped_variants, - variants: renderer.ids(&variants.as_slice().raw), - impls: Vec::new(), // Added in JsonRenderer::item - } - } -} - impl FromClean for Variant { fn from_clean(variant: &clean::Variant, renderer: &JsonRenderer<'_>) -> Self { use clean::VariantKind::*; @@ -835,13 +960,6 @@ impl FromClean for MacroKind { } } -impl FromClean for TypeAlias { - fn from_clean(type_alias: &clean::TypeAlias, renderer: &JsonRenderer<'_>) -> Self { - let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = type_alias; - TypeAlias { type_: type_.into_json(renderer), generics: generics.into_json(renderer) } - } -} - fn from_clean_static( stat: &clean::Static, safety: rustc_hir::Safety, @@ -859,15 +977,6 @@ fn from_clean_static( } } -impl FromClean for TraitAlias { - fn from_clean(alias: &clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self { - TraitAlias { - generics: alias.generics.into_json(renderer), - params: alias.bounds.into_json(renderer), - } - } -} - impl FromClean for ItemKind { fn from_clean(kind: &ItemType, _renderer: &JsonRenderer<'_>) -> Self { use ItemType::*; diff --git a/src/librustdoc/json/implied_bounds.rs b/src/librustdoc/json/implied_bounds.rs new file mode 100644 index 0000000000000..b1f38037b1c42 --- /dev/null +++ b/src/librustdoc/json/implied_bounds.rs @@ -0,0 +1,487 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_hir::{LangItem, OpaqueTyOrigin}; +use rustc_infer::infer::region_constraints::GenericKind; +use rustc_infer::traits::util::transitive_bounds_that_define_assoc_item; +use rustc_middle::ty::{self, AliasTy, ParamTy, Ty, TyCtxt, TypingMode}; +use rustc_span::symbol::Ident; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_trait_selection::infer::outlives::env::OutlivesEnvironment; +use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; +use rustdoc_json_types::GenericBound; + +use crate::clean; +use crate::config::OutputFormat; +use crate::core::DocContext; +use crate::formats::cache::Cache; +use crate::json::JsonRenderer; +use crate::json::conversions::IntoJson; + +pub(crate) fn implied_bounds_for_ty<'tcx>( + target_ty: Ty<'tcx>, + clauses: &[ty::Clause<'tcx>], + explicit_bounds: &[GenericBound], + owner_def_id: DefId, + renderer: &JsonRenderer<'tcx>, +) -> Vec { + let mut seen: FxHashSet<_> = explicit_bounds.iter().cloned().collect(); + let mut implied_bounds = Vec::new(); + let mut clean_cx = implied_bounds_doc_context(renderer, owner_def_id); + for clause in clauses { + if !clause_targets_ty(*clause, target_ty) { + continue; + } + + if let Some(bound) = clause_to_generic_bound(*clause, clauses, &mut clean_cx, renderer) { + if seen.insert(bound.clone()) { + implied_bounds.push(bound); + } + } + } + + implied_bounds +} + +pub(crate) fn implied_bounds_for_type_param<'tcx>( + owner_def_id: DefId, + param_def_id: DefId, + explicit_bounds: &[GenericBound], + renderer: &JsonRenderer<'tcx>, +) -> Vec { + let target_param = param_ty_for_param(renderer.tcx, owner_def_id, param_def_id); + let target_ty = target_param.to_ty(renderer.tcx); + + let clauses = renderer.tcx.param_env(owner_def_id).caller_bounds(); + let mut implied_bounds = implied_bounds_for_ty( + target_ty, + clauses.as_slice(), + explicit_bounds, + owner_def_id, + renderer, + ); + + let extra_bounds = implied_outlives_bounds_for_param(renderer.tcx, owner_def_id, target_param); + if !extra_bounds.is_empty() { + let mut seen: FxHashSet<_> = explicit_bounds.iter().cloned().collect(); + seen.extend(implied_bounds.iter().cloned()); + for bound in extra_bounds { + if seen.insert(bound.clone()) { + implied_bounds.push(bound); + } + } + } + + implied_bounds +} + +pub(crate) fn implied_bounds_for_assoc_type<'tcx>( + assoc_def_id: DefId, + explicit_bounds: &[GenericBound], + renderer: &JsonRenderer<'tcx>, +) -> Vec { + let assoc_item = renderer.tcx.associated_item(assoc_def_id); + if !matches!(assoc_item.container, ty::AssocContainer::Trait) { + return Vec::new(); + } + + let args = ty::GenericArgs::identity_for_item(renderer.tcx, assoc_def_id); + let target_ty = Ty::new_alias( + renderer.tcx, + ty::Projection, + AliasTy::new_from_args(renderer.tcx, assoc_def_id, args), + ); + let clauses = renderer.tcx.item_bounds(assoc_def_id).instantiate(renderer.tcx, args); + + implied_bounds_for_ty(target_ty, &clauses, explicit_bounds, assoc_def_id, renderer) +} + +pub(crate) fn implied_bounds_for_impl_trait<'tcx>( + origin: &clean::ImplTraitOrigin, + explicit_bounds: &[GenericBound], + renderer: &JsonRenderer<'tcx>, +) -> Vec { + match origin { + clean::ImplTraitOrigin::Param { def_id } => { + let owner_def_id = renderer.tcx.parent(*def_id); + implied_bounds_for_type_param(owner_def_id, *def_id, explicit_bounds, renderer) + } + clean::ImplTraitOrigin::Opaque { def_id } => { + let args = ty::GenericArgs::identity_for_item(renderer.tcx, *def_id); + let target_ty = Ty::new_alias( + renderer.tcx, + ty::Opaque, + AliasTy::new_from_args(renderer.tcx, *def_id, args), + ); + let clauses = renderer.tcx.item_bounds(*def_id).instantiate(renderer.tcx, args); + + let mut implied_bounds = + implied_bounds_for_ty(target_ty, &clauses, explicit_bounds, *def_id, renderer); + + let extra_bounds = implied_outlives_bounds_for_opaque(renderer.tcx, *def_id); + if !extra_bounds.is_empty() { + let mut seen: FxHashSet<_> = explicit_bounds.iter().cloned().collect(); + seen.extend(implied_bounds.iter().cloned()); + for bound in extra_bounds { + if seen.insert(bound.clone()) { + implied_bounds.push(bound); + } + } + } + + implied_bounds + } + } +} + +/// Build a minimal [`DocContext`] for implied-bounds rendering. +/// +/// This is intentionally a narrow, JSON-only shim that lets us reuse existing `clean::*` +/// conversion helpers when turning `ty::Clause` data into `rustdoc_json_types`: +/// - The implied-bounds logic starts from [`ty::Clause`] (param-env predicates) rather than +/// from HIR, so we don't have a preexisting clean representation to convert. +/// - The relevant clean helpers ([`crate::clean::clean_trait_ref_with_constraints`], +/// [`crate::clean::projection_to_path_segment`], [`crate::clean::clean_middle_term`], +/// [`crate::clean::clean_bound_vars`]) require a [`DocContext`] to access `tcx`, `param_env`, and +/// path/generic normalization logic. We don't want to duplicate them here. +/// +/// This context is read-only and intentionally minimal: it only carries the fields needed by +/// the clean helpers above (e.g., `tcx`, `param_env`, `auto_traits`, and a fresh [`Cache`] to +/// satisfy path lookups). It does not run passes, does not mutate global caches, and does not +/// depend on the rest of the cleaning pipeline. +/// +/// If this ever shows up as a hot path or becomes too heavyweight, the alternatives are: +/// - reimplement the clean logic directly in JSON and accept some duplication; +/// - move implied-bounds computation into `clean` itself, making it shared with rustdoc HTML, or +/// - refactor JSON rendering to get access to the main [`DocContext`]. +fn implied_bounds_doc_context<'tcx>( + renderer: &JsonRenderer<'tcx>, + owner_def_id: DefId, +) -> DocContext<'tcx> { + let auto_traits = renderer + .tcx + .visible_traits() + .filter(|&trait_def_id| renderer.tcx.trait_is_auto(trait_def_id)) + .collect(); + DocContext { + tcx: renderer.tcx, + param_env: renderer.tcx.param_env(owner_def_id), + external_traits: Default::default(), + active_extern_traits: Default::default(), + args: Default::default(), + current_type_aliases: Default::default(), + impl_trait_bounds: Default::default(), + generated_synthetics: Default::default(), + auto_traits, + cache: Cache::new(renderer.cache.document_private, renderer.cache.document_hidden), + inlined: Default::default(), + output_format: OutputFormat::Json, + show_coverage: false, + } +} + +fn is_allowed_lang_item_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + match tcx.as_lang_item(def_id) { + None => true, + + // These are all the language item traits that are stable in Rust today. + 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, + ) => true, + + Some(_) => false, + } +} + +fn clause_targets_ty<'tcx>(clause: ty::Clause<'tcx>, target: Ty<'tcx>) -> bool { + if let Some(trait_clause) = clause.as_trait_clause() { + trait_clause.self_ty().skip_binder() == target + } else if let Some(type_outlives) = clause.as_type_outlives_clause() { + type_outlives.skip_binder().0 == target + } else { + false + } +} + +fn clause_to_generic_bound<'tcx>( + clause: ty::Clause<'tcx>, + all_clauses: &[ty::Clause<'tcx>], + clean_cx: &mut DocContext<'tcx>, + renderer: &JsonRenderer<'tcx>, +) -> Option { + if let Some(trait_clause) = clause.as_trait_clause() { + let def_id = trait_clause.def_id(); + if !is_allowed_lang_item_trait(renderer.tcx, def_id) { + None + } else { + let poly_trait_ref = trait_clause.map_bound(|pred| pred.trait_ref); + let constraints = + assoc_item_constraints_for_trait_ref(all_clauses, poly_trait_ref, clean_cx); + let bound = + clean::clean_poly_trait_ref_with_constraints(clean_cx, poly_trait_ref, constraints); + Some(bound.into_json(renderer)) + } + } else if let Some(type_outlives) = clause.as_type_outlives_clause() { + let ty::OutlivesPredicate(_, region) = type_outlives.skip_binder(); + region.get_name(renderer.tcx).map(|name| GenericBound::Outlives(name.to_string())) + } else { + None + } +} + +fn assoc_item_constraints_for_trait_ref<'tcx>( + clauses: &[ty::Clause<'tcx>], + poly_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + clean_cx: &mut DocContext<'tcx>, +) -> ThinVec { + let mut constraints = ThinVec::new(); + for clause in clauses { + if let Some(proj_clause) = clause.as_projection_clause() { + let proj_pred = proj_clause.skip_binder(); + let proj_trait_ref = proj_pred.projection_term.trait_ref(clean_cx.tcx); + let Some(assoc_name) = clean_cx.tcx.opt_item_ident(proj_pred.projection_term.def_id) + else { + continue; + }; + if !projection_applies_to_trait_ref( + clean_cx.tcx, + proj_trait_ref, + poly_trait_ref, + assoc_name, + ) { + continue; + } + + constraints.push(clean::AssocItemConstraint { + assoc: clean::projection_to_path_segment( + proj_clause.map_bound(|pred| pred.projection_term), + clean_cx, + ), + kind: clean::AssocItemConstraintKind::Equality { + term: clean::clean_middle_term( + proj_clause.map_bound(|pred| pred.term), + clean_cx, + ), + }, + }); + continue; + } + + let Some(trait_clause) = clause.as_trait_clause() else { continue }; + let self_ty = trait_clause.skip_binder().trait_ref.self_ty(); + let ty::Alias(ty::Projection, alias_ty) = *self_ty.kind() else { continue }; + let proj_trait_ref = alias_ty.trait_ref(clean_cx.tcx); + let Some(assoc_name) = clean_cx.tcx.opt_item_ident(alias_ty.def_id) else { continue }; + if !projection_applies_to_trait_ref( + clean_cx.tcx, + proj_trait_ref, + poly_trait_ref, + assoc_name, + ) { + continue; + } + + let bound_trait_def_id = trait_clause.def_id(); + if !is_allowed_lang_item_trait(clean_cx.tcx, bound_trait_def_id) { + continue; + } + + let bound_trait_ref = trait_clause.map_bound(|pred| pred.trait_ref); + let bound = + clean::clean_poly_trait_ref_with_constraints(clean_cx, bound_trait_ref, ThinVec::new()); + + let assoc = + clean::projection_to_path_segment(trait_clause.rebind(alias_ty.into()), clean_cx); + let mut merged = false; + for existing in constraints.iter_mut() { + if existing.assoc == assoc { + if let clean::AssocItemConstraintKind::Bound { bounds } = &mut existing.kind { + if !bounds.contains(&bound) { + bounds.push(bound.clone()); + } + merged = true; + break; + } + } + } + + if !merged { + constraints.push(clean::AssocItemConstraint { + assoc, + kind: clean::AssocItemConstraintKind::Bound { bounds: vec![bound] }, + }); + } + } + constraints +} + +/// Returns `true` if a projection predicate on `proj_trait_ref` should be attached to `trait_ref`. +/// +/// Most projections target the exact trait that defines the associated item, which is the +/// `proj_trait_ref == trait_ref` fast path. When a bound uses a trait whose associated items are +/// defined on one of its supertraits (e.g. `Sub: Deref` and `Sub`), we attach those +/// projections to the supertrait so the implied bound carries the full constraint. +fn projection_applies_to_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + proj_trait_ref: ty::TraitRef<'tcx>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + assoc_name: Ident, +) -> bool { + if proj_trait_ref == trait_ref.skip_binder() { + return true; + } + + transitive_bounds_that_define_assoc_item(tcx, std::iter::once(trait_ref), assoc_name) + .any(|supertrait_ref| supertrait_ref.skip_binder() == proj_trait_ref) +} + +fn param_ty_for_param<'tcx>( + tcx: TyCtxt<'tcx>, + owner_def_id: DefId, + param_def_id: DefId, +) -> ParamTy { + let generics = tcx.generics_of(owner_def_id); + let index = generics + .param_def_id_to_index(tcx, param_def_id) + .unwrap_or_else(|| tcx.dcx().bug("param_def_id should belong to owner_def_id")); + let param_def = generics.param_at(index as usize, tcx); + match param_def.kind { + ty::GenericParamDefKind::Type { .. } => ParamTy::new(index, tcx.item_name(param_def_id)), + _ => tcx.dcx().bug("param_def_id should refer to a type parameter"), + } +} + +fn implied_outlives_bounds_for_param<'tcx>( + tcx: TyCtxt<'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 = tcx.assumed_wf_types(local_def_id); + if assumed_wf.is_empty() { + return Vec::new(); + } + + let param_env = tcx.param_env(owner_def_id); + let infcx = 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 => + { + region.get_name(tcx).map(|name| GenericBound::Outlives(name.to_string())) + } + _ => None, + }) + .collect() +} + +/// If the opaque is a TAIT / ATPIT, return any additional outlives bounds. +/// +/// For example, `type Foo<'a, T> = &'a impl PartialEq;` +/// has an implied `+ 'a` bound that would be returned here. +/// +/// If this function is called with a different kind of opaque, it returns no bounds. +/// +/// We also aren't able to return any bounds for cross-crate TAITs due to missing metadata. +fn implied_outlives_bounds_for_opaque<'tcx>( + tcx: TyCtxt<'tcx>, + opaque_def_id: DefId, +) -> Vec { + if tcx.def_kind(opaque_def_id) != DefKind::OpaqueTy { + return Vec::new(); + } + + let Some(local_def_id) = opaque_def_id.as_local() else { + // Cross-crate TAITs don't carry the parent WF info in metadata, + // so we can't infer outlives bounds here. + // FIXME: Get metadata on extern opaques, then make this precise. + return Vec::new(); + }; + + let OpaqueTyOrigin::TyAlias { parent, .. } = tcx.opaque_ty_origin(local_def_id.to_def_id()) + else { + return Vec::new(); + }; + + let Some(local_parent) = parent.as_local() else { + // Cross-crate TAITs don't carry the parent WF info in metadata, + // so we can't infer outlives bounds here. + return Vec::new(); + }; + + let param_env = tcx.param_env(parent); + let parent_ty = tcx.type_of(parent).instantiate_identity(); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let env = OutlivesEnvironment::new(&infcx, local_parent, param_env, [parent_ty]); + + let target_args = ty::GenericArgs::identity_for_item(tcx, parent).extend_to( + tcx, + opaque_def_id, + |param, _| tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into(), + ); + let target_alias = AliasTy::new_from_args(tcx, opaque_def_id, target_args); + env.region_bound_pairs() + .iter() + .filter_map(|predicate| match predicate { + ty::OutlivesPredicate(GenericKind::Alias(alias), region) if *alias == target_alias => { + region.get_name(tcx).map(|name| GenericBound::Outlives(name.to_string())) + } + _ => None, + }) + .collect() +} diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index a201f661d9f78..34db7ec66d42a 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -6,6 +6,7 @@ mod conversions; mod ids; +mod implied_bounds; mod import_finder; use std::cell::RefCell; @@ -350,7 +351,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); static_assert_size!(ItemSummary, 32); static_assert_size!(PolyTrait, 64); diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 9a59de4f844ab..553adce0c5242 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 `ExternCrate::path`. -pub const FORMAT_VERSION: u32 = 57; +// Latest feature: Add `implied_bounds`. +pub const FORMAT_VERSION: u32 = 58; /// The root of the emitted JSON blob. /// @@ -669,6 +669,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. @@ -957,6 +969,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 @@ -1176,7 +1201,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-impl-trait.rs b/tests/rustdoc-json/implied-bounds/assoc-type-impl-trait.rs new file mode 100644 index 0000000000000..b98c10b3325b8 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/assoc-type-impl-trait.rs @@ -0,0 +1,168 @@ +#![feature(impl_trait_in_assoc_type)] + +use std::fmt::{self, Debug}; + +pub trait NeedsSized: Sized {} +impl NeedsSized for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct StaticMarker; + +impl fmt::Debug for StaticMarker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("StaticMarker") + } +} + +impl PartialEq for StaticMarker { + fn eq(&self, _: &T) -> bool { + true + } +} + +#[allow(dead_code)] +static STATIC_MARKER: StaticMarker = StaticMarker; + +pub trait AssocTypes { + type Opaque; + type OpaqueRef; + type OpaqueMaybeUnsized: ?Sized; + type OpaqueMaybeUnsizedRef; + type OpaqueSizedViaTrait; + type OpaqueSizedViaTraitRef; + type OpaqueOverridden; + type OpaqueOverriddenRef; + type OpaqueStatic; + type OpaqueStaticRef; + type OpaqueStaticMaybeUnsized: ?Sized; + type OpaqueStaticMaybeUnsizedRef; + + // Ensure the opaques we'll use are not unconstrained. + fn define_opaques() -> ( + Self::Opaque, + Self::OpaqueRef, + Box, + Self::OpaqueMaybeUnsizedRef, + Self::OpaqueSizedViaTrait, + Self::OpaqueSizedViaTraitRef, + Self::OpaqueOverridden, + Self::OpaqueOverriddenRef, + Self::OpaqueStatic, + Self::OpaqueStaticRef, + Box, + Self::OpaqueStaticMaybeUnsizedRef, + ); +} + +pub struct Holder; + +impl AssocTypes for Holder { + //@ has "$.index[?(@.name=='Opaque')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='Opaque')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='Opaque')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" + type Opaque = impl Debug; + + //@ has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + type OpaqueRef = &'static impl Debug; + + //@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ !has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ count "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[*]" 0 + type OpaqueMaybeUnsized = impl Debug + ?Sized; + + //@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + type OpaqueMaybeUnsizedRef = &'static (impl Debug + ?Sized); + + //@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + type OpaqueSizedViaTrait = impl NeedsSized; + + //@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + type OpaqueSizedViaTraitRef = &'static impl NeedsSized; + + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + type OpaqueOverridden = impl NeedsSized + ?Sized; + + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + type OpaqueOverriddenRef = &'static (impl NeedsSized + ?Sized); + + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStatic = impl StaticOnly; + + //@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ !has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStaticRef = &'static impl StaticOnly; + + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStaticMaybeUnsized = impl StaticOnly + ?Sized; + + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStaticMaybeUnsizedRef = &'static (impl StaticOnly + ?Sized); + + // Ensure opaques we define above are not unconstrained. + fn define_opaques() -> ( + Self::Opaque, + Self::OpaqueRef, + Box, + Self::OpaqueMaybeUnsizedRef, + Self::OpaqueSizedViaTrait, + Self::OpaqueSizedViaTraitRef, + Self::OpaqueOverridden, + Self::OpaqueOverriddenRef, + Self::OpaqueStatic, + Self::OpaqueStaticRef, + Box, + Self::OpaqueStaticMaybeUnsizedRef, + ) { + ( + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + ) + } +} 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..602b90c2311a5 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/assoc-type.rs @@ -0,0 +1,23 @@ +pub trait SizedOnly: Sized {} +impl SizedOnly for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +pub trait Container { + //@ has "$.index[?(@.name=='Item')].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='Item')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='Item')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='SizedOnly')]" + type Item: SizedOnly + ?Sized; + + //@ has "$.index[?(@.name=='UnsizedItem')].inner.assoc_type.bounds[?(@.trait_bound.modifier=='maybe' && @.trait_bound.trait.path=='Sized')]" + //@ !has "$.index[?(@.name=='UnsizedItem')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + type UnsizedItem: ?Sized; +} + +//@ has "$.index[?(@.name=='Output')].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ 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/bounded-generic-type-alias-impl-trait.rs b/tests/rustdoc-json/implied-bounds/bounded-generic-type-alias-impl-trait.rs new file mode 100644 index 0000000000000..b00e2b6f9a7b5 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/bounded-generic-type-alias-impl-trait.rs @@ -0,0 +1,84 @@ +#![feature(type_alias_impl_trait)] + +use std::fmt::{self, Debug}; + +pub trait NeedsSized: Sized {} +impl NeedsSized for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct StaticMarker; + +impl fmt::Debug for StaticMarker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("StaticMarker") + } +} + +impl PartialEq for StaticMarker { + fn eq(&self, _: &T) -> bool { + true + } +} + +//@ has "$.index[?(@.name=='SizedParam')].inner.type_alias.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='SizedParam')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='SizedParam')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub type SizedParam<'a, T: NeedsSized> = impl Debug + PartialEq + 'a; + +#[allow(dead_code)] +#[define_opaque(SizedParam)] +fn define_sized_param<'a, T: NeedsSized>() -> SizedParam<'a, T> { + StaticMarker +} + +//@ has "$.index[?(@.name=='MaybeSizedParam')].inner.type_alias.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='MaybeSizedParam')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ !has "$.index[?(@.name=='MaybeSizedParam')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub type MaybeSizedParam<'a, T: ?Sized> = impl Debug + PartialEq + 'a; + +#[allow(dead_code)] +#[define_opaque(MaybeSizedParam)] +fn define_maybe_sized_param<'a, T: ?Sized>() -> MaybeSizedParam<'a, T> { + StaticMarker +} + +//@ has "$.index[?(@.name=='SizedMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='SizedMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='SizedMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='SizedMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +pub type SizedMaybeUnsized<'a, T: NeedsSized + ?Sized> = impl Debug + PartialEq + 'a; + +#[allow(dead_code)] +#[define_opaque(SizedMaybeUnsized)] +fn define_sized_maybe_unsized<'a, T: NeedsSized + ?Sized>() -> SizedMaybeUnsized<'a, T> { + StaticMarker +} + +//@ has "$.index[?(@.name=='StaticParam')].inner.type_alias.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='StaticParam')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ has "$.index[?(@.name=='StaticParam')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='StaticParam')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type StaticParam<'a, T: StaticOnly> = impl Debug + PartialEq + 'a; + +#[allow(dead_code)] +#[define_opaque(StaticParam)] +fn define_static_param<'a, T: StaticOnly>() -> StaticParam<'a, T> { + StaticMarker +} + +//@ has "$.index[?(@.name=='StaticMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='StaticMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='StaticMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='StaticMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ !has "$.index[?(@.name=='StaticMaybeUnsized')].inner.type_alias.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub type StaticMaybeUnsized<'a, T: StaticOnly + ?Sized> = impl Debug + PartialEq + 'a; + +#[allow(dead_code)] +#[define_opaque(StaticMaybeUnsized)] +fn define_static_maybe_unsized<'a, T: StaticOnly + ?Sized>() -> StaticMaybeUnsized<'a, T> { + StaticMarker +} diff --git a/tests/rustdoc-json/implied-bounds/gat.rs b/tests/rustdoc-json/implied-bounds/gat.rs new file mode 100644 index 0000000000000..7edfefc7a4d10 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/gat.rs @@ -0,0 +1,58 @@ +pub trait SizedOnly: Sized {} + +impl SizedOnly for T {} + +pub trait StaticOnly: 'static {} + +impl StaticOnly for T {} + +pub trait GatContainer { + //@ count "$.index[?(@.name=='Plain' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[*]" 2 + //@ has "$.index[?(@.name=='Plain' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + type Plain<'a, T>; + + //@ has "$.index[?(@.name=='MaybeUnsized' && @.inner.assoc_type.type==null)].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ count "$.index[?(@.name=='MaybeUnsized' && @.inner.assoc_type.type==null)].inner.assoc_type.implied_bounds[*]" 0 + //@ has "$.index[?(@.name=='MaybeUnsized' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ count "$.index[?(@.name=='MaybeUnsized' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[*]" 0 + type MaybeUnsized<'a, T: ?Sized>: ?Sized; + + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type==null)].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type==null)].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type==null)].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.modifier=='maybe')]" + type MaybeUnsizedButSized<'a, T: SizedOnly + ?Sized>: SizedOnly + ?Sized; + + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type==null)].inner.assoc_type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type==null)].inner.assoc_type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type==null)].inner.assoc_type.implied_bounds[?(@.outlives==\"'static\")]" + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type==null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.outlives==\"'static\")]" + type Static<'a, T: StaticOnly>: StaticOnly; +} + +pub struct UsesArray; + +impl GatContainer for UsesArray { + //@ has "$.index[?(@.name=='Plain' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + type Plain<'a, T> = (&'a (), [T; 1]); + + //@ count "$.index[?(@.name=='MaybeUnsized' && @.inner.assoc_type.type!=null)].inner.assoc_type.implied_bounds[*]" 0 + //@ has "$.index[?(@.name=='MaybeUnsized' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ count "$.index[?(@.name=='MaybeUnsized' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[*]" 0 + type MaybeUnsized<'a, T: ?Sized> = *const T; + + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='MaybeUnsizedButSized' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + type MaybeUnsizedButSized<'a, T: SizedOnly + ?Sized> = [T; 1]; + + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ has "$.index[?(@.name=='Static' && @.inner.assoc_type.type!=null)].inner.assoc_type.generics.params[1].kind.type.implied_bounds[?(@.outlives==\"'static\")]" + type Static<'a, T: StaticOnly> = T; +} diff --git a/tests/rustdoc-json/implied-bounds/generic-assoc-type-impl-trait.rs b/tests/rustdoc-json/implied-bounds/generic-assoc-type-impl-trait.rs new file mode 100644 index 0000000000000..a6890c62f2ffe --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/generic-assoc-type-impl-trait.rs @@ -0,0 +1,185 @@ +#![feature(impl_trait_in_assoc_type)] + +use std::fmt::{self, Debug}; + +pub trait NeedsSized: Sized {} +impl NeedsSized for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct StaticMarker; + +impl fmt::Debug for StaticMarker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("StaticMarker") + } +} + +impl PartialEq for StaticMarker { + fn eq(&self, _: &T) -> bool { + true + } +} + +#[allow(dead_code)] +static STATIC_MARKER: StaticMarker = StaticMarker; + +pub trait AssocTypes<'a, T> { + type Opaque; + type OpaqueRef; + type OpaqueMaybeUnsized: ?Sized; + type OpaqueMaybeUnsizedRef; + type OpaqueSizedViaTrait; + type OpaqueSizedViaTraitRef; + type OpaqueOverridden; + type OpaqueOverriddenRef; + type OpaqueStatic; + type OpaqueStaticRef; + type OpaqueStaticMaybeUnsized: ?Sized; + type OpaqueStaticMaybeUnsizedRef; + + // Ensure the opaques we'll use are not unconstrained. + fn define_opaques() -> ( + Self::Opaque, + Self::OpaqueRef, + Box, + Self::OpaqueMaybeUnsizedRef, + Self::OpaqueSizedViaTrait, + Self::OpaqueSizedViaTraitRef, + Self::OpaqueOverridden, + Self::OpaqueOverriddenRef, + Self::OpaqueStatic, + Self::OpaqueStaticRef, + Box, + Self::OpaqueStaticMaybeUnsizedRef, + ); +} + +pub struct Holder; + +impl<'a, T: 'a> AssocTypes<'a, T> for Holder { + //@ has "$.index[?(@.name=='Opaque')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='Opaque')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='Opaque')].inner.assoc_type.type.impl_trait.bounds[?(@.outlives==\"'a\")]" + //@ has "$.index[?(@.name=='Opaque')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + type Opaque = impl Debug + PartialEq + 'a; + + //@ has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" + type OpaqueRef = &'a (impl Debug + PartialEq); + + //@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.outlives==\"'a\")]" + //@ !has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ count "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[*]" 0 + type OpaqueMaybeUnsized = impl Debug + PartialEq + ?Sized + 'a; + + //@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" + //@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" + type OpaqueMaybeUnsizedRef = &'a (impl Debug + PartialEq + ?Sized); + + //@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.bounds[?(@.outlives==\"'a\")]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + type OpaqueSizedViaTrait = impl NeedsSized + PartialEq + 'a; + + //@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" + type OpaqueSizedViaTraitRef = &'a (impl NeedsSized + PartialEq); + + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.bounds[?(@.outlives==\"'a\")]" + //@ has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueOverridden')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + type OpaqueOverridden = impl NeedsSized + PartialEq + ?Sized + 'a; + + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" + //@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" + type OpaqueOverriddenRef = &'a (impl NeedsSized + PartialEq + ?Sized); + + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.bounds[?(@.outlives==\"'a\")]" + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ !has "$.index[?(@.name=='OpaqueStatic')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStatic = impl StaticOnly + PartialEq + 'a; + + //@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" + //@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ !has "$.index[?(@.name=='OpaqueStaticRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStaticRef = &'a (impl StaticOnly + PartialEq); + + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.bounds[?(@.outlives==\"'a\")]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.assoc_type.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStaticMaybeUnsized = impl StaticOnly + PartialEq + ?Sized + 'a; + + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" + //@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" + //@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.assoc_type.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" + type OpaqueStaticMaybeUnsizedRef = &'a (impl StaticOnly + PartialEq + ?Sized); + + // Ensure opaques we define above are not unconstrained. + fn define_opaques() -> ( + Self::Opaque, + Self::OpaqueRef, + Box, + Self::OpaqueMaybeUnsizedRef, + Self::OpaqueSizedViaTrait, + Self::OpaqueSizedViaTraitRef, + Self::OpaqueOverridden, + Self::OpaqueOverriddenRef, + Self::OpaqueStatic, + Self::OpaqueStaticRef, + Box, + Self::OpaqueStaticMaybeUnsizedRef, + ) { + ( + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + ) + } +} diff --git a/tests/rustdoc-json/implied-bounds/generic-type-alias-impl-trait.rs b/tests/rustdoc-json/implied-bounds/generic-type-alias-impl-trait.rs new file mode 100644 index 0000000000000..50d7674e0e76c --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/generic-type-alias-impl-trait.rs @@ -0,0 +1,162 @@ +#![feature(type_alias_impl_trait)] + +use std::fmt::{self, Debug}; + +pub trait NeedsSized: Sized {} +impl NeedsSized for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct StaticMarker; + +impl fmt::Debug for StaticMarker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("StaticMarker") + } +} + +impl PartialEq for StaticMarker { + fn eq(&self, _: &T) -> bool { + true + } +} + +#[allow(dead_code)] +static STATIC_MARKER: StaticMarker = StaticMarker; + +//@ has "$.index[?(@.name=='Opaque')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='Opaque')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='Opaque')].inner.type_alias.type.impl_trait.bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='Opaque')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +pub type Opaque<'a, T> = impl Debug + PartialEq + 'a; + +//@ has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" +pub type OpaqueRef<'a, T> = &'a (impl Debug + PartialEq); + +//@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.outlives==\"'a\")]" +//@ count "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.implied_bounds[*]" 0 +pub type OpaqueMaybeUnsized<'a, T> = impl Debug + PartialEq + ?Sized + 'a; + +//@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" +pub type OpaqueMaybeUnsizedRef<'a, T> = &'a (impl Debug + PartialEq + ?Sized); + +//@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub type OpaqueSizedViaTrait<'a, T> = impl NeedsSized + PartialEq + 'a; + +//@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" +pub type OpaqueSizedViaTraitRef<'a, T> = &'a (impl NeedsSized + PartialEq); + +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub type OpaqueOverridden<'a, T> = impl NeedsSized + PartialEq + ?Sized + 'a; + +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'a\")]" +pub type OpaqueOverriddenRef<'a, T> = &'a (impl NeedsSized + PartialEq + ?Sized); + +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStatic<'a, T> = impl StaticOnly + PartialEq + 'a; + +//@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStaticRef<'a, T> = &'a (impl StaticOnly + PartialEq); + +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.outlives==\"'a\")]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStaticMaybeUnsized<'a, T> = impl StaticOnly + PartialEq + ?Sized + 'a; + +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='PartialEq' && @.trait_bound.trait.args.angle_bracketed.args[0].type.generic=='T')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStaticMaybeUnsizedRef<'a, T> = &'a (impl StaticOnly + PartialEq + ?Sized); + +// Ensure the opaques we defined above are not unconstrained. +#[allow(dead_code)] +#[define_opaque( + Opaque, + OpaqueRef, + OpaqueMaybeUnsized, + OpaqueMaybeUnsizedRef, + OpaqueSizedViaTrait, + OpaqueSizedViaTraitRef, + OpaqueOverridden, + OpaqueOverriddenRef, + OpaqueStatic, + OpaqueStaticRef, + OpaqueStaticMaybeUnsized, + OpaqueStaticMaybeUnsizedRef +)] +fn define_all<'a, T>() -> ( + Opaque<'a, T>, + OpaqueRef<'a, T>, + Box>, + OpaqueMaybeUnsizedRef<'a, T>, + OpaqueSizedViaTrait<'a, T>, + OpaqueSizedViaTraitRef<'a, T>, + OpaqueOverridden<'a, T>, + OpaqueOverriddenRef<'a, T>, + OpaqueStatic<'a, T>, + OpaqueStaticRef<'a, T>, + Box>, + OpaqueStaticMaybeUnsizedRef<'a, T>, +) { + ( + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + ) +} 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..19a0423c022d9 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/impl-trait-args.rs @@ -0,0 +1,39 @@ +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' && @.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' && @.trait_bound.modifier=='none')]" +//@ !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' && @.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' && @.trait_bound.modifier=='none')]" +//@ !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) {} + +// By-value `impl Trait + ?Sized` does not add an implied `Sized` bound unless it's from the trait. +//@ 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.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' && @.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' && @.trait_bound.modifier=='none')]" +pub fn sized_only_ref(arg: &(impl SizedOnly + ?Sized)) { + let _ = arg; +} diff --git a/tests/rustdoc-json/implied-bounds/impl-trait-return-async.rs b/tests/rustdoc-json/implied-bounds/impl-trait-return-async.rs new file mode 100644 index 0000000000000..7859c19bd6aad --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/impl-trait-return-async.rs @@ -0,0 +1,26 @@ +//@ edition: 2024 +use std::fmt::Debug; + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +//@ has "$.index[?(@.name=='async_returns_static')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='async_returns_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ has "$.index[?(@.name=='async_returns_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='async_returns_static')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub async fn async_returns_static() -> impl StaticOnly { + 0u8 +} + +//@ has "$.index[?(@.name=='async_returns_maybe_unsized')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='async_returns_maybe_unsized')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='async_returns_maybe_unsized')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub async fn async_returns_maybe_unsized() -> impl Debug + ?Sized { + 123 +} + +//@ !has "$.index[?(@.name=='async_returns_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='async_returns_ref')].inner.function.sig.output.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub async fn async_returns_ref() -> &'static (impl Debug + ?Sized) { + "hello world" +} 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..55fba432b207e --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/impl-trait-return.rs @@ -0,0 +1,28 @@ +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' && @.trait_bound.modifier=='none')]" +//@ 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')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='Clone')]" +//@ has "$.index[?(@.name=='returns_maybe_unsized')].inner.function.sig.output.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='returns_maybe_unsized')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='returns_maybe_unsized')].inner.function.sig.output.impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn returns_maybe_unsized() -> impl Clone + ?Sized { + 123u8 +} + +//@ 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' && @.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' && @.trait_bound.modifier=='none')]" +//@ !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) { + "hello world" +} diff --git a/tests/rustdoc-json/implied-bounds/implicitly-not-dst.rs b/tests/rustdoc-json/implied-bounds/implicitly-not-dst.rs new file mode 100644 index 0000000000000..077b2fb14bcf7 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/implicitly-not-dst.rs @@ -0,0 +1,75 @@ +pub trait NeedsSized: Sized {} +impl NeedsSized for T {} + +// Tuple structs and named-field structs are the ADTs that can be DSTs. +pub struct TupleStruct(pub T); +pub struct NamedStruct { + pub value: T, +} + +// By-value tuple parameters must be Sized, so the tail cannot stay unsized. +//@ has "$.index[?(@.name=='takes_tuple_value')].inner.function.sig.inputs[0][1].tuple[1].impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='takes_tuple_value')].inner.function.sig.inputs[0][1].tuple[1].impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='takes_tuple_value')].inner.function.sig.inputs[0][1].tuple[1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='takes_tuple_value')].inner.function.sig.inputs[0][1].tuple[1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ !has "$.index[?(@.name=='takes_tuple_value')].inner.function.sig.inputs[0][1].tuple[1].impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn takes_tuple_value(arg: (u8, impl NeedsSized + ?Sized)) { + let _ = arg; +} + +// Return-position tuples are also required to be Sized, so the tail is forced to be Sized here. +//@ has "$.index[?(@.name=='returns_tuple_value')].inner.function.sig.output.tuple[1].impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='returns_tuple_value')].inner.function.sig.output.tuple[1].impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='returns_tuple_value')].inner.function.sig.output.tuple[1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='returns_tuple_value')].inner.function.sig.output.tuple[1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ !has "$.index[?(@.name=='returns_tuple_value')].inner.function.sig.output.tuple[1].impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn returns_tuple_value() -> (u8, impl NeedsSized + ?Sized) { + (7u8, 9u8) +} + +// By-value tuple structs must be Sized, but we don't surface that as an implied bound. +//@ has "$.index[?(@.name=='takes_tuple_struct_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_tuple_struct_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_tuple_struct_value(value: TupleStruct) { + let _ = value; +} + +// Return-position tuple structs must be Sized as well, so we keep `?Sized` explicit while +// adding a Sized-implying trait bound to stay compilable. +//@ has "$.index[?(@.name=='returns_tuple_struct_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='returns_tuple_struct_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='returns_tuple_struct_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='returns_tuple_struct_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub fn returns_tuple_struct_value() -> TupleStruct { + todo!() +} + +// By-value named-field structs must be Sized, but we don't surface that as an implied bound. +//@ has "$.index[?(@.name=='takes_named_struct_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_named_struct_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_named_struct_value(value: NamedStruct) { + let _ = value; +} + +// Return-position named-field structs must be Sized as well, so we keep `?Sized` explicit while +// adding a Sized-implying trait bound to stay compilable. +//@ has "$.index[?(@.name=='returns_named_struct_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='returns_named_struct_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='returns_named_struct_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='returns_named_struct_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub fn returns_named_struct_value() -> NamedStruct { + todo!() +} + +// Indirections (references/pointers) can point to DSTs, so they do not imply `T: Sized`. +//@ has "$.index[?(@.name=='takes_tuple_struct_ref')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_tuple_struct_ref')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_tuple_struct_ref(value: &TupleStruct) { + let _ = value; +} + +//@ has "$.index[?(@.name=='takes_named_struct_ptr')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_named_struct_ptr')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_named_struct_ptr(value: *const NamedStruct) { + let _ = value; +} diff --git a/tests/rustdoc-json/implied-bounds/indirect-opaque-type-return.rs b/tests/rustdoc-json/implied-bounds/indirect-opaque-type-return.rs new file mode 100644 index 0000000000000..e02dfeb12324d --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/indirect-opaque-type-return.rs @@ -0,0 +1,60 @@ +//! This file exercises return-position `impl Trait` that shows up under indirections +//! or inside tuples. Some positions allow the opaque to stay unsized (behind pointers), +//! while others force it to be `Sized`. + +use std::fmt::Debug; + +pub trait NeedsSized: Sized {} +impl NeedsSized for T {} + +// Raw pointers can point to DSTs, so the opaque can stay unsized here. +//@ has "$.index[?(@.name=='returns_raw_ptr')].inner.function.sig.output.raw_pointer.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='returns_raw_ptr')].inner.function.sig.output.raw_pointer.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_raw_ptr')].inner.function.sig.output.raw_pointer.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn returns_raw_ptr() -> *const (impl Debug + ?Sized) { + core::ptr::null::() +} + +// Array elements must be Sized, so we only allow the opaque behind a reference element. +//@ has "$.index[?(@.name=='returns_array_ref')].inner.function.sig.output.array.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='returns_array_ref')].inner.function.sig.output.array.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_array_ref')].inner.function.sig.output.array.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn returns_array_ref() -> [&'static (impl Debug + ?Sized); 1] { + ["hello world"] +} + +// Slice elements must be Sized, so we only allow an unsized opaque behind the element reference. +//@ has "$.index[?(@.name=='returns_slice_ref')].inner.function.sig.output.borrowed_ref.type.slice.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='returns_slice_ref')].inner.function.sig.output.borrowed_ref.type.slice.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_slice_ref')].inner.function.sig.output.borrowed_ref.type.slice.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn returns_slice_ref() -> &'static [&'static (impl Debug + ?Sized)] { + &["hello world"] +} + +// A tuple element can be unsized only in the tail. This one is not tail, so it must be Sized, +// but the opaque is still behind a reference, so it stays unsized. +//@ has "$.index[?(@.name=='returns_tuple_ref')].inner.function.sig.output.tuple[0].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='returns_tuple_ref')].inner.function.sig.output.tuple[0].borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_tuple_ref')].inner.function.sig.output.tuple[0].borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn returns_tuple_ref() -> (&'static (impl Debug + ?Sized), u8) { + ("hello world", 123u8) +} + +// The tuple tail determines whether the tuple is a DST. Since this tuple is returned by value, +// the tail must be Sized, so we rely on a `Sized`-implying trait while still spelling `?Sized`. +//@ has "$.index[?(@.name=='returns_tuple_tail_value')].inner.function.sig.output.tuple[1].impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='returns_tuple_tail_value')].inner.function.sig.output.tuple[1].impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='returns_tuple_tail_value')].inner.function.sig.output.tuple[1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='returns_tuple_tail_value')].inner.function.sig.output.tuple[1].impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ !has "$.index[?(@.name=='returns_tuple_tail_value')].inner.function.sig.output.tuple[1].impl_trait.implied_bounds[?(@.trait_bound.modifier=='maybe')]" +pub fn returns_tuple_tail_value() -> (u8, impl NeedsSized + ?Sized) { + (42u8, 123u8) +} + +// Opaques under `Option<&T>` are still behind a reference, so they can stay unsized. +//@ has "$.index[?(@.name=='returns_option_ref')].inner.function.sig.output.resolved_path.args.angle_bracketed.args[0].type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='returns_option_ref')].inner.function.sig.output.resolved_path.args.angle_bracketed.args[0].type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_option_ref')].inner.function.sig.output.resolved_path.args.angle_bracketed.args[0].type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn returns_option_ref() -> Option<&'static (impl Debug + ?Sized)> { + Some("hello world") +} 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..3cf5b0d032ac5 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/multi-lifetimes.rs @@ -0,0 +1,31 @@ +//@ 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' && @.trait_bound.modifier=='none')]" +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' && @.trait_bound.modifier=='none')]" +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' && @.trait_bound.modifier=='none')]" +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"' +// FIXME: Eventually we also want `implied_bounds` on lifetimes too. When implemented, +// enable the following test too: +// - @ is "$.index[?(@.name=='require_outlive_pair')].inner.function.generics.params[1].kind.lifetime.implied_bounds" '["\'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' && @.trait_bound.modifier=='none')]" +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..3384c5f917f23 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/needing-elaboration.rs @@ -0,0 +1,22 @@ +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' && @.trait_bound.modifier=='none')]" +//@ 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/nested-dst-structs.rs b/tests/rustdoc-json/implied-bounds/nested-dst-structs.rs new file mode 100644 index 0000000000000..7afc3dc67872e --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/nested-dst-structs.rs @@ -0,0 +1,54 @@ +//! Nested DST-capable structs can still imply `T: Sized` when used by value, +//! even though each layer allows `T: ?Sized`. + +pub struct InnerTuple(pub T); +pub struct InnerNamed { + pub value: T, +} + +pub struct OuterTuple(pub T); +pub struct OuterNamed { + pub value: T, +} + +// By-value outer tuple struct requires `InnerTuple` to be Sized, but we don't surface that. +//@ has "$.index[?(@.name=='takes_outer_tuple_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_outer_tuple_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_outer_tuple_value(value: OuterTuple>) { + let _ = value; +} + +// By-value outer named struct requires `InnerNamed` to be Sized, but we don't surface that. +//@ has "$.index[?(@.name=='takes_outer_named_value')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_outer_named_value')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_outer_named_value(value: OuterNamed>) { + let _ = value; +} + +// Indirections allow the outer struct to be a DST, so `T: Sized` is not implied here. +//@ has "$.index[?(@.name=='takes_outer_tuple_ref')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_outer_tuple_ref')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_outer_tuple_ref(value: &OuterTuple>) { + let _ = value; +} + +// Return-position references also allow DSTs, so `T: Sized` is not implied here either. +//@ has "$.index[?(@.name=='returns_outer_tuple_ref')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_outer_tuple_ref')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn returns_outer_tuple_ref() -> &'static OuterTuple> { + todo!() +} + +// Raw pointers also allow DSTs, so `T: Sized` is not implied here either. +//@ has "$.index[?(@.name=='takes_outer_named_ptr')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='takes_outer_named_ptr')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn takes_outer_named_ptr(value: *const OuterNamed>) { + let _ = value; +} + +// Return-position raw pointers also allow DSTs. +//@ has "$.index[?(@.name=='returns_outer_named_ptr')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='returns_outer_named_ptr')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn returns_outer_named_ptr() -> *const OuterNamed> { + core::ptr::null() +} 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..032c68707f786 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/no-duplicates.rs @@ -0,0 +1,152 @@ +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_where_bounds_only')]" +//@ count "$.index[?(@.name=='duplicate_where_bounds_only')].inner.function.generics.params[0].kind.type.bounds[*]" 0 +//@ count "$.index[?(@.name=='duplicate_where_bounds_only')].inner.function.generics.where_predicates[0].bound_predicate.bounds[?(@.trait_bound.trait.path=='SizedOnly')]" 2 +//@ count "$.index[?(@.name=='duplicate_where_bounds_only')].inner.function.generics.params[0].kind.type.implied_bounds[*]" 1 +//@ has "$.index[?(@.name=='duplicate_where_bounds_only')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn duplicate_where_bounds_only(_t: T) +where + T: SizedOnly + SizedOnly, +{ +} + +//@ 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..8b25276c2fa10 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/redundant-sized.rs @@ -0,0 +1,17 @@ +//@ 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' && @.trait_bound.modifier=='none')]" +//@ 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..5437ef32d8ac5 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/refs.rs @@ -0,0 +1,40 @@ +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' && @.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' && @.trait_bound.modifier=='none')]" +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' && @.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' && @.trait_bound.modifier=='none')]" +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' && @.trait_bound.modifier=='none')]" +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..a841caa75974d --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/simple.rs @@ -0,0 +1,45 @@ +//@ 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' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='sized_through_trait')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !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' && @.trait_bound.modifier=='none')]" +//@ 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')]" +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' && @.trait_bound.modifier=='none')]" +//@ 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' && @.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' && @.trait_bound.modifier=='none')]" +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-args.rs b/tests/rustdoc-json/implied-bounds/trait-args.rs new file mode 100644 index 0000000000000..80f10753a4de5 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/trait-args.rs @@ -0,0 +1,103 @@ +use std::ops::{AsyncFn, Deref}; + +pub trait Bar {} +pub trait Foo: Bar {} +pub trait ArgBase {} +pub trait ArgDerived: ArgBase {} +pub trait AsyncCallable: AsyncFn(u8, u16) -> i32 {} +pub trait Callable: Fn(u8, u16) -> i32 {} +pub trait AssocBase { + type Item; + type Extra; +} +pub trait AssocDerived: AssocBase {} +pub trait AssocBoundBase { + type Item; +} +pub trait AssocBoundDerived: AssocBoundBase {} +pub trait HrtbCallable: for<'a> Fn(&'a i32) -> &'a i32 {} +pub trait GuardBase { + type Item; +} +pub trait GuardOther { + type Item; +} +pub trait GuardCombo: GuardBase + GuardOther {} +pub trait SendOnly: Send {} +pub trait DerefBase: Deref {} +pub trait DerefSub: DerefBase {} + +// `T: Bar` is implied via `Foo: Bar`. +//@ has "$.index[?(@.name=='implied_bound_args')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='Foo' && @.trait_bound.trait.args.angle_bracketed.args[0].type.primitive=='u8')]" +//@ has "$.index[?(@.name=='implied_bound_args')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Bar' && @.trait_bound.trait.args.angle_bracketed.args[0].type.primitive=='u8')]" +pub fn implied_bound_args>(value: T) { + let _ = value; +} + +// `T: ArgBase` is implied via `ArgDerived: ArgBase`, even with an explicit `ArgBase`. +//@ has "$.index[?(@.name=='implied_bound_args_distinct')].inner.function.generics.params[0].kind.type.bounds[?(@.trait_bound.trait.path=='ArgBase' && @.trait_bound.trait.args.angle_bracketed.args[0].type.primitive=='u16')]" +//@ has "$.index[?(@.name=='implied_bound_args_distinct')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='ArgBase' && @.trait_bound.trait.args.angle_bracketed.args[0].type.primitive=='u8')]" +pub fn implied_bound_args_distinct + ArgDerived>(value: T) { + let _ = value; +} + +// `T: Send` is implied via `SendOnly: Send`. +//@ has "$.index[?(@.name=='implied_bound_send')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Send')]" +pub fn implied_bound_send(value: T) { + let _ = value; +} + +// `T: AsyncFn(u8, u16) -> i32` is implied via `AsyncCallable: AsyncFn(u8, u16) -> i32`; +// we also surface the `AsyncFnMut`/`AsyncFnOnce` supertraits with the same signature. +//@ has "$.index[?(@.name=='implied_bound_async_fn_args')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='AsyncFn' && @.trait_bound.trait.args.parenthesized.inputs[0].primitive=='u8' && @.trait_bound.trait.args.parenthesized.inputs[1].primitive=='u16' && @.trait_bound.trait.args.parenthesized.output.primitive=='i32')]" +//@ has "$.index[?(@.name=='implied_bound_async_fn_args')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='AsyncFnMut' && @.trait_bound.trait.args.parenthesized.inputs[0].primitive=='u8' && @.trait_bound.trait.args.parenthesized.inputs[1].primitive=='u16' && @.trait_bound.trait.args.parenthesized.output.primitive=='i32')]" +//@ has "$.index[?(@.name=='implied_bound_async_fn_args')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='AsyncFnOnce' && @.trait_bound.trait.args.parenthesized.inputs[0].primitive=='u8' && @.trait_bound.trait.args.parenthesized.inputs[1].primitive=='u16' && @.trait_bound.trait.args.parenthesized.output.primitive=='i32')]" +pub fn implied_bound_async_fn_args(value: T) { + let _ = value; +} + +// `T: Fn(u8, u16) -> i32` is implied via `Callable: Fn(u8, u16) -> i32`; +// we also surface the `FnMut`/`FnOnce` supertraits with the same signature. +//@ has "$.index[?(@.name=='implied_bound_fn_args')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Fn' && @.trait_bound.trait.args.parenthesized.inputs[0].primitive=='u8' && @.trait_bound.trait.args.parenthesized.inputs[1].primitive=='u16' && @.trait_bound.trait.args.parenthesized.output.primitive=='i32')]" +//@ has "$.index[?(@.name=='implied_bound_fn_args')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='FnMut' && @.trait_bound.trait.args.parenthesized.inputs[0].primitive=='u8' && @.trait_bound.trait.args.parenthesized.inputs[1].primitive=='u16' && @.trait_bound.trait.args.parenthesized.output.primitive=='i32')]" +//@ has "$.index[?(@.name=='implied_bound_fn_args')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='FnOnce' && @.trait_bound.trait.args.parenthesized.inputs[0].primitive=='u8' && @.trait_bound.trait.args.parenthesized.inputs[1].primitive=='u16' && @.trait_bound.trait.args.parenthesized.output.primitive=='i32')]" +pub fn implied_bound_fn_args(value: T) { + let _ = value; +} + +// `T: AssocBase` is implied via `AssocDerived`. +//@ has "$.index[?(@.name=='implied_bound_constraints')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='AssocBase')].trait_bound.trait.args.angle_bracketed.constraints[?(@.name=='Item' && @.binding.equality.type.primitive=='u8')]" +//@ has "$.index[?(@.name=='implied_bound_constraints')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='AssocBase')].trait_bound.trait.args.angle_bracketed.constraints[?(@.name=='Extra' && @.binding.equality.type.primitive=='u8')]" +pub fn implied_bound_constraints>(value: T) { + let _ = value; +} + +// `T: AssocBoundBase` is implied via `AssocBoundDerived`. +//@ has "$.index[?(@.name=='implied_bound_assoc_bound')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='AssocBoundBase')].trait_bound.trait.args.angle_bracketed.constraints[?(@.name=='Item')].binding.constraint[?(@.trait_bound.trait.path=='Clone')]" +pub fn implied_bound_assoc_bound(value: T) { + let _ = value; +} + +// `for<'a> Fn(&'a i32) -> &'a i32` is preserved in implied bounds, +// and the `FnMut`/`FnOnce` supertraits are present too. +//@ has "$.index[?(@.name=='implied_bound_hrtb')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Fn' && @.trait_bound.generic_params[0].name==\"'a\" && @.trait_bound.trait.args.parenthesized.inputs[0].borrowed_ref.lifetime==\"'a\" && @.trait_bound.trait.args.parenthesized.output.borrowed_ref.lifetime==\"'a\")]" +//@ has "$.index[?(@.name=='implied_bound_hrtb')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='FnMut' && @.trait_bound.generic_params[0].name==\"'a\" && @.trait_bound.trait.args.parenthesized.inputs[0].borrowed_ref.lifetime==\"'a\" && @.trait_bound.trait.args.parenthesized.output.borrowed_ref.lifetime==\"'a\")]" +//@ has "$.index[?(@.name=='implied_bound_hrtb')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='FnOnce' && @.trait_bound.generic_params[0].name==\"'a\" && @.trait_bound.trait.args.parenthesized.inputs[0].borrowed_ref.lifetime==\"'a\" && @.trait_bound.trait.args.parenthesized.output.borrowed_ref.lifetime==\"'a\")]" +pub fn implied_bound_hrtb(value: T) { + let _ = value; +} + +// `GuardOther` is implied, but its constraint should not attach to `GuardBase`. +//@ has "$.index[?(@.name=='implied_bound_unrelated')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='GuardBase' && @.trait_bound.trait.args==null)]" +//@ has "$.index[?(@.name=='implied_bound_unrelated')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='GuardOther')].trait_bound.trait.args.angle_bracketed.constraints[?(@.name=='Item' && @.binding.equality.type.primitive=='u16')]" +pub fn implied_bound_unrelated>(value: T) { + let _ = value; +} + +// `T: Deref` and `T: DerefBase` are implied via `DerefSub`. +//@ has "$.index[?(@.name=='implied_bound_deref_target')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Deref')].trait_bound.trait.args.angle_bracketed.constraints[?(@.name=='Target' && @.binding.equality.type.primitive=='u8')]" +//@ has "$.index[?(@.name=='implied_bound_deref_target')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='DerefBase')].trait_bound.trait.args.angle_bracketed.constraints[?(@.name=='Target' && @.binding.equality.type.primitive=='u8')]" +//@ has "$.index[?(@.name=='implied_bound_deref_target')].inner.function.generics.params[0].kind.type.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +pub fn implied_bound_deref_target>(value: T) { + let _ = value; +} 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..71406538912de --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/trait-rpit.rs @@ -0,0 +1,19 @@ +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' && @.trait_bound.modifier=='none')]" + //@ 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')]" + 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' && @.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' && @.trait_bound.modifier=='none')]" + //@ !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/type-alias-impl-trait.rs b/tests/rustdoc-json/implied-bounds/type-alias-impl-trait.rs new file mode 100644 index 0000000000000..bc256f70876f8 --- /dev/null +++ b/tests/rustdoc-json/implied-bounds/type-alias-impl-trait.rs @@ -0,0 +1,145 @@ +#![feature(type_alias_impl_trait)] + +use std::fmt::{self, Debug}; + +pub trait NeedsSized: Sized {} +impl NeedsSized for T {} + +pub trait StaticOnly: 'static {} +impl StaticOnly for T {} + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct StaticMarker; + +impl fmt::Debug for StaticMarker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("StaticMarker") + } +} + +impl PartialEq for StaticMarker { + fn eq(&self, _: &T) -> bool { + true + } +} + +#[allow(dead_code)] +static STATIC_MARKER: StaticMarker = StaticMarker; + +//@ has "$.index[?(@.name=='Opaque')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='Opaque')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='Opaque')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" +pub type Opaque = impl Debug; + +//@ has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub type OpaqueRef = &'static impl Debug; + +//@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ count "$.index[?(@.name=='OpaqueMaybeUnsized')].inner.type_alias.type.impl_trait.implied_bounds[*]" 0 +pub type OpaqueMaybeUnsized = impl Debug + ?Sized; + +//@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Debug')]" +//@ !has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized')]" +//@ has "$.index[?(@.name=='OpaqueMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub type OpaqueMaybeUnsizedRef = &'static (impl Debug + ?Sized); + +//@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueSizedViaTrait')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub type OpaqueSizedViaTrait = impl NeedsSized; + +//@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueSizedViaTraitRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub type OpaqueSizedViaTraitRef = &'static impl NeedsSized; + +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueOverridden')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +pub type OpaqueOverridden = impl NeedsSized + ?Sized; + +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='NeedsSized')]" +//@ has "$.index[?(@.name=='OpaqueOverriddenRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +pub type OpaqueOverriddenRef = &'static (impl NeedsSized + ?Sized); + +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ !has "$.index[?(@.name=='OpaqueStatic')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStatic = impl StaticOnly; + +//@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='none')]" +//@ has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='OpaqueStaticRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStaticRef = &'static impl StaticOnly; + +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsized')].inner.type_alias.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStaticMaybeUnsized = impl StaticOnly + ?Sized; + +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.bounds[?(@.trait_bound.trait.path=='Sized' && @.trait_bound.modifier=='maybe')]" +//@ has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.outlives==\"'static\")]" +//@ !has "$.index[?(@.name=='OpaqueStaticMaybeUnsizedRef')].inner.type_alias.type.borrowed_ref.type.impl_trait.implied_bounds[?(@.trait_bound.trait.path=='StaticOnly')]" +pub type OpaqueStaticMaybeUnsizedRef = &'static (impl StaticOnly + ?Sized); + +// Ensure the opaques we defined above are not unconstrained. +#[allow(dead_code)] +#[define_opaque( + Opaque, + OpaqueRef, + OpaqueMaybeUnsized, + OpaqueMaybeUnsizedRef, + OpaqueSizedViaTrait, + OpaqueSizedViaTraitRef, + OpaqueOverridden, + OpaqueOverriddenRef, + OpaqueStatic, + OpaqueStaticRef, + OpaqueStaticMaybeUnsized, + OpaqueStaticMaybeUnsizedRef +)] +fn define_all() -> ( + Opaque, + OpaqueRef, + Box, + OpaqueMaybeUnsizedRef, + OpaqueSizedViaTrait, + OpaqueSizedViaTraitRef, + OpaqueOverridden, + OpaqueOverriddenRef, + OpaqueStatic, + OpaqueStaticRef, + Box, + OpaqueStaticMaybeUnsizedRef, +) { + ( + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + StaticMarker, + &STATIC_MARKER, + Box::new(StaticMarker), + &STATIC_MARKER, + ) +} 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?