about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/json/conversions.rs26
-rw-r--r--src/librustdoc/json/mod.rs30
-rw-r--r--src/rustdoc-json-types/lib.rs14
-rw-r--r--src/tools/jsondoclint/src/validator.rs13
-rw-r--r--tests/rustdoc-json/generic-args.rs20
5 files changed, 84 insertions, 19 deletions
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 8b4be107ace..cb4c1f7fbc0 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -169,10 +169,13 @@ pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation {
     Deprecation { since, note: note.map(|s| s.to_string()) }
 }
 
-impl FromClean<clean::GenericArgs> for GenericArgs {
+impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> {
     fn from_clean(args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericArgs::*;
-        match args {
+        if args.is_empty() {
+            return None;
+        }
+        Some(Box::new(match args {
             AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
                 args: args.into_json(renderer),
                 constraints: constraints.into_json(renderer),
@@ -182,7 +185,7 @@ impl FromClean<clean::GenericArgs> for GenericArgs {
                 output: output.as_ref().map(|a| a.as_ref().into_json(renderer)),
             },
             ReturnTypeNotation => GenericArgs::ReturnTypeNotation,
-        }
+        }))
     }
 }
 
@@ -579,7 +582,20 @@ impl FromClean<clean::Path> for Path {
         Path {
             path: path.whole_name(),
             id: renderer.id_from_item_default(path.def_id().into()),
-            args: path.segments.last().map(|args| Box::new(args.args.into_json(renderer))),
+            args: {
+                if let Some((final_seg, rest_segs)) = path.segments.split_last() {
+                    // In general, `clean::Path` can hold things like
+                    // `std::vec::Vec::<u32>::new`, where generic args appear
+                    // in a middle segment. But for the places where `Path` is
+                    // used by rustdoc-json-types, generic args can only be
+                    // used in the final segment, e.g. `std::vec::Vec<u32>`. So
+                    // check that the non-final segments have no generic args.
+                    assert!(rest_segs.iter().all(|seg| seg.args.is_empty()));
+                    final_seg.args.into_json(renderer)
+                } else {
+                    None // no generics on any segments because there are no segments
+                }
+            },
         }
     }
 }
@@ -590,7 +606,7 @@ impl FromClean<clean::QPathData> for Type {
 
         Self::QualifiedPath {
             name: assoc.name.to_string(),
-            args: Box::new(assoc.args.into_json(renderer)),
+            args: assoc.args.into_json(renderer),
             self_type: Box::new(self_type.into_json(renderer)),
             trait_: trait_.as_ref().map(|trait_| trait_.into_json(renderer)),
         }
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 61493c1ed70..600a4b429f3 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -377,3 +377,33 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         }
     }
 }
+
+// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
+//
+// These assertions are here, not in `src/rustdoc-json-types/lib.rs` where the types are defined,
+// because we have access to `static_assert_size` here.
+#[cfg(target_pointer_width = "64")]
+mod size_asserts {
+    use rustc_data_structures::static_assert_size;
+
+    use super::types::*;
+    // tidy-alphabetical-start
+    static_assert_size!(AssocItemConstraint, 112);
+    static_assert_size!(Crate, 184);
+    static_assert_size!(ExternalCrate, 48);
+    static_assert_size!(FunctionPointer, 168);
+    static_assert_size!(GenericArg, 80);
+    static_assert_size!(GenericArgs, 104);
+    static_assert_size!(GenericBound, 72);
+    static_assert_size!(GenericParamDef, 136);
+    static_assert_size!(Impl, 304);
+    // `Item` contains a `PathBuf`, which is different sizes on different OSes.
+    static_assert_size!(Item, 528 + size_of::<std::path::PathBuf>());
+    static_assert_size!(ItemSummary, 32);
+    static_assert_size!(PolyTrait, 64);
+    static_assert_size!(PreciseCapturingArg, 32);
+    static_assert_size!(TargetFeature, 80);
+    static_assert_size!(Type, 80);
+    static_assert_size!(WherePredicate, 160);
+    // tidy-alphabetical-end
+}
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 4d25124f9f2..c9b4da183a3 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // 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: Pretty printing of cold attributes changed
-pub const FORMAT_VERSION: u32 = 50;
+// Latest feature: improve handling of generic args
+pub const FORMAT_VERSION: u32 = 51;
 
 /// The root of the emitted JSON blob.
 ///
