Repository navigation
Re-export information is lost [rustdoc's json output] #101059
Description
Hi!
I have been playing around with rustdoc's JSON output for the past few weeks and I have now stumbled on a road block that I can't easily sort out on my own.
The problem
I have a function signature that looks like this:
pub fn extract_path(
_inner: pavex_runtime::http::Request<pavex_runtime::hyper::body::Body>,
) -> PathBuf {
todo!()
}where pavex_runtime is a third-party dependency that does nothing more than a public re-export:
//! pavex_runtime/src/lib.rs
pub use hyper;
pub use http;If I look at that function in the JSON output for the relevant crate, I see this item (stripping out irrelevant fields):
{
"id": "0:18:1598",
"crate_id": 0,
"name": "extract_path",
"kind": "function",
"inner": {
"decl": {
"inputs": [
[
"_inner",
{
"kind": "resolved_path",
"inner": {
"name": "pavex_runtime::http::Request",
"id": "31:1361:1602",
}
}
}
]
],
}The name in inner hints at the fact that we depend on http::Request through a re-export via pavex_runtime.
If I follow the id, I instead go directly to the canonical item definition in http:
"31:1361:1602": {
"crate_id": 31,
"path": [
"http",
"request",
"Request"
],
"kind": "struct"
},name seems to be the only place where rustdoc exposes the information that the dependency from my crate to http flows through a re-export in pavex_runtime.
Further experiments seem to suggest that name is set to whatever is used as the type name alias in the function definition.
E.g. by rewriting the definition as
use pavex_runtime::http::Request;
pub fn extract_path(
_inner: Request<pavex_runtime::hyper::body::Body>,
) -> PathBuf {
todo!()
}the name now shows up as Request.
This would imply that there is no way to reliably detect that extract_path (and the crate where it is defined) depend on http through a re-export in pavex_runtime.
What I expect to see
I'd expect the extract_path function item to refer, in its parameter list, to some kind of re-export item kind that is associated with pavex_runtime instead of going directly to http.
E.g. an item of kind "import".
This would allow consumers of the JSON representation to navigate up to the source type (http::Request) while retaining the information that the current crate does not depend on it directly (via pavex_runtime).
Other relevant information
Latest nightly, format version 18.
Activity
Enselic commentedon Aug 27, 2022
Also see #99287 which is the PR that stopped including re-exported items of external crates to fix a bunch of ICEs
Also related: #99513
aDotInTheVoid commentedon Aug 27, 2022
MCVE
src/lib.rs
pavex_runtime/src/lib.rs
http/src/lib.rs
Cargo files
**Cargo.toml** ```toml [package] name = "rust-101059" version = "0.1.0" edition = "2021" [dependencies] pavex_runtime = { path = "./pavex_runtime" } ``` **pavex_runtime/Cargo.toml** ```toml [package] name = "pavex_runtime" edition = "2021" version = "0.1.0" [dependencies] http = { path = "../http" } ``` **http/Cargo.toml** ```toml [package] name = "http" edition = "2021" version = "0.1.0" ```Which produces (abridged)
{ "crate_version": "0.1.0", "external_crates": { "1": {"html_root_url": null, "name": "pavex_runtime"}, "2": {"html_root_url": null, "name": "http"} }, "format_version": 18, "includes_private": false, "index": { "0:0:1570": { "crate_id": 0, "id": "0:0:1570", "inner": {"is_crate": true, "is_stripped": false, "items": ["0:1:1565"]}, "kind": "module", "name": "rust_101059" }, "0:1:1565": { "crate_id": 0, "id": "0:1:1565", "inner": { "decl": { "inputs": [ [ "_req", { "inner": { "id": "2:3:1569", "name": "pavex_runtime::http::Request" }, "kind": "resolved_path" } ] ], "output": null } }, "kind": "function", "name": "extract" } }, "paths": { "0:0:1570": {"crate_id": 0, "kind": "module", "path": ["rust_101059"]}, "0:1:1565": {"crate_id": 0, "kind": "function", "path": ["rust_101059", "extract"]}, "1:0:1567": {"crate_id": 1, "kind": "module", "path": ["pavex_runtime"]}, "2:0:1568": {"crate_id": 2, "kind": "module", "path": ["http"]}, "2:3:1569": {"crate_id": 2, "kind": "struct", "path": ["http", "Request"]} }, "root": "0:0:1570" }full
aDotInTheVoid commentedon Aug 27, 2022
Single crate version:
Produces
{ "crate_version": null, "external_crates": {}, "format_version": 18, "includes_private": false, "index": { "0:0:1570": { "crate_id": 0, "id": "0:0:1570", "inner": {"is_crate": true, "is_stripped": false, "items": ["0:1:1565", "0:4:1567", "0:8:1568"]}, "kind": "module", "name": "single_file" }, "0:1:1565": { "crate_id": 0, "id": "0:1:1565", "inner": {"is_crate": false, "is_stripped": false, "items": ["0:2:1566"]}, "kind": "module", "name": "http" }, "0:2:1566": { "crate_id": 0, "id": "0:2:1566", "inner": {"struct_type": "unit"}, "kind": "struct", "name": "Request" }, "0:4:1567": { "crate_id": 0, "id": "0:4:1567", "inner": {"is_crate": false, "is_stripped": false, "items": ["0:5"]}, "kind": "module", "name": "pavex_runtime" }, "0:5": { "crate_id": 0, "id": "0:5", "inner": {"glob": false, "id": "0:1:1565", "name": "http", "source": "crate::http"}, "kind": "import", "name": null }, "0:8:1568": { "crate_id": 0, "id": "0:8:1568", "inner": { "decl": { "c_variadic": false, "inputs": [ [ "_req", { "inner": {"id": "0:2:1566", "name": "pavex_runtime::http::Request"}, "kind": "resolved_path" } ] ], "output": null } }, "kind": "function", "name": "extract" } }, "paths": { "0:0:1570": {"crate_id": 0, "kind": "module", "path": ["single_file"]}, "0:1:1565": {"crate_id": 0, "kind": "module", "path": ["single_file", "http"]}, "0:2:1566": {"crate_id": 0, "kind": "struct", "path": ["single_file", "http", "Request"]}, "0:4:1567": {"crate_id": 0, "kind": "module", "path": ["single_file", "pavex_runtime"]}, "0:8:1568": {"crate_id": 0, "kind": "function", "path": ["single_file", "extract"]} }, "root": "0:0:1570" }Full
```json { "crate_version": null, "external_crates": {}, "format_version": 18, "includes_private": false, "index": { "0:0:1570": { "attrs": ["#![feature(no_core)]", "#![no_core]"], "crate_id": 0, "deprecation": null, "docs": null, "id": "0:0:1570", "inner": {"is_crate": true, "is_stripped": false, "items": ["0:1:1565", "0:4:1567", "0:8:1568"]}, "kind": "module", "links": {}, "name": "single_file", "span": {"begin": [1, 0], "end": [12, 53], "filename": "single-file.rs"}, "visibility": "public" }, "0:1:1565": { "attrs": [], "crate_id": 0, "deprecation": null, "docs": null, "id": "0:1:1565", "inner": {"is_crate": false, "is_stripped": false, "items": ["0:2:1566"]}, "kind": "module", "links": {}, "name": "http", "span": {"begin": [4, 0], "end": [4, 12], "filename": "single-file.rs"}, "visibility": "public" }, "0:2:1566": { "attrs": [], "crate_id": 0, "deprecation": null, "docs": null, "id": "0:2:1566", "inner": { "fields": [], "fields_stripped": false, "generics": {"params": [], "where_predicates": []}, "impls": [], "struct_type": "unit" }, "kind": "struct", "links": {}, "name": "Request", "span": {"begin": [5, 4], "end": [5, 23], "filename": "single-file.rs"}, "visibility": "public" }, "0:4:1567": { "attrs": [], "crate_id": 0, "deprecation": null, "docs": null, "id": "0:4:1567", "inner": {"is_crate": false, "is_stripped": false, "items": ["0:5"]}, "kind": "module", "links": {}, "name": "pavex_runtime", "span": {"begin": [8, 0], "end": [8, 21], "filename": "single-file.rs"}, "visibility": "public" }, "0:5": { "attrs": [], "crate_id": 0, "deprecation": null, "docs": null, "id": "0:5", "inner": {"glob": false, "id": "0:1:1565", "name": "http", "source": "crate::http"}, "kind": "import", "links": {}, "name": null, "span": {"begin": [9, 4], "end": [9, 24], "filename": "single-file.rs"}, "visibility": "public" }, "0:8:1568": { "attrs": [], "crate_id": 0, "deprecation": null, "docs": null, "id": "0:8:1568", "inner": { "decl": { "c_variadic": false, "inputs": [ [ "_req", { "inner": { "args": {"angle_bracketed": {"args": [], "bindings": []}}, "id": "0:2:1566", "name": "pavex_runtime::http::Request" }, "kind": "resolved_path" } ] ], "output": null }, "generics": {"params": [], "where_predicates": []}, "header": {"abi": "Rust", "async": false, "const": false, "unsafe": false} }, "kind": "function", "links": {}, "name": "extract", "span": {"begin": [12, 0], "end": [12, 53], "filename": "single-file.rs"}, "visibility": "public" } }, "paths": { "0:0:1570": {"crate_id": 0, "kind": "module", "path": ["single_file"]}, "0:1:1565": {"crate_id": 0, "kind": "module", "path": ["single_file", "http"]}, "0:2:1566": {"crate_id": 0, "kind": "struct", "path": ["single_file", "http", "Request"]}, "0:4:1567": {"crate_id": 0, "kind": "module", "path": ["single_file", "pavex_runtime"]}, "0:8:1568": {"crate_id": 0, "kind": "function", "path": ["single_file", "extract"]} }, "root": "0:0:1570" } ```aDotInTheVoid commentedon Aug 27, 2022
One of the issues here is that the only item is
pavex_runtimeis its export ofhttp, but not one for it's export ofhttp::RequestI'm not sure how to solve this without having a item in
pavex_runtimefor every item inhttpthat it exports, instead of just one item for exportinghttpHowever, even when just using the struct, instead of a module, we don't link to the import
{ "crate_version": null, "external_crates": {}, "format_version": 18, "includes_private": false, "index": { "0:0:1570": { "crate_id": 0, "id": "0:0:1570", "inner": {"is_crate": true, "is_stripped": false, "items": ["0:1:1565", "0:4:1567", "0:8:1568"]}, "kind": "module", "name": "single_file_mod" }, "0:1:1565": { "crate_id": 0, "id": "0:1:1565", "inner": {"is_crate": false, "is_stripped": false, "items": ["0:2:1566"]}, "kind": "module", "name": "http" }, "0:2:1566": { "crate_id": 0, "id": "0:2:1566", "inner": {"struct_type": "unit"}, "kind": "struct", "name": "Request" }, "0:4:1567": { "crate_id": 0, "id": "0:4:1567", "inner": {"is_crate": false, "is_stripped": false, "items": ["0:5"]}, "kind": "module", "name": "pavex_runtime" }, "0:5": { "crate_id": 0, "id": "0:5", "inner": {"glob": false, "id": "0:2:1566", "name": "Request", "source": "crate::http::Request"}, "kind": "import", "name": null }, "0:8:1568": { "crate_id": 0, "id": "0:8:1568", "inner": { "decl": { "c_variadic": false, "inputs": [ [ "_req", { "inner": {"id": "0:2:1566", "name": "pavex_runtime::Request"}, "kind": "resolved_path" } ] ], "output": null } }, "kind": "function", "name": "extract" } }, "root": "0:0:1570" }LukeMathWalker commentedon Aug 27, 2022
To an extent, this is what I'd expect to see - an item for every type that is exposed through the public API of the crate via the re-export.
obi1kenobi commentedon Aug 29, 2022
Let's ignore the fact that
httpis quite a stable crate for a second. Say the re-export is for a fictional cratefoo. Also say we're generating rustdoc JSON for a cratebar, which re-exportsfoo.Is there a risk that "list what types are re-exported via
foo" in thebarJSON is sensitive to the specific version offoothat was installed at rustdoc JSON generation time?If
bardoesn't restrict the minor version offoo(the default) then newfoominor versions may cause more things to be accessible via the re-export. That would put thebarrustdoc JSON output somewhere between incorrect and misleading, in my book:bar::foo::Quuxcould compile fine withbar==1.2.3but that version's rustdoc JSON would be allowed to not mentionbar::foo::Quuxwhile mentioning other re-exported types.