Skip to content

Commit

Permalink
Merge pull request #246 from dtolnay/errorprovide
Browse files Browse the repository at this point in the history
Update to nightly's new Error::provide API
  • Loading branch information
dtolnay authored Aug 15, 2023
2 parents 543e123 + 8a95c25 commit a11330f
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 83 deletions.
34 changes: 26 additions & 8 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,40 @@ use std::path::Path;
use std::process::{Command, ExitStatus, Stdio};
use std::str;

// This code exercises the surface area that we expect of the Provider API. If
// the current toolchain is able to compile it, then thiserror is able to use
// providers for backtrace support.
// This code exercises the surface area that we expect of the Error generic
// member access API. If the current toolchain is able to compile it, then
// thiserror is able to provide backtrace support.
const PROBE: &str = r#"
#![feature(provide_any)]
#![feature(error_generic_member_access)]
use std::any::{Demand, Provider};
use std::error::{Error, Request};
use std::fmt::{self, Debug, Display};
fn _f<'a, P: Provider>(p: &'a P, demand: &mut Demand<'a>) {
p.provide(demand);
struct MyError(Thing);
struct Thing;
impl Debug for MyError {
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
unimplemented!()
}
}
impl Display for MyError {
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
unimplemented!()
}
}
impl Error for MyError {
fn provide<'a>(&'a self, request: &mut Request<'a>) {
request.provide_ref(&self.0);
}
}
"#;

fn main() {
match compile_probe() {
Some(status) if status.success() => println!("cargo:rustc-cfg=provide_any"),
Some(status) if status.success() => println!("cargo:rustc-cfg=error_generic_member_access"),
_ => {}
}
}
Expand Down
36 changes: 18 additions & 18 deletions impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,32 +60,32 @@ fn impl_struct(input: Struct) -> TokenStream {
});