@@ -277,8 +277,8 @@ pub struct PolyTrait {
 /// A set of generic arguments provided to a path segment, e.g.
 ///
 /// ```text
-/// std::option::Option::<u32>::None
-///                      ^^^^^
+/// std::option::Option<u32>
+///                    ^^^^^
 /// ```
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
@@ -331,7 +331,7 @@ pub enum GenericArg {
     Const(Constant),
     /// A generic argument that's explicitly set to be inferred.
     /// ```text
-    /// std::vec::Vec::<_>::new()
+    /// std::vec::Vec::<_>
     ///                 ^
     /// ```
     Infer,
@@ -362,7 +362,7 @@ pub struct AssocItemConstraint {
     /// The name of the associated type/constant.
     pub name: String,
     /// Arguments provided to the associated type/constant.
-    pub args: GenericArgs,
+    pub args: Option<Box<GenericArgs>>,
     /// The kind of bound applied to the associated type/constant.
     pub binding: AssocItemConstraintKind,
 }
@@ -1118,7 +1118,7 @@ pub enum Type {
         /// <core::slice::IterMut<'static, u32> as BetterIterator>::Item<'static>
         /// //                                                          ^^^^^^^^^
         /// ```
-        args: Box<GenericArgs>,
+        args: Option<Box<GenericArgs>>,
         /// The type with which this type is associated.
         ///
         /// ```ignore (incomplete expression)
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index 8c9e4c8bb3a..0a4051fcbe8 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -271,7 +271,7 @@ impl<'a> Validator<'a> {
             Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),
             Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),
             Type::QualifiedPath { name: _, args, self_type, trait_ } => {
-                self.check_generic_args(&**args);
+                self.check_opt_generic_args(&args);
                 self.check_type(&**self_type);
                 if let Some(trait_) = trait_ {
                     self.check_path(trait_, PathKind::Trait);
@@ -309,13 +309,12 @@ impl<'a> Validator<'a> {
             self.fail(&x.id, ErrorKind::Custom(format!("No entry in '$.paths' for {x:?}")));
         }
 
-        if let Some(args) = &x.args {
-            self.check_generic_args(&**args);
-        }
+        self.check_opt_generic_args(&x.args);
     }
 
-    fn check_generic_args(&mut self, x: &'a GenericArgs) {
-        match x {
+    fn check_opt_generic_args(&mut self, x: &'a Option<Box<GenericArgs>>) {
+        let Some(x) = x else { return };
+        match &**x {
             GenericArgs::AngleBracketed { args, constraints } => {
                 args.iter().for_each(|arg| self.check_generic_arg(arg));
                 constraints.iter().for_each(|bind| self.check_assoc_item_constraint(bind));
@@ -355,7 +354,7 @@ impl<'a> Validator<'a> {
     }
 
     fn check_assoc_item_constraint(&mut self, bind: &'a AssocItemConstraint) {
-        self.check_generic_args(&bind.args);
+        self.check_opt_generic_args(&bind.args);
         match &bind.binding {
             AssocItemConstraintKind::Equality(term) => self.check_term(term),
             AssocItemConstraintKind::Constraint(bounds) => {
diff --git a/tests/rustdoc-json/generic-args.rs b/tests/rustdoc-json/generic-args.rs
new file mode 100644
index 00000000000..0f588820da7
--- /dev/null
+++ b/tests/rustdoc-json/generic-args.rs
@@ -0,0 +1,20 @@
+pub struct MyStruct(u32);
+
+pub trait MyTrait {
+    type MyType;
+    fn my_fn(&self);
+}
+
+impl MyTrait for MyStruct {
+    type MyType = u32;
+    fn my_fn(&self) {}
+}
+
+//@ is "$.index[?(@.name=='my_fn1')].inner.function.sig.inputs[0][1].qualified_path.args" null
+//@ is "$.index[?(@.name=='my_fn1')].inner.function.sig.inputs[0][1].qualified_path.self_type.resolved_path.args" null
+pub fn my_fn1(_: <MyStruct as MyTrait>::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<Item = MyStruct, IntoIter = impl Clone>) {}
+
+fn main() {}