Coverage Report

Created: 2026-11-12 06:36

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.4k
fn parse_and_serialize(input: &str, preserving_comments: bool) -> String {
8
16.4k
    let mut parser = Parser::new(input);
9
16.4k
    let mut serialization = String::new();
10
16.4k
    let result = do_parse_and_serialize(
11
16.4k
        &mut parser,
12
16.4k
        preserving_comments,
13
16.4k
        TokenSerializationType::Nothing,
14
16.4k
        &mut serialization,
15
        0,
16
    );
17
16.4k
    if result.is_err() {
18
1.17k
        return String::new();
19
15.2k
    }
20
15.2k
    serialization
21
16.4k
}
22
23
238k
fn do_parse_and_serialize(
24
238k
    input: &mut Parser,
25
238k
    preserving_comments: bool,
26
238k
    mut previous_token_type: TokenSerializationType,
27
238k
    serialization: &mut String,
28
238k
    indent_level: usize,
29
238k
) -> Result<(), ParseError<()>> {
30
    loop {
31
90.7M
        let token = if preserving_comments {
32
0
            input.next_including_whitespace_and_comments()
33
        } else {
34
90.7M
            input.next_including_whitespace()
35
        };
36
90.7M
        let token = match token {
37
90.5M
            Ok(token) => token,
38
234k
            Err(..) => break,
39
        };
40
90.5M
        if DEBUG {
41
0
            for _ in 0..indent_level {
42
0
                print!(" ");
43
0
            }
44
0
            println!("{:?}", token);
45
90.5M
        }
46
90.5M
        if token.is_parse_error() {
47
1.16k
            return Err(ParseError::unexpected_token());
48
90.5M
        }
49
90.5M
        let token_type = token.serialization_type();
50
90.5M
        if previous_token_type.needs_separator_when_before(token_type) {
51
87.4M
            serialization.push_str("/**/");
52
87.4M
        }
53
90.5M
        previous_token_type = token_type;
54
90.5M
        token.to_css(serialization).unwrap();
55
90.5M
        let closing_token = match token {
56
18.8k
            Token::Function(_) | Token::ParenthesisBlock => Token::CloseParenthesis,
57
8.58k
            Token::SquareBracketBlock => Token::CloseSquareBracket,
58
194k
            Token::CurlyBracketBlock => Token::CloseCurlyBracket,
59
90.3M
            _ => continue,
60
        };
61
62
222k
        input.parse_nested_block(|input| -> Result<_, ParseError<()>> {
63
222k
            do_parse_and_serialize(
64
222k
                input,
65
222k
                preserving_comments,
66
222k
                previous_token_type,
67
222k
                serialization,
68
222k
                indent_level + 1,
69
            )
70
222k
        })?;
71
72
219k
        closing_token.to_css(serialization).unwrap();
73
    }
74
234k
    Ok(())
75
238k
}
76
77
8.21k
fn fuzz(data: &str, preserving_comments: bool) {
78
8.21k
    let serialization = parse_and_serialize(data, preserving_comments);
79
8.21k
    let reserialization = parse_and_serialize(&serialization, preserving_comments);
80
8.21k
    if DEBUG {
81
0
        println!("IN: {:?}", serialization);
82
0
        println!("OUT: {:?}", reserialization);
83
8.21k
    }
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.21k
}
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
});