diff --git a/src/ensure.rs b/src/ensure.rs index 795757d4..6e7b707a 100644 --- a/src/ensure.rs +++ b/src/ensure.rs @@ -248,8 +248,11 @@ macro_rules! __parse_ensure { }; (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($const:tt $block:tt $($dup:tt)*) const {$($body:tt)*} $($rest:tt)*) => { - // TODO: this is mostly useless due to https://github.com/rust-lang/rust/issues/86730 - $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $const $block) $($parse)*} ($($rest)*) $($rest)*) + // Parenthesized so that the const block is behind a token tree group by + // the time it reaches __fancy_ensure's `:expr` matcher, which does not + // accept a bare const block before the 2024 edition. + // https://github.com/rust-lang/rust/issues/86730 + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* ($const $block)) $($parse)*} ($($rest)*) $($rest)*) }; (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($literal:tt $($dup:tt)*) $lit:literal $($rest:tt)*) => { @@ -932,4 +935,14 @@ macro_rules! __fallback_ensure { return $crate::__private::Err($crate::__anyhow!($fmt, $($arg)*)); } }; + + // A condition beginning with a const block is not accepted by the `:expr` + // matcher before the 2024 edition, so put it behind a token tree group and + // re-dispatch. https://github.com/rust-lang/rust/issues/86730 + // + // This arm goes last so that a malformed invocation such as `ensure!()` + // still reports the `$cond:expr` above as the thing it failed to match. + (const $cond:block $($rest:tt)*) => { + $crate::__fallback_ensure!((const $cond) $($rest)*) + }; } diff --git a/tests/test_ensure.rs b/tests/test_ensure.rs index dc951606..9a435e32 100644 --- a/tests/test_ensure.rs +++ b/tests/test_ensure.rs @@ -15,6 +15,7 @@ clippy::match_bool, clippy::needless_else, clippy::never_loop, + clippy::nonminimal_bool, clippy::overly_complex_bool_expr, clippy::ptr_cast_constness, clippy::redundant_closure_call, @@ -294,6 +295,58 @@ fn test_loop() { ); } +#[test] +fn test_const_block() { + #[rustfmt::skip] + let test = || Ok(ensure!(const { 1 + 1 } == 3)); + assert_err( + test, + "Condition failed: `(const { 1 + 1 }) == 3` (2 vs 3)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(3 == const { 1 + 1 })); + assert_err( + test, + "Condition failed: `3 == (const { 1 + 1 })` (3 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(const { 1 } == const { 2 })); + assert_err( + test, + "Condition failed: `(const { 1 }) == (const { 2 })` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(1 + const { 1 } == 3)); + assert_err( + test, + "Condition failed: `1 + (const { 1 }) == 3` (2 vs 3)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!([const { 1 }][0] == 2)); + assert_err( + test, + "Condition failed: `[const { 1 }][0] == 2` (1 vs 2)", + ); + + // Conditions that do not go through the comparison machinery. + + #[rustfmt::skip] + let test = || Ok(ensure!(const { false })); + assert_err(test, "Condition failed: `(const { false })`"); + + #[rustfmt::skip] + let test = || Ok(ensure!(const { false } && true)); + assert_err(test, "Condition failed: `(const { false }) && true`"); + + #[rustfmt::skip] + let test = || Ok(ensure!(true && const { false })); + assert_err(test, "Condition failed: `true && const { false }`"); +} + #[test] fn test_match() { #[rustfmt::skip]