From 79d1684cd33e329d8cf7919af3c1d86e4b8c02dc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 17 May 2024 10:42:12 -0700 Subject: [PATCH 1/2] Add test of ensure through a Not impl --- tests/test_macros.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_macros.rs b/tests/test_macros.rs index 10740a9..55e0fc9 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -15,6 +15,7 @@ use self::common::*; use anyhow::{anyhow, ensure, Result}; use std::cell::Cell; use std::future; +use std::ops::Not; #[test] fn test_messages() { @@ -60,11 +61,20 @@ fn test_ensure_nonbool() -> Result<()> { condition: bool, } + impl Not for Struct { + type Output = bool; + fn not(self) -> Self::Output { + !self.condition + } + } + let s = Struct { condition: true }; match &s { Struct { condition } => ensure!(condition), // &bool } + ensure!(s); + Ok(()) } From cbd91b854f9ccdcf6db1faaaa81fbe353feaa4d0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 17 May 2024 10:46:21 -0700 Subject: [PATCH 2/2] Switch to custom Not trait instead of '!' operator --- src/ensure.rs | 8 +-- src/lib.rs | 28 +++++++++ tests/test_macros.rs | 10 ---- tests/ui/ensure-nonbool.rs | 14 ++++- tests/ui/ensure-nonbool.stderr | 106 ++++++++++++++++++++++++--------- 5 files changed, 123 insertions(+), 43 deletions(-) diff --git a/src/ensure.rs b/src/ensure.rs index c40cb92..cf35f10 100644 --- a/src/ensure.rs +++ b/src/ensure.rs @@ -815,24 +815,24 @@ macro_rules! __fancy_ensure { #[macro_export] macro_rules! __fallback_ensure { ($cond:expr $(,)?) => { - if !$cond { + if $crate::__private::not($cond) { return $crate::__private::Err($crate::Error::msg( $crate::__private::concat!("Condition failed: `", $crate::__private::stringify!($cond), "`") )); } }; ($cond:expr, $msg:literal $(,)?) => { - if !$cond { + if $crate::__private::not($cond) { return $crate::__private::Err($crate::__anyhow!($msg)); } }; ($cond:expr, $err:expr $(,)?) => { - if !$cond { + if $crate::__private::not($cond) { return $crate::__private::Err($crate::__anyhow!($err)); } }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { - if !$cond { + if $crate::__private::not($cond) { return $crate::__private::Err($crate::__anyhow!($fmt, $($arg)*)); } }; diff --git a/src/lib.rs b/src/lib.rs index 15af58f..5cac33a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -651,6 +651,7 @@ pub fn Ok(t: T) -> Result { // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod __private { + use self::not::Bool; use crate::Error; use alloc::fmt; use core::fmt::Arguments; @@ -699,4 +700,31 @@ pub mod __private { pub fn must_use(error: Error) -> Error { error } + + #[doc(hidden)] + #[inline] + pub fn not(cond: impl Bool) -> bool { + cond.not() + } + + mod not { + #[doc(hidden)] + pub trait Bool { + fn not(self) -> bool; + } + + impl Bool for bool { + #[inline] + fn not(self) -> bool { + !self + } + } + + impl Bool for &bool { + #[inline] + fn not(self) -> bool { + !*self + } + } + } } diff --git a/tests/test_macros.rs b/tests/test_macros.rs index 55e0fc9..10740a9 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -15,7 +15,6 @@ use self::common::*; use anyhow::{anyhow, ensure, Result}; use std::cell::Cell; use std::future; -use std::ops::Not; #[test] fn test_messages() { @@ -61,20 +60,11 @@ fn test_ensure_nonbool() -> Result<()> { condition: bool, } - impl Not for Struct { - type Output = bool; - fn not(self) -> Self::Output { - !self.condition - } - } - let s = Struct { condition: true }; match &s { Struct { condition } => ensure!(condition), // &bool } - ensure!(s); - Ok(()) } diff --git a/tests/ui/ensure-nonbool.rs b/tests/ui/ensure-nonbool.rs index acb56df..c7df4e1 100644 --- a/tests/ui/ensure-nonbool.rs +++ b/tests/ui/ensure-nonbool.rs @@ -1,10 +1,12 @@ use anyhow::{ensure, Result}; -use std::ops::Deref; +use std::ops::{Deref, Not}; struct Bool(bool); struct DerefBool(bool); +struct NotBool(bool); + impl Deref for DerefBool { type Target = bool; fn deref(&self) -> &Self::Target { @@ -12,6 +14,13 @@ impl Deref for DerefBool { } } +impl Not for NotBool { + type Output = bool; + fn not(self) -> Self::Output { + !self.0 + } +} + fn main() -> Result<()> { ensure!("..."); @@ -24,5 +33,8 @@ fn main() -> Result<()> { ensure!(db); ensure!(&db); + let nb = NotBool(true); + ensure!(nb); + Ok(()) } diff --git a/tests/ui/ensure-nonbool.stderr b/tests/ui/ensure-nonbool.stderr index 4c43862..3fdb8e6 100644 --- a/tests/ui/ensure-nonbool.stderr +++ b/tests/ui/ensure-nonbool.stderr @@ -1,41 +1,91 @@ -error[E0600]: cannot apply unary operator `!` to type `&'static str` - --> tests/ui/ensure-nonbool.rs:16:5 +error[E0277]: the trait bound `&str: __private::not::Bool` is not satisfied + --> tests/ui/ensure-nonbool.rs:25:13 | -16 | ensure!("..."); - | ^^^^^^^^^^^^^^ cannot apply unary operator `!` +25 | ensure!("..."); + | --------^^^^^- + | | | + | | the trait `__private::not::Bool` is not implemented for `&str` + | required by a bound introduced by this call | - = note: this error originates in the macro `$crate::__fallback_ensure` which comes from the expansion of the macro `ensure` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: the following other types implement trait `__private::not::Bool`: + &bool + bool +note: required by a bound in `anyhow::__private::not` + --> src/lib.rs + | + | pub fn not(cond: impl Bool) -> bool { + | ^^^^ required by this bound in `not` -error[E0600]: cannot apply unary operator `!` to type `&mut bool` - --> tests/ui/ensure-nonbool.rs:20:23 +error[E0277]: the trait bound `&mut bool: __private::not::Bool` is not satisfied + --> tests/ui/ensure-nonbool.rs:29:31 + | +29 | Bool(cond) => ensure!(cond), + | --------^^^^- + | | | + | | the trait `__private::not::Bool` is not implemented for `&mut bool` + | required by a bound introduced by this call | -20 | Bool(cond) => ensure!(cond), - | ^^^^^^^^^^^^^ cannot apply unary operator `!` + = help: the following other types implement trait `__private::not::Bool`: + &bool + bool + = note: `__private::not::Bool` is implemented for `&bool`, but not for `&mut bool` +note: required by a bound in `anyhow::__private::not` + --> src/lib.rs | - = note: this error originates in the macro `$crate::__fallback_ensure` which comes from the expansion of the macro `ensure` (in Nightly builds, run with -Z macro-backtrace for more info) + | pub fn not(cond: impl Bool) -> bool { + | ^^^^ required by this bound in `not` -error[E0600]: cannot apply unary operator `!` to type `DerefBool` - --> tests/ui/ensure-nonbool.rs:24:5 +error[E0277]: the trait bound `DerefBool: __private::not::Bool` is not satisfied + --> tests/ui/ensure-nonbool.rs:33:13 | -24 | ensure!(db); - | ^^^^^^^^^^^ cannot apply unary operator `!` +33 | ensure!(db); + | --------^^- + | | | + | | the trait `__private::not::Bool` is not implemented for `DerefBool` + | required by a bound introduced by this call | -note: an implementation of `Not` might be missing for `DerefBool` - --> tests/ui/ensure-nonbool.rs:6:1 + = help: the following other types implement trait `__private::not::Bool`: + &bool + bool +note: required by a bound in `anyhow::__private::not` + --> src/lib.rs + | + | pub fn not(cond: impl Bool) -> bool { + | ^^^^ required by this bound in `not` + +error[E0277]: the trait bound `&DerefBool: __private::not::Bool` is not satisfied + --> tests/ui/ensure-nonbool.rs:34:13 | -6 | struct DerefBool(bool); - | ^^^^^^^^^^^^^^^^ must implement `Not` -note: the trait `Not` must be implemented - --> $RUST/core/src/ops/bit.rs +34 | ensure!(&db); + | --------^^^- + | | | + | | the trait `__private::not::Bool` is not implemented for `&DerefBool` + | required by a bound introduced by this call | - | pub trait Not { - | ^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::__fallback_ensure` which comes from the expansion of the macro `ensure` (in Nightly builds, run with -Z macro-backtrace for more info) +note: required by a bound in `anyhow::__private::not` + --> src/lib.rs + | + | pub fn not(cond: impl Bool) -> bool { + | ^^^^ required by this bound in `not` +help: consider dereferencing here + | +34 | ensure!(&*db); + | + -error[E0600]: cannot apply unary operator `!` to type `&DerefBool` - --> tests/ui/ensure-nonbool.rs:25:5 +error[E0277]: the trait bound `NotBool: __private::not::Bool` is not satisfied + --> tests/ui/ensure-nonbool.rs:37:13 + | +37 | ensure!(nb); + | --------^^- + | | | + | | the trait `__private::not::Bool` is not implemented for `NotBool` + | required by a bound introduced by this call | -25 | ensure!(&db); - | ^^^^^^^^^^^^ cannot apply unary operator `!` + = help: the following other types implement trait `__private::not::Bool`: + &bool + bool +note: required by a bound in `anyhow::__private::not` + --> src/lib.rs | - = note: this error originates in the macro `$crate::__fallback_ensure` which comes from the expansion of the macro `ensure` (in Nightly builds, run with -Z macro-backtrace for more info) + | pub fn not(cond: impl Bool) -> bool { + | ^^^^ required by this bound in `not`