about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/etc/check_missing_items.py4
-rw-r--r--src/librustdoc/json/conversions.rs120
-rw-r--r--src/rustdoc-json-types/lib.rs38
-rw-r--r--src/test/rustdoc-json/type/dyn.rs48
-rw-r--r--src/test/rustdoc-json/type/hrtb.rs26
5 files changed, 159 insertions, 77 deletions
diff --git a/src/etc/check_missing_items.py b/src/etc/check_missing_items.py
index 343dd0387f4..a705e238495 100644
--- a/src/etc/check_missing_items.py
+++ b/src/etc/check_missing_items.py
@@ -88,8 +88,8 @@ def check_type(ty):
                         for bound in binding["binding"]["constraint"]:
                             check_generic_bound(bound)
             elif "parenthesized" in args:
-                for ty in args["parenthesized"]["inputs"]:
-                    check_type(ty)
+                for input_ty in args["parenthesized"]["inputs"]:
+                    check_type(input_ty)
                 if args["parenthesized"]["output"]:
                     check_type(args["parenthesized"]["output"])
         if not valid_id(ty["inner"]["id"]):
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 716a4c9ea43..8caba8cb902 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -119,6 +119,16 @@ where
     }
 }
 
+impl<I, T, U> FromWithTcx<I> for Vec<U>
+where
+    I: IntoIterator<Item = T>,
+    U: FromWithTcx<T>,
+{
+    fn from_tcx(f: I, tcx: TyCtxt<'_>) -> Vec<U> {
+        f.into_iter().map(|x| x.into_tcx(tcx)).collect()
+    }
+}
+
 pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation {
     #[rustfmt::skip]
     let rustc_attr::Deprecation { since, note, is_since_rustc_version: _, suggestion: _ } = deprecation;
@@ -130,11 +140,11 @@ impl FromWithTcx<clean::GenericArgs> for GenericArgs {
         use clean::GenericArgs::*;
         match args {
             AngleBracketed { args, bindings } => GenericArgs::AngleBracketed {
-                args: args.into_vec().into_iter().map(|a| a.into_tcx(tcx)).collect(),
-                bindings: bindings.into_iter().map(|a| a.into_tcx(tcx)).collect(),
+                args: args.into_vec().into_tcx(tcx),
+                bindings: bindings.into_tcx(tcx),
             },
             Parenthesized { inputs, output } => GenericArgs::Parenthesized {
-                inputs: inputs.into_vec().into_iter().map(|a| a.into_tcx(tcx)).collect(),
+                inputs: inputs.into_vec().into_tcx(tcx),
                 output: output.map(|a| (*a).into_tcx(tcx)),
             },
         }
@@ -145,7 +155,7 @@ impl FromWithTcx<clean::GenericArg> for GenericArg {
     fn from_tcx(arg: clean::GenericArg, tcx: TyCtxt<'_>) -> Self {
         use clean::GenericArg::*;
         match arg {
-            Lifetime(l) => GenericArg::Lifetime(l.0.to_string()),
+            Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)),
             Type(t) => GenericArg::Type(t.into_tcx(tcx)),
             Const(box c) => GenericArg::Const(c.into_tcx(tcx)),
             Infer => GenericArg::Infer,
@@ -177,9 +187,7 @@ impl FromWithTcx<clean::TypeBindingKind> for TypeBindingKind {
         use clean::TypeBindingKind::*;
         match kind {
             Equality { term } => TypeBindingKind::Equality(term.into_tcx(tcx)),
-            Constraint { bounds } => {
-                TypeBindingKind::Constraint(bounds.into_iter().map(|a| a.into_tcx(tcx)).collect())
-            }
+            Constraint { bounds } => TypeBindingKind::Constraint(bounds.into_tcx(tcx)),
         }
     }
 }
@@ -244,7 +252,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
         TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_tcx(tcx)),
         MethodItem(m, _) => ItemEnum::Method(from_function_method(m, true, header.unwrap(), tcx)),
         TyMethodItem(m) => ItemEnum::Method(from_function_method(m, false, header.unwrap(), tcx)),
-        ImplItem(i) => ItemEnum::Impl(i.into_tcx(tcx)),
+        ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
         StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
         ForeignStaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
         ForeignTypeItem => ItemEnum::ForeignType,
@@ -260,12 +268,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
         }
         TyAssocTypeItem(g, b) => ItemEnum::AssocType {
             generics: (*g).into_tcx(tcx),
-            bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+            bounds: b.into_tcx(tcx),
             default: None,
         },
         AssocTypeItem(t, b) => ItemEnum::AssocType {
             generics: t.generics.into_tcx(tcx),
-            bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+            bounds: b.into_tcx(tcx),
             default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
         },
         // `convert_item` early returns `None` for stripped items and keywords.
