Coverage Report

Created: 2025-01-09 07:53

/src/wasmtime/cranelift/filetests/src/test_verifier.rs
Line
Count
Source (jump to first uncovered line)
1
//! Test command for checking the IR verifier.
2
//!
3
//! The `test verifier` test command looks for annotations on instructions like this:
4
//!
5
//! ```clif
6
//!     jump block3 ; error: jump to non-existent block
7
//! ```
8
//!
9
//! This annotation means that the verifier is expected to given an error for the jump instruction
10
//! containing the substring "jump to non-existent block".
11
12
use crate::match_directive::match_directive;
13
use crate::subtest::{Context, SubTest};
14
use cranelift_codegen::ir::Function;
15
use cranelift_codegen::verify_function;
16
use cranelift_reader::TestCommand;
17
use std::borrow::{Borrow, Cow};
18
use std::fmt::Write;
19
20
struct TestVerifier;
21
22
0
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
23
0
    assert_eq!(parsed.command, "verifier");
24
0
    if !parsed.options.is_empty() {
25
0
        anyhow::bail!("No options allowed on {}", parsed);
26
0
    }
27
0
    Ok(Box::new(TestVerifier))
28
0
}
29
30
impl SubTest for TestVerifier {
31
0
    fn name(&self) -> &'static str {
32
0
        "verifier"
33
0
    }
34
35
0
    fn needs_verifier(&self) -> bool {
36
0
        // Running the verifier before this test would defeat its purpose.
37
0
        false
38
0
    }
39
40
0
    fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
41
0
        let func = func.borrow();
42
0
43
0
        // Scan source annotations for "error:" directives.
44
0
        let mut expected = Vec::new();
45
46
0
        for comment in &context.details.comments {
47
0
            if let Some(tail) = match_directive(comment.text, "error:") {
48
0
                expected.push((comment.entity, tail));
49
0
            }
50
        }
51
52
0
        match verify_function(func, context.flags_or_isa()) {
53
0
            Ok(()) if expected.is_empty() => Ok(()),
54
0
            Ok(()) => anyhow::bail!("passed, but expected errors: {:?}", expected),
55
56
0
            Err(ref errors) if expected.is_empty() => {
57
0
                anyhow::bail!("expected no error, but got:\n{}", errors);
58
            }
59
60
0
            Err(errors) => {
61
0
                let mut errors = errors.0;
62
0
                let mut msg = String::new();
63
64
                // For each expected error, find a suitable match.
65
0
                for expect in expected {
66
0
                    let pos = errors
67
0
                        .iter()
68
0
                        .position(|err| err.location == expect.0 && err.message.contains(expect.1));
69
0
70
0
                    match pos {
71
0
                        None => {
72
0
                            writeln!(msg, "  expected error {}: {}", expect.0, expect.1).unwrap();
73
0
                        }
74
0
                        Some(pos) => {
75
0
                            errors.swap_remove(pos);
76
0
                        }
77
                    }
78
                }
79
80
                // Report remaining errors.
81
0
                for err in errors {
82
0
                    writeln!(msg, "unexpected error {err}").unwrap();
83
0
                }
84
85
0
                if msg.is_empty() {
86
0
                    Ok(())
87
                } else {
88
0
                    anyhow::bail!("{}", msg);
89
                }
90
            }
91
        }
92
0
    }
93
}