Coverage Report

Created: 2025-09-27 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
});