diff --git a/Cargo.lock b/Cargo.lock index 6b17c0d309cba..602eb8cf1c30c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,6 +291,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "basic-toml" version = "0.1.10" @@ -1750,6 +1756,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hifijson" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "242402749acf71e6f32f5857598b7002c4058a4e3c3b22b4c7d51cab9aea754e" + [[package]] name = "home" version = "0.5.12" @@ -2121,6 +2133,54 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jaq-core" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7561783b20275a6c9cb576e39208b0c635f34ef14357f1f05a2927a774f3adec" +dependencies = [ + "dyn-clone", + "once_cell", + "typed-arena", +] + +[[package]] +name = "jaq-json" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ec9aaad7340e6990c6c1878ef3b46dbec624e535d7f786cc9ddcf94f773d33" +dependencies = [ + "bstr", + "bytes", + "foldhash 0.1.5", + "hifijson", + "indexmap", + "jaq-core", + "jaq-std", + "num-bigint", + "num-traits", + "ryu", + "self_cell", + "serde_core", +] + +[[package]] +name = "jaq-std" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bdc5a74b0feeb5e6a1dc2dd08c34280a61e37668d10a6a3b27ad69d0fb9ce2e" +dependencies = [ + "aho-corasick", + "base64 0.22.1", + "bstr", + "jaq-core", + "jiff", + "libm", + "log", + "regex-bites", + "urlencoding", +] + [[package]] name = "jiff" version = "0.2.16" @@ -2128,10 +2188,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", + "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde_core", + "windows-sys 0.61.2", ] [[package]] @@ -2145,6 +2207,21 @@ dependencies = [ "syn", ] +[[package]] +name = "jiff-tzdb" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -2171,6 +2248,9 @@ version = "0.1.0" dependencies = [ "fs-err", "getopts", + "jaq-core", + "jaq-json", + "jaq-std", "jsonpath-rust", "regex", "serde_json", @@ -3408,6 +3488,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-bites" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a15a2fa0bfda9361941c45550896ae87b15cc6c8c939ea350079670332e211" + [[package]] name = "regex-lite" version = "0.1.8" @@ -4942,7 +5028,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "askama", - "base64", + "base64 0.21.7", "expect-test", "indexmap", "itertools", @@ -5941,6 +6027,12 @@ dependencies = [ "rustc-hash 2.1.1", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typeid" version = "1.0.3" diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index b7130c0870800..ca878a7de915b 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -324,5 +324,23 @@ pub(crate) const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[ "!snapshot", ]; -pub(crate) const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] = - &["count", "!count", "has", "!has", "is", "!is", "ismany", "!ismany", "set", "!set"]; +pub(crate) const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] = &[ + "count", + "!count", + "has", + "!has", + "is", + "!is", + "ismany", + "!ismany", + "set", + "jq_count", + "!jq_count", + "jq_has", + "!jq_has", + "jq_is", + "!jq_is", + "jq_ismany", + "!jq_ismany", + "jq_set", +]; diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index 80fc26cbe6680..c292cc4ea4ba8 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -10,3 +10,6 @@ regex = "1.4" shlex = "1.0" serde_json = "1.0" fs-err = "2.5.0" +jaq-core = "3" +jaq-std = { version = "3", default-features = false } +jaq-json = { version = "2", features = ["serde"] } diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs index 1369c8ded0070..8d9273e2ceb67 100644 --- a/src/tools/jsondocck/src/cache.rs +++ b/src/tools/jsondocck/src/cache.rs @@ -2,12 +2,17 @@ use std::collections::HashMap; use std::path::Path; use fs_err as fs; +use jaq_core::load::{Arena, File, Loader}; +use jaq_core::{Ctx, Vars, data, unwrap_valr}; +use jaq_json::Val; use serde_json::Value; use crate::config::Config; #[derive(Debug)] pub struct Cache { + jq_value: Val, + pub jq_variables: HashMap, value: Value, pub variables: HashMap, } @@ -23,6 +28,8 @@ impl Cache { let content = fs::read_to_string(&file_path).expect("failed to read JSON file"); Cache { + jq_value: serde_json::from_str::(&content).expect("failed to convert from JSON"), + jq_variables: HashMap::from([("FILE".to_owned(), config.template.clone().into())]), value: serde_json::from_str::(&content).expect("failed to convert from JSON"), variables: HashMap::from([("FILE".to_owned(), config.template.clone().into())]), } @@ -32,4 +39,31 @@ impl Cache { pub fn select(&self, path: &str) -> Vec<&Value> { jsonpath_rust::query::js_path_vals(path, &self.value).unwrap() } + + pub fn jq_select(&self, filter_src: &str) -> Vec { + let program = File { code: filter_src, path: () }; + + let defs = jaq_core::defs().chain(jaq_std::defs()).chain(jaq_json::defs()); + let funs = jaq_core::funs().chain(jaq_std::funs()).chain(jaq_json::funs()); + + let loader = Loader::new(defs); + let arena = Arena::default(); + + let modules = loader.load(&arena, program).unwrap(); + + let vars = self.jq_variables.iter().map(|t| (format!("${}", t.0), t.1)).collect::>(); + + let filter = jaq_core::Compiler::default() + .with_funs(funs) + .with_global_vars(vars.clone().iter().map(|t| t.0.as_str())) + .compile(modules) + .unwrap(); + + let ctx = Ctx::>::new( + &filter.lut, + Vars::new(vars.iter().map(|t| t.1.clone())), + ); + + filter.id.run((ctx, self.jq_value.clone())).map(unwrap_valr).map(Result::unwrap).collect() + } } diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs index c52c16866608d..6154374b48fde 100644 --- a/src/tools/jsondocck/src/directive.rs +++ b/src/tools/jsondocck/src/directive.rs @@ -1,5 +1,7 @@ use std::borrow::Cow; +use std::fmt::Debug; +use jaq_json::Val; use serde_json::Value; use crate::cache::Cache; @@ -13,6 +15,12 @@ pub struct Directive { #[derive(Debug)] pub enum DirectiveKind { + JsonPath(JsonPathDirective), + Jq(JqDirective), +} + +#[derive(Debug)] +pub enum JsonPathDirective { /// `//@ has ` /// /// Checks the path exists. @@ -57,6 +65,42 @@ pub enum DirectiveKind { Set { variable: String }, } +#[derive(Debug)] +pub enum JqDirective { + /// `//@ jq_has ` + /// + /// Check one thing at the path is equal to the value. + HasValue { value: String }, + + /// `//@ !jq_has ` + /// + /// Checks the path exists, but doesn't have the given value. + HasNotValue { value: String }, + + /// `//@ jq_is ` + /// + /// Check the path is the given value. + Is { value: String }, + + /// `//@ jq_is ...` + /// + /// Check that the path matches to exactly every given value. + IsMany { values: Vec }, + + /// `//@ !jq_is ` + /// + /// Check the path isn't the given value. + IsNot { value: String }, + + /// `//@ jq_count ` + /// + /// Check the path has the expected number of matches. + CountIs { expected: usize }, + + /// `//@ jq_set = ` + Set { variable: String }, +} + impl DirectiveKind { /// Returns both the kind and the path. /// @@ -66,45 +110,84 @@ impl DirectiveKind { negated: bool, args: &'a [String], ) -> Option<(Self, &'a str)> { + // FIXME(lolbinarycat): refactor this parsing into something more compact with better error reporting. let kind = match (directive_name, negated) { + // jsonpath ("count", false) => { assert_eq!(args.len(), 2); let expected = args[1].parse().expect("invalid number for `count`"); - Self::CountIs { expected } + Self::JsonPath(JsonPathDirective::CountIs { expected }) } - ("ismany", false) => { - // FIXME: Make this >= 3, and migrate len(values)==1 cases to @is + // FIXME: Make this >= 3, and migrate len(values)==1 cases to `is` assert!(args.len() >= 2, "Not enough args to `ismany`"); let values = args[1..].to_owned(); - Self::IsMany { values } + Self::JsonPath(JsonPathDirective::IsMany { values }) } - ("is", false) => { assert_eq!(args.len(), 2); - Self::Is { value: args[1].clone() } + Self::JsonPath(JsonPathDirective::Is { value: args[1].clone() }) } ("is", true) => { assert_eq!(args.len(), 2); - Self::IsNot { value: args[1].clone() } + Self::JsonPath(JsonPathDirective::IsNot { value: args[1].clone() }) } - ("set", false) => { assert_eq!(args.len(), 3); assert_eq!(args[1], "="); - return Some((Self::Set { variable: args[0].clone() }, &args[2])); + return Some(( + Self::JsonPath(JsonPathDirective::Set { variable: args[0].clone() }), + &args[2], + )); } - ("has", false) => match args { - [_path] => Self::HasPath, - [_path, value] => Self::HasValue { value: value.clone() }, + [_path] => Self::JsonPath(JsonPathDirective::HasPath), + [_path, value] => { + Self::JsonPath(JsonPathDirective::HasValue { value: value.clone() }) + } _ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"), }, ("has", true) => match args { - [_path] => Self::HasNotPath, - [_path, value] => Self::HasNotValue { value: value.clone() }, + [_path] => Self::JsonPath(JsonPathDirective::HasNotPath), + [_path, value] => { + Self::JsonPath(JsonPathDirective::HasNotValue { value: value.clone() }) + } _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), }, + + // jq + ("jq_count", false) => { + assert_eq!(args.len(), 2); + let expected = args[1].parse().expect("invalid number for `jq_count`"); + Self::Jq(JqDirective::CountIs { expected }) + } + ("jq_ismany", false) => { + // FIXME: Make this >= 3, and migrate len(values)==1 cases to `jq_is` + assert!(args.len() >= 2, "Not enough args to `jq_ismany`"); + let values = args[1..].to_owned(); + Self::Jq(JqDirective::IsMany { values }) + } + ("jq_is", false) => { + assert_eq!(args.len(), 2); + Self::Jq(JqDirective::Is { value: args[1].clone() }) + } + ("jq_is", true) => { + assert_eq!(args.len(), 2); + Self::Jq(JqDirective::IsNot { value: args[1].clone() }) + } + ("jq_has", false) => { + assert_eq!(args.len(), 2); + Self::Jq(JqDirective::HasValue { value: args[1].clone() }) + } + ("jq_has", true) => { + assert_eq!(args.len(), 2); + Self::Jq(JqDirective::HasNotValue { value: args[1].clone() }) + } + ("jq_set", false) => { + assert_eq!(args.len(), 3); + assert_eq!(args[1], "="); + return Some((Self::Jq(JqDirective::Set { variable: args[0].clone() }), &args[2])); + } // Ignore unknown directives as they might be compiletest directives // since they share the same `//@` prefix by convention. In any case, // compiletest rejects unknown directives for us. @@ -118,94 +201,188 @@ impl DirectiveKind { impl Directive { /// Performs the actual work of ensuring a directive passes. pub fn check(&self, cache: &mut Cache) -> Result<(), String> { - let matches = cache.select(&self.path); match &self.kind { - DirectiveKind::HasPath => { - if matches.is_empty() { - return Err("matched to no values".to_owned()); - } - } - DirectiveKind::HasNotPath => { - if !matches.is_empty() { - return Err(format!("matched to {matches:?}, but wanted no matches")); - } - } - DirectiveKind::HasValue { value } => { - let want_value = string_to_value(value, cache); - if !matches.contains(&want_value.as_ref()) { - return Err(format!( - "matched to {matches:?}, which didn't contain {want_value:?}" - )); - } - } - DirectiveKind::HasNotValue { value } => { - let wantnt_value = string_to_value(value, cache); - if matches.contains(&wantnt_value.as_ref()) { - return Err(format!( - "matched to {matches:?}, which contains unwanted {wantnt_value:?}" - )); - } else if matches.is_empty() { - return Err(format!( - "got no matches, but expected some matched (not containing {wantnt_value:?}" - )); - } - } + DirectiveKind::JsonPath(d) => { + let matches = cache.select(&self.path); + match d { + JsonPathDirective::HasPath => { + if matches.is_empty() { + return Err("matched to no values".to_owned()); + } + } + JsonPathDirective::HasNotPath => { + if !matches.is_empty() { + return Err(format!("matched to {matches:?}, but expected no matches")); + } + } + JsonPathDirective::HasValue { value } => { + let want_value = string_to_value(value, cache); + if !matches.contains(&want_value.as_ref()) { + return Err(format!( + "matched to {matches:?}, which didn't contain {want_value:?}" + )); + } + } + JsonPathDirective::HasNotValue { value } => { + let unwanted_value = string_to_value(value, cache); + if matches.contains(&unwanted_value.as_ref()) { + return Err(format!( + "matched to {matches:?}, which contains unwanted {unwanted_value:?}" + )); + } else if matches.is_empty() { + return Err(format!( + "got no matches, but expected some matched (not containing {unwanted_value:?}" + )); + } + } - DirectiveKind::Is { value } => { - let want_value = string_to_value(value, cache); - let matched = get_one(&matches)?; - if matched != want_value.as_ref() { - return Err(format!("matched to {matched:?} but want {want_value:?}")); - } - } - DirectiveKind::IsNot { value } => { - let wantnt_value = string_to_value(value, cache); - let matched = get_one(&matches)?; - if matched == wantnt_value.as_ref() { - return Err(format!("got value {wantnt_value:?}, but want anything else")); - } - } + JsonPathDirective::Is { value } => { + let want_value = string_to_value(value, cache); + let matched = get_one(&matches)?; + if matched != want_value.as_ref() { + return Err(format!("matched to {matched:?} but want {want_value:?}")); + } + } + JsonPathDirective::IsNot { value } => { + let unwanted_value = string_to_value(value, cache); + let matched = get_one(&matches)?; + if matched == unwanted_value.as_ref() { + return Err(format!( + "got value {unwanted_value:?}, but want anything else" + )); + } + } - DirectiveKind::IsMany { values } => { - // Serde json doesn't implement Ord or Hash for Value, so we must - // use a Vec here. While in theory that makes setwize equality - // O(n^2), in practice n will never be large enough to matter. - let expected_values = - values.iter().map(|v| string_to_value(v, cache)).collect::>(); - if expected_values.len() != matches.len() { - return Err(format!( - "Expected {} values, but matched to {} values ({:?})", - expected_values.len(), - matches.len(), - matches - )); - }; - for got_value in matches { - if !expected_values.iter().any(|exp| &**exp == got_value) { - return Err(format!("has match {got_value:?}, which was not expected",)); + JsonPathDirective::IsMany { values } => { + let expected_values = + values.iter().map(|v| string_to_value(v, cache)).collect::>(); + if expected_values.len() != matches.len() { + return Err(format!( + "Expected {} values, but matched to {} values ({:?})", + expected_values.len(), + matches.len(), + matches + )); + }; + for got_value in matches { + if !expected_values.iter().any(|exp| &**exp == got_value) { + return Err(format!( + "has match {got_value:?}, which was not expected", + )); + } + } + } + JsonPathDirective::CountIs { expected } => { + if *expected != matches.len() { + return Err(format!( + "matched to `{matches:?}` with length {}, but expected length {expected}", + matches.len(), + )); + } + } + JsonPathDirective::Set { variable } => { + let value = get_one(&matches)?; + // this should never fail since `Val` is a superset of `Value` + let val = serde_json::from_value(value.to_owned()).unwrap(); + let vars = cache.variables.insert(variable.to_owned(), value.clone()); + let jq_vars = cache.jq_variables.insert(variable.to_owned(), val); + assert!( + vars.is_none() && jq_vars.is_none(), + "name collision: {variable:?} is duplicated" + ); } } } - DirectiveKind::CountIs { expected } => { - if *expected != matches.len() { - return Err(format!( - "matched to `{matches:?}` with length {}, but expected length {expected}", - matches.len(), - )); + DirectiveKind::Jq(d) => { + let matches = cache.jq_select(&self.path); + match d { + JqDirective::HasValue { value } => { + let want_value = string_to_val(value, cache); + if !matches.contains(&want_value.as_ref()) { + return Err(format!( + "matched to {matches:?}, which didn't contain {want_value:?}" + )); + } + } + JqDirective::HasNotValue { value } => { + let unwanted_value = string_to_val(value, cache); + if matches.contains(&unwanted_value.as_ref()) { + return Err(format!( + "matched to {matches:?}, which contains unwanted {unwanted_value:?}" + )); + } else if matches.is_empty() { + return Err(format!( + "got no matches, but expected some matched (not containing {unwanted_value:?}" + )); + } + } + + JqDirective::Is { value } => { + let want_value = string_to_val(value, cache); + let matched = get_one(&matches.iter().collect::>())?; + if matched != want_value.as_ref() { + return Err(format!( + "matched to {matched:?} but expected {want_value:?}" + )); + } + } + JqDirective::IsNot { value } => { + let unwanted_value = string_to_val(value, cache); + let matched = get_one(&matches.iter().collect::>())?; + if matched == unwanted_value.as_ref() { + return Err(format!( + "got value {unwanted_value:?}, but expected anything else" + )); + } + } + + JqDirective::IsMany { values } => { + let expected_values = + values.iter().map(|v| string_to_val(v, cache)).collect::>(); + if expected_values.len() != matches.len() { + return Err(format!( + "Expected {} values, but matched to {} values ({:?})", + expected_values.len(), + matches.len(), + matches + )); + }; + for got_value in matches { + if !expected_values.iter().any(|exp| **exp == got_value) { + return Err(format!( + "has match {got_value:?}, which was not expected", + )); + } + } + } + JqDirective::CountIs { expected } => { + if *expected != matches.len() { + return Err(format!( + "matched to `{matches:?}` with length {}, but expected length {expected}", + matches.len(), + )); + } + } + JqDirective::Set { variable } => { + let val = get_one(&matches.iter().collect::>())?; + // this might fail but only if value contains binary data or non-string object keys which are never needed + let value = serde_json::to_value(val.to_string()).unwrap(); + let vars = cache.variables.insert(variable.to_owned(), value); + let jq_vars = cache.jq_variables.insert(variable.to_owned(), val.clone()); + assert!( + jq_vars.is_none() && vars.is_none(), + "name collision: {variable:?} is duplicated" + ); + } } } - DirectiveKind::Set { variable } => { - let value = get_one(&matches)?; - let r = cache.variables.insert(variable.to_owned(), value.clone()); - assert!(r.is_none(), "name collision: {variable:?} is duplicated"); - } } Ok(()) } } -fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> { +fn get_one<'a, T: Debug>(matches: &[&'a T]) -> Result<&'a T, String> { match matches { [] => Err("matched to no values".to_owned()), [matched] => Ok(matched), @@ -223,3 +400,14 @@ fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) } } + +fn string_to_val<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Val> { + if s.starts_with("$") { + Cow::Borrowed(&cache.jq_variables.get(&s[1..]).unwrap_or_else(|| { + // FIXME(adotinthevoid): Show line number + panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.jq_variables) + })) + } else { + Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) + } +} diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 89aae46ec8f23..9327dfa267ce1 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -359,6 +359,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "itoa", "jiff", "jiff-static", + "jiff-tzdb", + "jiff-tzdb-platform", "jobserver", "lazy_static", "leb128", diff --git a/tests/rustdoc-json/attrs/cold.rs b/tests/rustdoc-json/attrs/cold.rs index ec1926e894e4f..9433a9c05f807 100644 --- a/tests/rustdoc-json/attrs/cold.rs +++ b/tests/rustdoc-json/attrs/cold.rs @@ -1,3 +1,3 @@ -//@ is "$.index[?(@.name=='cold_fn')].attrs" '[{"other": "#[attr = Cold]"}]' +//@ jq_is '.index[] | select(.name == "cold_fn").attrs.[0].other' '"#[attr = Cold]"' #[cold] pub fn cold_fn() {} diff --git a/tests/rustdoc-json/blanket_impls.rs b/tests/rustdoc-json/blanket_impls.rs index d500bf5af6bd7..2def2c2c5a956 100644 --- a/tests/rustdoc-json/blanket_impls.rs +++ b/tests/rustdoc-json/blanket_impls.rs @@ -2,7 +2,6 @@ #![no_std] -//@ has "$.index[?(@.name=='Error')].inner.assoc_type" -//@ has "$.index[?(@.name=='Error')].inner.assoc_type.type.resolved_path" -//@ has "$.index[?(@.name=='Error')].inner.assoc_type.type.resolved_path.path" \"Infallible\" +//@ jq_is '[.index[] | select(.name == "Error").inner | has("assoc_type")]' '[true, true]' +//@ jq_has '.index[] | select(.name == "Error").inner.assoc_type.type.resolved_path.path' '"Infallible"' pub struct ForBlanketTryFromImpl; diff --git a/tests/rustdoc-json/enums/discriminant/struct.rs b/tests/rustdoc-json/enums/discriminant/struct.rs index fed0e545798af..cb28b80cc238b 100644 --- a/tests/rustdoc-json/enums/discriminant/struct.rs +++ b/tests/rustdoc-json/enums/discriminant/struct.rs @@ -1,13 +1,17 @@ #[repr(i32)] -//@ is "$.index[?(@.name=='Foo')].attrs[*].repr.int" '"i32"' +//@ jq_is '.index[] | select(.name == "Foo").attrs[0].repr.int' '"i32"' pub enum Foo { - //@ is "$.index[?(@.name=='Struct')].inner.variant.discriminant" null - //@ count "$.index[?(@.name=='Struct')].inner.variant.kind.struct.fields[*]" 0 + //@ jq_set struct = '.index[] | select(.name == "Struct").inner.variant' + //@ jq_is '$struct | has("discriminant")' 'true' + //@ jq_is '$struct.discriminant?' 'null' + //@ jq_count '$struct.kind?.struct.fields[]' 0 Struct {}, - //@ is "$.index[?(@.name=='StructWithDiscr')].inner.variant.discriminant" '{"expr": "42", "value": "42"}' - //@ count "$.index[?(@.name=='StructWithDiscr')].inner.variant.kind.struct.fields[*]" 1 + //@ jq_set struct_with_discr = '.index[] | select(.name == "StructWithDiscr").inner.variant' + //@ jq_is '$struct_with_discr.discriminant?' '{"expr": "42", "value": "42"}' + //@ jq_count '$struct_with_discr.kind?.struct.fields' 1 StructWithDiscr { x: i32 } = 42, - //@ is "$.index[?(@.name=='StructWithHexDiscr')].inner.variant.discriminant" '{"expr": "0x42", "value": "66"}' - //@ count "$.index[?(@.name=='StructWithHexDiscr')].inner.variant.kind.struct.fields[*]" 2 + //@ jq_set struct_with_hex_discr = '.index[] | select(.name == "StructWithHexDiscr").inner.variant' + //@ jq_is '$struct_with_hex_discr.discriminant?' '{"expr": "0x42", "value": "66"}' + //@ jq_count '$struct_with_hex_discr.kind?.struct.fields[]' 2 StructWithHexDiscr { x: i32, y: bool } = 0x42, } diff --git a/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs b/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs index 6ecd98fadf5b8..42f5acd0c2521 100644 --- a/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs +++ b/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs @@ -5,7 +5,5 @@ extern crate color; use color::Color::Red; -//@ set red = "$.index[?(@.inner.module.is_crate)].links.Red" - -//@ !has "$.index[?(@.name == 'Red')]" -//@ !has "$.index[?(@.name == 'Color')]" +//@ jq_is '.index["\(.root)"].links | has("Red")' true +//@ jq_count '[.index[] | select(.name == "Red" or .name == "Color")][]' 0 diff --git a/tests/rustdoc-json/enums/use_glob.rs b/tests/rustdoc-json/enums/use_glob.rs index 18f21ff090334..388c62de34e0b 100644 --- a/tests/rustdoc-json/enums/use_glob.rs +++ b/tests/rustdoc-json/enums/use_glob.rs @@ -1,15 +1,15 @@ // Regression test for -//@ set Color = "$.index[?(@.name == 'Color')].id" +//@ jq_set color = '.index[] | select(.name == "Color").id' pub enum Color { Red, Green, Blue, } -//@ set use_Color = "$.index[?(@.inner.use)].id" -//@ is "$.index[?(@.inner.use)].inner.use.id" $Color -//@ is "$.index[?(@.inner.use)].inner.use.is_glob" true +//@ jq_set use_color = '.index[] | select(.inner.use).id' +//@ jq_is '.index[] | select(.inner.use).inner.use.id' $color +//@ jq_is '.index[] | select(.inner.use).inner.use.is_glob' true pub use Color::*; -//@ ismany "$.index[?(@.name == 'use_glob')].inner.module.items[*]" $Color $use_Color +//@ jq_ismany '.index[] | select(.name == "use_glob").inner.module.items[]' $color $use_color diff --git a/tests/rustdoc-json/pub_mod_in_private_mod.rs b/tests/rustdoc-json/pub_mod_in_private_mod.rs index 8b31ad0bcfe28..bc5a1df27906b 100644 --- a/tests/rustdoc-json/pub_mod_in_private_mod.rs +++ b/tests/rustdoc-json/pub_mod_in_private_mod.rs @@ -1,6 +1,6 @@ // See https://github.com/rust-lang/rust/issues/101105 -//@ !has "$.index[?(@.name=='nucleus')]" +//@ jq_count '.index[] | select(.name == "nucleus")' 0 mod corpus { pub mod nucleus {} } diff --git a/tests/rustdoc-json/reexport/simple_private.rs b/tests/rustdoc-json/reexport/simple_private.rs index 40be1708dc69d..a31c6fc44a998 100644 --- a/tests/rustdoc-json/reexport/simple_private.rs +++ b/tests/rustdoc-json/reexport/simple_private.rs @@ -1,20 +1,20 @@ //@ edition:2018 -//@ !has "$.index[?(@.name=='inner')]" +//@ jq_count '.index[] | select(.name == "inner")' 0 mod inner { - //@ set pub_id = "$.index[?(@.name=='Public')].id" + //@ jq_set pub_id = '.index[] | select(.name == "Public").id' pub struct Public; } -//@ is "$.index[?(@.inner.use)].inner.use.name" \"Public\" -//@ is "$.index[?(@.inner.use)].inner.use.id" $pub_id -//@ set use_id = "$.index[?(@.inner.use)].id" +//@ jq_is '.index[] | select(.inner.use).inner.use.name' '"Public"' +//@ jq_is '.index[] | select(.inner.use).inner.use.id' $pub_id +//@ jq_set use_id = '.index[] | select(.inner.use).id' pub use inner::Public; -//@ ismany "$.index[?(@.name=='simple_private')].inner.module.items[*]" $use_id +//@ jq_ismany '.index[] | select(.name == "simple_private").inner.module.items[]' $use_id // Test for https://github.com/rust-lang/rust/issues/135309 -//@ has "$.paths[?(@.kind=='module')].path" '["simple_private"]' -//@ !has "$.paths[*].path" '["simple_private", "inner"]' -//@ has "$.paths[?(@.kind=='struct')].path" '["simple_private", "inner", "Public"]' -//@ !has "$.paths[*].path" '["simple_private", "Public"]' +//@ jq_has '.paths[] | select(.kind == "module").path' '["simple_private"]' +//@ !jq_has '.paths[].path' '["simple_private", "inner"]' +//@ jq_has '.paths[] | select(.kind == "struct").path' '["simple_private", "inner", "Public"]' +//@ !jq_has '.paths[].path' '["simple_private", "Public"]' diff --git a/tests/rustdoc-json/type_alias.rs b/tests/rustdoc-json/type_alias.rs index 7fd23a48040d1..007f0863d4e59 100644 --- a/tests/rustdoc-json/type_alias.rs +++ b/tests/rustdoc-json/type_alias.rs @@ -1,15 +1,15 @@ -//@ set IntVec = "$.index[?(@.name=='IntVec')].id" -//@ is "$.index[?(@.name=='IntVec')].visibility" \"public\" -//@ has "$.index[?(@.name=='IntVec')].inner.type_alias" -//@ is "$.index[?(@.name=='IntVec')].span.filename" $FILE +//@ jq_set IntVec = '.index[] | select(.name == "IntVec").id' +//@ jq_is '.index[] | select(.name == "IntVec").visibility' '"public"' +//@ jq_is '.index[] | select(.name == "IntVec").inner | has("type_alias")' true +//@ jq_is '.index[] | select(.name == "IntVec").span.filename' $FILE pub type IntVec = Vec; -//@ is "$.index[?(@.name=='f')].inner.function.sig.output.resolved_path.id" $IntVec +//@ jq_is '.index[] | select(.name == "f").inner.function.sig.output.resolved_path.id' $IntVec pub fn f() -> IntVec { vec![0; 32] } -//@ !is "$.index[?(@.name=='g')].inner.function.sig.output.resolved_path.id" $IntVec +//@ !jq_is '.index[] | select(.name == "g").inner.function.sig.output.resolved_path.id' $IntVec pub fn g() -> Vec { vec![0; 32] }