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 ),*
55        $( , )?
56    ) => {
57        {
58            fn emit(msg: impl ::std::convert::AsRef<str>) {
59                if Ok("ignore") == ::std::env::var("DFIR_EXPECT_WARNINGS").as_deref() {
60                    eprintln!("{}", msg.as_ref());
61                } else {
62                    panic!("{}", msg.as_ref());
63                }
64            }
65
66            let __file = ::std::file!();
67            let __line = ::std::line!() as usize;
68            let __hf = $crate::dfir_syntax_noemit! $hf;
69
70            let actuals = __hf.diagnostics().expect("Expected `diagnostics()` to be set.");
71            let actuals_len = actuals.len();
72            let actuals = ::std::collections::BTreeSet::from_iter(actuals.iter().cloned().map(|mut actual| {
73                actual.span.line = actual.span.line.saturating_sub(__line);
74                ::std::borrow::Cow::<'static, str>::Owned(actual.to_string().replace(__file, "$FILE"))
75            }));
76
77            let expecteds = [
78                $(
79                    ::std::borrow::Cow::Borrowed( $msg ),
80                )*
81            ];
82            let expecteds_len = expecteds.len();
83            let expecteds = ::std::collections::BTreeSet::from(expecteds);
84
85            let missing_errs = expecteds.difference(&actuals).map(|missing| {
86                format!("Expected diagnostic `{}` was not emitted.", missing)
87            });
88            let extra_errs = actuals.difference(&expecteds).map(|extra| {
89                format!("Unexpected extra diagnostic `{}` was emitted", extra)
90            });
91            let all_errs: ::std::vec::Vec<_> = missing_errs.chain(extra_errs).collect();
92            if !all_errs.is_empty() {
93                emit(all_errs.join("\n"));
94            }
95
96            if actuals_len != expecteds_len {
97                emit(format!(
98                    "Number of expected warnings ({}) does not match number of actual warnings ({}), were there duplicates?",
99                    expecteds_len,
100                    actuals_len
101                ));
102            }
103
104            __hf
105        }
106    };
107}
108
109/// Test helper, emits and checks snapshots for the mermaid and dot graphs.
110#[doc(hidden)]
111#[macro_export]
112macro_rules! assert_graphvis_snapshots {
113    ($df:ident) => {
114        {
115            #[cfg(not(target_arch = "wasm32"))]
116            {
117                insta::with_settings!({snapshot_suffix => "graphvis_mermaid"}, {
118                    insta::assert_snapshot!($df.meta_graph().unwrap().to_mermaid(&Default::default()));
119                });
120                insta::with_settings!({snapshot_suffix => "graphvis_dot"}, {
121                    insta::assert_snapshot!($df.meta_graph().unwrap().to_dot(&Default::default()));
122                });
123            }
124        }
125    }
126}
127
128#[doc(hidden)]
129#[macro_export]
130#[cfg(feature = "python")]
131macro_rules! __python_feature_gate {
132    (
133        {
134            $( $ypy:tt )*
135        },
136        {
137            $( $npy:tt )*
138        }
139    ) => {
140        $( $ypy )*
141    };
142}
143
144#[doc(hidden)]
145#[macro_export]
146#[cfg(not(feature = "python"))]
147macro_rules! __python_feature_gate {
148    (
149        {
150            $( $ypy:tt )*
151        },
152        {
153            $( $npy:tt )*
154        }
155    ) => {
156        $( $npy )*
157    };
158}