Description
There is currently a problematic inconsistency with regards to the generated rustdoc JSON for tuple structs. If the tuple struct is an enum variant, a "parentless" or "free-floating" item is generated. This does not happen with regular tuple structs.
Consider this code in src/lib.rs
.
pub struct S1(pub usize);
pub enum E1 {
S2(usize)
}
pub struct S3 {
pub s3: usize,
}
pub enum E2 {
S4 {
s4: usize,
}
}
Now we generate the rustdoc JSON (I used rustc 1.60.0-nightly (ad46af247 2022-01-14)
):
RUSTDOCFLAGS='-Z unstable-options --output-format json' cargo doc
The rustdoc JSON output will contain 4 struct_field
items, corresponding to S1.0
, S2.0
, S3.s3
and S4.s4
above.
The JSON item for S1
includes a reference to S1.0
(some parts of the JSON are omitted for brevity):
"0:3": {
"id": "0:3",
"crate_id": 0,
"name": "S1",
"kind": "struct",
"inner": {
"struct_type": "tuple",
"fields": [
"0:5" <-- this is `S1.0`
],
},
And the JSON item for S3
includes a reference to S3.s3
:
"0:10": {
"id": "0:10",
"crate_id": 0,
"name": "S3",
"kind": "struct",
"inner": {
"struct_type": "plain",
"fields": [
"0:11" <-- this is `S3.s3`
],
}
},
And the JSON item for S4
includes a reference to S4.s4
:
"0:13": {
"id": "0:13",
"crate_id": 0,
"name": "S4",
"kind": "variant",
"inner": {
"variant_kind": "struct",
"variant_inner": [
"0:14" <-- this is `S4.s4`
]
}
},
However, and this is the bug, the JSON item for S2
does not contain a reference to S2.0
:
"0:7": {
"id": "0:7",
"crate_id": 0,
"name": "S2",
"kind": "variant",
"inner": {
"variant_kind": "tuple",
"variant_inner": [
{
"kind": "primitive",
"inner": "usize"
}
]
}
},
But an item for S2.0
is still included in the output. Here are all struct_field
items in the JSON output:
% jq '.index | .[] | select(.crate_id == 0) | select(.kind == "struct_field")' target/doc/repro.json
{
"id": "0:11",
"crate_id": 0,
"name": "s3",
"kind": "struct_field",
"inner": {
"kind": "primitive",
"inner": "usize"
}
}
{
"id": "0:14",
"crate_id": 0,
"name": "s4",
"kind": "struct_field",
"inner": {
"kind": "primitive",
"inner": "usize"
}
}
{
"id": "0:9",
"crate_id": 0,
"name": "0",
"kind": "struct_field",
"inner": {
"kind": "primitive",
"inner": "usize"
}
}
{
"id": "0:5",
"crate_id": 0,
"name": "0",
"kind": "struct_field",
"inner": {
"kind": "primitive",
"inner": "usize"
}
}
but "0:9"
is "free-floating" or "parentless", because it is not referenced by any other item. These "free-floating" rustdoc JSON items that are not referenced by any other items are problematic, because tools that processes the rustdoc JSON can't do anything sensible with them. They can't be ignored, because they describe items that are part of the public API of a crate. But they can't be used either, because they can not be put in a context since they are not referenced by anything.
Fortunately, I think fixing this is pretty straightforward. First, let us note that for a regular struct, the fields of it are referenced regardless of if it is a regular struct or a tuple struct (code from https://github.com/rust-lang/rust/blob/master/src/rustdoc-json-types/lib.rs):
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Struct {
pub struct_type: StructType,
pub generics: Generics,
pub fields_stripped: bool,
pub fields: Vec<Id>,
pub impls: Vec<Id>,
}
However, for enum variant structs, fields are only referenced for regular struct variants:
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "variant_kind", content = "variant_inner")]
pub enum Variant {
Plain,
Tuple(Vec<Type>),
Struct(Vec<Id>),
}
Conceptually I think the fix should be to replace Tuple(Vec<Type>),
with Tuple(Vec<Id>),
in the snippet above. But I have not thought this through in depth, it just intuitively seems like the right way to tackle this.