about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-01-03 17:19:27 -0800
committerGitHub <noreply@github.com>2023-01-03 17:19:27 -0800
commitf24dab764f0bfea5fcae397cbe2b93e9f1570cd4 (patch)
tree52ec9098281e03f9effed79ecf9bf45a49ca8979 /src
parent5d828d2440ed6cc4c27057fe6aa1d305f7a559cb (diff)
parent226ab7fd759d94e81d6831f869e41502a7f183c7 (diff)
downloadrust-f24dab764f0bfea5fcae397cbe2b93e9f1570cd4.tar.gz
rust-f24dab764f0bfea5fcae397cbe2b93e9f1570cd4.zip
Rollup merge of #106381 - aDotInTheVoid:jsondoclint-more-options, r=notriddle
Jsondoclint: Add `--verbose` and `--json-output` options

There quite helpful for manually using jsondoclint as a debugging tool, especially on large files (these were written to look into core.json).

r? rustdoc
Diffstat (limited to 'src')
-rw-r--r--src/tools/jsondoclint/Cargo.toml2
-rw-r--r--src/tools/jsondoclint/src/json_find.rs3
-rw-r--r--src/tools/jsondoclint/src/main.rs98
-rw-r--r--src/tools/jsondoclint/src/validator.rs13
-rw-r--r--src/tools/jsondoclint/src/validator/tests.rs20
5 files changed, 103 insertions, 33 deletions
diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml
index 84a6c7f96c4..8990310a4f4 100644
--- a/src/tools/jsondoclint/Cargo.toml
+++ b/src/tools/jsondoclint/Cargo.toml
@@ -7,6 +7,8 @@ edition = "2021"
 
 [dependencies]
 anyhow = "1.0.62"
+clap = { version = "4.0.15", features = ["derive"] }
 fs-err = "2.8.1"
 rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" }
+serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.85"
diff --git a/src/tools/jsondoclint/src/json_find.rs b/src/tools/jsondoclint/src/json_find.rs
index 70e7440f730..a183c4068ce 100644
--- a/src/tools/jsondoclint/src/json_find.rs
+++ b/src/tools/jsondoclint/src/json_find.rs
@@ -1,8 +1,9 @@
 use std::fmt::Write;
 
+use serde::Serialize;
 use serde_json::Value;
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
 pub enum SelectorPart {
     Field(String),
     Index(usize),
diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs
index fc54c421b4b..05e938f4f7d 100644
--- a/src/tools/jsondoclint/src/main.rs
+++ b/src/tools/jsondoclint/src/main.rs
@@ -1,59 +1,103 @@
-use std::env;
+use std::io::{BufWriter, Write};
 
-use anyhow::{anyhow, bail, Result};
+use anyhow::{bail, Result};
+use clap::Parser;
 use fs_err as fs;
 use rustdoc_json_types::{Crate, Id, FORMAT_VERSION};
+use serde::Serialize;
 use serde_json::Value;
 
 pub(crate) mod item_kind;
 mod json_find;
 mod validator;
 
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, Serialize, Clone)]
 struct Error {
     kind: ErrorKind,
     id: Id,
 }
 
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, Serialize, Clone)]
 enum ErrorKind {
-    NotFound,
+    NotFound(Vec<json_find::Selector>),
     Custom(String),
 }
 
+#[derive(Debug, Serialize)]
+struct JsonOutput {
+    path: String,
+    errors: Vec<Error>,
+}
+
+#[derive(Parser)]
+struct Cli {
+    /// The path to the json file to be linted
+    path: String,
+
+    /// Show verbose output
+    #[arg(long)]
+    verbose: bool,
+
+    #[arg(long)]
+    json_output: Option<String>,
+}
+
 fn main() -> Result<()> {
-    let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?;
+    let Cli { path, verbose, json_output } = Cli::parse();
+
     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);
+    let krate_json: Value = serde_json::from_str(&contents)?;
+
+    let mut validator = validator::Validator::new(&krate, krate_json);
     validator.check_crate();
 
