Skip to content

Commit 2a454f5

Browse files
authored
Preserve error.is::<anyhow::Error>() for errors created via format_err! (#12253)
* Preserve `error.is::<anyhow::Error>()` for errors created via `format_err!` * Add test for `format_err!(anyhow_error)`
1 parent 7b4a3e1 commit 2a454f5

2 files changed

Lines changed: 62 additions & 7 deletions

File tree

crates/error/src/macros.rs

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ use core::fmt::{self, write};
6464
/// assert_eq!(error.to_string(), "some other error (code 36)");
6565
/// # }
6666
/// ```
67+
///
68+
/// # From an `anyhow::Error`
69+
///
70+
/// The `format_err!` macro can always convert an `anyhow::Error` into a
71+
/// `wasmtime::Error`, but when the `"anyhow"` cargo feature is enabled the
72+
/// resulting error will also return true for
73+
/// [`error.is::<anyhow::Error>()`][crate::Error::is] invocations.
74+
///
75+
/// ```
76+
/// # fn _foo() {
77+
/// #![cfg(feature = "anyhow")]
78+
/// # use wasmtime_internal_error as wasmtime;
79+
/// use wasmtime::format_err;
80+
///
81+
/// let anyhow_error: anyhow::Error = anyhow::anyhow!("aw crap");
82+
/// let wasmtime_error: wasmtime::Error = format_err!(anyhow_error);
83+
/// assert!(wasmtime_error.is::<anyhow::Error>());
84+
/// # }
85+
/// ```
6786
#[macro_export]
6887
macro_rules! format_err {
6988
// Format-style invocation without explicit format arguments.
@@ -81,7 +100,7 @@ macro_rules! format_err {
81100
( $error:expr $(,)? ) => {{
82101
use $crate::macros::ctor_specialization::*;
83102
let error = $error;
84-
(&&error).wasmtime_error_choose_ctor().construct(error)
103+
(&&&error).wasmtime_error_choose_ctor().construct(error)
85104
}};
86105
}
87106

@@ -204,14 +223,16 @@ macro_rules! ensure {
204223
/// well-typed at the trait method call site `(&&&&&t).method()`, then
205224
/// `Specialization1` will be prioritized over `Specialization3`.
206225
///
207-
/// In our specific case here of choosing an `Error` constructor, we only have
208-
/// two specializations:
226+
/// In our specific case here of choosing an `Error` constructor, we have
227+
/// three specializations:
228+
///
229+
/// 1. For `anyhow::Error`, we want to use the `Error::from_anyhow` constructor.
209230
///
210-
/// 1. When the type implements `core::error::Error`, we want to use the
231+
/// 2. When the type implements `core::error::Error`, we want to use the
211232
/// `Error::new` constructor, which will preserve
212233
/// `core::error::Error::source` chains.
213234
///
214-
/// 2. Otherwise, we want to use the `Error::msg` constructor.
235+
/// 3. Otherwise, we want to use the `Error::msg` constructor.
215236
///
216237
/// The `*CtorTrait`s are our `n` specialization traits. Their
217238
/// `wasmtime_error_choose_ctor` methods will return different types, each of
@@ -222,14 +243,39 @@ macro_rules! ensure {
222243
pub mod ctor_specialization {
223244
use super::*;
224245

246+
#[cfg(feature = "anyhow")]
247+
pub use anyhow::*;
248+
#[cfg(feature = "anyhow")]
249+
mod anyhow {
250+
use super::*;
251+
252+
pub trait AnyhowCtorTrait {
253+
#[inline]
254+
fn wasmtime_error_choose_ctor(&self) -> AnyhowCtor {
255+
AnyhowCtor
256+
}
257+
}
258+
259+
impl AnyhowCtorTrait for &anyhow::Error {}
260+
261+
pub struct AnyhowCtor;
262+
263+
impl AnyhowCtor {
264+
#[inline]
265+
pub fn construct(&self, anyhow_error: ::anyhow::Error) -> Error {
266+
Error::from_anyhow(anyhow_error)
267+
}
268+
}
269+
}
270+
225271
pub trait NewCtorTrait {
226272
#[inline]
227273
fn wasmtime_error_choose_ctor(&self) -> NewCtor {
228274
NewCtor
229275
}
230276
}
231277

232-
impl<E: core::error::Error + Send + Sync + 'static> NewCtorTrait for &E {}
278+
impl<E: core::error::Error + Send + Sync + 'static> NewCtorTrait for &&E {}
233279

234280
pub struct NewCtor;
235281

@@ -250,7 +296,7 @@ pub mod ctor_specialization {
250296
}
251297
}
252298

253-
impl<M: fmt::Debug + fmt::Display + Send + Sync + 'static> MsgCtorTrait for &&M {}
299+
impl<M: fmt::Debug + fmt::Display + Send + Sync + 'static> MsgCtorTrait for &&&M {}
254300

255301
pub struct MsgCtor;
256302

crates/error/tests/tests.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,15 @@ fn format_err_macro_msg() {
301301
assert_eq!(error.to_string(), "42");
302302
}
303303

304+
#[test]
305+
#[cfg(feature = "anyhow")]
306+
fn format_err_is_anyhow() {
307+
let error: anyhow::Error = anyhow::anyhow!("oof");
308+
let error: Error = format_err!(error);
309+
assert!(error.is::<anyhow::Error>());
310+
assert_eq!(error.to_string(), "oof");
311+
}
312+
304313
#[test]
305314
fn bail_macro() {
306315
fn bail_string_literal() -> Result<()> {

0 commit comments

Comments
 (0)