Rollup merge of #108626 - ozkanonur:consistent-json-docs, r=aDotInTheVoid

rustdoc-json: switch from HashMap to FxHashMap to fix non-determinism

Using `HashMap` in `rustdoc_json_types::Crate` were causing creating randomly ordered objects in the json doc files. Which might cause problems to people who are doing comparison on those files specially in CI pipelines. See https://github.com/rust-lang/rust/issues/103785#issuecomment-1307425590

This PR fixes that issue and extends the coverage of `tests/run-make/rustdoc-verify-output-files` testing ability.
This commit is contained in:
Matthias Krüger 2023-03-05 14:29:09 +01:00 committed by GitHub
commit 03c1e4d4ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 31 additions and 33 deletions

View File

@ -2287,6 +2287,7 @@ dependencies = [
"anyhow", "anyhow",
"clap 4.1.4", "clap 4.1.4",
"fs-err", "fs-err",
"rustc-hash",
"rustdoc-json-types", "rustdoc-json-types",
"serde", "serde",
"serde_json", "serde_json",
@ -4850,6 +4851,7 @@ dependencies = [
name = "rustdoc-json-types" name = "rustdoc-json-types"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"rustc-hash",
"serde", "serde",
"serde_json", "serde_json",
] ]

View File

@ -8,6 +8,7 @@ path = "lib.rs"
[dependencies] [dependencies]
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
rustc-hash = "1.1.0"
[dev-dependencies] [dev-dependencies]
serde_json = "1.0" serde_json = "1.0"

View File

