/src/regex/fuzz/fuzz_targets/ast_roundtrip.rs
Line | Count | Source |
1 | | #![no_main] |
2 | | |
3 | | use { |
4 | | libfuzzer_sys::{fuzz_target, Corpus}, |
5 | | regex_syntax::ast::{ |
6 | | parse::Parser, visit, Ast, Flag, Flags, GroupKind, Visitor, |
7 | | }, |
8 | | }; |
9 | | |
10 | | #[derive(Eq, PartialEq, arbitrary::Arbitrary)] |
11 | | struct FuzzData { |
12 | | ast: Ast, |
13 | | } |
14 | | |
15 | | impl std::fmt::Debug for FuzzData { |
16 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
17 | 0 | let mut builder = f.debug_struct("FuzzData"); |
18 | 0 | builder.field("ast", &self.ast); |
19 | 0 | builder.field("stringified", &format!("{}", self.ast)); |
20 | 0 | builder.finish() |
21 | 0 | } |
22 | | } |
23 | | |
24 | | struct VerboseVisitor; |
25 | | |
26 | | impl Visitor for VerboseVisitor { |
27 | | type Output = (); |
28 | | type Err = (); |
29 | | |
30 | 4.37k | fn finish(self) -> Result<Self::Output, Self::Err> { |
31 | 4.37k | Ok(()) |
32 | 4.37k | } |
33 | | |
34 | 142k | fn visit_pre(&mut self, ast: &Ast) -> Result<Self::Output, Self::Err> { |
35 | 142k | let reject_flags = |flags: &Flags| { |
36 | 3.15k | flags.flag_state(Flag::IgnoreWhitespace).unwrap_or(false) |
37 | 3.15k | }; |
38 | 1.65k | match ast { |
39 | 1.65k | Ast::Flags(x) if reject_flags(&x.flags) => return Err(()), |
40 | 5.89k | Ast::Group(x) => match x.kind { |
41 | 1.50k | GroupKind::NonCapturing(ref flags) if reject_flags(flags) => { |
42 | 164 | return Err(()) |
43 | | } |
44 | 5.73k | _ => Ok(()), |
45 | | }, |
46 | 136k | _ => Ok(()), |
47 | | } |
48 | 142k | } |
49 | | } |
50 | | |
51 | | fuzz_target!(|data: FuzzData| -> Corpus { |
52 | | let _ = env_logger::try_init(); |
53 | | |
54 | | let pattern = format!("{}", data.ast); |
55 | | let Ok(ast) = Parser::new().parse(&pattern) else { |
56 | | return Corpus::Keep; |
57 | | }; |
58 | | if visit(&ast, VerboseVisitor).is_err() { |
59 | | return Corpus::Reject; |
60 | | } |
61 | | let ast2 = Parser::new().parse(&ast.to_string()).unwrap(); |
62 | | assert_eq!( |
63 | | ast, |
64 | | ast2, |
65 | | "Found difference:\ |
66 | | \nleft: {:?}\ |
67 | | \nright: {:?}\ |
68 | | \nIf these two match, then there was a parsing difference; \ |
69 | | maybe non-determinism?", |
70 | | ast.to_string(), |
71 | | ast2.to_string() |
72 | | ); |
73 | | Corpus::Keep |
74 | | }); |