@@ -347,15 +355,15 @@ fn convert_abi(a: RustcAbi) -> Abi {
     }
 }
 
+fn convert_lifetime(l: clean::Lifetime) -> String {
+    l.0.to_string()
+}
+
 impl FromWithTcx<clean::Generics> for Generics {
     fn from_tcx(generics: clean::Generics, tcx: TyCtxt<'_>) -> Self {
         Generics {
-            params: generics.params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
-            where_predicates: generics
-                .where_predicates
-                .into_iter()
-                .map(|x| x.into_tcx(tcx))
-                .collect(),
+            params: generics.params.into_tcx(tcx),
+            where_predicates: generics.where_predicates.into_tcx(tcx),
         }
     }
 }
@@ -374,10 +382,10 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
         use clean::GenericParamDefKind::*;
         match kind {
             Lifetime { outlives } => GenericParamDefKind::Lifetime {
-                outlives: outlives.into_iter().map(|lt| lt.0.to_string()).collect(),
+                outlives: outlives.into_iter().map(convert_lifetime).collect(),
             },
             Type { did: _, bounds, default, synthetic } => GenericParamDefKind::Type {
-                bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+                bounds: bounds.into_tcx(tcx),
                 default: default.map(|x| (*x).into_tcx(tcx)),
                 synthetic,
             },
@@ -395,7 +403,7 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
         match predicate {
             BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
                 type_: ty.into_tcx(tcx),
-                bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+                bounds: bounds.into_tcx(tcx),
                 generic_params: bound_params
                     .into_iter()
                     .map(|x| GenericParamDef {
@@ -405,8 +413,8 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                     .collect(),
             },
             RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate {
-                lifetime: lifetime.0.to_string(),
-                bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+                lifetime: convert_lifetime(lifetime),
+                bounds: bounds.into_tcx(tcx),
             },
             EqPredicate { lhs, rhs } => {
                 WherePredicate::EqPredicate { lhs: lhs.into_tcx(tcx), rhs: rhs.into_tcx(tcx) }
@@ -424,11 +432,11 @@ impl FromWithTcx<clean::GenericBound> for GenericBound {
                 let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx);
                 GenericBound::TraitBound {
                     trait_,
-                    generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+                    generic_params: generic_params.into_tcx(tcx),
                     modifier: from_trait_bound_modifier(modifier),
                 }
             }
-            Outlives(lifetime) => GenericBound::Outlives(lifetime.0.to_string()),
+            Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)),
         }
     }
 }
@@ -447,8 +455,8 @@ pub(crate) fn from_trait_bound_modifier(
 impl FromWithTcx<clean::Type> for Type {
     fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self {
         use clean::Type::{
-            Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive,
-            QPath, RawPointer, Slice, Tuple,
+            Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath,
+            RawPointer, Slice, Tuple,
         };
 
         match ty {
@@ -458,40 +466,24 @@ impl FromWithTcx<clean::Type> for Type {
                 args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))),
                 param_names: Vec::new(),
             },
-            DynTrait(mut bounds, lt) => {
-                let first_trait = bounds.remove(0).trait_;
-
-                Type::ResolvedPath {
-                    name: first_trait.whole_name(),
-                    id: from_item_id(first_trait.def_id().into(), tcx),
-                    args: first_trait
-                        .segments
-                        .last()
-                        .map(|args| Box::new(args.clone().args.into_tcx(tcx))),
-                    param_names: bounds
-                        .into_iter()
-                        .map(|t| {
-                            clean::GenericBound::TraitBound(t, rustc_hir::TraitBoundModifier::None)
-                        })
-                        .chain(lt.map(clean::GenericBound::Outlives))
-                        .map(|bound| bound.into_tcx(tcx))
-                        .collect(),
-                }
-            }
+            clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait {
+                lifetime: lt.map(convert_lifetime),
+                traits: bounds.into_tcx(tcx),
+            }),
             Generic(s) => Type::Generic(s.to_string()),
             Primitive(p) => Type::Primitive(p.as_sym().to_string()),
             BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
