1use std::collections::HashSet;2use std::hash::Hash;34use rustdoc_json_types::{5 AssocItemConstraint, AssocItemConstraintKind, Constant, Crate, DynTrait, Enum, Function,6 FunctionPointer, FunctionSignature, GenericArg, GenericArgs, GenericBound, GenericParamDef,7 Generics, Id, Impl, ItemEnum, ItemSummary, Module, Path, Primitive, ProcMacro, Static, Struct,8 StructKind, Term, Trait, TraitAlias, Type, TypeAlias, Union, Use, Variant, VariantKind,9 WherePredicate,10};11use serde_json::Value;1213use crate::item_kind::Kind;14use crate::{Error, ErrorKind, json_find};1516// This is a rustc implementation detail that we rely on here17const LOCAL_CRATE_ID: u32 = 0;1819/// The Validator walks over the JSON tree, and ensures it is well formed.20/// It is made of several parts.21///22/// - `check_*`: These take a type from [`rustdoc_json_types`], and check that23/// it is well formed. This involves calling `check_*` functions on24/// fields of that item, and `add_*` functions on [`Id`]s.25/// - `add_*`: These add an [`Id`] to the worklist, after validating it to check if26/// the `Id` is a kind expected in this situation.27#[derive(Debug)]28pub struct Validator<'a> {29 pub(crate) errs: Vec<Error>,30 krate: &'a Crate,31 krate_json: Value,32 /// Worklist of Ids to check.33 todo: HashSet<&'a Id>,34 /// Ids that have already been visited, so don't need to be checked again.35 seen_ids: HashSet<&'a Id>,36 /// Ids that have already been reported missing.37 missing_ids: HashSet<&'a Id>,38}3940enum PathKind {41 Trait,42 /// Structs, Enums, Unions and TypeAliases.43 ///44 /// This doesn't include trait's because traits are not types.45 Type,46}4748impl<'a> Validator<'a> {49 pub fn new(krate: &'a Crate, krate_json: Value) -> Self {50 Self {51 krate,52 krate_json,53 errs: Vec::new(),54 seen_ids: HashSet::new(),55 todo: HashSet::new(),56 missing_ids: HashSet::new(),57 }58 }5960 pub fn check_crate(&mut self) {61 // Graph traverse the index62 let root = &self.krate.root;63 self.add_mod_id(root);64 while let Some(id) = set_remove(&mut self.todo) {65 self.seen_ids.insert(id);66 self.check_item(id);67 }6869 let root_crate_id = self.krate.index[root].crate_id;70 assert_eq!(root_crate_id, LOCAL_CRATE_ID, "LOCAL_CRATE_ID is wrong");71 for (id, item_info) in &self.krate.paths {72 self.check_item_info(id, item_info);73 }74 }7576 fn check_items(&mut self, id: &Id, items: &[Id]) {77 let mut visited_ids = HashSet::with_capacity(items.len());7879 for item in items {80 if !visited_ids.insert(item) {81 self.fail(82 id,83 ErrorKind::Custom(format!("Duplicated entry in `items` field: `{item:?}`")),84 );85 }86 }87 }8889 fn check_item(&mut self, id: &'a Id) {90 if let Some(item) = &self.krate.index.get(id) {91 item.links.values().for_each(|id| self.add_any_id(id));9293 match &item.inner {94 ItemEnum::Use(x) => self.check_use(x),95 ItemEnum::Union(x) => self.check_union(x),96 ItemEnum::Struct(x) => self.check_struct(x),97 ItemEnum::StructField(x) => self.check_struct_field(x),98 ItemEnum::Enum(x) => self.check_enum(x),99 ItemEnum::Variant(x) => self.check_variant(x, id),100 ItemEnum::Function(x) => self.check_function(x),101 ItemEnum::Trait(x) => self.check_trait(x, id),102 ItemEnum::TraitAlias(x) => self.check_trait_alias(x),103 ItemEnum::Impl(x) => self.check_impl(x, id),104 ItemEnum::TypeAlias(x) => self.check_type_alias(x),105 ItemEnum::Constant { type_, const_ } => {106 self.check_type(type_);107 self.check_constant(const_);108 }109 ItemEnum::Static(x) => self.check_static(x),110 ItemEnum::ExternType => {} // nop111 ItemEnum::Macro(x) => self.check_macro(x),112 ItemEnum::ProcMacro(x) => self.check_proc_macro(x),113 ItemEnum::Primitive(x) => self.check_primitive_type(x),114 ItemEnum::Module(x) => self.check_module(x, id),115 // FIXME: Why don't these have their own structs?116 ItemEnum::ExternCrate { .. } => {}117 ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_),118 ItemEnum::AssocType { generics, bounds, type_ } => {119 self.check_generics(generics);120 bounds.iter().for_each(|b| self.check_generic_bound(b));121 if let Some(ty) = type_ {122 self.check_type(ty);123 }124 }125 }126 } else {127 assert!(self.krate.paths.contains_key(id));128 }129 }130131 // Core checkers132 fn check_module(&mut self, module: &'a Module, id: &Id) {133 self.check_items(id, &module.items);134 module.items.iter().for_each(|i| self.add_mod_item_id(i));135 }136137 fn check_use(&mut self, x: &'a Use) {138 if x.is_glob {139 self.add_glob_import_item_id(x.id.as_ref().unwrap());140 } else if let Some(id) = &x.id {141 self.add_import_item_id(id);142 }143 }144145 fn check_union(&mut self, x: &'a Union) {146 self.check_generics(&x.generics);147 x.fields.iter().for_each(|i| self.add_field_id(i));148 x.impls.iter().for_each(|i| self.add_impl_id(i));149 }150151 fn check_struct(&mut self, x: &'a Struct) {152 self.check_generics(&x.generics);153 match &x.kind {154 StructKind::Unit => {}155 StructKind::Tuple(fields) => fields.iter().flatten().for_each(|f| self.add_field_id(f)),156 StructKind::Plain { fields, has_stripped_fields: _ } => {157 fields.iter().for_each(|f| self.add_field_id(f))158 }159 }160 x.impls.iter().for_each(|i| self.add_impl_id(i));161 }162163 fn check_struct_field(&mut self, x: &'a Type) {164 self.check_type(x);165 }166167 fn check_enum(&mut self, x: &'a Enum) {168 self.check_generics(&x.generics);169 x.variants.iter().for_each(|i| self.add_variant_id(i));170 x.impls.iter().for_each(|i| self.add_impl_id(i));171 }172173 fn check_variant(&mut self, x: &'a Variant, id: &'a Id) {174 let Variant { kind, discriminant } = x;175176 if let Some(discr) = discriminant {177 if let (Err(_), Err(_)) = (discr.value.parse::<i128>(), discr.value.parse::<u128>()) {178 self.fail(179 id,180 ErrorKind::Custom(format!(181 "Failed to parse discriminant value `{}`",182 discr.value183 )),184 );185 }186 }187188 match kind {189 VariantKind::Plain => {}190 VariantKind::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)),191 VariantKind::Struct { fields, has_stripped_fields: _ } => {192 fields.iter().for_each(|f| self.add_field_id(f))193 }194 }195 }196197 fn check_function(&mut self, x: &'a Function) {198 self.check_generics(&x.generics);199 self.check_function_signature(&x.sig);200 }201202 fn check_trait(&mut self, x: &'a Trait, id: &Id) {203 self.check_items(id, &x.items);204 self.check_generics(&x.generics);205 x.items.iter().for_each(|i| self.add_trait_item_id(i));206 x.bounds.iter().for_each(|i| self.check_generic_bound(i));207 x.implementations.iter().for_each(|i| self.add_impl_id(i));208 }209210 fn check_trait_alias(&mut self, x: &'a TraitAlias) {211 self.check_generics(&x.generics);212 x.params.iter().for_each(|i| self.check_generic_bound(i));213 }214215 fn check_impl(&mut self, x: &'a Impl, id: &Id) {216 self.check_items(id, &x.items);217 self.check_generics(&x.generics);218 if let Some(path) = &x.trait_ {219 self.check_path(path, PathKind::Trait);220 }221 self.check_type(&x.for_);222 x.items.iter().for_each(|i| self.add_trait_item_id(i));223 if let Some(blanket_impl) = &x.blanket_impl {224 self.check_type(blanket_impl)225 }226 }227228 fn check_type_alias(&mut self, x: &'a TypeAlias) {229 self.check_generics(&x.generics);230 self.check_type(&x.type_);231 }232233 fn check_constant(&mut self, _x: &'a Constant) {234 // nop235 }236237 fn check_static(&mut self, x: &'a Static) {238 self.check_type(&x.type_);239 }240241 fn check_macro(&mut self, _: &'a str) {242 // nop243 }244245 fn check_proc_macro(&mut self, _: &'a ProcMacro) {246 // nop247 }248249 fn check_primitive_type(&mut self, x: &'a Primitive) {250 x.impls.iter().for_each(|i| self.add_impl_id(i));251 }252253 fn check_generics(&mut self, x: &'a Generics) {254 x.params.iter().for_each(|p| self.check_generic_param_def(p));255 x.where_predicates.iter().for_each(|w| self.check_where_predicate(w));256 }257258 fn check_type(&mut self, x: &'a Type) {259 match x {260 Type::ResolvedPath(path) => self.check_path(path, PathKind::Type),261 Type::DynTrait(dyn_trait) => self.check_dyn_trait(dyn_trait),262 Type::Generic(_) => {}263 Type::Primitive(_) => {}264 Type::Pat { type_, __pat_unstable_do_not_use: _ } => self.check_type(type_),265 Type::FunctionPointer(fp) => self.check_function_pointer(&**fp),266 Type::Tuple(tys) => tys.iter().for_each(|ty| self.check_type(ty)),267 Type::Slice(inner) => self.check_type(&**inner),268 Type::Array { type_, len: _ } => self.check_type(&**type_),269 Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)),270 Type::Infer => {}271 Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),272 Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),273 Type::QualifiedPath { name: _, args, self_type, trait_ } => {274 self.check_opt_generic_args(&args);275 self.check_type(&**self_type);276 if let Some(trait_) = trait_ {277 self.check_path(trait_, PathKind::Trait);278 }279 }280 }281 }282283 fn check_function_signature(&mut self, x: &'a FunctionSignature) {284 x.inputs.iter().for_each(|(_name, ty)| self.check_type(ty));285 if let Some(output) = &x.output {286 self.check_type(output);287 }288 }289290 fn check_generic_bound(&mut self, x: &'a GenericBound) {291 match x {292 GenericBound::TraitBound { trait_, generic_params, modifier: _ } => {293 self.check_path(trait_, PathKind::Trait);294 generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));295 }296 GenericBound::Outlives(_) => {}297 GenericBound::Use(_) => {}298 }299 }300301 fn check_path(&mut self, x: &'a Path, kind: PathKind) {302 match kind {303 PathKind::Trait => self.add_trait_or_alias_id(&x.id),304 PathKind::Type => self.add_type_id(&x.id),305 }306307 // FIXME: More robust support for checking things in $.index also exist in $.paths308 if !self.krate.paths.contains_key(&x.id) {309 self.fail(&x.id, ErrorKind::Custom(format!("No entry in '$.paths' for {x:?}")));310 }311312 self.check_opt_generic_args(&x.args);313 }314315 fn check_opt_generic_args(&mut self, x: &'a Option<Box<GenericArgs>>) {316 let Some(x) = x else { return };317 match &**x {318 GenericArgs::AngleBracketed { args, constraints } => {319 args.iter().for_each(|arg| self.check_generic_arg(arg));320 constraints.iter().for_each(|bind| self.check_assoc_item_constraint(bind));321 }322 GenericArgs::Parenthesized { inputs, output } => {323 inputs.iter().for_each(|ty| self.check_type(ty));324 if let Some(o) = output {325 self.check_type(o);326 }327 }328 GenericArgs::ReturnTypeNotation => {}329 }330 }331332 fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) {333 match &gpd.kind {334 rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {}335 rustdoc_json_types::GenericParamDefKind::Type { bounds, default, is_synthetic: _ } => {336 bounds.iter().for_each(|b| self.check_generic_bound(b));337 if let Some(ty) = default {338 self.check_type(ty);339 }340 }341 rustdoc_json_types::GenericParamDefKind::Const { type_, default: _ } => {342 self.check_type(type_)343 }344 }345 }346347 fn check_generic_arg(&mut self, arg: &'a GenericArg) {348 match arg {349 GenericArg::Lifetime(_) => {}350 GenericArg::Type(ty) => self.check_type(ty),351 GenericArg::Const(c) => self.check_constant(c),352 GenericArg::Infer => {}353 }354 }355356 fn check_assoc_item_constraint(&mut self, bind: &'a AssocItemConstraint) {357 self.check_opt_generic_args(&bind.args);358 match &bind.binding {359 AssocItemConstraintKind::Equality(term) => self.check_term(term),360 AssocItemConstraintKind::Constraint(bounds) => {361 bounds.iter().for_each(|b| self.check_generic_bound(b))362 }363 }364 }365366 fn check_term(&mut self, term: &'a Term) {367 match term {368 Term::Type(ty) => self.check_type(ty),369 Term::Constant(con) => self.check_constant(con),370 }371 }372373 fn check_where_predicate(&mut self, w: &'a WherePredicate) {374 match w {375 WherePredicate::BoundPredicate { type_, bounds, generic_params } => {376 self.check_type(type_);377 bounds.iter().for_each(|b| self.check_generic_bound(b));378 generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));379 }380 WherePredicate::LifetimePredicate { lifetime: _, outlives: _ } => {381 // nop, all strings.382 }383 WherePredicate::EqPredicate { lhs, rhs } => {384 self.check_type(lhs);385 self.check_term(rhs);386 }387 }388 }389390 fn check_dyn_trait(&mut self, dyn_trait: &'a DynTrait) {391 for pt in &dyn_trait.traits {392 self.check_path(&pt.trait_, PathKind::Trait);393 pt.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));394 }395 }396397 fn check_function_pointer(&mut self, fp: &'a FunctionPointer) {398 self.check_function_signature(&fp.sig);399 fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));400 }401402 fn check_item_info(&mut self, id: &Id, item_info: &ItemSummary) {403 // FIXME: Their should be a better way to determine if an item is local, rather than relying on `LOCAL_CRATE_ID`,404 // which encodes rustc implementation details.405 if item_info.crate_id == LOCAL_CRATE_ID && !self.krate.index.contains_key(id) {406 self.errs.push(Error {407 id: id.clone(),408 kind: ErrorKind::Custom(409 "Id for local item in `paths` but not in `index`".to_owned(),410 ),411 })412 }413 }414415 fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) {416 if let Some(kind) = self.kind_of(id) {417 if valid(kind) {418 if !self.seen_ids.contains(id) {419 self.todo.insert(id);420 }421 } else {422 self.fail_expecting(id, expected);423 }424 } else if !self.missing_ids.contains(id) {425 self.missing_ids.insert(id);426427 let sels = json_find::find_selector(&self.krate_json, &Value::Number(id.0.into()));428 assert_ne!(sels.len(), 0);429430 self.fail(id, ErrorKind::NotFound(sels))431 }432 }433434 fn add_any_id(&mut self, id: &'a Id) {435 self.add_id_checked(id, |_| true, "any kind of item");436 }437438 fn add_field_id(&mut self, id: &'a Id) {439 self.add_id_checked(id, Kind::is_struct_field, "StructField");440 }441442 fn add_mod_id(&mut self, id: &'a Id) {443 self.add_id_checked(id, Kind::is_module, "Module");444 }445 fn add_impl_id(&mut self, id: &'a Id) {446 self.add_id_checked(id, Kind::is_impl, "Impl");447 }448449 fn add_variant_id(&mut self, id: &'a Id) {450 self.add_id_checked(id, Kind::is_variant, "Variant");451 }452453 fn add_trait_or_alias_id(&mut self, id: &'a Id) {454 self.add_id_checked(id, Kind::is_trait_or_alias, "Trait (or TraitAlias)");455 }456457 fn add_type_id(&mut self, id: &'a Id) {458 self.add_id_checked(id, Kind::is_type, "Type (Struct, Enum, Union or TypeAlias)");459 }460461 /// Add an Id that appeared in a trait462 fn add_trait_item_id(&mut self, id: &'a Id) {463 self.add_id_checked(id, Kind::can_appear_in_trait, "Trait inner item");464 }465466 /// Add an Id that can be `use`d467 fn add_import_item_id(&mut self, id: &'a Id) {468 self.add_id_checked(id, Kind::can_appear_in_import, "Import inner item");469 }470471 fn add_glob_import_item_id(&mut self, id: &'a Id) {472 self.add_id_checked(id, Kind::can_appear_in_glob_import, "Glob import inner item");473 }474475 /// Add an Id that appeared in a mod476 fn add_mod_item_id(&mut self, id: &'a Id) {477 self.add_id_checked(id, Kind::can_appear_in_mod, "Module inner item")478 }479480 fn fail_expecting(&mut self, id: &Id, expected: &str) {481 let kind = self.kind_of(id).unwrap(); // We know it has a kind, as it's wrong.482 self.fail(id, ErrorKind::Custom(format!("Expected {expected} but found {kind:?}")));483 }484485 fn fail(&mut self, id: &Id, kind: ErrorKind) {486 self.errs.push(Error { id: id.clone(), kind });487 }488489 fn kind_of(&mut self, id: &Id) -> Option<Kind> {490 if let Some(item) = self.krate.index.get(id) {491 Some(Kind::from_item(item))492 } else if let Some(summary) = self.krate.paths.get(id) {493 Some(Kind::from_summary(summary))494 } else {495 None496 }497 }498}499500fn set_remove<T: Hash + Eq + Clone>(set: &mut HashSet<T>) -> Option<T> {501 if let Some(id) = set.iter().next() {502 let id = id.clone();503 set.take(&id)504 } else {505 None506 }507}508509#[cfg(test)]510mod tests;