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 error generic member access #243

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +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 trait's generic member access.
// If the current toolchain is able to compile it, then thiserror is able to offer backtrace
// support via generic member access.
const PROBE: &str = r#"
#![feature(provide_any)]
#![feature(error_generic_member_access)]

use std::any::{Demand, Provider};
use std::any::Request;

fn _f<'a, P: Provider>(p: &'a P, demand: &mut Demand<'a>) {
p.provide(demand);
#[derive(Debug)]
struct ErrorWithGenericMemberAccess {
it: u32,
}

impl std::fmt::Display for ErrorWithGenericMemberAccess {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ErrorWithGenericMemberAccess")
}
}

impl std::error::Error for ErrorWithGenericMemberAccess {
fn provide<'a>(&'a self, request: &mut Request<'a>) {
request.provide_value::<u32>(self.it);
}
}

fn _get_u32(e: &dyn std::error::Error) -> Option<u32> {
e.request_value::<u32>()
}
"#;

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
39 changes: 18 additions & 21 deletions impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,52 +60,51 @@ 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.provide(#request);
}
}
} else {
quote_spanned! {source.span()=>
self.#source.thiserror_provide(#demand);
self.#source.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! {
use thiserror::__private::ThiserrorProvide;
#source_provide
#self_provide
}
} 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::any::Request<'_request>) {
#body
}
}
Expand Down Expand Up @@ -246,7 +245,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 +258,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.provide(#request);
}
}
} else {
quote_spanned! {source.span()=>
#varsource.thiserror_provide(#demand);
#varsource.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 @@ -284,7 +283,6 @@ fn impl_enum(input: Enum) -> TokenStream {
#source: #varsource,
..
} => {
use thiserror::__private::ThiserrorProvide;
#source_provide
#self_provide
}
Expand All @@ -298,17 +296,16 @@ 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.provide(#request);
}
}
} else {
quote_spanned! {backtrace.span()=>
#varsource.thiserror_provide(#demand);
#varsource.provide(#request);
}
};
quote! {
#ty::#ident {#backtrace: #varsource, ..} => {
use thiserror::__private::ThiserrorProvide;
#source_provide
}
}
Expand All @@ -318,12 +315,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 +335,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::any::Request<'_request>) {
#[allow(deprecated)]
match self {
#(#arms)*
Expand Down
6 changes: 1 addition & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,10 @@
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)]
mod provide;

pub use thiserror_impl::*;

Expand All @@ -250,6 +248,4 @@ pub use thiserror_impl::*;
pub mod __private {
pub use crate::aserror::AsDynError;
pub use crate::display::{DisplayAsDisplay, PathAsDisplay};
#[cfg(provide_any)]
pub use crate::provide::ThiserrorProvide;
}
15 changes: 0 additions & 15 deletions src/provide.rs

This file was deleted.

59 changes: 19 additions & 40 deletions tests/test_backtrace.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![cfg_attr(
thiserror_nightly_testing,
feature(error_generic_member_access, provide_any)
feature(error_generic_member_access)
)]

use thiserror::Error;
Expand All @@ -19,7 +19,6 @@ 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::sync::Arc;
Expand Down Expand Up @@ -106,74 +105,54 @@ pub mod structs {
let error = PlainBacktrace {
backtrace: Backtrace::capture(),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!((&error as &dyn std::error::Error).request_ref::<Backtrace>().is_some());

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

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

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

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

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

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

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

let error = AnyhowBacktrace {
source: anyhow::Error::msg("..."),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!((&error as &dyn std::error::Error).request_ref::<Backtrace>().is_some());

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 as &dyn std::error::Error).request_ref::<Backtrace>().is_some());
}
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
use super::{Inner, InnerBacktrace};
use std::any;
use std::backtrace::Backtrace;
use std::sync::Arc;
use thiserror::Error;
Expand Down Expand Up @@ -259,36 +238,36 @@ pub mod enums {
let error = PlainBacktrace::Test {
backtrace: Backtrace::capture(),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
assert!((&error as &dyn std::error::Error).request_ref::<Backtrace>().is_some());

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

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

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

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

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

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

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

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

#[cfg(thiserror_nightly_testing)]
Expand Down