+    if let Some(json_output) = json_output {
+        let output = JsonOutput { path: path.clone(), errors: validator.errs.clone() };
+        let mut f = BufWriter::new(fs::File::create(json_output)?);
+        serde_json::to_writer(&mut f, &output)?;
+        f.flush()?;
+    }
+
     if !validator.errs.is_empty() {
         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[..] {
-                        [] => 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::NotFound(sels) => match &sels[..] {
+                    [] => {
+                        unreachable!(
+                            "id {:?} must be in crate, or it wouldn't be reported as not found",
+                            err.id
+                        )
+                    }
+                    [sel] => eprintln!(
+                        "{} not in index or paths, but refered to at '{}'",
+                        err.id.0,
+                        json_find::to_jsonpath(&sel)
+                    ),
+                    [sel, ..] => {
+                        if verbose {
+                            let sels = sels
+                                .iter()
+                                .map(json_find::to_jsonpath)
+                                .map(|i| format!("'{i}'"))
+                                .collect::<Vec<_>>()
+                                .join(", ");
+                            eprintln!(
+                                "{} not in index or paths, but refered to at {sels}",
+                                err.id.0
+                            );
+                        } else {
+                            eprintln!(
+                                "{} not in index or paths, but refered to at '{}' and {} more",
+                                err.id.0,
+                                json_find::to_jsonpath(&sel),
+                                sels.len() - 1,
+                            )
+                        }
                     }
-                }
+                },
                 ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg),
             }
         }
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index 291d02d67bd..f1b9c1acbae 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -7,8 +7,9 @@ use rustdoc_json_types::{
     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, Error, ErrorKind};
+use crate::{item_kind::Kind, json_find, Error, ErrorKind};
 
 /// The Validator walks over the JSON tree, and ensures it is well formed.
 /// It is made of several parts.
@@ -22,6 +23,7 @@ use crate::{item_kind::Kind, Error, ErrorKind};
 pub struct Validator<'a> {
     pub(crate) errs: Vec<Error>,
     krate: &'a Crate,
+    krate_json: Value,
     /// Worklist of Ids to check.
     todo: HashSet<&'a Id>,
     /// Ids that have already been visited, so don't need to be checked again.
@@ -39,9 +41,10 @@ enum PathKind {
 }
 
 impl<'a> Validator<'a> {
-    pub fn new(krate: &'a Crate) -> Self {
+    pub fn new(krate: &'a Crate, krate_json: Value) -> Self {
         Self {
             krate,
+            krate_json,
             errs: Vec::new(),
             seen_ids: HashSet::new(),
             todo: HashSet::new(),
@@ -373,7 +376,11 @@ impl<'a> Validator<'a> {
         } else {
             if !self.missing_ids.contains(id) {
                 self.missing_ids.insert(id);
-                self.fail(id, ErrorKind::NotFound)
+
+                let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone()));
+                assert_ne!(sels.len(), 0);
+
+                self.fail(id, ErrorKind::NotFound(sels))
             }
         }
     }
diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs
index c4aeee9c53b..37b826153ef 100644
--- a/src/tools/jsondoclint/src/validator/tests.rs
+++ b/src/tools/jsondoclint/src/validator/tests.rs
@@ -2,11 +2,16 @@ use std::collections::HashMap;
 
 use rustdoc_json_types::{Crate, Item, Visibility};
 
+use crate::json_find::SelectorPart;
+
 use super::*;
 
 #[track_caller]
 fn check(krate: &Crate, errs: &[Error]) {
-    let mut validator = Validator::new(krate);
+    let krate_string = serde_json::to_string(krate).unwrap();
+    let krate_json = serde_json::from_str(&krate_string).unwrap();
+
+    let mut validator = Validator::new(krate, krate_json);
     validator.check_crate();
 
     assert_eq!(errs, &validator.errs[..]);
@@ -46,5 +51,16 @@ fn errors_on_missing_links() {
         format_version: rustdoc_json_types::FORMAT_VERSION,
     };
 
-    check(&k, &[Error { kind: ErrorKind::NotFound, id: id("1") }]);
+    check(
+        &k,
+        &[Error {
+            kind: ErrorKind::NotFound(vec![vec![
+                SelectorPart::Field("index".to_owned()),
+                SelectorPart::Field("0".to_owned()),
+                SelectorPart::Field("links".to_owned()),
+                SelectorPart::Field("Not Found".to_owned()),
+            ]]),
+            id: id("1"),
+        }],
+    );
 }