From 2c9fb22f040094b4cb982c84254bb5d0589c38e8 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Wed, 28 May 2025 21:16:15 +0000 Subject: [PATCH 1/3] jsondoclint: Extract `Command`/`CommandKind` into its own file --- src/tools/jsondocck/src/directive.rs | 230 +++++++++++++++++++++++++++ src/tools/jsondocck/src/main.rs | 225 +------------------------- 2 files changed, 233 insertions(+), 222 deletions(-) create mode 100644 src/tools/jsondocck/src/directive.rs diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs new file mode 100644 index 0000000000000..8617379ed270b --- /dev/null +++ b/src/tools/jsondocck/src/directive.rs @@ -0,0 +1,230 @@ +use std::borrow::Cow; +use serde_json::Value; +use crate::cache::Cache; + +#[derive(Debug)] +pub struct Command { + pub kind: CommandKind, + pub path: String, + pub lineno: usize, +} + +#[derive(Debug)] +pub enum CommandKind { + /// `//@ has ` + /// + /// Checks the path exists. + HasPath, + + /// `//@ has ` + /// + /// Check one thing at the path is equal to the value. + HasValue { value: String }, + + /// `//@ !has ` + /// + /// Checks the path doesn't exist. + HasNotPath, + + /// `//@ !has ` + /// + /// Checks the path exists, but doesn't have the given value. + HasNotValue { value: String }, + + /// `//@ is ` + /// + /// Check the path is the given value. + Is { value: String }, + + /// `//@ is ...` + /// + /// Check that the path matches to exactly every given value. + IsMany { values: Vec }, + + /// `//@ !is ` + /// + /// Check the path isn't the given value. + IsNot { value: String }, + + /// `//@ count ` + /// + /// Check the path has the expected number of matches. + CountIs { expected: usize }, + + /// `//@ set = ` + Set { variable: String }, +} + +impl CommandKind { + /// Returns both the kind and the path. + /// + /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest). + pub fn parse<'a>( + command_name: &str, + negated: bool, + args: &'a [String], + ) -> Option<(Self, &'a str)> { + let kind = match (command_name, negated) { + ("count", false) => { + assert_eq!(args.len(), 2); + let expected = args[1].parse().expect("invalid number for `count`"); + Self::CountIs { expected } + } + + ("ismany", false) => { + // 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 } + } + + ("is", false) => { + assert_eq!(args.len(), 2); + Self::Is { value: args[1].clone() } + } + ("is", true) => { + assert_eq!(args.len(), 2); + Self::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])); + } + + ("has", false) => match args { + [_path] => Self::HasPath, + [_path, value] => Self::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() }, + _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), + }, + + (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => { + return None; + } + _ => { + panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" }) + } + }; + + Some((kind, &args[0])) + } +} + +impl Command { + /// Performs the actual work of ensuring a command passes. + pub fn check(&self, cache: &mut Cache) -> Result<(), String> { + let matches = cache.select(&self.path); + match &self.kind { + CommandKind::HasPath => { + if matches.is_empty() { + return Err("matched to no values".to_owned()); + } + } + CommandKind::HasNotPath => { + if !matches.is_empty() { + return Err(format!("matched to {matches:?}, but wanted no matches")); + } + } + CommandKind::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:?}" + )); + } + } + CommandKind::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:?}" + )); + } + } + + CommandKind::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:?}")); + } + } + CommandKind::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")); + } + } + + CommandKind::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",)); + } + } + } + CommandKind::CountIs { expected } => { + if *expected != matches.len() { + return Err(format!( + "matched to `{matches:?}` with length {}, but expected length {expected}", + matches.len(), + )); + } + } + CommandKind::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> { + match matches { + [] => Err("matched to no values".to_owned()), + [matched] => Ok(matched), + _ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")), + } +} + +// FIXME: This setup is temporary until we figure out how to improve this situation. +// See . +include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs")); + +fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { + if s.starts_with("$") { + Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| { + // FIXME(adotinthevoid): Show line number + panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables) + })) + } else { + Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) + } +} \ No newline at end of file diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 65ad38da98b08..80557b682430c 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -1,17 +1,17 @@ -use std::borrow::Cow; use std::process::ExitCode; use std::sync::LazyLock; use std::{env, fs}; use regex::{Regex, RegexBuilder}; -use serde_json::Value; mod cache; mod config; +mod directive; mod error; use cache::Cache; use config::parse_config; +use directive::{Command, CommandKind}; use error::CkError; fn main() -> ExitCode { @@ -25,7 +25,7 @@ fn main() -> ExitCode { }; for command in commands { - if let Err(message) = check_command(&command, &mut cache) { + if let Err(message) = command.check( &mut cache) { failed.push(CkError { command, message }); } } @@ -41,116 +41,6 @@ fn main() -> ExitCode { } } -#[derive(Debug)] -pub struct Command { - kind: CommandKind, - path: String, - lineno: usize, -} - -#[derive(Debug)] -enum CommandKind { - /// `//@ has ` - /// - /// Checks the path exists. - HasPath, - - /// `//@ has ` - /// - /// Check one thing at the path is equal to the value. - HasValue { value: String }, - - /// `//@ !has ` - /// - /// Checks the path doesn't exist. - HasNotPath, - - /// `//@ !has ` - /// - /// Checks the path exists, but doesn't have the given value. - HasNotValue { value: String }, - - /// `//@ is ` - /// - /// Check the path is the given value. - Is { value: String }, - - /// `//@ is ...` - /// - /// Check that the path matches to exactly every given value. - IsMany { values: Vec }, - - /// `//@ !is ` - /// - /// Check the path isn't the given value. - IsNot { value: String }, - - /// `//@ count ` - /// - /// Check the path has the expected number of matches. - CountIs { expected: usize }, - - /// `//@ set = ` - Set { variable: String }, -} - -impl CommandKind { - /// Returns both the kind and the path. - /// - /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest). - fn parse<'a>(command_name: &str, negated: bool, args: &'a [String]) -> Option<(Self, &'a str)> { - let kind = match (command_name, negated) { - ("count", false) => { - assert_eq!(args.len(), 2); - let expected = args[1].parse().expect("invalid number for `count`"); - Self::CountIs { expected } - } - - ("ismany", false) => { - // 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 } - } - - ("is", false) => { - assert_eq!(args.len(), 2); - Self::Is { value: args[1].clone() } - } - ("is", true) => { - assert_eq!(args.len(), 2); - Self::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])); - } - - ("has", false) => match args { - [_path] => Self::HasPath, - [_path, value] => Self::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() }, - _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), - }, - - (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => { - return None; - } - _ => { - panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" }) - } - }; - - Some((kind, &args[0])) - } -} - static LINE_PATTERN: LazyLock = LazyLock::new(|| { RegexBuilder::new( r#" @@ -183,10 +73,6 @@ fn print_err(msg: &str, lineno: usize) { eprintln!("Invalid command: {} on line {}", msg, lineno) } -// FIXME: This setup is temporary until we figure out how to improve this situation. -// See . -include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs")); - /// Get a list of commands from a file. fn get_commands(template: &str) -> Result, ()> { let mut commands = Vec::new(); @@ -222,108 +108,3 @@ fn get_commands(template: &str) -> Result, ()> { if !errors { Ok(commands) } else { Err(()) } } - -/// Performs the actual work of ensuring a command passes. -fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> { - let matches = cache.select(&command.path); - match &command.kind { - CommandKind::HasPath => { - if matches.is_empty() { - return Err("matched to no values".to_owned()); - } - } - CommandKind::HasNotPath => { - if !matches.is_empty() { - return Err(format!("matched to {matches:?}, but wanted no matches")); - } - } - CommandKind::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:?}")); - } - } - CommandKind::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:?}" - )); - } - } - - CommandKind::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:?}")); - } - } - CommandKind::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")); - } - } - - CommandKind::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",)); - } - } - } - CommandKind::CountIs { expected } => { - if *expected != matches.len() { - return Err(format!( - "matched to `{matches:?}` with length {}, but expected length {expected}", - matches.len(), - )); - } - } - CommandKind::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> { - match matches { - [] => Err("matched to no values".to_owned()), - [matched] => Ok(matched), - _ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")), - } -} - -fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { - if s.starts_with("$") { - Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| { - // FIXME(adotinthevoid): Show line number - panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables) - })) - } else { - Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) - } -} From 14db1b5b1c2b55dfa89c7b958e70efb936a33e7c Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Wed, 28 May 2025 21:22:21 +0000 Subject: [PATCH 2/3] jsondocck: command -> directive --- src/tools/jsondocck/src/config.rs | 2 +- src/tools/jsondocck/src/directive.rs | 44 +++++++++++++++------------- src/tools/jsondocck/src/error.rs | 4 +-- src/tools/jsondocck/src/main.rs | 30 +++++++++---------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/tools/jsondocck/src/config.rs b/src/tools/jsondocck/src/config.rs index 9b3ba3f3fbe39..6bef37c225973 100644 --- a/src/tools/jsondocck/src/config.rs +++ b/src/tools/jsondocck/src/config.rs @@ -4,7 +4,7 @@ use getopts::Options; pub struct Config { /// The directory documentation output was generated in pub doc_dir: String, - /// The file documentation was generated for, with docck commands to check + /// The file documentation was generated for, with docck directives to check pub template: String, } diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs index 8617379ed270b..5269ffb469fa9 100644 --- a/src/tools/jsondocck/src/directive.rs +++ b/src/tools/jsondocck/src/directive.rs @@ -1,16 +1,18 @@ use std::borrow::Cow; + use serde_json::Value; + use crate::cache::Cache; #[derive(Debug)] -pub struct Command { - pub kind: CommandKind, +pub struct Directive { + pub kind: DirectiveKind, pub path: String, pub lineno: usize, } #[derive(Debug)] -pub enum CommandKind { +pub enum DirectiveKind { /// `//@ has ` /// /// Checks the path exists. @@ -55,16 +57,16 @@ pub enum CommandKind { Set { variable: String }, } -impl CommandKind { +impl DirectiveKind { /// Returns both the kind and the path. /// - /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest). + /// Returns `None` if the directive isn't from jsondocck (e.g. from compiletest). pub fn parse<'a>( - command_name: &str, + directive_name: &str, negated: bool, args: &'a [String], ) -> Option<(Self, &'a str)> { - let kind = match (command_name, negated) { + let kind = match (directive_name, negated) { ("count", false) => { assert_eq!(args.len(), 2); let expected = args[1].parse().expect("invalid number for `count`"); @@ -104,11 +106,11 @@ impl CommandKind { _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), }, - (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => { + (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => { return None; } _ => { - panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" }) + panic!("Invalid directive `//@ {}{directive_name}`", if negated { "!" } else { "" }) } }; @@ -116,22 +118,22 @@ impl CommandKind { } } -impl Command { - /// Performs the actual work of ensuring a command passes. +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 { - CommandKind::HasPath => { + DirectiveKind::HasPath => { if matches.is_empty() { return Err("matched to no values".to_owned()); } } - CommandKind::HasNotPath => { + DirectiveKind::HasNotPath => { if !matches.is_empty() { return Err(format!("matched to {matches:?}, but wanted no matches")); } } - CommandKind::HasValue { value } => { + DirectiveKind::HasValue { value } => { let want_value = string_to_value(value, cache); if !matches.contains(&want_value.as_ref()) { return Err(format!( @@ -139,7 +141,7 @@ impl Command { )); } } - CommandKind::HasNotValue { value } => { + DirectiveKind::HasNotValue { value } => { let wantnt_value = string_to_value(value, cache); if matches.contains(&wantnt_value.as_ref()) { return Err(format!( @@ -152,14 +154,14 @@ impl Command { } } - CommandKind::Is { 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:?}")); } } - CommandKind::IsNot { value } => { + DirectiveKind::IsNot { value } => { let wantnt_value = string_to_value(value, cache); let matched = get_one(&matches)?; if matched == wantnt_value.as_ref() { @@ -167,7 +169,7 @@ impl Command { } } - CommandKind::IsMany { values } => { + 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. @@ -187,7 +189,7 @@ impl Command { } } } - CommandKind::CountIs { expected } => { + DirectiveKind::CountIs { expected } => { if *expected != matches.len() { return Err(format!( "matched to `{matches:?}` with length {}, but expected length {expected}", @@ -195,7 +197,7 @@ impl Command { )); } } - CommandKind::Set { variable } => { + 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"); @@ -227,4 +229,4 @@ fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { } else { Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) } -} \ No newline at end of file +} diff --git a/src/tools/jsondocck/src/error.rs b/src/tools/jsondocck/src/error.rs index 0a3e085b405ba..eb2932f780323 100644 --- a/src/tools/jsondocck/src/error.rs +++ b/src/tools/jsondocck/src/error.rs @@ -1,7 +1,7 @@ -use crate::Command; +use crate::Directive; #[derive(Debug)] pub struct CkError { pub message: String, - pub command: Command, + pub directive: Directive, } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 80557b682430c..d84be4d3a3a6f 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -11,7 +11,7 @@ mod error; use cache::Cache; use config::parse_config; -use directive::{Command, CommandKind}; +use directive::{Directive, DirectiveKind}; use error::CkError; fn main() -> ExitCode { @@ -19,14 +19,14 @@ fn main() -> ExitCode { let mut failed = Vec::new(); let mut cache = Cache::new(&config); - let Ok(commands) = get_commands(&config.template) else { + let Ok(directives) = get_directives(&config.template) else { eprintln!("Jsondocck failed for {}", &config.template); return ExitCode::FAILURE; }; - for command in commands { - if let Err(message) = command.check( &mut cache) { - failed.push(CkError { command, message }); + for directive in directives { + if let Err(message) = directive.check(&mut cache) { + failed.push(CkError { directive, message }); } } @@ -34,7 +34,7 @@ fn main() -> ExitCode { ExitCode::SUCCESS } else { for i in failed { - eprintln!("{}:{}, command failed", config.template, i.command.lineno); + eprintln!("{}:{}, directive failed", config.template, i.directive.lineno); eprintln!("{}", i.message) } ExitCode::FAILURE @@ -47,7 +47,7 @@ static LINE_PATTERN: LazyLock = LazyLock::new(|| { ^\s* //@\s+ (?P!?) - (?P[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*) + (?P[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*) (?P.*)$ "#, ) @@ -70,12 +70,12 @@ static DEPRECATED_LINE_PATTERN: LazyLock = LazyLock::new(|| { }); fn print_err(msg: &str, lineno: usize) { - eprintln!("Invalid command: {} on line {}", msg, lineno) + eprintln!("Invalid directive: {} on line {}", msg, lineno) } -/// Get a list of commands from a file. -fn get_commands(template: &str) -> Result, ()> { - let mut commands = Vec::new(); +/// Get a list of directives from a file. +fn get_directives(template: &str) -> Result, ()> { + let mut directives = Vec::new(); let mut errors = false; let file = fs::read_to_string(template).unwrap(); @@ -83,7 +83,7 @@ fn get_commands(template: &str) -> Result, ()> { let lineno = lineno + 1; if DEPRECATED_LINE_PATTERN.is_match(line) { - print_err("Deprecated command syntax, replace `// @` with `//@ `", lineno); + print_err("Deprecated directive syntax, replace `// @` with `//@ `", lineno); errors = true; continue; } @@ -101,10 +101,10 @@ fn get_commands(template: &str) -> Result, ()> { continue; }; - if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) { - commands.push(Command { kind, lineno, path: path.to_owned() }) + if let Some((kind, path)) = DirectiveKind::parse(&cap["directive"], negated, &args) { + directives.push(Directive { kind, lineno, path: path.to_owned() }) } } - if !errors { Ok(commands) } else { Err(()) } + if !errors { Ok(directives) } else { Err(()) } } From 91ad4bf08784916edfb4e498c3749bed76b0686a Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Thu, 5 Jun 2025 11:33:38 +0000 Subject: [PATCH 3/3] jsondocck: Explain what `KNOWN_DIRECTIVE_NAMES` is doing --- src/tools/jsondocck/src/directive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs index 5269ffb469fa9..fdb2fa6dbbe01 100644 --- a/src/tools/jsondocck/src/directive.rs +++ b/src/tools/jsondocck/src/directive.rs @@ -105,7 +105,7 @@ impl DirectiveKind { [_path, value] => Self::HasNotValue { value: value.clone() }, _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), }, - + // Ignore compiletest directives, like //@ edition (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => { return None; }