nobug/
assert.rs

1#[allow(unused_imports)]
2use crate::*;
3
4/// Asserts that a condition is true.
5///
6/// This defines the underlying machinery for the other assertion macros below.
7///
8/// # Arguments
9///
10/// * `cond` - The condition to check.
11/// * `fmt`  - can be either a literal string or a sequence of tokens within parenthesis
12///            that are concat!()'ed to form a format string.
13/// * `args` - Optional comma separated Arguments for the message.
14///
15/// # Semantics
16///
17/// * When the assertion fails it will abort the program and panic in test mode.
18/// * The condition is always checked and when it fails it ends in a call that yields 'Never'.
19///   Thus the compiler can make assumptions about the code after the assertion.
20///
21/// # Example
22///
23/// ```
24/// # use nobug::ASSERT;
25/// ASSERT!(true, "this must always be true");
26/// ```
27#[macro_export]
28macro_rules! ASSERT {
29    ($cond:expr, ($($fmt:tt)*) $(,$($args:expr),*)?) => {{
30        $crate::TRACE_NOBUG!(($($fmt)*) $(,$($args),*)?);
31        #[cfg(all(debug_assertions,test))] $crate::set_testing();
32        if !$cond {
33            $crate::DIE!(($($fmt)*) $(,$($args),*)?)
34        }
35    }};
36    ($cond:expr, $fmt:literal $(,$($args:expr),*)?) => {{
37        $crate::TRACE_NOBUG!($fmt $(,$($args),*)?);
38        #[cfg(all(debug_assertions,test))] $crate::set_testing();
39        if !$cond {
40            $crate::DIE!($fmt $(,$($args),*)?)
41        }
42    }};
43    ($cond:expr) => {{
44        $crate::TRACE_NOBUG!("ASSERTION FAILED: {}", stringify!($cond));
45        #[cfg(all(debug_assertions,test))] $crate::set_testing();
46        if !$cond {
47            $crate::DIE!("ASSERTION FAILED: {}", stringify!($cond))
48        }
49    }};
50}
51
52/// Asserts that a condition is true only in debug mode.
53#[macro_export]
54macro_rules! ASSERT_DBG {
55    ($($tt:tt)*) => {
56        #[cfg(debug_assertions)]
57        $crate::ASSERT!($($tt)*);
58    }
59}
60
61#[test]
62fn test_assert() {
63    ASSERT!(true);
64}
65
66#[test]
67#[cfg(debug_assertions)]
68#[should_panic = "ASSERTION FAILED: false"]
69fn test_assert1() {
70    ASSERT!(false);
71}
72
73#[test]
74#[cfg(debug_assertions)]
75#[should_panic = "foo"]
76fn test_assert2() {
77    ASSERT!(false, "foo");
78}
79
80#[test]
81#[cfg(debug_assertions)]
82#[should_panic = "foo 42"]
83fn test_assert3() {
84    ASSERT!(false, "foo {}", 42);
85}
86
87/// Assert some condition only once. Returns `true` on success.
88///
89/// This is a special form which is mainly useful within test blocks in the `FIXME!`/`FIXED!`
90/// macros when the bug can be checked without local context only once.  It can be used to to
91/// do expensive tests on immutable data as well. By returning `true` on success this can be
92/// nested in other assertions and annotations.
93///
94/// ```rust
95/// # use nobug::{FIXED, ASSERT_ONCE};
96/// FIXED!({ASSERT_ONCE!(true)} "this was an error");
97/// ```
98#[macro_export]
99macro_rules! ASSERT_ONCE {
100($($code:tt)*) => {
101        {
102            $crate::ONCE!($crate::ASSERT!($($code)*));
103            true
104        }
105    };
106}
107
108#[test]
109#[cfg(debug_assertions)]
110#[should_panic = "ASSERTION FAILED"]
111fn test_assert_once() {
112    ASSERT_ONCE!(false, "ASSERTION FAILED");
113}
114
115#[test]
116#[cfg(debug_assertions)]
117fn test_fixme_assert_once() {
118    for i in 0..10 {
119        FIXME!({ASSERT_ONCE!(i == 0)} "not to be used this way in real code");
120    }
121}
122
123/// Checks preconditions.
124///
125/// # Arguments
126///
127/// * `cond` - The condition(s) to check.
128/// * `fmt`  - can be either a literal string or a sequence of tokens within parenthesis
129///            that are concat!()'ed to form a format string.
130/// * `args` - Optional comma separated Arguments for the message.
131///
132/// the `cond` part has specializations for `const { }` expressions and the common comparisons
133/// when both sides are in parenthesis eg. `(x) == (y)`. This requires that the arguments
134/// implement `Debug` and giving more detailed error messages.
135///
136/// Multiple checks can be grouped as list of conditions with messages in brackets separated by semicolons.
137/// The specializations from above are then not available, consequently they don't need to implement `Debug`.
138///
139/// # Example
140///
141/// ```
142/// # use nobug::REQUIRE;
143/// fn foo(x: u32, y: u32) {
144///     REQUIRE!((x) > (0), "x must be positive");
145///     // or
146///     REQUIRE!([
147///       x > 0, "x must be positive";
148///       x < y, "{} must be less than {}", x, y;
149///     ]);
150///     REQUIRE!(const { true }, "its not true");
151///     // ...
152/// }
153/// ```
154#[macro_export]
155macro_rules! REQUIRE {
156    ([$($cond:expr $(, $fmt:literal $(,$args:expr)*)?;)*]) => {
157        $($crate::REQUIRE!($cond $(,$fmt $(,$args)*)?);)*
158    };
159    (const { $cond:expr } $(, $fmt:literal $(, $($args:expr),*)?)?) => {
160            $crate::CFG_IF! {
161                if #[cfg(feature = "const_expr")] {
162                    $crate::ASSERT!(
163                        (const { $cond }),
164                        ("PRECONDITION FAILED: {}" $(, ": ", $fmt)?),
165                        stringify!($cond) $($(,$($args),*)?)?
166                    );
167                } else {
168                    const COND: bool = $cond;
169                    $crate::ASSERT!(
170                        COND,
171                        ("PRECONDITION FAILED: {}" $(, ": ", $fmt)?),
172                        stringify!($cond) $($(,$($args),*)?)?
173                    );
174                }
175            }
176    };
177    (($lhs:expr) == ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
178        $crate::ASSERT!($lhs == $rhs,
179            ("PRECONDITION FAILED: {} == {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
180            stringify!($lhs), stringify!($rhs)
181            $($(, $($args),*)?)?, $lhs, $rhs)
182    };
183    (($lhs:expr) != ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
184        $crate::ASSERT!($lhs != $rhs,
185            ("PRECONDITION FAILED: {} != {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
186            stringify!($lhs), stringify!($rhs)
187            $($(, $($args),*)?)?, $lhs, $rhs)
188    };
189    (($lhs:expr) < ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
190        $crate::ASSERT!($lhs < $rhs,
191            ("PRECONDITION FAILED: {} < {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
192            stringify!($lhs), stringify!($rhs)
193            $($(, $($args),*)?)?, $lhs, $rhs)
194    };
195    (($lhs:expr) <= ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
196        $crate::ASSERT!($lhs <= $rhs,
197            ("PRECONDITION FAILED: {} <= {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
198            stringify!($lhs), stringify!($rhs)
199            $($(, $($args),*)?)?, $lhs, $rhs)
200    };
201    (($lhs:expr) > ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
202        $crate::ASSERT!($lhs > $rhs,
203            ("PRECONDITION FAILED: {} > {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
204            stringify!($lhs), stringify!($rhs)
205            $($(, $($args),*)?)?, $lhs, $rhs)
206    };
207    (($lhs:expr) >= ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
208        $crate::ASSERT!($lhs >= $rhs,
209            ("PRECONDITION FAILED: {} >= {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
210            stringify!($lhs), stringify!($rhs)
211            $($(, $($args),*)?)?, $lhs, $rhs)
212    };
213    ($cond:expr $(, $fmt:literal $(, $($args:expr),*)?)?) => {
214        $crate::ASSERT!($cond, ("PRECONDITION FAILED: {}" $(, ": ", $fmt)?), stringify!($cond)
215            $($(, $($args),*)?)?)
216    };
217}
218
219/// Checks preconditions only in debug mode.
220#[macro_export]
221macro_rules! REQUIRE_DBG {
222    ($($tt:tt)*) => {
223        #[cfg(debug_assertions)]
224        $crate::REQUIRE!($($tt)*);
225    }
226}
227
228#[test]
229fn test_require() {
230    let yes = true;
231    REQUIRE!(true);
232    REQUIRE!(const { true });
233    REQUIRE!((yes) == (true));
234}
235
236#[test]
237#[cfg(debug_assertions)]
238#[should_panic = "PRECONDITION FAILED: false"]
239fn test_require1() {
240    REQUIRE!(false);
241}
242
243#[test]
244fn test_braced() {
245    REQUIRE!({ true }, "foo {}", 42);
246}
247
248#[test]
249#[cfg(debug_assertions)]
250#[should_panic = "PRECONDITION FAILED: false: foo"]
251fn test_require2() {
252    REQUIRE!(false, "foo");
253}
254
255#[test]
256#[cfg(debug_assertions)]
257#[should_panic = "PRECONDITION FAILED: false: foo 42"]
258fn test_require3() {
259    REQUIRE!(false, "foo {}", 42);
260}
261
262#[test]
263#[cfg(debug_assertions)]
264#[should_panic = "PRECONDITION FAILED: false: baz 43"]
265fn test_require4() {
266    REQUIRE!([
267        true;
268        true, "bar";
269        false, "baz {}", 43;
270    ]);
271}
272
273#[cfg(feature = "const_expr")]
274#[cfg(debug_assertions)]
275#[allow(clippy::items_after_test_module)]
276#[cfg(test)]
277mod test {
278    struct ConstGeneric<const N: i32>;
279
280    impl<const N: i32> ConstGeneric<N> {
281        fn new() -> Self {
282            REQUIRE!(const { N > 0 }, "N out of range");
283            Self
284        }
285    }
286
287    #[test]
288    #[should_panic(expected = "N out of range")]
289    fn test_const_generic() {
290        let _a = ConstGeneric::<1>::new();
291        let _b = ConstGeneric::<0>::new();
292    }
293}
294
295/// Checks postconditions.
296///
297/// `ENSURE!` augments a expression/code-block with a list of conditions that must be true and
298/// returns the result of the executed code.
299///
300/// # Example
301///
302/// With multiple conditions:
303/// ```
304/// # use nobug::ENSURE;
305/// fn foo(x: u32, y: u32) -> u32 {
306///     ENSURE!{
307///         [
308///             result < 100, "result must be less than 100";
309///             result >= 50, "result must be greater or equal than 50";
310///         ]
311///         result = {
312///             // code which result shall be checked ...
313///             x + y
314///         }
315///     }
316/// }
317/// ```
318///
319/// When only one condition is checked then the brackets can be omitted:
320/// ```
321/// # use nobug::{ENSURE, CHECK};
322/// fn foo(x: u32, y: u32) -> u32 {
323///     ENSURE!{
324///         result < 100, "result must be less than 100";
325///         result = {
326///             // code which result shall be checked ...
327///             // ENSURE! catches returns too
328///             if x > 100 {return 0;}
329///             x + y
330///         }
331///     }
332/// }
333///
334/// CHECK!((foo(20,30)) == (50));
335/// ```
336#[macro_export]
337macro_rules! ENSURE {
338    (
339        [$($cond:expr $(, $fmt:literal $(, $($args:expr),*)? )?);* $(;)?]
340        $result:ident = $code:expr) => {{
341            let mut early_return = true;
342            let $result = (|| {
343                let result = $code;
344                early_return = false;
345                result
346            })();
347            $($crate::ASSERT!($cond, ("POSTCONDITON FAILED: {}" $(, ": ", $fmt)?), stringify!($cond) $($(, $($args),*)?)?);)*
348            if early_return {
349                return $result;
350            }
351            $result
352        }};
353    (
354        $cond:expr $(, $fmt:literal $(, $($args:expr),*)? )?;
355        $result:ident = $code:expr) => {{
356            let mut early_return = true;
357            let $result = (|| {
358                let result = $code;
359                early_return = false;
360                result
361            })();
362            $crate::ASSERT!($cond, ("POSTCONDITON FAILED: {}" $(, ": ", $fmt)?), stringify!($cond) $($(, $($args),*)?)?);
363            if early_return {
364                return $result;
365            }
366            $result
367        }};
368}
369
370/// Checks postconditions only in debug mode.
371#[macro_export]
372macro_rules! ENSURE_DBG {
373    (
374        [$($cond:expr $(, $fmt:literal $(, $($args:expr),*)? )?);* $(;)?]
375        $result:ident = $code:expr) => {
376            #[cfg(debug_assertions)]
377            $crate::ENSURE!(
378                [$($cond $(, $fmt $(, $($args),*)?)?);*]
379                $result = $code
380            );
381            #[cfg(not(debug_assertions))]
382            {$code}
383        };
384    (
385        $cond:expr $(, $fmt:literal $(, $($args:expr),*)? )?;
386        $result:ident = $code:expr) => {
387            #[cfg(debug_assertions)]
388            $crate::ENSURE!(
389                $cond $(, $fmt $(, $($args),*)?)?;
390                $result = $code
391            );
392            #[cfg(not(debug_assertions))]
393            {$code}
394        };
395}
396
397/// Asserts that a condition is true.
398///
399/// This takes the same arguments as [`REQUIRE!()`] but is intended to be used in test suites.
400/// When the condition check succeeds it returns `true` (which might be ignored).
401/// There is no `_DBG` variant because assertions in tests should be checked unconditionally.
402///
403/// # Example
404///
405/// ```
406/// # use nobug::CHECK;
407/// CHECK!((1) == (1));
408/// CHECK!((1) != (2));
409/// CHECK!((1) < (2));
410/// CHECK!((1) <= (1));
411/// CHECK!((2) > (1));
412/// CHECK!((2) >= (2));
413/// CHECK!({ true });
414/// # if false {
415/// CHECK!([
416///     true;
417///     true, "bar";
418///     false, "baz {}", 43;
419/// ]);
420/// # }
421/// ```
422#[macro_export]
423macro_rules! CHECK {
424    ([$($cond:expr $(, $fmt:literal $(,$args:expr)*)?;)*]) => {
425        $($crate::CHECK!($cond $(,$fmt $(,$args)*)?);)*
426        true
427    };
428    (const { $cond:expr } $(, $fmt:literal $(, $($args:expr),*)?)?) => {
429        $crate::CFG_IF! {
430            if #[cfg(feature = "const_expr")] {
431                $crate::ASSERT!(
432                    (const { $cond }),
433                    ("TEST FAILED: {}" $(, ": ", $fmt)?),
434                    stringify!($cond) $($(,$($args),*)?)?
435                );
436                true
437            } else {
438                const COND: bool = $cond;
439                $crate::ASSERT!(
440                    COND,
441                    ("TEST FAILED: {}" $(, ": ", $fmt)?),
442                    stringify!($cond) $($(,$($args),*)?)?
443                );
444                true
445            }
446        }
447    };
448    (($lhs:expr) == ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
449        $crate::ASSERT!($lhs == $rhs,
450            ("TEST FAILED: {} == {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
451            stringify!($lhs), stringify!($rhs)
452            $($(, $($args),*)?)?, $lhs, $rhs);
453        true
454    };
455    (($lhs:expr) != ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
456        $crate::ASSERT!($lhs != $rhs,
457            ("TEST FAILED: {} != {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
458            stringify!($lhs), stringify!($rhs)
459            $($(, $($args),*)?)?, $lhs, $rhs);
460        true
461    };
462    (($lhs:expr) < ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
463        $crate::ASSERT!($lhs < $rhs,
464            ("TEST FAILED: {} < {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
465            stringify!($lhs), stringify!($rhs)
466            $($(, $($args),*)?)?, $lhs, $rhs);
467        true
468    };
469    (($lhs:expr) <= ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
470        $crate::ASSERT!($lhs <= $rhs,
471            ("TEST FAILED: {} <= {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
472            stringify!($lhs), stringify!($rhs)
473            $($(, $($args),*)?)?, $lhs, $rhs);
474        true
475    };
476    (($lhs:expr) > ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
477        $crate::ASSERT!($lhs > $rhs,
478            ("TEST FAILED: {} > {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
479            stringify!($lhs), stringify!($rhs)
480            $($(, $($args),*)?)?, $lhs, $rhs);
481        true
482    };
483    (($lhs:expr) >= ($rhs:expr) $(, $fmt:literal $(, $($args:expr),*)?)?) => {
484        $crate::ASSERT!($lhs >= $rhs,
485            ("TEST FAILED: {} >= {}" $(, ": ", $fmt)? , "\n  lhs: {:?}\n  rhs: {:?}"),
486            stringify!($lhs), stringify!($rhs)
487            $($(, $($args),*)?)?, $lhs, $rhs);
488        true
489    };
490    ($cond:expr $(, $fmt:literal $(, $($args:expr),*)?)?) => {
491        $crate::ASSERT!($cond, ("TEST FAILED: {}" $(, ": ", $fmt)?), stringify!($cond)
492            $($(, $($args),*)?)?);
493        true
494    };
495}
496
497#[test]
498fn test_check() {
499    CHECK!(const { true });
500}