-            Tuple(t) => Type::Tuple(t.into_iter().map(|x| x.into_tcx(tcx)).collect()),
+            Tuple(t) => Type::Tuple(t.into_tcx(tcx)),
             Slice(t) => Type::Slice(Box::new((*t).into_tcx(tcx))),
             Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s },
-            ImplTrait(g) => Type::ImplTrait(g.into_iter().map(|x| x.into_tcx(tcx)).collect()),
+            ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
             Infer => Type::Infer,
             RawPointer(mutability, type_) => Type::RawPointer {
                 mutable: mutability == ast::Mutability::Mut,
                 type_: Box::new((*type_).into_tcx(tcx)),
             },
             BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
-                lifetime: lifetime.map(|l| l.0.to_string()),
+                lifetime: lifetime.map(convert_lifetime),
                 mutable: mutability == ast::Mutability::Mut,
                 type_: Box::new((*type_).into_tcx(tcx)),
             },
@@ -528,7 +520,7 @@ impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
                 async_: false,
                 abi: convert_abi(abi),
             },
-            generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+            generic_params: generic_params.into_tcx(tcx),
             decl: decl.into_tcx(tcx),
         }
     }
@@ -562,16 +554,28 @@ impl FromWithTcx<clean::Trait> for Trait {
             is_unsafe,
             items: ids(items, tcx),
             generics: generics.into_tcx(tcx),
-            bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+            bounds: bounds.into_tcx(tcx),
             implementations: Vec::new(), // Added in JsonRenderer::item
         }
     }
 }
 
