From 2506aa0394fa1f03d09a40cdabc5ade1215b0969 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Tue, 23 Aug 2022 14:49:37 +0100 Subject: [PATCH 01/11] jsondoclint: New Tool --- Cargo.lock | 4 ++++ Cargo.toml | 1 + src/bootstrap/test.rs | 2 ++ src/bootstrap/tool.rs | 1 + src/tools/compiletest/src/common.rs | 3 +++ src/tools/compiletest/src/main.rs | 2 ++ src/tools/compiletest/src/runtest.rs | 8 ++++++++ src/tools/jsondoclint/Cargo.toml | 8 ++++++++ src/tools/jsondoclint/src/main.rs | 1 + triagebot.toml | 2 ++ 10 files changed, 32 insertions(+) create mode 100644 src/tools/jsondoclint/Cargo.toml create mode 100644 src/tools/jsondoclint/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4e0e72d34153f..93b622627b417 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1891,6 +1891,10 @@ dependencies = [ "shlex", ] +[[package]] +name = "jsondoclint" +version = "0.1.0" + [[package]] name = "jsonpath_lib" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index 5753730053f4c..e49fe5e2f6356 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ members = [ "src/tools/unicode-table-generator", "src/tools/expand-yaml-anchors", "src/tools/jsondocck", + "src/tools/jsondoclint", "src/tools/html-checker", "src/tools/bump-stage0", "src/tools/replace-version-placeholder", diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index f61c95830858f..9d286ddd6d164 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1341,6 +1341,8 @@ note: if you're sure you want to do this, please open an issue as to why. In the let json_compiler = compiler.with_stage(0); cmd.arg("--jsondocck-path") .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target })); + cmd.arg("--jsondoclint-path") + .arg(builder.ensure(tool::JsonDocLint { compiler: json_compiler, target })); } if mode == "run-make" { diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index c3b04a9bbce3f..7d4ed24b64850 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -376,6 +376,7 @@ bootstrap_tool!( ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; LintDocs, "src/tools/lint-docs", "lint-docs"; JsonDocCk, "src/tools/jsondocck", "jsondocck"; + JsonDocLint, "src/tools/jsondoclint", "jsondoclint"; HtmlChecker, "src/tools/html-checker", "html-checker"; BumpStage0, "src/tools/bump-stage0", "bump-stage0"; ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder"; diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 6f17b9e1be9a3..64df76e27720d 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -203,6 +203,9 @@ pub struct Config { /// The jsondocck executable. pub jsondocck_path: Option, + /// The jsondoclint executable. + pub jsondoclint_path: Option, + /// The LLVM `FileCheck` binary path. pub llvm_filecheck: Option, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 0e2cc52a645bc..38c7b87fc0d9d 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -64,6 +64,7 @@ pub fn parse_config(args: Vec) -> Config { .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH") .reqopt("", "python", "path to python to use for doc tests", "PATH") .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH") + .optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH") .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM") .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind") .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH") @@ -226,6 +227,7 @@ pub fn parse_config(args: Vec) -> Config { rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from), python: matches.opt_str("python").unwrap(), jsondocck_path: matches.opt_str("jsondocck-path"), + jsondoclint_path: matches.opt_str("jsondoclint-path"), valgrind_path: matches.opt_str("valgrind-path"), force_valgrind: matches.opt_present("force-valgrind"), run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index e2afa5ef59020..9cbb6b7c393bd 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2572,6 +2572,14 @@ impl<'test> TestCx<'test> { if !res.status.success() { self.fatal_proc_rec("check_missing_items failed!", &res); } + + let res = self.cmd2procres( + Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out), + ); + + if !res.status.success() { + self.fatal_proc_rec("jsondoclint failed!", &res); + } } fn get_lines>( diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml new file mode 100644 index 0000000000000..42bb77fde41af --- /dev/null +++ b/src/tools/jsondoclint/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "jsondoclint" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/jsondoclint/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/triagebot.toml b/triagebot.toml index 4b2dcc246e4ee..11caedbb9597a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -132,6 +132,7 @@ trigger_files = [ "src/etc/htmldocck.py", "src/etc/check_missing_items.py", "src/tools/jsondocck", + "src/tools/jsondoclint", "src/tools/rustdoc-gui", "src/tools/rustdoc-js", "src/tools/rustdoc-themes", @@ -147,6 +148,7 @@ trigger_files = [ "src/rustdoc-json-types", "src/test/rustdoc-json", "src/tools/jsondocck", + "src/tools/jsondoclint", ] [autolabel."T-compiler"] From a7a4fe9ffac69ec96b8ba6ec3da8fd4673dfef6b Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 17:32:39 +0100 Subject: [PATCH 02/11] jsondoclint: Tree Walk Validator --- Cargo.lock | 18 +- src/tools/jsondoclint/Cargo.toml | 4 + src/tools/jsondoclint/src/item_kind.rs | 29 ++ src/tools/jsondoclint/src/main.rs | 34 ++- src/tools/jsondoclint/src/validator.rs | 391 +++++++++++++++++++++++++ 5 files changed, 469 insertions(+), 7 deletions(-) create mode 100644 src/tools/jsondoclint/src/item_kind.rs create mode 100644 src/tools/jsondoclint/src/validator.rs diff --git a/Cargo.lock b/Cargo.lock index 93b622627b417..69f96bcbe63b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.60" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c794e162a5eff65c72ef524dfe393eb923c354e350bb78b9c7383df13f3bc142" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "array_tool" @@ -1362,9 +1362,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "2.5.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431" +checksum = "64db3e262960f0662f43a6366788d5f10f7f244b8f7d7d987f560baf5ded5c50" [[package]] name = "fs_extra" @@ -1894,6 +1894,12 @@ dependencies = [ [[package]] name = "jsondoclint" version = "0.1.0" +dependencies = [ + "anyhow", + "fs-err", + "rustdoc-json-types", + "serde_json", +] [[package]] name = "jsonpath_lib" @@ -4449,9 +4455,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "indexmap", "itoa", diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index 42bb77fde41af..84a6c7f96c464 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -6,3 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.62" +fs-err = "2.8.1" +rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" } +serde_json = "1.0.85" diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs new file mode 100644 index 0000000000000..7c6c0b5437928 --- /dev/null +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -0,0 +1,29 @@ +use rustdoc_json_types::ItemEnum; + +pub(crate) fn can_appear_in_mod(kind: &ItemEnum) -> bool { + match kind { + ItemEnum::Module(_) => true, + ItemEnum::ExternCrate { .. } => true, + ItemEnum::Import(_) => true, + ItemEnum::Union(_) => true, + ItemEnum::Struct(_) => true, + ItemEnum::StructField(_) => false, // Only in structs or variants + ItemEnum::Enum(_) => true, + ItemEnum::Variant(_) => false, // Only in enums + ItemEnum::Function(_) => true, + ItemEnum::Trait(_) => true, + ItemEnum::TraitAlias(_) => true, + ItemEnum::Method(_) => false, // Only in traits + ItemEnum::Impl(_) => true, + ItemEnum::Typedef(_) => true, + ItemEnum::OpaqueTy(_) => todo!("IDK"), // On + ItemEnum::Constant(_) => true, + ItemEnum::Static(_) => true, + ItemEnum::ForeignType => todo!("IDK"), + ItemEnum::Macro(_) => true, + ItemEnum::ProcMacro(_) => true, + ItemEnum::PrimitiveType(_) => todo!("IDK"), + ItemEnum::AssocConst { .. } => false, // Trait Only + ItemEnum::AssocType { .. } => false, // Trait only + } +} diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index f328e4d9d04c3..b98601f7d89f9 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -1 +1,33 @@ -fn main() {} +use std::env; + +use anyhow::{anyhow, bail, Result}; +use fs_err as fs; +use rustdoc_json_types::{Crate, Id, FORMAT_VERSION}; + +pub(crate) mod item_kind; +mod validator; + +#[derive(Debug)] +struct Error { + message: String, + id: Id, +} + +fn main() -> Result<()> { + let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; + let contents = fs::read_to_string(path)?; + let krate: Crate = serde_json::from_str(&contents)?; + assert_eq!(krate.format_version, FORMAT_VERSION); + + let mut validator = validator::Validator::new(&krate); + validator.check_crate(); + + if !validator.errs.is_empty() { + for err in validator.errs { + eprintln!("`{}`: `{}`", err.id.0, err.message); + } + bail!("Errors validating json"); + } + + Ok(()) +} diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs new file mode 100644 index 0000000000000..2bb63bc3a44e5 --- /dev/null +++ b/src/tools/jsondoclint/src/validator.rs @@ -0,0 +1,391 @@ +use std::collections::HashSet; +use std::hash::Hash; + +use rustdoc_json_types::{ + Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs, + GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, Method, Module, OpaqueTy, + Path, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeBinding, + TypeBindingKind, Typedef, Union, Variant, WherePredicate, +}; + +use crate::{item_kind::can_appear_in_mod, Error}; + +#[derive(Debug)] +pub struct Validator<'a> { + pub(crate) errs: Vec, + krate: &'a Crate, + seen_ids: HashSet<&'a Id>, + todo: HashSet<&'a Id>, +} + +fn set_remove(set: &mut HashSet) -> Option { + if let Some(id) = set.iter().next() { + let id = id.clone(); + set.take(&id) + } else { + None + } +} + +impl<'a> Validator<'a> { + pub fn new(krate: &'a Crate) -> Self { + Self { krate, errs: Vec::new(), seen_ids: HashSet::new(), todo: HashSet::new() } + } + + pub fn check_crate(&mut self) { + 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); + } + } + + fn check_item(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + match &item.inner { + ItemEnum::Import(x) => self.check_import(x), + ItemEnum::Union(x) => self.check_union(x), + ItemEnum::Struct(x) => self.check_struct(x), + ItemEnum::StructField(x) => self.check_struct_field(x), + ItemEnum::Enum(x) => self.check_enum(x), + ItemEnum::Variant(x) => self.check_variant(x), + ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Trait(x) => self.check_trait(x), + ItemEnum::TraitAlias(x) => self.check_trait_alias(x), + ItemEnum::Method(x) => self.check_method(x), + ItemEnum::Impl(x) => self.check_impl(x), + ItemEnum::Typedef(x) => self.check_typedef(x), + ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), + ItemEnum::Constant(x) => self.check_constant(x), + ItemEnum::Static(x) => self.check_static(x), + ItemEnum::ForeignType => todo!(), + ItemEnum::Macro(x) => self.check_macro(x), + ItemEnum::ProcMacro(x) => self.check_proc_macro(x), + ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), + ItemEnum::Module(x) => self.check_module(x), + + ItemEnum::ExternCrate { .. } => todo!(), + ItemEnum::AssocConst { .. } => todo!(), + ItemEnum::AssocType { .. } => todo!(), + } + } + + // Core checkers + fn check_module(&mut self, module: &'a Module) { + module.items.iter().for_each(|i| self.add_mod_item_id(i)); + } + + fn check_import(&mut self, x: &'a Import) { + if x.glob { + self.add_mod_id(x.id.as_ref().unwrap()); + } else if let Some(id) = &x.id { + self.add_mod_item_id(id); + } + } + + fn check_union(&mut self, x: &'a Union) { + self.check_generics(&x.generics); + x.fields.iter().for_each(|i| self.add_field_id(i)); + x.impls.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_struct(&mut self, x: &'a Struct) { + self.check_generics(&x.generics); + match &x.kind { + StructKind::Unit => {} + StructKind::Tuple(fields) => fields.iter().flatten().for_each(|f| self.add_field_id(f)), + StructKind::Plain { fields, fields_stripped: _ } => { + fields.iter().for_each(|f| self.add_field_id(f)) + } + } + x.impls.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_struct_field(&mut self, x: &'a Type) { + self.check_type(x); + } + + fn check_enum(&mut self, x: &'a Enum) { + self.check_generics(&x.generics); + x.variants.iter().for_each(|i| self.add_variant_id(i)); + x.impls.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_variant(&mut self, x: &'a Variant) { + match x { + Variant::Plain(_discriminant) => {} // TODO: Check discriminant value parses + Variant::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)), + Variant::Struct { fields, fields_stripped: _ } => { + fields.iter().for_each(|f| self.add_field_id(f)) + } + } + } + + fn check_function(&mut self, x: &'a Function) { + self.check_generics(&x.generics); + self.check_fn_decl(&x.decl); + } + + fn check_trait(&mut self, x: &'a Trait) { + self.check_generics(&x.generics); + x.items.iter().for_each(|i| self.add_trait_item_id(i)); + x.bounds.iter().for_each(|i| self.check_generic_bound(i)); + x.implementations.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_trait_alias(&mut self, x: &'a TraitAlias) { + self.check_generics(&x.generics); + x.params.iter().for_each(|i| self.check_generic_bound(i)); + } + + fn check_method(&mut self, x: &'a Method) { + self.check_fn_decl(&x.decl); + self.check_generics(&x.generics); + } + + fn check_impl(&mut self, x: &'a Impl) { + self.check_generics(&x.generics); + if let Some(path) = &x.trait_ { + self.check_path(path); // TODO: Check is trait. + } + self.check_type(&x.for_); + x.items.iter().for_each(|i| self.add_trait_item_id(i)); + if let Some(blanket_impl) = &x.blanket_impl { + self.check_type(blanket_impl) + } + } + + fn check_typedef(&mut self, x: &'a Typedef) { + self.check_generics(&x.generics); + self.check_type(&x.type_); + } + + fn check_opaque_ty(&mut self, x: &'a OpaqueTy) { + x.bounds.iter().for_each(|b| self.check_generic_bound(b)); + self.check_generics(&x.generics); + } + + fn check_constant(&mut self, x: &'a Constant) { + self.check_type(&x.type_); + } + + fn check_static(&mut self, x: &'a Static) { + self.check_type(&x.type_); + } + + fn check_macro(&mut self, _: &'a str) { + // TODO + } + + fn check_proc_macro(&mut self, _: &'a ProcMacro) { + // TODO + } + + fn check_primitive_type(&mut self, _: &'a str) { + // TODO + } + + fn check_generics(&mut self, x: &'a Generics) { + x.params.iter().for_each(|p| self.check_generic_param_def(p)); + x.where_predicates.iter().for_each(|w| self.check_where_predicate(w)); + } + + fn check_type(&mut self, x: &'a Type) { + match x { + Type::ResolvedPath(path) => self.check_path(path), + Type::DynTrait(dyn_trait) => self.check_dyn_trait(dyn_trait), + Type::Generic(_) => {} + Type::Primitive(_) => {} + Type::FunctionPointer(fp) => self.check_function_pointer(&**fp), + Type::Tuple(tys) => tys.iter().for_each(|ty| self.check_type(ty)), + Type::Slice(inner) => self.check_type(&**inner), + Type::Array { type_, len: _ } => self.check_type(&**type_), + Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)), + Type::Infer => {} + Type::RawPointer { mutable: _, type_ } => self.check_type(&**type_), + Type::BorrowedRef { lifetime: _, mutable: _, type_ } => self.check_type(&**type_), + Type::QualifiedPath { name: _, args, self_type, trait_ } => { + self.check_generic_args(&**args); + self.check_type(&**self_type); + self.check_path(trait_); + } + } + } + + fn check_fn_decl(&mut self, x: &'a FnDecl) { + x.inputs.iter().for_each(|(_name, ty)| self.check_type(ty)); + if let Some(output) = &x.output { + self.check_type(output); + } + } + + fn check_generic_bound(&mut self, x: &'a GenericBound) { + match x { + GenericBound::TraitBound { trait_, generic_params, modifier: _ } => { + self.check_path(trait_); + generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + GenericBound::Outlives(_) => todo!(), + } + } + + fn check_path(&mut self, x: &'a Path) { + self.add_id(&x.id); // TODO: What kinds are allowed here. + if let Some(args) = &x.args { + self.check_generic_args(&**args); + } + } + + fn check_generic_args(&mut self, x: &'a GenericArgs) { + match x { + GenericArgs::AngleBracketed { args, bindings } => { + args.iter().for_each(|arg| self.check_generic_arg(arg)); + bindings.iter().for_each(|bind| self.check_type_binding(bind)); + } + GenericArgs::Parenthesized { inputs, output } => { + inputs.iter().for_each(|ty| self.check_type(ty)); + if let Some(o) = output { + self.check_type(o); + } + } + } + } + + fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) { + match &gpd.kind { + rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {} + rustdoc_json_types::GenericParamDefKind::Type { bounds, default, synthetic: _ } => { + bounds.iter().for_each(|b| self.check_generic_bound(b)); + if let Some(ty) = default { + self.check_type(ty); + } + } + rustdoc_json_types::GenericParamDefKind::Const { type_, default: _ } => { + self.check_type(type_) + } + } + } + + fn check_generic_arg(&mut self, arg: &'a GenericArg) { + match arg { + GenericArg::Lifetime(_) => {} + GenericArg::Type(ty) => self.check_type(ty), + GenericArg::Const(c) => self.check_constant(c), + GenericArg::Infer => {} + } + } + + fn check_type_binding(&mut self, bind: &'a TypeBinding) { + self.check_generic_args(&bind.args); + match &bind.binding { + TypeBindingKind::Equality(term) => self.check_term(term), + TypeBindingKind::Constraint(bounds) => { + bounds.iter().for_each(|b| self.check_generic_bound(b)) + } + } + } + + fn check_term(&mut self, term: &'a Term) { + match term { + Term::Type(ty) => self.check_type(ty), + Term::Constant(con) => self.check_constant(con), + } + } + + fn check_where_predicate(&mut self, w: &'a WherePredicate) { + match w { + WherePredicate::BoundPredicate { type_, bounds, generic_params } => { + self.check_type(type_); + bounds.iter().for_each(|b| self.check_generic_bound(b)); + generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + WherePredicate::RegionPredicate { lifetime: _, bounds } => { + bounds.iter().for_each(|b| self.check_generic_bound(b)); + } + WherePredicate::EqPredicate { lhs, rhs } => { + self.check_type(lhs); + self.check_term(rhs); + } + } + } + + fn check_dyn_trait(&mut self, dyn_trait: &'a DynTrait) { + for pt in &dyn_trait.traits { + self.check_path(&pt.trait_); + pt.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + } + + fn check_function_pointer(&mut self, fp: &'a FunctionPointer) { + self.check_fn_decl(&fp.decl); + fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + + // Aux functions + fn add_id(&mut self, id: &'a Id) { + if !self.seen_ids.contains(id) { + self.todo.insert(id); + } + } + + fn add_field_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::StructField(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting field"); + } + } + + fn add_mod_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::Module(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting module"); + } + } + + fn add_impl_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::StructField(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting impl"); + } + } + + fn add_variant_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::StructField(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting variant"); + } + } + + /// Add an Id that appeared in a trait + fn add_trait_item_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if !can_appear_in_mod(&item.inner) { + self.fail(id, "Expecting item that can appear in trait"); + } else { + self.add_id(id); + } + } + + /// Add an Id that appeared in a mod + fn add_mod_item_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if can_appear_in_mod(&item.inner) { + self.add_id(id); + } else { + self.fail(id, "Expecting item that can appear in trait"); + } + } + + fn fail(&mut self, id: &Id, msg: &str) { + self.errs.push(Error { id: id.clone(), message: msg.to_string() }); + } +} From bb1911db393047382ae040c23598e25984244644 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 19:44:32 +0100 Subject: [PATCH 03/11] jsondoclint: Add `Kind` abstraction --- src/tools/jsondoclint/src/item_kind.rs | 205 +++++++++++++++++++++---- src/tools/jsondoclint/src/validator.rs | 74 +++++---- 2 files changed, 213 insertions(+), 66 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 7c6c0b5437928..15866ab6950f3 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -1,29 +1,180 @@ -use rustdoc_json_types::ItemEnum; - -pub(crate) fn can_appear_in_mod(kind: &ItemEnum) -> bool { - match kind { - ItemEnum::Module(_) => true, - ItemEnum::ExternCrate { .. } => true, - ItemEnum::Import(_) => true, - ItemEnum::Union(_) => true, - ItemEnum::Struct(_) => true, - ItemEnum::StructField(_) => false, // Only in structs or variants - ItemEnum::Enum(_) => true, - ItemEnum::Variant(_) => false, // Only in enums - ItemEnum::Function(_) => true, - ItemEnum::Trait(_) => true, - ItemEnum::TraitAlias(_) => true, - ItemEnum::Method(_) => false, // Only in traits - ItemEnum::Impl(_) => true, - ItemEnum::Typedef(_) => true, - ItemEnum::OpaqueTy(_) => todo!("IDK"), // On - ItemEnum::Constant(_) => true, - ItemEnum::Static(_) => true, - ItemEnum::ForeignType => todo!("IDK"), - ItemEnum::Macro(_) => true, - ItemEnum::ProcMacro(_) => true, - ItemEnum::PrimitiveType(_) => todo!("IDK"), - ItemEnum::AssocConst { .. } => false, // Trait Only - ItemEnum::AssocType { .. } => false, // Trait only +use rustdoc_json_types::{Item, ItemEnum, ItemKind, ItemSummary}; + +// We want a univeral way to represent an `ItemEnum` or `ItemKind` + +#[derive(Debug)] +pub(crate) enum Kind { + Module, + ExternCrate, + Import, + Struct, + StructField, + Union, + Enum, + Variant, + Function, + Typedef, + OpaqueTy, + Constant, + Trait, + TraitAlias, + Method, + Impl, + Static, + ForeignType, + Macro, + ProcAttribute, + ProcDerive, + AssocConst, + AssocType, + Primitive, + Keyword, + // Not in ItemKind + ProcMacro, + PrimitiveType, +} + +impl Kind { + pub fn can_appear_in_mod(self) -> bool { + use Kind::*; + match self { + Module => true, + ExternCrate => true, + Import => true, + Union => true, + Struct => true, + Enum => true, + Function => true, + Trait => true, + TraitAlias => true, + Impl => true, + Typedef => true, + Constant => true, + Static => true, + Macro => true, + ProcMacro => true, + + ForeignType => todo!("IDK"), + Keyword => todo!("IDK"), + OpaqueTy => todo!("IDK"), + Primitive => todo!("IDK"), + PrimitiveType => todo!("IDK"), + ProcAttribute => todo!("IDK"), + ProcDerive => todo!("IDK"), + + // Only in traits + AssocConst => false, + AssocType => false, + Method => false, + + StructField => false, // Only in structs or variants + Variant => false, // Only in enums + } + } + + pub fn can_appear_in_trait(self) -> bool { + match self { + Kind::AssocConst => true, + Kind::AssocType => true, + Kind::Method => true, + + Kind::Module => false, + Kind::ExternCrate => false, + Kind::Import => false, + Kind::Struct => false, + Kind::StructField => false, + Kind::Union => false, + Kind::Enum => false, + Kind::Variant => false, + Kind::Function => false, + Kind::Typedef => false, + Kind::OpaqueTy => false, + Kind::Constant => false, + Kind::Trait => false, + Kind::TraitAlias => false, + Kind::Impl => false, + Kind::Static => false, + Kind::ForeignType => false, + Kind::Macro => false, + Kind::ProcAttribute => false, + Kind::ProcDerive => false, + Kind::Primitive => false, + Kind::Keyword => false, + Kind::ProcMacro => false, + Kind::PrimitiveType => false, + } + } + + pub fn is_struct_field(self) -> bool { + matches!(self, Kind::StructField) + } + pub fn is_module(self) -> bool { + matches!(self, Kind::Module) + } + pub fn is_impl(self) -> bool { + matches!(self, Kind::Impl) + } + pub fn is_variant(self) -> bool { + matches!(self, Kind::Variant) + } + + pub fn from_item(i: &Item) -> Self { + use Kind::*; + match i.inner { + ItemEnum::Module(_) => Module, + ItemEnum::Import(_) => Import, + ItemEnum::Union(_) => Union, + ItemEnum::Struct(_) => Struct, + ItemEnum::StructField(_) => StructField, + ItemEnum::Enum(_) => Enum, + ItemEnum::Variant(_) => Variant, + ItemEnum::Function(_) => Function, + ItemEnum::Trait(_) => Trait, + ItemEnum::TraitAlias(_) => TraitAlias, + ItemEnum::Method(_) => Method, + ItemEnum::Impl(_) => Impl, + ItemEnum::Typedef(_) => Typedef, + ItemEnum::OpaqueTy(_) => OpaqueTy, + ItemEnum::Constant(_) => Constant, + ItemEnum::Static(_) => Static, + ItemEnum::Macro(_) => Macro, + ItemEnum::ProcMacro(_) => ProcMacro, + ItemEnum::PrimitiveType(_) => PrimitiveType, + ItemEnum::ForeignType => ForeignType, + ItemEnum::ExternCrate { .. } => ExternCrate, + ItemEnum::AssocConst { .. } => AssocConst, + ItemEnum::AssocType { .. } => AssocType, + } + } + + pub fn from_summary(s: &ItemSummary) -> Self { + use Kind::*; + match s.kind { + ItemKind::AssocConst => AssocConst, + ItemKind::AssocType => AssocType, + ItemKind::Constant => Constant, + ItemKind::Enum => Enum, + ItemKind::ExternCrate => ExternCrate, + ItemKind::ForeignType => ForeignType, + ItemKind::Function => Function, + ItemKind::Impl => Impl, + ItemKind::Import => Import, + ItemKind::Keyword => Keyword, + ItemKind::Macro => Macro, + ItemKind::Method => Method, + ItemKind::Module => Module, + ItemKind::OpaqueTy => OpaqueTy, + ItemKind::Primitive => Primitive, + ItemKind::ProcAttribute => ProcAttribute, + ItemKind::ProcDerive => ProcDerive, + ItemKind::Static => Static, + ItemKind::Struct => Struct, + ItemKind::StructField => StructField, + ItemKind::Trait => Trait, + ItemKind::TraitAlias => TraitAlias, + ItemKind::Typedef => Typedef, + ItemKind::Union => Union, + ItemKind::Variant => Variant, + } } } diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 2bb63bc3a44e5..b0e12479f92c6 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -8,7 +8,7 @@ use rustdoc_json_types::{ TypeBindingKind, Typedef, Union, Variant, WherePredicate, }; -use crate::{item_kind::can_appear_in_mod, Error}; +use crate::{item_kind::Kind, Error}; #[derive(Debug)] pub struct Validator<'a> { @@ -329,63 +329,59 @@ impl<'a> Validator<'a> { } } - fn add_field_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::StructField(_) = item.inner { - self.add_id(id); + 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) { + self.add_id(id); + } else { + self.fail_expecting(id, expected); + } } else { - self.fail(id, "Expecting field"); + self.fail(id, "Not found") } } - fn add_mod_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::Module(_) = item.inner { - self.add_id(id); - } else { - self.fail(id, "Expecting module"); - } + fn add_field_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_struct_field, "StructField"); } + fn add_mod_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_module, "Module"); + } fn add_impl_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::StructField(_) = item.inner { - self.add_id(id); - } else { - self.fail(id, "Expecting impl"); - } + self.add_id_checked(id, Kind::is_impl, "Impl"); } fn add_variant_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::StructField(_) = item.inner { - self.add_id(id); - } else { - self.fail(id, "Expecting variant"); - } + self.add_id_checked(id, Kind::is_variant, "Variant"); } /// Add an Id that appeared in a trait fn add_trait_item_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if !can_appear_in_mod(&item.inner) { - self.fail(id, "Expecting item that can appear in trait"); - } else { - self.add_id(id); - } + self.add_id_checked(id, Kind::can_appear_in_trait, "Trait inner item"); } /// Add an Id that appeared in a mod fn add_mod_item_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if can_appear_in_mod(&item.inner) { - self.add_id(id); - } else { - self.fail(id, "Expecting item that can appear in trait"); - } + self.add_id_checked(id, Kind::can_appear_in_mod, "Module inner item") + } + + fn fail_expecting(&mut self, id: &Id, expected: &str) { + let kind = self.kind_of(id).unwrap(); // We know it has a kind, as it's wrong. + self.fail(id, format!("Expected {expected} but found {kind:?}")); } - fn fail(&mut self, id: &Id, msg: &str) { - self.errs.push(Error { id: id.clone(), message: msg.to_string() }); + fn fail(&mut self, id: &Id, message: impl Into) { + self.errs.push(Error { id: id.clone(), message: message.into() }); + } + + fn kind_of(&mut self, id: &Id) -> Option { + if let Some(item) = self.krate.index.get(id) { + Some(Kind::from_item(item)) + } else if let Some(summary) = self.krate.paths.get(id) { + Some(Kind::from_summary(summary)) + } else { + None + } } } From 5f1bc6fc5e362c5955f6a06a7cf21e62b97c86c7 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 20:09:37 +0100 Subject: [PATCH 04/11] jsondocck: Better errors --- src/tools/jsondoclint/src/item_kind.rs | 19 +++--- src/tools/jsondoclint/src/main.rs | 13 +++- src/tools/jsondoclint/src/validator.rs | 83 ++++++++++++++++---------- 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 15866ab6950f3..f4153245e0b7f 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -31,7 +31,6 @@ pub(crate) enum Kind { Keyword, // Not in ItemKind ProcMacro, - PrimitiveType, } impl Kind { @@ -54,13 +53,13 @@ impl Kind { Macro => true, ProcMacro => true, - ForeignType => todo!("IDK"), - Keyword => todo!("IDK"), - OpaqueTy => todo!("IDK"), - Primitive => todo!("IDK"), - PrimitiveType => todo!("IDK"), - ProcAttribute => todo!("IDK"), - ProcDerive => todo!("IDK"), + // FIXME(adotinthevoid): I'm not sure if these are corrent + ForeignType => false, + Keyword => false, + OpaqueTy => false, + Primitive => false, + ProcAttribute => false, + ProcDerive => false, // Only in traits AssocConst => false, @@ -101,7 +100,6 @@ impl Kind { Kind::Primitive => false, Kind::Keyword => false, Kind::ProcMacro => false, - Kind::PrimitiveType => false, } } @@ -139,7 +137,8 @@ impl Kind { ItemEnum::Static(_) => Static, ItemEnum::Macro(_) => Macro, ItemEnum::ProcMacro(_) => ProcMacro, - ItemEnum::PrimitiveType(_) => PrimitiveType, + // https://github.com/rust-lang/rust/issues/100961 + ItemEnum::PrimitiveType(_) => Primitive, ItemEnum::ForeignType => ForeignType, ItemEnum::ExternCrate { .. } => ExternCrate, ItemEnum::AssocConst { .. } => AssocConst, diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index b98601f7d89f9..4df8fbc29a2bb 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -9,10 +9,16 @@ mod validator; #[derive(Debug)] struct Error { - message: String, + kind: ErrorKind, id: Id, } +#[derive(Debug)] +enum ErrorKind { + NotFound, + Custom(String), +} + fn main() -> Result<()> { let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; let contents = fs::read_to_string(path)?; @@ -24,7 +30,10 @@ fn main() -> Result<()> { if !validator.errs.is_empty() { for err in validator.errs { - eprintln!("`{}`: `{}`", err.id.0, err.message); + match err.kind { + ErrorKind::NotFound => eprintln!("{}: Not Found", err.id.0), + ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg), + } } bail!("Errors validating json"); } diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index b0e12479f92c6..06b114f6c2fe6 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -8,13 +8,14 @@ use rustdoc_json_types::{ TypeBindingKind, Typedef, Union, Variant, WherePredicate, }; -use crate::{item_kind::Kind, Error}; +use crate::{item_kind::Kind, Error, ErrorKind}; #[derive(Debug)] pub struct Validator<'a> { pub(crate) errs: Vec, krate: &'a Crate, seen_ids: HashSet<&'a Id>, + missing_ids: HashSet<&'a Id>, todo: HashSet<&'a Id>, } @@ -29,7 +30,13 @@ fn set_remove(set: &mut HashSet) -> Option { impl<'a> Validator<'a> { pub fn new(krate: &'a Crate) -> Self { - Self { krate, errs: Vec::new(), seen_ids: HashSet::new(), todo: HashSet::new() } + Self { + krate, + errs: Vec::new(), + seen_ids: HashSet::new(), + todo: HashSet::new(), + missing_ids: HashSet::new(), + } } pub fn check_crate(&mut self) { @@ -42,32 +49,39 @@ impl<'a> Validator<'a> { } fn check_item(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - match &item.inner { - ItemEnum::Import(x) => self.check_import(x), - ItemEnum::Union(x) => self.check_union(x), - ItemEnum::Struct(x) => self.check_struct(x), - ItemEnum::StructField(x) => self.check_struct_field(x), - ItemEnum::Enum(x) => self.check_enum(x), - ItemEnum::Variant(x) => self.check_variant(x), - ItemEnum::Function(x) => self.check_function(x), - ItemEnum::Trait(x) => self.check_trait(x), - ItemEnum::TraitAlias(x) => self.check_trait_alias(x), - ItemEnum::Method(x) => self.check_method(x), - ItemEnum::Impl(x) => self.check_impl(x), - ItemEnum::Typedef(x) => self.check_typedef(x), - ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), - ItemEnum::Constant(x) => self.check_constant(x), - ItemEnum::Static(x) => self.check_static(x), - ItemEnum::ForeignType => todo!(), - ItemEnum::Macro(x) => self.check_macro(x), - ItemEnum::ProcMacro(x) => self.check_proc_macro(x), - ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), - ItemEnum::Module(x) => self.check_module(x), - - ItemEnum::ExternCrate { .. } => todo!(), - ItemEnum::AssocConst { .. } => todo!(), - ItemEnum::AssocType { .. } => todo!(), + if let Some(item) = &self.krate.index.get(id) { + match &item.inner { + ItemEnum::Import(x) => self.check_import(x), + ItemEnum::Union(x) => self.check_union(x), + ItemEnum::Struct(x) => self.check_struct(x), + ItemEnum::StructField(x) => self.check_struct_field(x), + ItemEnum::Enum(x) => self.check_enum(x), + ItemEnum::Variant(x) => self.check_variant(x), + ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Trait(x) => self.check_trait(x), + ItemEnum::TraitAlias(x) => self.check_trait_alias(x), + ItemEnum::Method(x) => self.check_method(x), + ItemEnum::Impl(x) => self.check_impl(x), + ItemEnum::Typedef(x) => self.check_typedef(x), + ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), + ItemEnum::Constant(x) => self.check_constant(x), + ItemEnum::Static(x) => self.check_static(x), + ItemEnum::ForeignType => todo!(), + ItemEnum::Macro(x) => self.check_macro(x), + ItemEnum::ProcMacro(x) => self.check_proc_macro(x), + ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), + ItemEnum::Module(x) => self.check_module(x), + // FIXME: Why don't these have their own structs? + ItemEnum::ExternCrate { .. } => {} + ItemEnum::AssocConst { type_, default: _ } => self.check_type(type_), + ItemEnum::AssocType { generics, bounds, default } => { + self.check_generics(generics); + bounds.iter().for_each(|b| self.check_generic_bound(b)); + if let Some(ty) = default { + self.check_type(ty); + } + } + } } } @@ -226,7 +240,7 @@ impl<'a> Validator<'a> { self.check_path(trait_); generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } - GenericBound::Outlives(_) => todo!(), + GenericBound::Outlives(_) => {} } } @@ -337,7 +351,10 @@ impl<'a> Validator<'a> { self.fail_expecting(id, expected); } } else { - self.fail(id, "Not found") + if !self.missing_ids.contains(id) { + self.missing_ids.insert(id); + self.fail(id, ErrorKind::NotFound) + } } } @@ -368,11 +385,11 @@ impl<'a> Validator<'a> { fn fail_expecting(&mut self, id: &Id, expected: &str) { let kind = self.kind_of(id).unwrap(); // We know it has a kind, as it's wrong. - self.fail(id, format!("Expected {expected} but found {kind:?}")); + self.fail(id, ErrorKind::Custom(format!("Expected {expected} but found {kind:?}"))); } - fn fail(&mut self, id: &Id, message: impl Into) { - self.errs.push(Error { id: id.clone(), message: message.into() }); + fn fail(&mut self, id: &Id, kind: ErrorKind) { + self.errs.push(Error { id: id.clone(), kind }); } fn kind_of(&mut self, id: &Id) -> Option { From 41d35a97f9f33e265de53b8f04abe307d7616641 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 23:18:53 +0100 Subject: [PATCH 05/11] jsondocck: Find path to Id's not in index --- src/tools/jsondoclint/src/json_find.rs | 74 ++++++++++++++++++++++++++ src/tools/jsondoclint/src/main.rs | 28 ++++++++-- 2 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/tools/jsondoclint/src/json_find.rs diff --git a/src/tools/jsondoclint/src/json_find.rs b/src/tools/jsondoclint/src/json_find.rs new file mode 100644 index 0000000000000..95ea8866609d0 --- /dev/null +++ b/src/tools/jsondoclint/src/json_find.rs @@ -0,0 +1,74 @@ +use std::fmt::Write; + +use serde_json::Value; + +#[derive(Debug, Clone)] +pub enum SelectorPart { + Field(String), + Index(usize), +} + +pub type Selector = Vec; + +pub fn to_jsonpath(sel: &Selector) -> String { + let mut s = String::from("$"); + for part in sel { + match part { + SelectorPart::Field(name) => { + if is_jsonpath_safe(name) { + write!(&mut s, ".{}", name).unwrap(); + } else { + // This is probably wrong in edge cases, but all Id's are + // just ascii alphanumerics, `-` `_`, and `:` + write!(&mut s, "[{name:?}]").unwrap(); + } + } + SelectorPart::Index(idx) => write!(&mut s, "[{idx}]").unwrap(), + } + } + s +} + +fn is_jsonpath_safe(s: &str) -> bool { + s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') +} + +pub fn find_selector(haystack: &Value, needle: &Value) -> Vec { + let mut result = Vec::new(); + let mut sel = Selector::new(); + find_selector_recursive(haystack, needle, &mut result, &mut sel); + result +} + +fn find_selector_recursive( + haystack: &Value, + needle: &Value, + result: &mut Vec, + pos: &mut Selector, +) { + if needle == haystack { + result.push(pos.clone()); + // Haystack cant both contain needle and be needle + } else { + match haystack { + Value::Null => {} + Value::Bool(_) => {} + Value::Number(_) => {} + Value::String(_) => {} + Value::Array(arr) => { + for (idx, subhaystack) in arr.iter().enumerate() { + pos.push(SelectorPart::Index(idx)); + find_selector_recursive(subhaystack, needle, result, pos); + pos.pop().unwrap(); + } + } + Value::Object(obj) => { + for (key, subhaystack) in obj { + pos.push(SelectorPart::Field(key.clone())); + find_selector_recursive(subhaystack, needle, result, pos); + pos.pop().unwrap(); + } + } + } + } +} diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index 4df8fbc29a2bb..1d02482421ba4 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -3,8 +3,10 @@ use std::env; use anyhow::{anyhow, bail, Result}; use fs_err as fs; use rustdoc_json_types::{Crate, Id, FORMAT_VERSION}; +use serde_json::Value; pub(crate) mod item_kind; +mod json_find; mod validator; #[derive(Debug)] @@ -21,8 +23,10 @@ enum ErrorKind { fn main() -> Result<()> { let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; - let contents = fs::read_to_string(path)?; + let contents = fs::read_to_string(&path)?; let krate: Crate = serde_json::from_str(&contents)?; + // TODO: Only load if nessessary. + let krate_json: Value = serde_json::from_str(&contents)?; assert_eq!(krate.format_version, FORMAT_VERSION); let mut validator = validator::Validator::new(&krate); @@ -31,11 +35,29 @@ fn main() -> Result<()> { if !validator.errs.is_empty() { for err in validator.errs { match err.kind { - ErrorKind::NotFound => eprintln!("{}: Not Found", err.id.0), + ErrorKind::NotFound => { + let sels = + json_find::find_selector(&krate_json, &Value::String(err.id.0.clone())); + match &sels[..] { + [] => unreachable!( + "id must be in crate, or it wouldn't be reported as not found" + ), + [sel] => eprintln!( + "{} not in index or paths, but refered to at '{}'", + err.id.0, + json_find::to_jsonpath(&sel) + ), + [sel, ..] => eprintln!( + "{} not in index or paths, but refered to at '{}' and more", + err.id.0, + json_find::to_jsonpath(&sel) + ), + } + } ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg), } } - bail!("Errors validating json"); + bail!("Errors validating json {path}"); } Ok(()) From c98c7cbfa5e9d9f414101f17abd4639b9541c9e7 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Tue, 30 Aug 2022 14:16:38 +0100 Subject: [PATCH 06/11] Primitives can appear in modules. --- src/tools/jsondoclint/src/item_kind.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index f4153245e0b7f..f46da20fd5814 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -52,12 +52,12 @@ impl Kind { Static => true, Macro => true, ProcMacro => true, + Primitive => true, // FIXME(adotinthevoid): I'm not sure if these are corrent ForeignType => false, Keyword => false, OpaqueTy => false, - Primitive => false, ProcAttribute => false, ProcDerive => false, From 5956b56ab251b9aafecd2ad6f431c42e069b3058 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 14:47:25 +0100 Subject: [PATCH 07/11] jsondoclint: Document validator --- src/tools/jsondoclint/src/item_kind.rs | 3 +- src/tools/jsondoclint/src/main.rs | 4 +-- src/tools/jsondoclint/src/validator.rs | 44 +++++++++++++++----------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index f46da20fd5814..b3e88a9081383 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -1,7 +1,6 @@ use rustdoc_json_types::{Item, ItemEnum, ItemKind, ItemSummary}; -// We want a univeral way to represent an `ItemEnum` or `ItemKind` - +/// A univeral way to represent an [`ItemEnum`] or [`ItemKind`] #[derive(Debug)] pub(crate) enum Kind { Module, diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index 1d02482421ba4..70d7a82a57605 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -25,8 +25,6 @@ fn main() -> Result<()> { let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; let contents = fs::read_to_string(&path)?; let krate: Crate = serde_json::from_str(&contents)?; - // TODO: Only load if nessessary. - let krate_json: Value = serde_json::from_str(&contents)?; assert_eq!(krate.format_version, FORMAT_VERSION); let mut validator = validator::Validator::new(&krate); @@ -36,6 +34,8 @@ fn main() -> Result<()> { for err in validator.errs { match err.kind { ErrorKind::NotFound => { + let krate_json: Value = serde_json::from_str(&contents)?; + let sels = json_find::find_selector(&krate_json, &Value::String(err.id.0.clone())); match &sels[..] { diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 06b114f6c2fe6..efe2c165b6c4e 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -10,22 +10,24 @@ use rustdoc_json_types::{ use crate::{item_kind::Kind, Error, ErrorKind}; +/// The Validator walks over the JSON tree, and ensures it is well formed. +/// It is made of several parts. +/// +/// - `check_*`: These take a type from [`rustdoc_json_types`], and check that +/// it is well formed. This involves calling `check_*` functions on +/// fields of that item, and `add_*` functions on [`Id`]s. +/// - `add_*`: These add an [`Id`] to the worklist, after validating it to check if +/// the `Id` is a kind expected in this suituation. #[derive(Debug)] pub struct Validator<'a> { pub(crate) errs: Vec, krate: &'a Crate, + /// Worklist of Ids to check. + todo: HashSet<&'a Id>, + /// Ids that have already been visited, so don't need to be checked again. seen_ids: HashSet<&'a Id>, + /// Ids that have already been reported missing. missing_ids: HashSet<&'a Id>, - todo: HashSet<&'a Id>, -} - -fn set_remove(set: &mut HashSet) -> Option { - if let Some(id) = set.iter().next() { - let id = id.clone(); - set.take(&id) - } else { - None - } } impl<'a> Validator<'a> { @@ -82,6 +84,8 @@ impl<'a> Validator<'a> { } } } + } else { + assert!(self.krate.paths.contains_key(id)); } } @@ -336,17 +340,12 @@ impl<'a> Validator<'a> { fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } - // Aux functions - fn add_id(&mut self, id: &'a Id) { - if !self.seen_ids.contains(id) { - self.todo.insert(id); - } - } - 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) { - self.add_id(id); + if !self.seen_ids.contains(id) { + self.todo.insert(id); + } } else { self.fail_expecting(id, expected); } @@ -402,3 +401,12 @@ impl<'a> Validator<'a> { } } } + +fn set_remove(set: &mut HashSet) -> Option { + if let Some(id) = set.iter().next() { + let id = id.clone(); + set.take(&id) + } else { + None + } +} From 393792da8d8a931643baf855369df0a009acdb19 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 14:51:18 +0100 Subject: [PATCH 08/11] Remove check_missing_items.py --- src/etc/check_missing_items.py | 202 --------------------------- src/tools/compiletest/src/runtest.rs | 9 -- triagebot.toml | 2 - 3 files changed, 213 deletions(-) delete mode 100644 src/etc/check_missing_items.py diff --git a/src/etc/check_missing_items.py b/src/etc/check_missing_items.py deleted file mode 100644 index 0026c4cbdca2c..0000000000000 --- a/src/etc/check_missing_items.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python - -# This test ensures that every ID in the produced json actually resolves to an item either in -# `index` or `paths`. It DOES NOT check that the structure of the produced json is actually in -# any way correct, for example an empty map would pass. - -# FIXME: Better error output - -import sys -import json - -crate = json.load(open(sys.argv[1], encoding="utf-8")) - - -def get_local_item(item_id): - if item_id in crate["index"]: - return crate["index"][item_id] - print("Missing local ID:", item_id) - sys.exit(1) - - -# local IDs have to be in `index`, external ones can sometimes be in `index` but otherwise have -# to be in `paths` -def valid_id(item_id): - return item_id in crate["index"] or item_id[0] != "0" and item_id in crate["paths"] - - -def check_generics(generics): - for param in generics["params"]: - check_generic_param(param) - for where_predicate in generics["where_predicates"]: - if "bound_predicate" in where_predicate: - pred = where_predicate["bound_predicate"] - check_type(pred["type"]) - for bound in pred["bounds"]: - check_generic_bound(bound) - elif "region_predicate" in where_predicate: - pred = where_predicate["region_predicate"] - for bound in pred["bounds"]: - check_generic_bound(bound) - elif "eq_predicate" in where_predicate: - pred = where_predicate["eq_predicate"] - check_type(pred["rhs"]) - check_type(pred["lhs"]) - - -def check_generic_param(param): - if "type" in param["kind"]: - ty = param["kind"]["type"] - if ty["default"]: - check_type(ty["default"]) - for bound in ty["bounds"]: - check_generic_bound(bound) - elif "const" in param["kind"]: - check_type(param["kind"]["const"]) - - -def check_generic_bound(bound): - if "trait_bound" in bound: - for param in bound["trait_bound"]["generic_params"]: - check_generic_param(param) - check_path(bound["trait_bound"]["trait"]) - - -def check_decl(decl): - for (_name, ty) in decl["inputs"]: - check_type(ty) - if decl["output"]: - check_type(decl["output"]) - -def check_path(path): - args = path["args"] - if args: - if "angle_bracketed" in args: - for arg in args["angle_bracketed"]["args"]: - if "type" in arg: - check_type(arg["type"]) - elif "const" in arg: - check_type(arg["const"]["type"]) - for binding in args["angle_bracketed"]["bindings"]: - if "equality" in binding["binding"]: - term = binding["binding"]["equality"] - if "type" in term: check_type(term["type"]) - elif "const" in term: check_type(term["const"]) - elif "constraint" in binding["binding"]: - for bound in binding["binding"]["constraint"]: - check_generic_bound(bound) - elif "parenthesized" in args: - for input_ty in args["parenthesized"]["inputs"]: - check_type(input_ty) - if args["parenthesized"]["output"]: - check_type(args["parenthesized"]["output"]) - - if path["id"] in crate["index"]: - work_list.add(path["id"]) - elif path["id"] not in crate["paths"]: - print("Id not in index or paths:", path["id"]) - sys.exit(1) - -def check_type(ty): - if ty["kind"] == "resolved_path": - check_path(ty["inner"]) - elif ty["kind"] == "tuple": - for ty in ty["inner"]: - check_type(ty) - elif ty["kind"] == "slice": - check_type(ty["inner"]) - elif ty["kind"] == "impl_trait": - for bound in ty["inner"]: - check_generic_bound(bound) - elif ty["kind"] in ("raw_pointer", "borrowed_ref", "array"): - check_type(ty["inner"]["type"]) - elif ty["kind"] == "function_pointer": - for param in ty["inner"]["generic_params"]: - check_generic_param(param) - check_decl(ty["inner"]["decl"]) - elif ty["kind"] == "qualified_path": - check_type(ty["inner"]["self_type"]) - check_path(ty["inner"]["trait"]) - - -work_list = set([crate["root"]]) -visited = work_list.copy() - -while work_list: - current = work_list.pop() - visited.add(current) - item = get_local_item(current) - # check intradoc links - for (_name, link) in item["links"].items(): - if not valid_id(link): - print("Intra-doc link contains invalid ID:", link) - - # check all fields that reference types such as generics as well as nested items - # (modules, structs, traits, and enums) - if item["kind"] == "module": - work_list |= set(item["inner"]["items"]) - visited - elif item["kind"] == "struct": - check_generics(item["inner"]["generics"]) - work_list |= set(item["inner"]["impls"]) - visited - if "tuple" in item["inner"]["kind"]: - work_list |= set(filter(None, item["inner"]["kind"]["tuple"])) - visited - elif "plain" in item["inner"]["kind"]: - work_list |= set(item["inner"]["kind"]["plain"]["fields"]) - visited - elif item["kind"] == "struct_field": - check_type(item["inner"]) - elif item["kind"] == "enum": - check_generics(item["inner"]["generics"]) - work_list |= ( - set(item["inner"]["variants"]) | set(item["inner"]["impls"]) - ) - visited - elif item["kind"] == "variant": - if item["inner"]["variant_kind"] == "tuple": - for field_id in filter(None, item["inner"]["variant_inner"]): - work_list.add(field_id) - elif item["inner"]["variant_kind"] == "struct": - work_list |= set(item["inner"]["variant_inner"]["fields"]) - visited - elif item["kind"] in ("function", "method"): - check_generics(item["inner"]["generics"]) - check_decl(item["inner"]["decl"]) - elif item["kind"] in ("static", "constant", "assoc_const"): - check_type(item["inner"]["type"]) - elif item["kind"] == "typedef": - check_type(item["inner"]["type"]) - check_generics(item["inner"]["generics"]) - elif item["kind"] == "opaque_ty": - check_generics(item["inner"]["generics"]) - for bound in item["inner"]["bounds"]: - check_generic_bound(bound) - elif item["kind"] == "trait_alias": - check_generics(item["inner"]["params"]) - for bound in item["inner"]["bounds"]: - check_generic_bound(bound) - elif item["kind"] == "trait": - check_generics(item["inner"]["generics"]) - for bound in item["inner"]["bounds"]: - check_generic_bound(bound) - work_list |= ( - set(item["inner"]["items"]) | set(item["inner"]["implementations"]) - ) - visited - elif item["kind"] == "impl": - check_generics(item["inner"]["generics"]) - if item["inner"]["trait"]: - check_path(item["inner"]["trait"]) - if item["inner"]["blanket_impl"]: - check_type(item["inner"]["blanket_impl"]) - check_type(item["inner"]["for"]) - for assoc_item in item["inner"]["items"]: - if not valid_id(assoc_item): - print("Impl block referenced a missing ID:", assoc_item) - sys.exit(1) - elif item["kind"] == "assoc_type": - for bound in item["inner"]["bounds"]: - check_generic_bound(bound) - if item["inner"]["default"]: - check_type(item["inner"]["default"]) - elif item["kind"] == "import": - if item["inner"]["id"]: - inner_id = item["inner"]["id"] - assert valid_id(inner_id) - if inner_id in crate["index"] and inner_id not in visited: - work_list.add(inner_id) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 9cbb6b7c393bd..8f289876f7307 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2563,15 +2563,6 @@ impl<'test> TestCx<'test> { let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); json_out.set_extension("json"); - let res = self.cmd2procres( - Command::new(&self.config.python) - .arg(root.join("src/etc/check_missing_items.py")) - .arg(&json_out), - ); - - if !res.status.success() { - self.fatal_proc_rec("check_missing_items failed!", &res); - } let res = self.cmd2procres( Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out), diff --git a/triagebot.toml b/triagebot.toml index 11caedbb9597a..12a55fda7ef4d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -130,7 +130,6 @@ trigger_files = [ # Internal tooling "src/etc/htmldocck.py", - "src/etc/check_missing_items.py", "src/tools/jsondocck", "src/tools/jsondoclint", "src/tools/rustdoc-gui", @@ -143,7 +142,6 @@ exclude_labels = [ [autolabel."A-rustdoc-json"] trigger_files = [ - "src/etc/check_missing_items.py", "src/librustdoc/json/", "src/rustdoc-json-types", "src/test/rustdoc-json", From 24c751b2bad4a3cb6020462d3769f735762ef5b3 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 15:08:56 +0100 Subject: [PATCH 09/11] Rustdoc-Json: Add test for extern_types --- src/test/rustdoc-json/type/extern.rs | 10 ++++++++++ src/tools/jsondoclint/src/item_kind.rs | 2 +- src/tools/jsondoclint/src/validator.rs | 9 ++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 src/test/rustdoc-json/type/extern.rs diff --git a/src/test/rustdoc-json/type/extern.rs b/src/test/rustdoc-json/type/extern.rs new file mode 100644 index 0000000000000..d287d5ebec543 --- /dev/null +++ b/src/test/rustdoc-json/type/extern.rs @@ -0,0 +1,10 @@ +#![feature(extern_types)] + +extern { + /// No inner information + pub type Foo; +} + +// @is "$.index[*][?(@.docs=='No inner information')].name" '"Foo"' +// @is "$.index[*][?(@.docs=='No inner information')].kind" '"foreign_type"' +// @!has "$.index[*][?(@.docs=='No inner information')].inner" diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index b3e88a9081383..65d7143d13385 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -52,9 +52,9 @@ impl Kind { Macro => true, ProcMacro => true, Primitive => true, + ForeignType => true, // FIXME(adotinthevoid): I'm not sure if these are corrent - ForeignType => false, Keyword => false, OpaqueTy => false, ProcAttribute => false, diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index efe2c165b6c4e..d7bf6fe9e3ca4 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -68,7 +68,7 @@ impl<'a> Validator<'a> { ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), ItemEnum::Constant(x) => self.check_constant(x), ItemEnum::Static(x) => self.check_static(x), - ItemEnum::ForeignType => todo!(), + ItemEnum::ForeignType => {} // nop ItemEnum::Macro(x) => self.check_macro(x), ItemEnum::ProcMacro(x) => self.check_proc_macro(x), ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), @@ -340,6 +340,13 @@ impl<'a> Validator<'a> { fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } + // TODO: Remove + fn add_id(&mut self, id: &'a Id) { + if !self.seen_ids.contains(id) { + self.todo.insert(id); + } + } + 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) { From 6e21a28ddaf31bf5c6c78d83d051214d2212e803 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 15:31:45 +0100 Subject: [PATCH 10/11] jsondoclint: More precise `Path` checks --- src/tools/jsondoclint/src/item_kind.rs | 6 +++++ src/tools/jsondoclint/src/validator.rs | 37 ++++++++++++++++---------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 65d7143d13385..ad8e96a0bd81d 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -114,6 +114,12 @@ impl Kind { pub fn is_variant(self) -> bool { matches!(self, Kind::Variant) } + pub fn is_trait(self) -> bool { + matches!(self, Kind::Trait) + } + pub fn is_struct_enum_union(self) -> bool { + matches!(self, Kind::Struct | Kind::Enum | Kind::Union) + } pub fn from_item(i: &Item) -> Self { use Kind::*; diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index d7bf6fe9e3ca4..3226ea4f334ef 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -30,6 +30,11 @@ pub struct Validator<'a> { missing_ids: HashSet<&'a Id>, } +enum PathKind { + Trait, + StructEnumUnion, +} + impl<'a> Validator<'a> { pub fn new(krate: &'a Crate) -> Self { Self { @@ -165,7 +170,7 @@ impl<'a> Validator<'a> { fn check_impl(&mut self, x: &'a Impl) { self.check_generics(&x.generics); if let Some(path) = &x.trait_ { - self.check_path(path); // TODO: Check is trait. + self.check_path(path, PathKind::Trait); } self.check_type(&x.for_); x.items.iter().for_each(|i| self.add_trait_item_id(i)); @@ -211,7 +216,7 @@ impl<'a> Validator<'a> { fn check_type(&mut self, x: &'a Type) { match x { - Type::ResolvedPath(path) => self.check_path(path), + Type::ResolvedPath(path) => self.check_path(path, PathKind::StructEnumUnion), Type::DynTrait(dyn_trait) => self.check_dyn_trait(dyn_trait), Type::Generic(_) => {} Type::Primitive(_) => {} @@ -226,7 +231,7 @@ impl<'a> Validator<'a> { Type::QualifiedPath { name: _, args, self_type, trait_ } => { self.check_generic_args(&**args); self.check_type(&**self_type); - self.check_path(trait_); + self.check_path(trait_, PathKind::Trait); } } } @@ -241,15 +246,18 @@ impl<'a> Validator<'a> { fn check_generic_bound(&mut self, x: &'a GenericBound) { match x { GenericBound::TraitBound { trait_, generic_params, modifier: _ } => { - self.check_path(trait_); + self.check_path(trait_, PathKind::Trait); generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } GenericBound::Outlives(_) => {} } } - fn check_path(&mut self, x: &'a Path) { - self.add_id(&x.id); // TODO: What kinds are allowed here. + fn check_path(&mut self, x: &'a Path, kind: PathKind) { + match kind { + PathKind::Trait => self.add_trait_id(&x.id), + PathKind::StructEnumUnion => self.add_struct_enum_union_id(&x.id), + } if let Some(args) = &x.args { self.check_generic_args(&**args); } @@ -330,7 +338,7 @@ impl<'a> Validator<'a> { fn check_dyn_trait(&mut self, dyn_trait: &'a DynTrait) { for pt in &dyn_trait.traits { - self.check_path(&pt.trait_); + self.check_path(&pt.trait_, PathKind::Trait); pt.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } } @@ -340,13 +348,6 @@ impl<'a> Validator<'a> { fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } - // TODO: Remove - fn add_id(&mut self, id: &'a Id) { - if !self.seen_ids.contains(id) { - self.todo.insert(id); - } - } - 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) { @@ -379,6 +380,14 @@ impl<'a> Validator<'a> { self.add_id_checked(id, Kind::is_variant, "Variant"); } + fn add_trait_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_trait, "Trait"); + } + + fn add_struct_enum_union_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_struct_enum_union, "Struct or Enum or Union"); + } + /// Add an Id that appeared in a trait fn add_trait_item_id(&mut self, id: &'a Id) { self.add_id_checked(id, Kind::can_appear_in_trait, "Trait inner item"); From f69a6c2a8018ac69db62446996e815893b70dfcd Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 15:41:54 +0100 Subject: [PATCH 11/11] jsondoclint: Fix TODO's --- src/tools/jsondoclint/src/validator.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 3226ea4f334ef..a0e77127dc2ca 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -63,7 +63,7 @@ impl<'a> Validator<'a> { ItemEnum::Struct(x) => self.check_struct(x), ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::Enum(x) => self.check_enum(x), - ItemEnum::Variant(x) => self.check_variant(x), + ItemEnum::Variant(x) => self.check_variant(x, id), ItemEnum::Function(x) => self.check_function(x), ItemEnum::Trait(x) => self.check_trait(x), ItemEnum::TraitAlias(x) => self.check_trait_alias(x), @@ -135,9 +135,23 @@ impl<'a> Validator<'a> { x.impls.iter().for_each(|i| self.add_impl_id(i)); } - fn check_variant(&mut self, x: &'a Variant) { + fn check_variant(&mut self, x: &'a Variant, id: &'a Id) { match x { - Variant::Plain(_discriminant) => {} // TODO: Check discriminant value parses + Variant::Plain(discr) => { + if let Some(discr) = discr { + if let (Err(_), Err(_)) = + (discr.value.parse::(), discr.value.parse::()) + { + self.fail( + id, + ErrorKind::Custom(format!( + "Failed to parse discriminant value `{}`", + discr.value + )), + ); + } + } + } Variant::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)), Variant::Struct { fields, fields_stripped: _ } => { fields.iter().for_each(|f| self.add_field_id(f)) @@ -198,15 +212,15 @@ impl<'a> Validator<'a> { } fn check_macro(&mut self, _: &'a str) { - // TODO + // nop } fn check_proc_macro(&mut self, _: &'a ProcMacro) { - // TODO + // nop } fn check_primitive_type(&mut self, _: &'a str) { - // TODO + // nop } fn check_generics(&mut self, x: &'a Generics) {