Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to nightly's new Error::provide API #246

Merged
merged 1 commit into from Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 26 additions & 8 deletions build.rs
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
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
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
@@ -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
@@ -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
@@ -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