jsondoclint: Check local items in paths are also in index.

This commit is contained in:
Nixon Enraght-Moony 2023-01-06 23:10:14 +00:00
parent 7bbbaabbb6
commit d4139b36cf
2 changed files with 125 additions and 4 deletions

View File

@ -3,14 +3,17 @@
use rustdoc_json_types::{ use rustdoc_json_types::{
Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs, Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs,
GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, Module, OpaqueTy, Path, GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module,
Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeBinding, OpaqueTy, Path, Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias,
TypeBindingKind, Typedef, Union, Variant, VariantKind, WherePredicate, Type, TypeBinding, TypeBindingKind, Typedef, Union, Variant, VariantKind, WherePredicate,
}; };
use serde_json::Value; use serde_json::Value;
use crate::{item_kind::Kind, json_find, Error, ErrorKind}; 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. /// The Validator walks over the JSON tree, and ensures it is well formed.
/// It is made of several parts. /// It is made of several parts.
/// ///
@ -53,12 +56,19 @@ pub fn new(krate: &'a Crate, krate_json: Value) -> Self {
} }
pub fn check_crate(&mut self) { pub fn check_crate(&mut self) {
// Graph traverse the index
let root = &self.krate.root; let root = &self.krate.root;
self.add_mod_id(root); self.add_mod_id(root);
while let Some(id) = set_remove(&mut self.todo) { while let Some(id) = set_remove(&mut self.todo) {
self.seen_ids.insert(id); self.seen_ids.insert(id);
self.check_item(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) { fn check_item(&mut self, id: &'a Id) {
@ -364,6 +374,19 @@ fn check_function_pointer(&mut self, fp: &'a FunctionPointer) {
fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); 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) { fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) {
if let Some(kind) = self.kind_of(id) { if let Some(kind) = self.kind_of(id) {
if valid(kind) { if valid(kind) {

View File

@ -1,6 +1,6 @@
use std::collections::HashMap; 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; 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, &[]);
}