Coverage Report

Created: 2026-09-28 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rust-cssparser/fuzz/fuzz_targets/cssparser.rs
Line
Count
Source
1
#![no_main]
2
3
use cssparser::*;
4
5
const DEBUG: bool = false;
6
7
16.2k
fn parse_and_serialize(input: &str, preserving_comments: bool) -> String {
8
16.2k
    let mut parser = Parser::new(input);
9
16.2k
    let mut serialization = String::new();
10
16.2k
    let result = do_parse_and_serialize(
11
16.2k
        &mut parser,
12
16.2k
        preserving_comments,
13
16.2k
        TokenSerializationType::Nothing,
14
16.2k
        &mut serialization,
15
        0,
16
    );
17
16.2k
    if result.is_err() {
18
1.17k
        return String::new();
19
15.1k
    }
20
15.1k
    serialization
21
16.2k
}
22
23
382k
fn do_parse_and_serialize(
24
382k
    input: &mut Parser,
25
382k
    preserving_comments: bool,
26
382k
    mut previous_token_type: TokenSerializationType,
27
382k
    serialization: &mut String,
28
382k
    indent_level: usize,
29
382k
) -> Result<(), ParseError<()>> {
30
    loop {
31
92.6M
        let token = if preserving_comments {
32
0
            input.next_including_whitespace_and_comments()
33
        } else {
34
92.6M
            input.next_including_whitespace()
35
        };
36
92.6M
        let token = match token {
37
92.2M
            Ok(token) => token,
38
379k
            Err(..) => break,
39
        };
40
92.2M
        if DEBUG {
41
0
            for _ in 0..indent_level {
42
0
                print!(" ");
43
0
            }
44
0
            println!("{:?}", token);
45
92.2M
        }
46
92.2M
        if token.is_parse_error() {
47
1.16k
            return Err(ParseError::unexpected_token());
48
92.2M
        }
49
92.2M
        let token_type = token.serialization_type();
50
92.2M
        if previous_token_type.needs_separator_when_before(token_type) {
51
87.6M
            serialization.push_str("/**/");
52
87.6M
        }
53
92.2M
        previous_token_type = token_type;
54
92.2M
        token.to_css(serialization).unwrap();
55
92.2M
        let closing_token = match token {
56
19.9k
            Token::Function(_) | Token::ParenthesisBlock => Token::CloseParenthesis,
57
7.45k
            Token::SquareBracketBlock => Token::CloseSquareBracket,
58
339k
            Token::CurlyBracketBlock => Token::CloseCurlyBracket,
59
91.9M
            _ => continue,
60
        };
61
62
366k
        input.parse_nested_block(|input| -> Result<_, ParseError<()>> {
63
366k
            do_parse_and_serialize(
64
366k
                input,
65
366k
                preserving_comments,
66
366k
                previous_token_type,
67
366k
                serialization,
68
366k
                indent_level + 1,
69
            )
70
366k
        })?;
71
72
364k
        closing_token.to_css(serialization).unwrap();
73
    }
74
379k
    Ok(())
75
382k
}
76
77
8.13k
fn fuzz(data: &str, preserving_comments: bool) {
78
8.13k
    let serialization = parse_and_serialize(data, preserving_comments);
79
8.13k
    let reserialization = parse_and_serialize(&serialization, preserving_comments);
80
8.13k
    if DEBUG {
81
0
        println!("IN: {:?}", serialization);
82
0
        println!("OUT: {:?}", reserialization);
83
8.13k
    }
84
    // TODO: This should ideally pass, but it doesn't for numbers near our
85
    // precision limits, so parsing e.g., 9999995e-45 generates a serialization
86
    // of 10e-39 because of dtoa rounding, and parsing _that_ generates a
87
    // serialization of 1e-38.
88
    //
89
    // assert_eq!(
90
    //     serialization, reserialization,
91
    //     "Serialization should be idempotent"
92
    // );
93
8.13k
}
94
95
libfuzzer_sys::fuzz_target!(|data: &str| {
96
    fuzz(data, false);
97
    // TODO(emilio): Serialization when preserving comments is not idempotent.
98
    // But in browsers we never preserve comments so that's ok...
99
    // fuzz(data, true);
100
});