@ -3,10 +3,9 @@
//! These types are the public API exposed through the `--output-format json` flag. The [`Crate`] //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`]
//! struct is the root of the JSON blob and all other items are contained within. //! struct is the root of the JSON blob and all other items are contained within.
use std::collections::HashMap; use rustc_hash::FxHashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf;
/// rustdoc format-version. /// rustdoc format-version.
pub const FORMAT_VERSION: u32 = 24; pub const FORMAT_VERSION: u32 = 24;
@ -24,11 +23,11 @@ pub struct Crate {
pub includes_private: bool, pub includes_private: bool,
/// A collection of all items in the local crate as well as some external traits and their /// A collection of all items in the local crate as well as some external traits and their
/// items that are referenced locally. /// items that are referenced locally.
pub index: HashMap<Id, Item>, pub index: FxHashMap<Id, Item>,
/// Maps IDs to fully qualified paths and other info helpful for generating links. /// Maps IDs to fully qualified paths and other info helpful for generating links.
pub paths: HashMap<Id, ItemSummary>, pub paths: FxHashMap<Id, ItemSummary>,
/// Maps `crate_id` of items to a crate name and html_root_url if it exists. /// Maps `crate_id` of items to a crate name and html_root_url if it exists.
pub external_crates: HashMap<u32, ExternalCrate>, pub external_crates: FxHashMap<u32, ExternalCrate>,
/// A single version number to be used in the future when making backwards incompatible changes /// A single version number to be used in the future when making backwards incompatible changes
/// to the JSON output. /// to the JSON output.
pub format_version: u32, pub format_version: u32,
@ -54,8 +53,8 @@ pub struct ItemSummary {
/// ///
/// Note that items can appear in multiple paths, and the one chosen is implementation /// Note that items can appear in multiple paths, and the one chosen is implementation
/// defined. Currently, this is the full path to where the item was defined. Eg /// defined. Currently, this is the full path to where the item was defined. Eg
/// [`String`] is currently `["alloc", "string", "String"]` and [`HashMap`] is /// [`String`] is currently `["alloc", "string", "String"]` and [`HashMap`][`std::collections::HashMap`]
/// `["std", "collections", "hash", "map", "HashMap"]`, but this is subject to change. /// is `["std", "collections", "hash", "map", "HashMap"]`, but this is subject to change.
pub path: Vec<String>, pub path: Vec<String>,
/// Whether this item is a struct, trait, macro, etc. /// Whether this item is a struct, trait, macro, etc.
pub kind: ItemKind, pub kind: ItemKind,
@ -80,7 +79,7 @@ pub struct Item {
/// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`). /// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`).
pub docs: Option<String>, pub docs: Option<String>,
/// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
pub links: HashMap<String, Id>, pub links: FxHashMap<String, Id>,
/// Stringified versions of the attributes on this item (e.g. `"#[inline]"`) /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
pub attrs: Vec<String>, pub attrs: Vec<String>,
pub deprecation: Option<Deprecation>, pub deprecation: Option<Deprecation>,

View File

@ -9,6 +9,7 @@ edition = "2021"
anyhow = "1.0.62" anyhow = "1.0.62"
clap = { version = "4.0.15", features = ["derive"] } clap = { version = "4.0.15", features = ["derive"] }
fs-err = "2.8.1" fs-err = "2.8.1"
rustc-hash = "1.1.0"
rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" } rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85" serde_json = "1.0.85"

View File

@ -1,5 +1,4 @@
use std::collections::HashMap; use rustc_hash::FxHashMap;
use rustdoc_json_types::{Crate, Item, ItemKind, ItemSummary, Visibility, FORMAT_VERSION}; use rustdoc_json_types::{Crate, Item, ItemKind, ItemSummary, Visibility, FORMAT_VERSION};
use crate::json_find::SelectorPart; use crate::json_find::SelectorPart;
@ -27,7 +26,7 @@ fn errors_on_missing_links() {
root: id("0"), root: id("0"),
crate_version: None, crate_version: None,
includes_private: false, includes_private: false,
index: HashMap::from_iter([( index: FxHashMap::from_iter([(
id("0"), id("0"),
Item { Item {
name: Some("root".to_owned()), name: Some("root".to_owned()),
@ -36,7 +35,7 @@ fn errors_on_missing_links() {
span: None, span: None,
visibility: Visibility::Public, visibility: Visibility::Public,
docs: None, docs: None,
links: HashMap::from_iter([("Not Found".to_owned(), id("1"))]), links: FxHashMap::from_iter([("Not Found".to_owned(), id("1"))]),
attrs: vec![], attrs: vec![],
deprecation: None, deprecation: None,
inner: ItemEnum::Module(Module { inner: ItemEnum::Module(Module {
@ -46,8 +45,8 @@ fn errors_on_missing_links() {
}), }),
}, },
)]), )]),
paths: HashMap::new(), paths: FxHashMap::default(),
external_crates: HashMap::new(), external_crates: FxHashMap::default(),
format_version: rustdoc_json_types::FORMAT_VERSION, format_version: rustdoc_json_types::FORMAT_VERSION,
}; };
@ -73,7 +72,7 @@ fn errors_on_local_in_paths_and_not_index() {
root: id("0:0:1572"), root: id("0:0:1572"),
crate_version: None, crate_version: None,
includes_private: false, includes_private: false,
index: HashMap::from_iter([ index: FxHashMap::from_iter([
( (
id("0:0:1572"), id("0:0:1572"),
Item { Item {
@ -83,7 +82,7 @@ fn errors_on_local_in_paths_and_not_index() {
span: None, span: None,
visibility: Visibility::Public, visibility: Visibility::Public,
docs: None, docs: None,
links: HashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]), links: FxHashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]),
attrs: Vec::new(), attrs: Vec::new(),
deprecation: None, deprecation: None,
inner: ItemEnum::Module(Module { inner: ItemEnum::Module(Module {
@ -102,14 +101,14 @@ fn errors_on_local_in_paths_and_not_index() {
span: None, span: None,
visibility: Visibility::Public, visibility: Visibility::Public,
docs: None, docs: None,
links: HashMap::default(), links: FxHashMap::default(),
attrs: Vec::new(), attrs: Vec::new(),
deprecation: None, deprecation: None,
inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }), inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }),
}, },
), ),
]), ]),
paths: HashMap::from_iter([( paths: FxHashMap::from_iter([(
id("0:1:1571"), id("0:1:1571"),
ItemSummary { ItemSummary {
crate_id: 0, crate_id: 0,
@ -117,7 +116,7 @@ fn errors_on_local_in_paths_and_not_index() {
kind: ItemKind::Primitive, kind: ItemKind::Primitive,
}, },
)]), )]),
external_crates: HashMap::default(), external_crates: FxHashMap::default(),
format_version: rustdoc_json_types::FORMAT_VERSION, format_version: rustdoc_json_types::FORMAT_VERSION,
}; };
@ -137,7 +136,7 @@ fn checks_local_crate_id_is_correct() {
root: id("root"), root: id("root"),
crate_version: None, crate_version: None,
includes_private: false, includes_private: false,
index: HashMap::from_iter([( index: FxHashMap::from_iter([(
id("root"), id("root"),
Item { Item {
id: id("root"), id: id("root"),
@ -146,7 +145,7 @@ fn checks_local_crate_id_is_correct() {
span: None, span: None,
visibility: Visibility::Public, visibility: Visibility::Public,
docs: None, docs: None,
links: HashMap::default(), links: FxHashMap::default(),
attrs: Vec::new(), attrs: Vec::new(),
deprecation: None, deprecation: None,
inner: ItemEnum::Module(Module { inner: ItemEnum::Module(Module {
@ -156,8 +155,8 @@ fn checks_local_crate_id_is_correct() {
}), }),
}, },
)]), )]),
paths: HashMap::default(), paths: FxHashMap::default(),
external_crates: HashMap::default(), external_crates: FxHashMap::default(),
format_version: FORMAT_VERSION, format_version: FORMAT_VERSION,
}; };
check(&krate, &[]); check(&krate, &[]);

View File

@ -22,15 +22,11 @@ all:
# Check if expected json file is generated # Check if expected json file is generated
[ -e $(OUTPUT_DIR)/foobar.json ] [ -e $(OUTPUT_DIR)/foobar.json ]
# TODO # Copy first json output to check if it's exactly same after second compilation
# We should re-generate json doc once again and compare the diff with previously cp -R $(OUTPUT_DIR)/foobar.json $(TMP_OUTPUT_DIR)/foobar.json
# generated one. Because layout of json docs changes in each compilation, we can't
# do that currently.
#
# See https://github.com/rust-lang/rust/issues/103785#issuecomment-1307425590 for details.
# remove generated json doc # Generate json doc on the same output
rm $(OUTPUT_DIR)/foobar.json $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) -Z unstable-options --output-format json
# Check if json doc compilation broke any of the html files generated previously # Check if all docs(including both json and html formats) are still the same after multiple compilations
$(DIFF) -r -q $(OUTPUT_DIR) $(TMP_OUTPUT_DIR) $(DIFF) -r -q $(OUTPUT_DIR) $(TMP_OUTPUT_DIR)