-impl FromWithTcx<Box<clean::Impl>> for Impl {
-    fn from_tcx(impl_: Box<clean::Impl>, tcx: TyCtxt<'_>) -> Self {
+impl FromWithTcx<clean::PolyTrait> for PolyTrait {
+    fn from_tcx(
+        clean::PolyTrait { trait_, generic_params }: clean::PolyTrait,
+        tcx: TyCtxt<'_>,
+    ) -> Self {
+        PolyTrait {
+            trait_: clean::Type::Path { path: trait_ }.into_tcx(tcx),
+            generic_params: generic_params.into_tcx(tcx),
+        }
+    }
+}
+
+impl FromWithTcx<clean::Impl> for Impl {
+    fn from_tcx(impl_: clean::Impl, tcx: TyCtxt<'_>) -> Self {
         let provided_trait_methods = impl_.provided_trait_methods(tcx);
-        let clean::Impl { unsafety, generics, trait_, for_, items, polarity, kind } = *impl_;
+        let clean::Impl { unsafety, generics, trait_, for_, items, polarity, kind } = impl_;
         // FIXME: should `trait_` be a clean::Path equivalent in JSON?
         let trait_ = trait_.map(|path| clean::Type::Path { path }.into_tcx(tcx));
         // FIXME: use something like ImplKind in JSON?
@@ -730,10 +734,7 @@ impl FromWithTcx<Box<clean::Typedef>> for Typedef {
 
 impl FromWithTcx<clean::OpaqueTy> for OpaqueTy {
     fn from_tcx(opaque: clean::OpaqueTy, tcx: TyCtxt<'_>) -> Self {
-        OpaqueTy {
-            bounds: opaque.bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
-            generics: opaque.generics.into_tcx(tcx),
-        }
+        OpaqueTy { bounds: opaque.bounds.into_tcx(tcx), generics: opaque.generics.into_tcx(tcx) }
     }
 }
 
@@ -749,10 +750,7 @@ impl FromWithTcx<clean::Static> for Static {
 
 impl FromWithTcx<clean::TraitAlias> for TraitAlias {
     fn from_tcx(alias: clean::TraitAlias, tcx: TyCtxt<'_>) -> Self {
-        TraitAlias {
-            generics: alias.generics.into_tcx(tcx),
-            params: alias.bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
-        }
+        TraitAlias { generics: alias.generics.into_tcx(tcx), params: alias.bounds.into_tcx(tcx) }
     }
 }
 
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 761e94c7ebb..bd4ea98441d 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -9,7 +9,7 @@ use std::path::PathBuf;
 use serde::{Deserialize, Serialize};
 
 /// rustdoc format-version.
-pub const FORMAT_VERSION: u32 = 16;
+pub const FORMAT_VERSION: u32 = 17;
 
 /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
 /// about the language items in the local crate, as well as info about external items to allow
@@ -116,6 +116,35 @@ pub enum Visibility {
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+pub struct DynTrait {
+    /// All the traits implemented. One of them is the vtable, and the rest must be auto traits.
+    pub traits: Vec<PolyTrait>,
+    /// The lifetime of the whole dyn object
+    /// ```text
+    /// dyn Debug + 'static
+    ///             ^^^^^^^
+    ///             |
+    ///             this part
+    /// ```
+    pub lifetime: Option<String>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+/// A trait and potential HRTBs
+pub struct PolyTrait {
+    #[serde(rename = "trait")]
+    pub trait_: Type,
+    /// Used for Higher-Rank Trait Bounds (HRTBs)
+    /// ```text
+    /// dyn for<'a> Fn() -> &'a i32"
+    ///     ^^^^^^^
+    ///       |
+    ///       this part
+    /// ```
+    pub generic_params: Vec<GenericParamDef>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum GenericArgs {
     /// <'a, 32, B: Copy, C = u32>
@@ -395,7 +424,7 @@ pub enum WherePredicate {
         type_: Type,
         bounds: Vec<GenericBound>,
         /// Used for Higher-Rank Trait Bounds (HRTBs)
-        /// ```plain
+        /// ```text
         /// where for<'a> &'a T: Iterator,"
         ///       ^^^^^^^
         ///       |
@@ -420,7 +449,7 @@ pub enum GenericBound {
         #[serde(rename = "trait")]
         trait_: Type,
         /// Used for Higher-Rank Trait Bounds (HRTBs)
-        /// ```plain
+        /// ```text
         /// where F: for<'a, 'b> Fn(&'a u8, &'b u8)
         ///          ^^^^^^^^^^^
         ///          |
@@ -458,6 +487,7 @@ pub enum Type {
         args: Option<Box<GenericArgs>>,
         param_names: Vec<GenericBound>,
     },
+    DynTrait(DynTrait),
     /// Parameterized types
     Generic(String),
     /// Fixed-size numeric types (plus int/usize/float), char, arrays, slices, and tuples
@@ -505,7 +535,7 @@ pub enum Type {
 pub struct FunctionPointer {
     pub decl: FnDecl,
     /// Used for Higher-Rank Trait Bounds (HRTBs)
-    /// ```plain
+    /// ```text
     /// for<'c> fn(val: &'c i32) -> i32
     /// ^^^^^^^
     ///       |
diff --git a/src/test/rustdoc-json/type/dyn.rs b/src/test/rustdoc-json/type/dyn.rs
index f53dc03f4b4..c18b54d1fdf 100644
--- a/src/test/rustdoc-json/type/dyn.rs
+++ b/src/test/rustdoc-json/type/dyn.rs
@@ -1,8 +1,13 @@
 // ignore-tidy-linelength
+use std::fmt::Debug;
 
-// @count dyn.json "$.index[*][?(@.name=='dyn')].inner.items" 1
+// @count dyn.json "$.index[*][?(@.name=='dyn')].inner.items[*]" 3
 // @set sync_int_gen = - "$.index[*][?(@.name=='SyncIntGen')].id"
-// @is - "$.index[*][?(@.name=='dyn')].inner.items[0]" $sync_int_gen
+// @set ref_fn       = - "$.index[*][?(@.name=='RefFn')].id"
+// @set weird_order  = - "$.index[*][?(@.name=='WeirdOrder')].id"
+// @has - "$.index[*][?(@.name=='dyn')].inner.items[*]" $sync_int_gen
+// @has - "$.index[*][?(@.name=='dyn')].inner.items[*]" $ref_fn
+// @has - "$.index[*][?(@.name=='dyn')].inner.items[*]" $weird_order
 
 // @is    - "$.index[*][?(@.name=='SyncIntGen')].kind" \"typedef\"
 // @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.generics" '{"params": [], "where_predicates": []}'
@@ -10,12 +15,35 @@
 // @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.name" \"Box\"
 // @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.bindings" []
 // @count - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args" 1
-// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"resolved_path\"
-// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"resolved_path\"
-// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.name" \"Fn\"
-// @count - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[*]" 3
-// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[0].trait_bound.trait.inner.name" \"Send\"
-// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[1].trait_bound.trait.inner.name" \"Sync\"
-// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[2]" "{\"outlives\": \"'static\"}"
-// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.args" '{"parenthesized": {"inputs": [],"output": {"inner": "i32","kind": "primitive"}}}'
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"dyn_trait\"
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.lifetime" \"\'static\"
+// @count - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[*]" 3
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].generic_params" []
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].generic_params" []
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[2].generic_params" []
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.inner.name" '"Fn"'
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.inner.name" '"Send"'
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[2].trait.inner.name" '"Sync"'
+// @is    - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.inner.args" '{"parenthesized": {"inputs": [],"output": {"inner": "i32","kind": "primitive"}}}'
 pub type SyncIntGen = Box<dyn Fn() -> i32 + Send + Sync + 'static>;
+
+// @is - "$.index[*][?(@.name=='RefFn')].kind" \"typedef\"
+// @is - "$.index[*][?(@.name=='RefFn')].inner.generics" '{"params": [{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"}],"where_predicates": []}'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.kind" '"borrowed_ref"'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.mutable" 'false'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.lifetime" "\"'a\""
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.kind" '"dyn_trait"'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.lifetime" null
+// @count - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[*]" 1
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.kind" '"resolved_path"'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.name" '"Fn"'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.inputs[0].kind" '"borrowed_ref"'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.inputs[0].inner.lifetime" "\"'b\""
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.output.kind" '"borrowed_ref"'
+// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.output.inner.lifetime" "\"'b\""
+pub type RefFn<'a> = &'a dyn for<'b> Fn(&'b i32) -> &'b i32;
+
+// @is    - "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.inner.name" '"Send"'
+// @is    - "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.inner.name" '"Debug"'
+pub type WeirdOrder = Box<dyn Send + Debug>;
diff --git a/src/test/rustdoc-json/type/hrtb.rs b/src/test/rustdoc-json/type/hrtb.rs
new file mode 100644
index 00000000000..9311737be0f
--- /dev/null
+++ b/src/test/rustdoc-json/type/hrtb.rs
@@ -0,0 +1,26 @@
+// ignore-tidy-linelength
+
+// @has hrtb.json
+
+// @is - "$.index[*][?(@.name=='genfn')].inner.generics.where_predicates[0].bound_predicate.type" '{"inner": "F","kind": "generic"}'
+// @is - "$.index[*][?(@.name=='genfn')].inner.generics.where_predicates[0].bound_predicate.generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]'
+pub fn genfn<F>(f: F)
+where
+    for<'a, 'b> F: Fn(&'a i32, &'b i32),
+{
+    let zero = 0;
+    f(&zero, &zero);
+}
+
+// @is - "$.index[*][?(@.name=='dynfn')].inner.generics" '{"params": [], "where_predicates": []}'
+// @is - "$.index[*][?(@.name=='dynfn')].inner.generics" '{"params": [], "where_predicates": []}'
+// @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].kind" '"borrowed_ref"'
+// @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.kind" '"dyn_trait"'
+// @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.lifetime" null
+// @count - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[*]" 1
+// @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]'
+// @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[0].trait.inner.name" '"Fn"'
+pub fn dynfn(f: &dyn for<'a, 'b> Fn(&'a i32, &'b i32)) {
+    let zero = 0;
+    f(&zero, &zero);
+}