1pub mod ast;
2pub mod check;
3mod context;
4mod cursor;
5mod env;
6pub mod errors;
7pub mod expr;
8mod input;
9pub mod parse;
10pub mod pattern;
11pub mod rules;
12mod test;
13
14pub use self::errors::{CheckFailedError, RelatedCheckError, RelatedError, TestFailed};
15#[cfg(test)]
16pub use self::test::TestContext;
17pub use self::test::{Test, TestResult};
18
19use clap::{builder::ValueParser, ArgAction, Args, ColorChoice, ValueEnum};
20use std::sync::Arc;
21
22#[doc(hidden)]
23pub use litcheck;
24
25pub(crate) mod common {
26 pub use std::{
27 borrow::Cow,
28 fmt,
29 ops::{ControlFlow, RangeBounds},
30 sync::Arc,
31 };
32
33 pub use either::Either::{self, Left, Right};
34 pub use litcheck::{
35 diagnostics::{
36 ArcSource, Diag, DiagResult, Diagnostic, Label, NamedSourceFile, Report, Source,
37 SourceFile, SourceSpan, Span, Spanned,
38 },
39 range::{self, Range},
40 text::{self, Newline},
41 StringInterner, Symbol,
42 };
43 pub use regex_automata::{meta::Regex, util::look::LookMatcher};
44 pub use smallvec::{smallvec, SmallVec};
45
46 pub use crate::ast::{Check, Constraint};
47 pub use crate::context::{Context, ContextExt, ContextGuard, MatchContext};
48 pub use crate::cursor::{Cursor, CursorGuard, CursorPosition};
49 pub use crate::env::{Env, LexicalScope, LexicalScopeExtend, LexicalScopeMut, ScopeGuard};
50 pub use crate::errors::{
51 CheckFailedError, RelatedCheckError, RelatedError, RelatedLabel, TestFailed,
52 };
53 pub use crate::expr::{BinaryOp, Expr, Number, NumberFormat, Value, VariableName};
54 pub use crate::input::Input;
55 pub use crate::pattern::{
56 AnyMatcher, CaptureInfo, MatchInfo, MatchResult, MatchType, Matcher, MatcherMut, Matches,
57 Pattern, PatternIdentifier, PatternSearcher, Searcher,
58 };
59 pub use crate::rules::{DynRule, Rule};
60 #[cfg(test)]
61 pub(crate) use crate::test::TestContext;
62 pub use crate::test::TestResult;
63 pub use crate::Config;
64}
65
66pub const DEFAULT_CHECK_PREFIXES: &[&str] = &["CHECK"];
67pub const DEFAULT_COMMENT_PREFIXES: &[&str] = &["COM", "RUN"];
68
69#[derive(Debug, Args)]
72pub struct Config {
73 #[arg(
75 long,
76 default_value_t = false,
77 action(clap::ArgAction::SetTrue),
78 help_heading = "Input"
79 )]
80 pub allow_empty: bool,
81 #[arg(
85 long = "check-prefix",
86 value_name = "PREFIX",
87 default_value = "CHECK",
88 action(clap::ArgAction::Append),
89 value_parser(re_value_parser("^[A-Za-z][A-Za-z0-9_]*")),
90 help_heading = "Syntax"
91 )]
92 pub check_prefixes: Vec<Arc<str>>,
93 #[arg(
98 long = "comment-prefix",
99 value_name = "PREFIX",
100 default_value = "COM,RUN",
101 action(clap::ArgAction::Append),
102 value_parser(re_value_parser("^[A-Za-z][A-Za-z0-9_]*")),
103 help_heading = "Syntax"
104 )]
105 pub comment_prefixes: Vec<Arc<str>>,
106 #[arg(long, default_value_t = false, help_heading = "Syntax")]
109 pub allow_unused_prefixes: bool,
110 #[arg(
119 long = "strict-whitespace",
120 default_value_t = false,
121 help_heading = "Matching"
122 )]
123 pub strict_whitespace: bool,
124 #[arg(long, default_value_t = false, help_heading = "Matching")]
134 pub match_full_lines: bool,
135 #[arg(long, default_value_t = false, help_heading = "Matching")]
137 pub ignore_case: bool,
138 #[arg(long, value_name = "CHECK", help_heading = "Matching")]
147 pub implicit_check_not: Vec<String>,
148 #[arg(long, value_enum, value_name = "TYPE", default_value_t = Dump::Fail, help_heading = "Output")]
150 pub dump_input: Dump,
151 #[arg(
157 long,
158 value_enum,
159 value_name = "KIND",
160 default_value_t = DumpFilter::Error,
161 default_value_ifs([("dump-input", "fail", Some("error")), ("dump-input", "always", Some("all"))]),
162 help_heading = "Output"
163 )]
164 pub dump_input_filter: DumpFilter,
165 #[arg(long, default_value_t = false, help_heading = "Variables")]
171 pub enable_var_scope: bool,
172 #[arg(
176 long = "define",
177 short = 'D',
178 value_name = "NAME=VALUE",
179 help_heading = "Variables"
180 )]
181 pub variables: Vec<expr::CliVariable>,
182 #[arg(long, short = 'v', action = ArgAction::Count, help_heading = "Output")]
190 pub verbose: u8,
191 #[arg(
193 global(true),
194 value_enum,
195 long,
196 default_value_t = ColorChoice::Auto,
197 default_missing_value = "auto",
198 help_heading = "Output"
199 )]
200 pub color: ColorChoice,
201}
202impl Default for Config {
203 fn default() -> Self {
204 Self {
205 allow_empty: false,
206 check_prefixes: vec![Arc::from("CHECK".to_string().into_boxed_str())],
207 comment_prefixes: vec![
208 Arc::from("COM".to_string().into_boxed_str()),
209 Arc::from("RUN".to_string().into_boxed_str()),
210 ],
211 allow_unused_prefixes: false,
212 strict_whitespace: false,
213 match_full_lines: false,
214 ignore_case: false,
215 implicit_check_not: vec![],
216 dump_input: Default::default(),
217 dump_input_filter: Default::default(),
218 enable_var_scope: false,
219 variables: vec![],
220 verbose: 0,
221 color: Default::default(),
222 }
223 }
224}
225
226#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)]
227pub enum Dump {
228 Help,
230 Always,
232 #[default]
234 Fail,
235 Never,
237}
238
239#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)]
240pub enum DumpFilter {
241 All,
243 AnnotationFull,
245 Annotation,
247 #[default]
249 Error,
250}
251
252fn re_value_parser(r: &'static str) -> ValueParser {
253 use clap::{error::ErrorKind, Error};
254
255 ValueParser::from(move |s: &str| -> Result<Arc<str>, clap::Error> {
256 let re = regex::Regex::new(r).unwrap();
257 if re.is_match(s) {
258 Ok(Arc::from(s.to_owned().into_boxed_str()))
259 } else {
260 Err(Error::raw(
261 ErrorKind::ValueValidation,
262 "'{s}' does not match expected pattern `{r}`",
263 ))
264 }
265 })
266}
267
268#[macro_export]
314macro_rules! filecheck {
315 ($input:expr, $checks:expr) => {
316 $crate::filecheck!($input, $checks, $crate::Config::default())
317 };
318
319 ($input:expr, $checks:expr, $config:expr) => {{
320 let config = $config;
321 let input = $input.to_string();
322 let checks = $checks.to_string();
323 let mut test = $crate::Test::new(checks, &config);
324 match test.verify(input) {
325 Err(err) => {
326 let printer = $crate::litcheck::diagnostics::reporting::PrintDiagnostic::new(err);
327 panic!("{printer}");
328 }
329 Ok(matches) => matches,
330 }
331 }};
332}