diff --git a/Cargo.lock b/Cargo.lock index 9dce64ce66ab6..35d1cea30d221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3285,6 +3285,7 @@ dependencies = [ "libc", "object 0.37.3", "regex", + "rustdoc-json-types", "serde_json", "similar", "wasmparser 0.236.1", diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index b020e3d924a46..37d456ae796bd 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -302,6 +302,13 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { ExternalLocation::Remote(s) => Some(s.clone()), _ => None, }, + path: self + .tcx + .used_crate_source(*crate_num) + .paths() + .next() + .expect("crate should have at least 1 path") + .clone(), }, ) }) @@ -339,15 +346,12 @@ mod size_asserts { // tidy-alphabetical-start static_assert_size!(AssocItemConstraint, 112); static_assert_size!(Crate, 184); - static_assert_size!(ExternalCrate, 48); static_assert_size!(FunctionPointer, 168); static_assert_size!(GenericArg, 80); static_assert_size!(GenericArgs, 104); static_assert_size!(GenericBound, 72); static_assert_size!(GenericParamDef, 136); static_assert_size!(Impl, 304); - // `Item` contains a `PathBuf`, which is different sizes on different OSes. - static_assert_size!(Item, 528 + size_of::()); static_assert_size!(ItemSummary, 32); static_assert_size!(PolyTrait, 64); static_assert_size!(PreciseCapturingArg, 32); @@ -355,4 +359,8 @@ mod size_asserts { static_assert_size!(Type, 80); static_assert_size!(WherePredicate, 160); // tidy-alphabetical-end + + // These contains a `PathBuf`, which is different sizes on different OSes. + static_assert_size!(Item, 528 + size_of::()); + static_assert_size!(ExternalCrate, 48 + size_of::()); } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 658d3791d2578..9a59de4f844ab 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -37,8 +37,8 @@ pub type FxHashMap = HashMap; // 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: Add `ItemKind::Attribute`. -pub const FORMAT_VERSION: u32 = 56; +// Latest feature: Add `ExternCrate::path`. +pub const FORMAT_VERSION: u32 = 57; /// The root of the emitted JSON blob. /// @@ -135,6 +135,12 @@ pub struct ExternalCrate { pub name: String, /// The root URL at which the crate's documentation lives. pub html_root_url: Option, + + /// A path from where this crate was loaded. + /// + /// This will typically be a `.rlib` or `.rmeta`. It can be used to determine which crate + /// this was in terms of whatever build-system invoked rustc. + pub path: PathBuf, } /// Information about an external (not defined in the local crate) [`Item`]. diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 250e0f65a9f44..9d9cd656f5700 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -22,6 +22,8 @@ wasmparser = { version = "0.236", default-features = false, features = ["std", " # Shared with bootstrap and compiletest build_helper = { path = "../../build_helper" } +# Shared with rustdoc +rustdoc-json-types = { path = "../../rustdoc-json-types" } [lib] crate-type = ["lib", "dylib"] diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index fef75401d9456..5253dc04a93e2 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -34,7 +34,7 @@ pub mod rfs { } // Re-exports of third-party library crates. -pub use {bstr, gimli, libc, object, regex, serde_json, similar, wasmparser}; +pub use {bstr, gimli, libc, object, regex, rustdoc_json_types, serde_json, similar, wasmparser}; // Helpers for building names of output artifacts that are potentially target-specific. pub use crate::artifact_names::{ diff --git a/tests/run-make/rustdoc-json-external-crate-path/dep.rs b/tests/run-make/rustdoc-json-external-crate-path/dep.rs new file mode 100644 index 0000000000000..25c5dcac9f755 --- /dev/null +++ b/tests/run-make/rustdoc-json-external-crate-path/dep.rs @@ -0,0 +1,5 @@ +#![no_std] + +pub struct S; + +pub use trans_dep::S as TransDep; diff --git a/tests/run-make/rustdoc-json-external-crate-path/entry.rs b/tests/run-make/rustdoc-json-external-crate-path/entry.rs new file mode 100644 index 0000000000000..5e0d8610e7ad6 --- /dev/null +++ b/tests/run-make/rustdoc-json-external-crate-path/entry.rs @@ -0,0 +1,4 @@ +#![no_std] + +pub type FromDep = dep::S; +pub type FromTransDep = dep::TransDep; diff --git a/tests/run-make/rustdoc-json-external-crate-path/rmake.rs b/tests/run-make/rustdoc-json-external-crate-path/rmake.rs new file mode 100644 index 0000000000000..40790e56cab51 --- /dev/null +++ b/tests/run-make/rustdoc-json-external-crate-path/rmake.rs @@ -0,0 +1,89 @@ +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::>(); + + 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"))) + ); +} diff --git a/tests/run-make/rustdoc-json-external-crate-path/trans_dep.rs b/tests/run-make/rustdoc-json-external-crate-path/trans_dep.rs new file mode 100644 index 0000000000000..8f185393a7c3a --- /dev/null +++ b/tests/run-make/rustdoc-json-external-crate-path/trans_dep.rs @@ -0,0 +1,3 @@ +#![no_std] + +pub struct S;