1#![feature(if_let_guard)]
2use globset::{Glob, GlobSet, GlobSetBuilder};
3pub use toml::Value;
4use tracing::Level;
5pub mod flags;
6
7use std::{
8 fmt,
9 io::Read,
10 path::{Path, PathBuf},
11 str::FromStr,
12 sync::LazyLock,
13};
14
15use flags::FLAGS;
16use serde::Deserialize;
17
18pub fn dump_checker_trace_info() -> bool {
19 match FLAGS.dump_checker_trace {
20 Some(l) => Level::INFO <= l,
21 None => false,
22 }
23}
24
25pub fn dump_checker_trace() -> Option<Level> {
26 FLAGS.dump_checker_trace
27}
28
29pub fn dump_constraint() -> bool {
30 FLAGS.dump_constraint
31}
32
33pub fn dump_fhir() -> bool {
34 FLAGS.dump_fhir
35}
36
37pub fn dump_rty() -> bool {
38 FLAGS.dump_rty
39}
40
41pub fn pointer_width() -> PointerWidth {
42 FLAGS.pointer_width
43}
44
45pub fn log_dir() -> &'static PathBuf {
46 &FLAGS.log_dir
47}
48
49pub fn lean_dir() -> &'static PathBuf {
50 &FLAGS.lean_dir
51}
52
53pub fn lean_project() -> &'static str {
54 &FLAGS.lean_project
55}
56
57pub fn is_cache_enabled() -> bool {
58 FLAGS.cache.is_some()
59}
60
61pub fn trusted_default() -> bool {
62 FLAGS.trusted_default
63}
64
65pub fn ignore_default() -> bool {
66 FLAGS.ignore_default
67}
68
69pub fn lean() -> LeanMode {
70 FLAGS.lean
71}
72
73pub fn cache_path() -> Option<&'static Path> {
74 FLAGS.cache.as_deref()
75}
76
77pub fn include_pattern() -> Option<&'static IncludePattern> {
78 FLAGS.include.as_ref()
79}
80
81fn check_overflow() -> OverflowMode {
82 FLAGS.check_overflow
83}
84
85fn allow_raw_deref() -> RawDerefMode {
86 FLAGS.allow_raw_deref
87}
88
89pub fn allow_uninterpreted_cast() -> bool {
90 FLAGS.allow_uninterpreted_cast
91}
92
93fn scrape_quals() -> bool {
94 FLAGS.scrape_quals
95}
96
97pub fn no_panic() -> bool {
98 FLAGS.no_panic
99}
100
101pub fn smt_define_fun() -> bool {
102 FLAGS.smt_define_fun
103}
104
105fn solver() -> SmtSolver {
106 FLAGS.solver
107}
108
109pub fn catch_bugs() -> bool {
110 FLAGS.catch_bugs
111}
112
113pub fn annots() -> bool {
114 FLAGS.annots
115}
116
117pub fn timings() -> bool {
118 FLAGS.timings
119}
120
121pub fn verify() -> bool {
122 FLAGS.verify
123}
124
125pub fn summary() -> bool {
126 FLAGS.summary
127}
128
129pub fn full_compilation() -> bool {
130 FLAGS.full_compilation
131}
132
133#[derive(Clone, Debug, Deserialize)]
134#[serde(try_from = "String")]
135pub struct Pos {
136 pub file: String,
137 pub line: usize,
138 pub column: usize,
139}
140
141impl FromStr for Pos {
142 type Err = &'static str;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 let s = s.trim();
146 let parts: Vec<&str> = s.split(':').collect();
147 if parts.len() != 3 {
148 return Err("span format should be '<file>:<line>:<column>'");
149 }
150 let file = parts[0].to_string();
151 let line = parts[1]
152 .parse::<usize>()
153 .map_err(|_| "invalid line number")?;
154 let column = parts[2]
155 .parse::<usize>()
156 .map_err(|_| "invalid column number")?;
157 Ok(Pos { file, line, column })
158 }
159}
160
161impl TryFrom<String> for Pos {
162 type Error = &'static str;
163
164 fn try_from(value: String) -> Result<Self, Self::Error> {
165 value.parse()
166 }
167}
168
169impl fmt::Display for IncludePattern {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 write!(f, "[")?;
172 write!(f, "glob:{:?},", self.glob)?;
173 for def in &self.defs {
174 write!(f, "def:{},", def)?;
175 }
176 for pos in &self.spans {
177 write!(f, "span:{}:{}:{}", pos.file, pos.line, pos.column)?;
178 }
179 write!(f, "]")?;
180 Ok(())
181 }
182}
183#[derive(Clone)]
187pub struct IncludePattern {
188 pub glob: GlobSet,
190 pub defs: Vec<String>,
192 pub spans: Vec<Pos>,
194}
195
196impl IncludePattern {
197 fn new(includes: Vec<String>) -> Result<Self, String> {
198 let mut defs = Vec::new();
199 let mut spans = Vec::new();
200 let mut glob = GlobSetBuilder::new();
201 for include in includes {
202 if let Some(suffix) = include.strip_prefix("def:") {
203 defs.push(suffix.to_string());
204 } else if let Some(suffix) = include.strip_prefix("span:") {
205 spans.push(Pos::from_str(suffix)?);
206 } else {
207 let suffix = include.strip_prefix("glob:").unwrap_or(&include);
208 let glob_pattern = Glob::new(suffix.trim()).map_err(|_| "invalid glob pattern")?;
209 glob.add(glob_pattern);
210 }
211 }
212 let glob = glob.build().map_err(|_| "failed to build glob set")?;
213 Ok(IncludePattern { glob, defs, spans })
214 }
215}
216
217#[derive(Clone, Copy, Debug, Deserialize, Default)]
218#[serde(try_from = "String")]
219pub enum LeanMode {
220 #[default]
222 Off,
223 Emit,
225 Check,
227}
228
229impl LeanMode {
230 const ERROR: &'static str = "expected one of `emit`, or `check`";
231
232 pub fn is_emit(self) -> bool {
233 matches!(self, LeanMode::Emit)
234 }
235
236 pub fn is_check(self) -> bool {
237 matches!(self, LeanMode::Check)
238 }
239}
240
241impl FromStr for LeanMode {
242 type Err = &'static str;
243
244 fn from_str(s: &str) -> Result<Self, Self::Err> {
245 let s = s.to_ascii_lowercase();
246 match s.as_str() {
247 "off" => Ok(LeanMode::Off),
248 "emit" => Ok(LeanMode::Emit),
249 "check" => Ok(LeanMode::Check),
250 _ => Err(Self::ERROR),
251 }
252 }
253}
254
255impl TryFrom<String> for LeanMode {
256 type Error = &'static str;
257
258 fn try_from(value: String) -> Result<Self, Self::Error> {
259 value.parse()
260 }
261}
262
263impl fmt::Display for LeanMode {
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 match self {
266 LeanMode::Off => write!(f, "off"),
267 LeanMode::Emit => write!(f, "emit"),
268 LeanMode::Check => write!(f, "check"),
269 }
270 }
271}
272
273#[derive(Clone, Copy, Debug, Deserialize, Default)]
274#[serde(try_from = "String")]
275pub enum OverflowMode {
276 #[default]
278 None,
279 Lazy,
282 StrictUnder,
285 Strict,
288}
289
290impl OverflowMode {
291 const ERROR: &'static str = "expected one of `none`, `lazy`, or `strict`";
292}
293impl FromStr for OverflowMode {
294 type Err = &'static str;
295
296 fn from_str(s: &str) -> Result<Self, Self::Err> {
297 let s = s.to_ascii_lowercase();
298 match s.as_str() {
299 "none" => Ok(OverflowMode::None),
300 "lazy" => Ok(OverflowMode::Lazy),
301 "strict" => Ok(OverflowMode::Strict),
302 "strict-under" => Ok(OverflowMode::StrictUnder),
303 _ => Err(Self::ERROR),
304 }
305 }
306}
307
308impl TryFrom<String> for OverflowMode {
309 type Error = &'static str;
310
311 fn try_from(value: String) -> Result<Self, Self::Error> {
312 value.parse()
313 }
314}
315
316impl fmt::Display for OverflowMode {
317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318 match self {
319 OverflowMode::None => write!(f, "none"),
320 OverflowMode::Lazy => write!(f, "lazy"),
321 OverflowMode::Strict => write!(f, "strict"),
322 OverflowMode::StrictUnder => write!(f, "strict-under"),
323 }
324 }
325}
326
327#[derive(Clone, Copy, Debug, Deserialize, Default)]
328#[serde(try_from = "String")]
329pub enum RawDerefMode {
330 #[default]
332 None,
333 Ok,
335}
336
337impl RawDerefMode {
338 const ERROR: &'static str = "expected one of `none` or `ok`";
339}
340
341impl FromStr for RawDerefMode {
342 type Err = &'static str;
343
344 fn from_str(s: &str) -> Result<Self, Self::Err> {
345 let s = s.to_ascii_lowercase();
346 match s.as_str() {
347 "none" => Ok(RawDerefMode::None),
348 "ok" => Ok(RawDerefMode::Ok),
349 _ => Err(Self::ERROR),
350 }
351 }
352}
353
354impl TryFrom<String> for RawDerefMode {
355 type Error = &'static str;
356
357 fn try_from(value: String) -> Result<Self, Self::Error> {
358 value.parse()
359 }
360}
361
362impl fmt::Display for RawDerefMode {
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364 match self {
365 RawDerefMode::None => write!(f, "none"),
366 RawDerefMode::Ok => write!(f, "ok"),
367 }
368 }
369}
370
371#[derive(Clone, Copy, Debug, Deserialize, Default)]
372#[serde(try_from = "String")]
373pub enum SmtSolver {
374 #[default]
375 Z3,
376 CVC5,
377}
378
379impl SmtSolver {
380 const ERROR: &'static str = "expected one of `z3` or `cvc5`";
381}
382
383impl FromStr for SmtSolver {
384 type Err = &'static str;
385
386 fn from_str(s: &str) -> Result<Self, Self::Err> {
387 let s = s.to_ascii_lowercase();
388 match s.as_str() {
389 "z3" => Ok(SmtSolver::Z3),
390 "cvc5" => Ok(SmtSolver::CVC5),
391 _ => Err(Self::ERROR),
392 }
393 }
394}
395
396impl TryFrom<String> for SmtSolver {
397 type Error = &'static str;
398
399 fn try_from(value: String) -> Result<Self, Self::Error> {
400 value.parse()
401 }
402}
403
404impl fmt::Display for SmtSolver {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 match self {
407 SmtSolver::Z3 => write!(f, "z3"),
408 SmtSolver::CVC5 => write!(f, "cvc5"),
409 }
410 }
411}
412
413#[derive(Clone, Copy, Debug)]
415pub struct InferOpts {
416 pub check_overflow: OverflowMode,
419 pub scrape_quals: bool,
421 pub solver: SmtSolver,
422 pub allow_uninterpreted_cast: bool,
424 pub allow_raw_deref: RawDerefMode,
426}
427
428impl From<PartialInferOpts> for InferOpts {
429 fn from(opts: PartialInferOpts) -> Self {
430 InferOpts {
431 check_overflow: opts.check_overflow.unwrap_or_else(check_overflow),
432 scrape_quals: opts.scrape_quals.unwrap_or_else(scrape_quals),
433 solver: opts.solver.unwrap_or_else(solver),
434 allow_uninterpreted_cast: opts
435 .allow_uninterpreted_cast
436 .unwrap_or_else(allow_uninterpreted_cast),
437 allow_raw_deref: opts.allow_raw_deref.unwrap_or_else(allow_raw_deref),
438 }
439 }
440}
441
442#[derive(Clone, Copy, Default, Deserialize, Debug)]
443pub struct PartialInferOpts {
444 pub check_overflow: Option<OverflowMode>,
445 pub scrape_quals: Option<bool>,
446 pub solver: Option<SmtSolver>,
447 pub allow_uninterpreted_cast: Option<bool>,
448 pub allow_raw_deref: Option<RawDerefMode>,
449}
450
451impl PartialInferOpts {
452 pub fn merge(&mut self, other: &Self) {
453 self.check_overflow = self.check_overflow.or(other.check_overflow);
454 self.allow_uninterpreted_cast = self
455 .allow_uninterpreted_cast
456 .or(other.allow_uninterpreted_cast);
457 self.scrape_quals = self.scrape_quals.or(other.scrape_quals);
458 self.solver = self.solver.or(other.solver);
459 self.allow_raw_deref = self.allow_raw_deref.or(other.allow_raw_deref);
460 }
461}
462
463#[derive(Copy, Clone, Deserialize, Default)]
464pub enum PointerWidth {
465 W32,
466 #[default]
467 W64,
468}
469
470impl PointerWidth {
471 const ERROR: &str = "pointer width must be 32 or 64";
472
473 pub fn bits(self) -> u64 {
474 match self {
475 PointerWidth::W32 => 32,
476 PointerWidth::W64 => 64,
477 }
478 }
479}
480
481impl FromStr for PointerWidth {
482 type Err = &'static str;
483
484 fn from_str(s: &str) -> Result<Self, Self::Err> {
485 match s {
486 "32" => Ok(PointerWidth::W32),
487 "64" => Ok(PointerWidth::W64),
488 _ => Err(Self::ERROR),
489 }
490 }
491}
492
493fn config_path() -> Option<PathBuf> {
494 let mut path = std::env::current_dir().unwrap();
496 loop {
497 for name in ["flux.toml", ".flux.toml"] {
498 let file = path.join(name);
499 if file.exists() {
500 return Some(file);
501 }
502 }
503 if !path.pop() {
504 return None;
505 }
506 }
507}
508
509pub static CONFIG_FILE: LazyLock<Value> = LazyLock::new(|| {
510 if let Some(path) = config_path() {
511 let mut file = std::fs::File::open(path).unwrap();
512 let mut contents = String::new();
513 file.read_to_string(&mut contents).unwrap();
514 toml::from_str(&contents).unwrap()
515 } else {
516 toml::from_str("").unwrap()
517 }
518});