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 default_value_t = false,
76 action(clap::ArgAction::SetTrue),
77 help_heading = "Input"
78 )]
79 pub allow_empty: bool,
80 #[arg(
84 long = "check-prefix",
85 value_name = "PREFIX",
86 default_value = "CHECK",
87 action(clap::ArgAction::Append),
88 value_parser(re_value_parser("^[A-Za-z][A-Za-z0-9_]*")),
89 help_heading = "Syntax"
90 )]
91 pub check_prefixes: Vec<Arc<str>>,
92 #[arg(
97 long = "comment-prefix",
98 value_name = "PREFIX",
99 default_value = "COM,RUN",
100 action(clap::ArgAction::Append),
101 value_parser(re_value_parser("^[A-Za-z][A-Za-z0-9_]*")),
102 help_heading = "Syntax"
103 )]
104 pub comment_prefixes: Vec<Arc<str>>,
105 #[arg(long, default_value_t = false, help_heading = "Syntax")]
108 pub allow_unused_prefixes: bool,
109 #[arg(
118 long = "strict-whitespace",
119 default_value_t = false,
120 help_heading = "Matching"
121 )]
122 pub strict_whitespace: bool,
123 #[arg(long, default_value_t = false, help_heading = "Matching")]
133 pub match_full_lines: bool,
134 #[arg(long, default_value_t = false, help_heading = "Matching")]
136 pub ignore_case: bool,
137 #[arg(long, value_name = "CHECK", help_heading = "Matching")]
146 pub implicit_check_not: Vec<String>,
147 #[arg(long, value_enum, value_name = "TYPE", default_value_t = Dump::Fail, help_heading = "Output")]
149 pub dump_input: Dump,
150 #[arg(
156 long,
157 value_enum,
158 value_name = "KIND",
159 default_value_t = DumpFilter::Error,
160 default_value_ifs([("dump-input", "fail", Some("error")), ("dump-input", "always", Some("all"))]),
161 help_heading = "Output"
162 )]
163 pub dump_input_filter: DumpFilter,
164 #[arg(long, default_value_t = false, help_heading = "Variables")]
170 pub enable_var_scope: bool,
171 #[arg(
175 long = "define",
176 short = 'D',
177 value_name = "NAME=VALUE",
178 help_heading = "Variables"
179 )]
180 pub variables: Vec<expr::CliVariable>,
181 #[arg(long, short = 'v', action = ArgAction::Count, help_heading = "Output")]
189 pub verbose: u8,
190 #[arg(
192 global(true),
193 value_enum,
194 long,
195 default_value_t = ColorChoice::Auto,
196 default_missing_value = "auto",
197 help_heading = "Output"
198 )]
199 pub color: ColorChoice,
200}
201impl Default for Config {
202 fn default() -> Self {
203 Self {
204 allow_empty: false,
205 check_prefixes: vec![Arc::from("CHECK".to_string().into_boxed_str())],
206 comment_prefixes: vec![
207 Arc::from("COM".to_string().into_boxed_str()),
208 Arc::from("RUN".to_string().into_boxed_str()),
209 ],
210 allow_unused_prefixes: false,
211 strict_whitespace: false,
212 match_full_lines: false,
213 ignore_case: false,
214 implicit_check_not: vec![],
215 dump_input: Default::default(),
216 dump_input_filter: Default::default(),
217 enable_var_scope: false,
218 variables: vec![],
219 verbose: 0,
220 color: Default::default(),
221 }
222 }
223}
224
225#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)]
226pub enum Dump {
227 Help,
229 Always,
231 #[default]
233 Fail,
234 Never,
236}
237
238#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)]
239pub enum DumpFilter {
240 All,
242 AnnotationFull,
244 Annotation,
246 #[default]
248 Error,
249}
250
251fn re_value_parser(r: &'static str) -> ValueParser {
252 use clap::{error::ErrorKind, Error};
253
254 ValueParser::from(move |s: &str| -> Result<Arc<str>, clap::Error> {
255 let re = regex::Regex::new(r).unwrap();
256 if re.is_match(s) {
257 Ok(Arc::from(s.to_owned().into_boxed_str()))
258 } else {
259 Err(Error::raw(
260 ErrorKind::ValueValidation,
261 "'{s}' does not match expected pattern `{r}`",
262 ))
263 }
264 })
265}
266
267#[macro_export]
313macro_rules! filecheck {
314 ($input:expr, $checks:expr) => {
315 $crate::filecheck!($input, $checks, $crate::Config::default())
316 };
317
318 ($input:expr, $checks:expr, $config:expr) => {{
319 let config = $config;
320 let input = $input.to_string();
321 let checks = $checks.to_string();
322 let mut test = $crate::Test::new(checks, &config);
323 match test.verify(input) {
324 Err(err) => {
325 let printer = $crate::litcheck::diagnostics::reporting::PrintDiagnostic::new(err);
326 panic!("{printer}");
327 }
328 Ok(matches) => matches,
329 }
330 }};
331}