diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index f1b9c1acbaec0..c6f55410e4437 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -3,14 +3,17 @@ use std::hash::Hash; use rustdoc_json_types::{ Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs, - GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, Module, OpaqueTy, Path, - Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeBinding, - TypeBindingKind, Typedef, Union, Variant, VariantKind, WherePredicate, + GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module, + OpaqueTy, Path, Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, + Type, TypeBinding, TypeBindingKind, Typedef, Union, Variant, VariantKind, WherePredicate, }; use serde_json::Value; use crate::{item_kind::Kind, json_find, Error, ErrorKind}; +// This is a rustc implementation detail that we rely on here +const LOCAL_CRATE_ID: u32 = 0; + /// The Validator walks over the JSON tree, and ensures it is well formed. /// It is made of several parts. /// @@ -53,12 +56,19 @@ impl<'a> Validator<'a> { } pub fn check_crate(&mut self) { + // Graph traverse the index let root = &self.krate.root; self.add_mod_id(root); while let Some(id) = set_remove(&mut self.todo) { self.seen_ids.insert(id); self.check_item(id); } + + let root_crate_id = self.krate.index[root].crate_id; + assert_eq!(root_crate_id, LOCAL_CRATE_ID, "LOCAL_CRATE_ID is wrong"); + for (id, item_info) in &self.krate.paths { + self.check_item_info(id, item_info); + } } fn check_item(&mut self, id: &'a Id) { @@ -364,6 +374,19 @@ impl<'a> Validator<'a> { fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } + fn check_item_info(&mut self, id: &Id, item_info: &ItemSummary) { + // FIXME: Their should be a better way to determine if an item is local, rather than relying on `LOCAL_CRATE_ID`, + // which encodes rustc implementation details. + if item_info.crate_id == LOCAL_CRATE_ID && !self.krate.index.contains_key(id) { + self.errs.push(Error { + id: id.clone(), + kind: ErrorKind::Custom( + "Id for local item in `paths` but not in `index`".to_owned(), + ), + }) + } + } + fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) { if let Some(kind) = self.kind_of(id) { if valid(kind) { diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index 37b826153efb1..1ef41ff123abf 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use rustdoc_json_types::{Crate, Item, Visibility}; +use rustdoc_json_types::{Crate, Item, ItemKind, ItemSummary, Visibility, FORMAT_VERSION}; use crate::json_find::SelectorPart; @@ -64,3 +64,101 @@ fn errors_on_missing_links() { }], ); } + +// Test we would catch +// https://github.com/rust-lang/rust/issues/104064#issuecomment-1368589718 +#[test] +fn errors_on_local_in_paths_and_not_index() { + let krate = Crate { + root: id("0:0:1572"), + crate_version: None, + includes_private: false, + index: HashMap::from_iter([ + ( + id("0:0:1572"), + Item { + id: id("0:0:1572"), + crate_id: 0, + name: Some("microcore".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: HashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]), + attrs: Vec::new(), + deprecation: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![id("0:1:717")], + is_stripped: false, + }), + }, + ), + ( + id("0:1:717"), + Item { + id: id("0:1:717"), + crate_id: 0, + name: Some("i32".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: HashMap::default(), + attrs: Vec::new(), + deprecation: None, + inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }), + }, + ), + ]), + paths: HashMap::from_iter([( + id("0:1:1571"), + ItemSummary { + crate_id: 0, + path: vec!["microcore".to_owned(), "i32".to_owned()], + kind: ItemKind::Primitive, + }, + )]), + external_crates: HashMap::default(), + format_version: rustdoc_json_types::FORMAT_VERSION, + }; + + check( + &krate, + &[Error { + id: id("0:1:1571"), + kind: ErrorKind::Custom("Id for local item in `paths` but not in `index`".to_owned()), + }], + ); +} + +#[test] +#[should_panic = "LOCAL_CRATE_ID is wrong"] +fn checks_local_crate_id_is_correct() { + let krate = Crate { + root: id("root"), + crate_version: None, + includes_private: false, + index: HashMap::from_iter([( + id("root"), + Item { + id: id("root"), + crate_id: LOCAL_CRATE_ID.wrapping_add(1), + name: Some("irrelavent".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: HashMap::default(), + attrs: Vec::new(), + deprecation: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![], + is_stripped: false, + }), + }, + )]), + paths: HashMap::default(), + external_crates: HashMap::default(), + format_version: FORMAT_VERSION, + }; + check(&krate, &[]); +}