dfir_rs/
declarative_macro.rs

1//! Declarative macros.
2
3/// [`assert!`] but returns a [`Result<(), String>`] instead of panicking.
4#[macro_export]
5macro_rules! rassert {
6    ($cond:expr $(,)?) => {
7        $crate::rassert!($cond, "assertion failed: `{}`", stringify!($cond))
8    };
9    ($cond:expr, $fmt:literal) => {
10        $crate::rassert!($cond, $fmt,)
11    };
12    ($cond:expr, $fmt:literal, $($arg:tt)*) => {
13        {
14            if $cond {
15                Ok(())
16            }
17            else {
18                Err(format!($fmt, $($arg)*))
19            }
20        }
21    };
22}
23
24/// [`assert_eq!`] but returns a [`Result<(), String>`] instead of panicking.
25#[macro_export]
26macro_rules! rassert_eq {
27    ($a:expr, $b:expr) => {
28        $crate::rassert!($a == $b,)
29    };
30    ($a:expr, $b:expr, $($arg:tt)*) => {
31        $crate::rassert!($a == $b, $($arg)*)
32    };
33}
34
35/// Asserts that the variable's type implements the given traits.
36#[macro_export]
37macro_rules! assert_var_impl {
38    ($var:ident: $($trait:path),+ $(,)?) => {
39        let _ = || {
40            // Only callable when `$var` implements all traits in `$($trait)+`.
41            fn assert_var_impl<T: ?Sized $(+ $trait)+>(_x: &T) {}
42            assert_var_impl(& $var);
43        };
44    };
45}
46
47/// Tests that the given warnings are emitted by the dfir macro invocation.
48///
49/// For example usage, see `dfir/tests/surface_warnings.rs`.
50#[macro_export]
51macro_rules! dfir_expect_warnings {
52    (
53        $hf:tt,
54        $( ( $msg:literal, $line:literal : $column:literal ) ),*
55        $( , )?
56    ) => {
57        {
58            let __file = ::std::file!();
59            let __line = ::std::line!() as usize;
60            let __hf = $crate::dfir_syntax_noemit! $hf;
61
62            let actuals = __hf.diagnostics().expect("Expected `diagnostics()` to be set.");
63            let actuals_len = actuals.len();
64            let mut missing_span_info = false;
65            let actuals = ::std::collections::BTreeSet::from_iter(actuals.iter().cloned().map(|mut actual| {
66                if actual.span.line == 0 {
67                    // 0 is not a valid line in a source file (source files start at line 1). So a zero value indicates missing data, likely because proc_macro_span feature is not enable because the crate was compiled with a non-nightly compiler.
68                    missing_span_info = true;
69                } else {
70                    actual.span.line = actual.span.line.checked_sub(__line).unwrap();
71                }
72
73                (actual.message.to_owned(), actual.span.line, actual.span.column)
74            }));
75
76            let expecteds = if missing_span_info {
77                [
78                    $(
79                        ($msg.to_owned(), 0, 0),
80                    )*
81                ]
82            } else {
83                [
84                    $(
85                        ($msg.to_owned(), $line, $column),
86                    )*
87                ]
88            };
89
90            let expecteds_len = expecteds.len();
91            let expecteds = ::std::collections::BTreeSet::from(expecteds);
92
93            let missing_errs = expecteds.difference(&actuals).map(|missing| {
94                format!("Expected diagnostic `{:?}` was not emitted.", missing)
95            });
96            let extra_errs = actuals.difference(&expecteds).map(|extra| {
97                format!("Unexpected extra diagnostic `{:?}` was emitted", extra)
98            });
99            let all_errs: ::std::vec::Vec<_> = missing_errs.chain(extra_errs).collect();
100            if !all_errs.is_empty() {
101                panic!("{}", all_errs.join("\n"));
102            }
103
104            if actuals_len != expecteds_len {
105                panic!("{}", format!(
106                    "Number of expected warnings ({:?}) does not match number of actual warnings ({:?}), were there duplicates?",
107                    expecteds_len,
108                    actuals_len
109                ));
110            }
111
112            __hf
113        }
114    };
115}
116
117/// Test helper, emits and checks snapshots for the mermaid and dot graphs.
118#[doc(hidden)]
119#[macro_export]
120macro_rules! assert_graphvis_snapshots {
121    ($df:ident) => {
122        $crate::assert_graphvis_snapshots!($df, &Default::default())
123    };
124    ($df:ident, $cfg:expr) => {
125        {
126            #[cfg(not(target_arch = "wasm32"))]
127            {
128                let cfg = $cfg;
129                hydro_build_utils::insta::with_settings!({
130                    snapshot_suffix => "graphvis_mermaid",
131                }, {
132                    hydro_build_utils::assert_snapshot!($df.meta_graph().unwrap().to_mermaid(cfg));
133                });
134                hydro_build_utils::insta::with_settings!({
135                    snapshot_suffix => "graphvis_dot",
136                }, {
137                    hydro_build_utils::assert_snapshot!($df.meta_graph().unwrap().to_dot(cfg));
138                });
139            }
140        }
141    }
142}