let provide_method = input.backtrace_field().map(|backtrace_field| {
let demand = quote!(demand);
let request = quote!(request);
let backtrace = &backtrace_field.member;
let body = if let Some(source_field) = input.source_field() {
let source = &source_field.member;
let source_provide = if type_is_option(source_field.ty) {
quote_spanned! {source.span()=>
if let std::option::Option::Some(source) = &self.#source {
source.thiserror_provide(#demand);
source.thiserror_provide(#request);
}
}
} else {
quote_spanned! {source.span()=>
self.#source.thiserror_provide(#demand);
self.#source.thiserror_provide(#request);
}
};
let self_provide = if source == backtrace {
None
} else if type_is_option(backtrace_field.ty) {
Some(quote! {
if let std::option::Option::Some(backtrace) = &self.#backtrace {
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(backtrace);
}
})
} else {
Some(quote! {
#demand.provide_ref::<std::backtrace::Backtrace>(&self.#backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(&self.#backtrace);
})
};
quote! {
Expand All @@ -96,16 +96,16 @@ fn impl_struct(input: Struct) -> TokenStream {
} else if type_is_option(backtrace_field.ty) {
quote! {
if let std::option::Option::Some(backtrace) = &self.#backtrace {
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(backtrace);
}
}
} else {
quote! {
#demand.provide_ref::<std::backtrace::Backtrace>(&self.#backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(&self.#backtrace);
}
};
quote! {
fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) {
fn provide<'_request>(&'_request self, #request: &mut std::error::Request<'_request>) {
#body
}
}
Expand Down Expand Up @@ -246,7 +246,7 @@ fn impl_enum(input: Enum) -> TokenStream {
};

let provide_method = if input.has_backtrace() {
let demand = quote!(demand);
let request = quote!(request);
let arms = input.variants.iter().map(|variant| {
let ident = &variant.ident;
match (variant.backtrace_field(), variant.source_field()) {
Expand All @@ -259,23 +259,23 @@ fn impl_enum(input: Enum) -> TokenStream {
let source_provide = if type_is_option(source_field.ty) {
quote_spanned! {source.span()=>
if let std::option::Option::Some(source) = #varsource {
source.thiserror_provide(#demand);
source.thiserror_provide(#request);
}
}
} else {
quote_spanned! {source.span()=>
#varsource.thiserror_provide(#demand);
#varsource.thiserror_provide(#request);
}
};
let self_provide = if type_is_option(backtrace_field.ty) {
quote! {
if let std::option::Option::Some(backtrace) = backtrace {
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(backtrace);
}
}
} else {
quote! {
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(backtrace);
}
};
quote! {
Expand All @@ -298,12 +298,12 @@ fn impl_enum(input: Enum) -> TokenStream {
let source_provide = if type_is_option(source_field.ty) {
quote_spanned! {backtrace.span()=>
if let std::option::Option::Some(source) = #varsource {
source.thiserror_provide(#demand);
source.thiserror_provide(#request);
}
}
} else {
quote_spanned! {backtrace.span()=>
#varsource.thiserror_provide(#demand);
#varsource.thiserror_provide(#request);
}
};
quote! {
Expand All @@ -318,12 +318,12 @@ fn impl_enum(input: Enum) -> TokenStream {
let body = if type_is_option(backtrace_field.ty) {
quote! {
if let std::option::Option::Some(backtrace) = backtrace {
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(backtrace);
}
}
} else {
quote! {
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
#request.provide_ref::<std::backtrace::Backtrace>(backtrace);
}
};
quote! {
Expand All @@ -338,7 +338,7 @@ fn impl_enum(input: Enum) -> TokenStream {
}
});
Some(quote! {
fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) {
fn provide<'_request>(&'_request self, #request: &mut std::error::Request<'_request>) {
#[allow(deprecated)]
match self {
#(#arms)*
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,11 @@
clippy::return_self_not_must_use,
clippy::wildcard_imports,
)]
#![cfg_attr(provide_any, feature(provide_any))]
#![cfg_attr(error_generic_member_access, feature(error_generic_member_access))]

mod aserror;
mod display;
#[cfg(provide_any)]
#[cfg(error_generic_member_access)]
mod provide;

pub use thiserror_impl::*;
Expand All @@ -250,6 +250,6 @@ pub use thiserror_impl::*;
pub mod __private {
pub use crate::aserror::AsDynError;
pub use crate::display::{DisplayAsDisplay, PathAsDisplay};
#[cfg(provide_any)]
#[cfg(error_generic_member_access)]
pub use crate::provide::ThiserrorProvide;
}
15 changes: 9 additions & 6 deletions src/provide.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use std::any::{Demand, Provider};
use std::error::{Error, Request};

pub trait ThiserrorProvide: Sealed {
fn thiserror_provide<'a>(&'a self, demand: &mut Demand<'a>);
fn thiserror_provide<'a>(&'a self, request: &mut Request<'a>);
}

impl<T: Provider + ?Sized> ThiserrorProvide for T {
impl<T> ThiserrorProvide for T
where
T: Error + ?Sized,
{
#[inline]
fn thiserror_provide<'a>(&'a self, demand: &mut Demand<'a>) {
self.provide(demand);
fn thiserror_provide<'a>(&'a self, request: &mut Request<'a>) {
self.provide(request);
}
}

pub trait Sealed {}
impl<T: Provider + ?Sized> Sealed for T {}
impl<T: Error + ?Sized> Sealed for T {}
65 changes: 21 additions & 44 deletions tests/test_backtrace.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#![cfg_attr(
thiserror_nightly_testing,
feature(error_generic_member_access, provide_any)
)]
#![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))]

use thiserror::Error;

Expand All @@ -19,9 +16,8 @@ pub struct InnerBacktrace {
#[cfg(thiserror_nightly_testing)]
pub mod structs {
use super::{Inner, InnerBacktrace};
use std::any;
use std::backtrace::Backtrace;
use std::error::Error;
use std::error::{self, Error};
use std::sync::Arc;
use thiserror::Error;

Expand Down Expand Up @@ -106,75 +102,56 @@ pub mod structs {
let error = PlainBacktrace {
backtrace: Backtrace::capture(),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = ExplicitBacktrace {
backtrace: Backtrace::capture(),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = OptBacktrace {
backtrace: Some(Backtrace::capture()),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = ArcBacktrace {
backtrace: Arc::new(Backtrace::capture()),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = BacktraceFrom::from(Inner);
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = CombinedBacktraceFrom::from(InnerBacktrace {
backtrace: Backtrace::capture(),
});
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = OptBacktraceFrom::from(Inner);
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = ArcBacktraceFrom::from(Inner);
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = AnyhowBacktrace {
source: anyhow::Error::msg("..."),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some() || true); // FIXME

let error = BoxDynErrorBacktrace {
source: Box::new(PlainBacktrace {
backtrace: Backtrace::capture(),
}),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
}

// https://github.com/dtolnay/thiserror/issues/185 -- std::error::Error and
// std::any::Provide both have a method called 'provide', so directly
// calling it from generated code could be ambiguous.
#[test]
fn test_provide_name_collision() {
use std::any::Provider;

#[derive(Error, Debug)]
#[error("...")]
struct MyError {
#[source]
#[backtrace]
x: std::io::Error,
}

let _: dyn Error;
let _: dyn Provider;
assert!(error::request_ref::<Backtrace>(&error).is_some());
}
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
use super::{Inner, InnerBacktrace};
use std::any;
use std::backtrace::Backtrace;
use std::error;
use std::sync::Arc;
use thiserror::Error;

Expand Down Expand Up @@ -259,36 +236,36 @@ pub mod enums {
let error = PlainBacktrace::Test {
backtrace: Backtrace::capture(),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = ExplicitBacktrace::Test {
backtrace: Backtrace::capture(),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = OptBacktrace::Test {
backtrace: Some(Backtrace::capture()),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = ArcBacktrace::Test {
backtrace: Arc::new(Backtrace::capture()),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = BacktraceFrom::from(Inner);
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = CombinedBacktraceFrom::from(InnerBacktrace {
backtrace: Backtrace::capture(),
});
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = OptBacktraceFrom::from(Inner);
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());

let error = ArcBacktraceFrom::from(Inner);
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!(error::request_ref::<Backtrace>(&error).is_some());
}
}

Expand Down
5 changes: 1 addition & 4 deletions tests/test_option.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#![cfg_attr(
thiserror_nightly_testing,
feature(error_generic_member_access, provide_any)
)]
#![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))]

#[cfg(thiserror_nightly_testing)]
pub mod structs {
Expand Down

0 comments on commit a11330f

Please sign in to comment.