diff options
| author | Nicholas Nethercote <n.nethercote@gmail.com> | 2025-06-14 20:14:51 +1000 |
|---|---|---|
| committer | Nicholas Nethercote <n.nethercote@gmail.com> | 2025-06-21 13:50:52 +1000 |
| commit | 40ba7913fc229ff2d17824fadc3b7c66a931f040 (patch) | |
| tree | 8fe518b2824aa9bf1767213567fbe43a80db500f | |
| parent | 18d742bda07d6f5d047c249cdd533c0e462b3298 (diff) | |
| download | rust-40ba7913fc229ff2d17824fadc3b7c66a931f040.tar.gz rust-40ba7913fc229ff2d17824fadc3b7c66a931f040.zip | |
rustdoc_json: Fix handling of paths with no generic args.
A path without generic args, like `Reader`, currently has JSON produced
like this:
```
{"path":"Reader","id":286,"args":{"angle_bracketed":{"args":[],"constraints":[]}}}
```
Even though `types::Path::args` is `Option` and allows for "no args",
instead it gets represented as "empty args". (More like `Reader<>` than
`Reader`.)
This is due to a problem in `clean::Path::from_clean`. It only produces
`None` if the path is an empty string. This commit changes it to also
produce `None` if there are no generic args. The example above becomes:
```
{"path":"Reader","id":286,"args":null}
```
I looked at a few examples and saw this reduce the size of the JSON
output by 3-9%.
The commit also adds an assertion that non-final segments don't have any
generics; something the old code was implicitly relying on.
Note: the original sin here is that `clean::PathSegment::args` is not an
`Option`, unlike `{ast,hir}::PathSegment::args`. I want to fix that, but
it can be done separately.
| -rw-r--r-- | src/librustdoc/json/conversions.rs | 19 | ||||
| -rw-r--r-- | tests/rustdoc-json/generic-args.rs | 2 |
2 files changed, 19 insertions, 2 deletions
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 8b4be107ace..4719bd5d118 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -579,7 +579,24 @@ 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())); + if final_seg.args.is_empty() { + None + } else { + Some(Box::new(final_seg.args.into_json(renderer))) + } + } else { + None // no generics on any segments because there are no segments + } + }, } } } diff --git a/tests/rustdoc-json/generic-args.rs b/tests/rustdoc-json/generic-args.rs index e48c3329f56..e87c1e62304 100644 --- a/tests/rustdoc-json/generic-args.rs +++ b/tests/rustdoc-json/generic-args.rs @@ -11,7 +11,7 @@ impl MyTrait for MyStruct { } //@ 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.self_type.resolved_path.args" {\"angle_bracketed\":{\"args\":[],\"constraints\":[]}} +//@ 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\":[]}} |
