Coverage Report

Created: 2026-10-10 06:48

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