/src/regex/fuzz/fuzz_targets/ast_roundtrip.rs
Line | Count | Source (jump to first uncovered line) |
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 | 31.5k | #[derive(Eq, PartialEq, arbitrary::Arbitrary)] Unexecuted instantiation: <ast_roundtrip::FuzzData as arbitrary::Arbitrary>::arbitrary::{closure#0} Unexecuted instantiation: <ast_roundtrip::FuzzData as arbitrary::Arbitrary>::arbitrary::{closure#1} Unexecuted instantiation: <ast_roundtrip::FuzzData as arbitrary::Arbitrary>::arbitrary::{closure#2} Unexecuted instantiation: <ast_roundtrip::FuzzData as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#0} Unexecuted instantiation: <ast_roundtrip::FuzzData as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#2} <ast_roundtrip::FuzzData as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1} Line | Count | Source | 10 | 10.5k | #[derive(Eq, PartialEq, arbitrary::Arbitrary)] |
<ast_roundtrip::FuzzData as arbitrary::Arbitrary>::try_size_hint::{closure#0} Line | Count | Source | 10 | 20.9k | #[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 | 5.06k | fn finish(self) -> Result<Self::Output, Self::Err> { |
31 | 5.06k | Ok(()) |
32 | 5.06k | } |
33 | | |
34 | 170k | fn visit_pre(&mut self, ast: &Ast) -> Result<Self::Output, Self::Err> { |
35 | 170k | let reject_flags = |flags: &Flags| { |
36 | 3.38k | flags.flag_state(Flag::IgnoreWhitespace).unwrap_or(false) |
37 | 3.38k | }; |
38 | 1.80k | match ast { |
39 | 1.80k | Ast::Flags(x) if reject_flags(&x.flags) => return Err(()), |
40 | 5.52k | Ast::Group(x) => match x.kind { |
41 | 1.57k | GroupKind::NonCapturing(ref flags) if reject_flags(flags) => { |
42 | 169 | return Err(()) |
43 | | } |
44 | 5.35k | _ => Ok(()), |
45 | | }, |
46 | 164k | _ => Ok(()), |
47 | | } |
48 | 170k | } |
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 | | }); |