From cd299d2b457c8e6d39d9b647085a1c568f224497 Mon Sep 17 00:00:00 2001
From: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Date: Tue, 21 Feb 2023 18:03:17 +0100
Subject: [PATCH] Add `defensive_assert!` macro (#13423)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add defensive_assert macro

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Apply review suggestions

Co-authored-by: Bastian Köcher <git@kchr.de>

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
---
 substrate/frame/support/src/traits/misc.rs | 44 +++++++++++++++++++++-
 1 file changed, 42 insertions(+), 2 deletions(-)

diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs
index 734b84ebd9d..5a13fc4ab65 100644
--- a/substrate/frame/support/src/traits/misc.rs
+++ b/substrate/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,25 @@ 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
+///
+/// ```should_panic
+/// frame_support::defensive_assert!(1 == 0, "Must fail")
+/// ```
+#[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 +1160,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() {
-- 
GitLab