Historically, it's not been possible to robustly resolve a cross-crate item in rustdoc-json. If you had a `Id` that wasn't in `Crate::index` (because it was defined in a different crate), you could only look it up it `Crate::paths`. But there, you don't get the full information, only an `ItemSummary`. This tells you the `path` and the `crate_id`. But knowing the `crate_id` isn't enough to be able to build/find the rustdoc-json output with this item. It's only use is to get a `ExternalCrate` (via `Crate::external_crates`). But that only tells you the `name` (as a string). This isn't enough to uniquely identify a crate, as there could be multiple versions/features [1] [2]. This was originally proposed to be solved via LukeMathWalker's `--orchestrator-id` proposal (https://www.github.com/rust-lang/compiler-team/issues/635). But that requires invasive changes to cargo/rustc. This PR instead implements Urgau's proposal to re-use the path to a crate's rmeta/rlib as a unique identifer. Callers can use that to determine which package it corresponds to in the language of the build-system above rustc. E.g. for cargo, `cargo rustdoc --message-format=json --output-format=json -Zunstable-options`). (Once you've found the right external crate's rustdoc-json output, you still need to resolve the path->id in that crate. But that's """just""" a matter of walking the module tree. We should probably still make that nicer (by, for example, allowing sharing `Id`s between rustdoc-json document), but that's a future concern) For some notes from RustWeek 2025, where this was designed, see https://hackmd.io/0jkdguobTnW7nXoGKAxfEQ [1]: https://www.github.com/rust-lang/compiler-team/issues/635#issue-1714254865 § Problem [2]: https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/Identifying.20external.20crates.20in.20Rustdoc.20JSON/with/352701211
89 lines
2.7 KiB
Rust
89 lines
2.7 KiB
Rust
use std::path;
|
|
|
|
use run_make_support::rustdoc_json_types::{Crate, ItemEnum, Path, Type, TypeAlias};
|
|
use run_make_support::{cwd, rfs, rust_lib_name, rustc, rustdoc, serde_json};
|
|
|
|
#[track_caller]
|
|
fn canonicalize(p: &path::Path) -> path::PathBuf {
|
|
std::fs::canonicalize(p).expect("path should be canonicalizeable")
|
|
}
|
|
|
|
fn main() {
|
|
rustc().input("trans_dep.rs").edition("2024").crate_type("lib").run();
|
|
|
|
rustc()
|
|
.input("dep.rs")
|
|
.edition("2024")
|
|
.crate_type("lib")
|
|
.extern_("trans_dep", rust_lib_name("trans_dep"))
|
|
.run();
|
|
|
|
rustdoc()
|
|
.input("entry.rs")
|
|
.edition("2024")
|
|
.output_format("json")
|
|
.library_search_path(cwd())
|
|
.extern_("dep", rust_lib_name("dep"))
|
|
.arg("-Zunstable-options")
|
|
.run();
|
|
|
|
let bytes = rfs::read("doc/entry.json");
|
|
|
|
let krate: Crate = serde_json::from_slice(&bytes).expect("output should be valid json");
|
|
|
|
let root_item = &krate.index[&krate.root];
|
|
let ItemEnum::Module(root_mod) = &root_item.inner else { panic!("expected ItemEnum::Module") };
|
|
|
|
assert_eq!(root_mod.items.len(), 2);
|
|
|
|
let items = root_mod.items.iter().map(|id| &krate.index[id]).collect::<Vec<_>>();
|
|
|
|
let from_dep = items
|
|
.iter()
|
|
.filter(|item| item.name.as_deref() == Some("FromDep"))
|
|
.next()
|
|
.expect("there should be en item called FromDep");
|
|
|
|
let from_trans_dep = items
|
|
.iter()
|
|
.filter(|item| item.name.as_deref() == Some("FromTransDep"))
|
|
.next()
|
|
.expect("there should be en item called FromDep");
|
|
|
|
let ItemEnum::TypeAlias(TypeAlias {
|
|
type_: Type::ResolvedPath(Path { id: from_dep_id, .. }),
|
|
..
|
|
}) = &from_dep.inner
|
|
else {
|
|
panic!("Expected FromDep to be a TypeAlias");
|
|
};
|
|
|
|
let ItemEnum::TypeAlias(TypeAlias {
|
|
type_: Type::ResolvedPath(Path { id: from_trans_dep_id, .. }),
|
|
..
|
|
}) = &from_trans_dep.inner
|
|
else {
|
|
panic!("Expected FromDep to be a TypeAlias");
|
|
};
|
|
|
|
assert_eq!(krate.index.get(from_dep_id), None);
|
|
assert_eq!(krate.index.get(from_trans_dep_id), None);
|
|
|
|
let from_dep_externalinfo = &krate.paths[from_dep_id];
|
|
let from_trans_dep_externalinfo = &krate.paths[from_trans_dep_id];
|
|
|
|
let dep_crate_id = from_dep_externalinfo.crate_id;
|
|
let trans_dep_crate_id = from_trans_dep_externalinfo.crate_id;
|
|
|
|
let dep = &krate.external_crates[&dep_crate_id];
|
|
let trans_dep = &krate.external_crates[&trans_dep_crate_id];
|
|
|
|
assert_eq!(dep.name, "dep");
|
|
assert_eq!(trans_dep.name, "trans_dep");
|
|
|
|
assert_eq!(canonicalize(&dep.path), canonicalize(&cwd().join(rust_lib_name("dep"))));
|
|
assert_eq!(
|
|
canonicalize(&trans_dep.path),
|
|
canonicalize(&cwd().join(rust_lib_name("trans_dep")))
|
|
);
|
|
}
|