about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicholas Nethercote <n.nethercote@gmail.com>2025-06-14 21:39:09 +1000
committerNicholas Nethercote <n.nethercote@gmail.com>2025-06-21 13:52:46 +1000
commit7fa8901cd090093a57723d3f196c27db3b98ad94 (patch)
treefa6a5d9f12aec8ce6746f09a78c12a63d9864ecd
parent59a3399148d02d412485999dee78c0e72a54c0a5 (diff)
downloadrust-7fa8901cd090093a57723d3f196c27db3b98ad94.tar.gz
rust-7fa8901cd090093a57723d3f196c27db3b98ad94.zip
rustdoc_json: represent generic args consistently.
They show up in three places: once as `Option<Box<GenericArgs>>`, once
as `Box<GenericArgs>`, and once as `GenericArgs`. The first option is
best. It is more compact because generic args are often missing. This
commit changes the latter two to the former.

Example output, before and after, for the `AssocItemConstraint` change:
```
{"name":"Offset","args":{"angle_bracketed":{"args":[],"constraints":[]}},"binding":{...}}
{"name":"Offset","args":null,"binding":{...}}
```
Example output, before and after, for the `Type::QualifiedPath` change:
```
{"qualified_path":{"name":"Offset","args":{"angle_bracketed":{"args":[],"constraints":[]}}, ...}}
{"qualified_path":{"name":"Offset","args":null, ...}}
```
This reduces JSON output size, but not by much (e.g. 0.5%), because
`AssocItemConstraint` and `Type::QualifiedPath` are uncommon.
-rw-r--r--src/librustdoc/json/conversions.rs17
-rw-r--r--src/librustdoc/json/mod.rs2
-rw-r--r--src/rustdoc-json-types/lib.rs8
-rw-r--r--src/tools/jsondoclint/src/validator.rs13
-rw-r--r--tests/rustdoc-json/generic-args.rs4
5 files changed, 21 insertions, 23 deletions
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 4719bd5d118..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,
-        }
+        }))
     }
 }
 
@@ -588,11 +591,7 @@ impl FromClean<clean::Path> for Path {
                     // 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()));
-                    if final_seg.args.is_empty() {
-                        None
-                    } else {
-                        Some(Box::new(final_seg.args.into_json(renderer)))
-                    }
+                    final_seg.args.into_json(renderer)
                 } else {
                     None // no generics on any segments because there are no segments
                 }
@@ -607,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 f5064ae55b8..600a4b429f3 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -388,7 +388,7 @@ mod size_asserts {
 
     use super::types::*;
     // tidy-alphabetical-start
-    static_assert_size!(AssocItemConstraint, 208);
+    static_assert_size!(AssocItemConstraint, 112);
     static_assert_size!(Crate, 184);
     static_assert_size!(ExternalCrate, 48);
     static_assert_size!(FunctionPointer, 168);
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 0404f3ff034..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.
 ///
@@ -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
index e87c1e62304..0f588820da7 100644
--- a/tests/rustdoc-json/generic-args.rs
+++ b/tests/rustdoc-json/generic-args.rs
@@ -10,11 +10,11 @@ impl MyTrait for MyStruct {
     fn my_fn(&self) {}
 }
 
-//@ is "$.index[?(@.name=='my_fn1')].inner.function.sig.inputs[0][1].qualified_path.args" {\"angle_bracketed\":{\"args\":[],\"constraints\":[]}}
+//@ 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" {\"angle_bracketed\":{\"args\":[],\"constraints\":[]}}
+//@ 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() {}