From 4c1ee51d5cd75123568f1f9eb96f0aff68d5e9d7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 20 Feb 2023 15:09:04 +0100 Subject: [PATCH 1/2] Add defensive_assert macro Signed-off-by: Oliver Tale-Yazdi --- frame/support/src/traits/misc.rs | 48 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index 734b84ebd9d85..08338ebacd5af 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -49,7 +49,7 @@ macro_rules! defensive { ); debug_assert!(false, "{}", $crate::traits::DEFENSIVE_OP_INTERNAL_ERROR); }; - ($error:tt) => { + ($error:expr $(,)?) => { frame_support::log::error!( target: "runtime", "{}: {:?}", @@ -58,7 +58,7 @@ macro_rules! defensive { ); debug_assert!(false, "{}: {:?}", $crate::traits::DEFENSIVE_OP_INTERNAL_ERROR, $error); }; - ($error:tt, $proof:tt) => { + ($error:expr, $proof:expr $(,)?) => { frame_support::log::error!( target: "runtime", "{}: {:?}: {:?}", @@ -70,6 +70,29 @@ macro_rules! defensive { } } +/// Trigger a defensive failure if a condition is not met. +/// +/// Similar to [`assert!`] but will print an error without `debug_assertions` instead of silently +/// ignoring it. Only accepts one instead of variable formatting arguments. +/// +/// # Example +/// +/// ``` +/// use frame_support::defensive_assert; +/// +/// std::panic::catch_unwind(|| { +/// defensive_assert!(1 == 0, "Must fail") +/// }).unwrap_err(); +/// ``` +#[macro_export] +macro_rules! defensive_assert { + ($cond:expr $(, $proof:expr )? $(,)?) => { + if !($cond) { + $crate::defensive!(::core::stringify!($cond) $(, $proof )?); + } + }; +} + /// Prelude module for all defensive traits to be imported at once. pub mod defensive_prelude { pub use super::{Defensive, DefensiveOption, DefensiveResult}; @@ -1141,6 +1164,27 @@ mod test { use sp_core::bounded::{BoundedSlice, BoundedVec}; use sp_std::marker::PhantomData; + #[test] + fn defensive_assert_works() { + defensive_assert!(true); + defensive_assert!(true,); + defensive_assert!(true, "must work"); + defensive_assert!(true, "must work",); + } + + #[test] + #[cfg(debug_assertions)] + #[should_panic(expected = "Defensive failure has been triggered!: \"1 == 0\": \"Must fail\"")] + fn defensive_assert_panics() { + defensive_assert!(1 == 0, "Must fail"); + } + + #[test] + #[cfg(not(debug_assertions))] + fn defensive_assert_does_not_panic() { + defensive_assert!(1 == 0, "Must fail"); + } + #[test] #[cfg(not(debug_assertions))] fn defensive_saturating_accrue_works() { From c352596b22abe7bdc7f9de901fce219fb8720bfa Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 21 Feb 2023 13:33:41 +0100 Subject: [PATCH 2/2] Apply review suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher Signed-off-by: Oliver Tale-Yazdi --- frame/support/src/traits/misc.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index 08338ebacd5af..5a13fc4ab6509 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -77,12 +77,8 @@ macro_rules! defensive { /// /// # Example /// -/// ``` -/// use frame_support::defensive_assert; -/// -/// std::panic::catch_unwind(|| { -/// defensive_assert!(1 == 0, "Must fail") -/// }).unwrap_err(); +/// ```should_panic +/// frame_support::defensive_assert!(1 == 0, "Must fail") /// ``` #[macro_export] macro_rules! defensive_assert {