Skip to content

Commit 905680e

Browse files
authored
Merge pull request #182 from dtolnay/provider
Expose backtrace via generic member access
2 parents 034c6ec + e11c97b commit 905680e

File tree

3 files changed

+85
-59
lines changed

3 files changed

+85
-59
lines changed

impl/src/expand.rs

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,45 +59,53 @@ fn impl_struct(input: Struct) -> TokenStream {
5959
}
6060
});
6161

62-
let backtrace_method = input.backtrace_field().map(|backtrace_field| {
62+
let provide_method = input.backtrace_field().map(|backtrace_field| {
63+
let demand = quote!(demand);
6364
let backtrace = &backtrace_field.member;
6465
let body = if let Some(source_field) = input.source_field() {
6566
let source = &source_field.member;
66-
let source_backtrace = if type_is_option(source_field.ty) {
67+
let source_provide = if type_is_option(source_field.ty) {
6768
quote_spanned! {source.span()=>
68-
self.#source.as_ref().and_then(|source| source.as_dyn_error().backtrace())
69+
if let std::option::Option::Some(source) = &self.#source {
70+
source.as_dyn_error().provide(#demand);
71+
}
6972
}
7073
} else {
7174
quote_spanned! {source.span()=>
72-
self.#source.as_dyn_error().backtrace()
75+
self.#source.as_dyn_error().provide(#demand);
7376
}
7477
};
75-
let combinator = if source == backtrace {
76-
source_backtrace
78+
let self_provide = if source == backtrace {
79+
None
7780
} else if type_is_option(backtrace_field.ty) {
78-
quote! {
79-
#source_backtrace.or(self.#backtrace.as_ref())
80-
}
81+
Some(quote! {
82+
if let std::option::Option::Some(backtrace) = &self.#backtrace {
83+
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
84+
}
85+
})
8186
} else {
82-
quote! {
83-
std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace))
84-
}
87+
Some(quote! {
88+
#demand.provide_ref::<std::backtrace::Backtrace>(&self.#backtrace);
89+
})
8590
};
8691
quote! {
8792
use thiserror::__private::AsDynError;
88-
#combinator
93+
#source_provide
94+
#self_provide
8995
}
9096
} else if type_is_option(backtrace_field.ty) {
9197
quote! {
92-
self.#backtrace.as_ref()
98+
if let std::option::Option::Some(backtrace) = &self.#backtrace {
99+
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
100+
}
93101
}
94102
} else {
95103
quote! {
96-
std::option::Option::Some(&self.#backtrace)
104+
#demand.provide_ref::<std::backtrace::Backtrace>(&self.#backtrace);
97105
}
98106
};
99107
quote! {
100-
fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
108+
fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) {
101109
#body
102110
}
103111
}
@@ -177,7 +185,7 @@ fn impl_struct(input: Struct) -> TokenStream {
177185
#[allow(unused_qualifications)]
178186
impl #impl_generics #error_trait for #ty #ty_generics #error_where_clause {
179187
#source_method
180-
#backtrace_method
188+
#provide_method
181189
}
182190
#display_impl
183191
#from_impl
@@ -237,7 +245,8 @@ fn impl_enum(input: Enum) -> TokenStream {
237245
None
238246
};
239247

240-
let backtrace_method = if input.has_backtrace() {
248+
let provide_method = if input.has_backtrace() {
249+
let demand = quote!(demand);
241250
let arms = input.variants.iter().map(|variant| {
242251
let ident = &variant.ident;
243252
match (variant.backtrace_field(), variant.source_field()) {
@@ -247,22 +256,26 @@ fn impl_enum(input: Enum) -> TokenStream {
247256
let backtrace = &backtrace_field.member;
248257
let source = &source_field.member;
249258
let varsource = quote!(source);
250-
let source_backtrace = if type_is_option(source_field.ty) {
259+
let source_provide = if type_is_option(source_field.ty) {
251260
quote_spanned! {source.span()=>
252-
#varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace())
261+
if let std::option::Option::Some(source) = #varsource {
262+
source.as_dyn_error().provide(#demand);
263+
}
253264
}
254265
} else {
255266
quote_spanned! {source.span()=>
256-
#varsource.as_dyn_error().backtrace()
267+
#varsource.as_dyn_error().provide(#demand);
257268
}
258269
};
259-
let combinator = if type_is_option(backtrace_field.ty) {
270+
let self_provide = if type_is_option(backtrace_field.ty) {
260271
quote! {
261-
#source_backtrace.or(backtrace.as_ref())
272+
if let std::option::Option::Some(backtrace) = backtrace {
273+
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
274+
}
262275
}
263276
} else {
264277
quote! {
265-
std::option::Option::Some(#source_backtrace.unwrap_or(backtrace))
278+
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
266279
}
267280
};
268281
quote! {
@@ -272,7 +285,8 @@ fn impl_enum(input: Enum) -> TokenStream {
272285
..
273286
} => {
274287
use thiserror::__private::AsDynError;
275-
#combinator
288+
#source_provide
289+
#self_provide
276290
}
277291
}
278292
}
@@ -281,40 +295,50 @@ fn impl_enum(input: Enum) -> TokenStream {
281295
{
282296
let backtrace = &backtrace_field.member;
283297
let varsource = quote!(source);
284-
let source_backtrace = if type_is_option(source_field.ty) {
298+
let source_provide = if type_is_option(source_field.ty) {
285299
quote_spanned! {backtrace.span()=>
286-
#varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace())
300+
if let std::option::Option::Some(source) = #varsource {
301+
source.as_dyn_error().provide(#demand);
302+
}
287303
}
288304
} else {
289305
quote_spanned! {backtrace.span()=>
290-
#varsource.as_dyn_error().backtrace()
306+
#varsource.as_dyn_error().provide(#demand);
291307
}
292308
};
293309
quote! {
294310
#ty::#ident {#backtrace: #varsource, ..} => {
295311
use thiserror::__private::AsDynError;
296-
#source_backtrace
312+
#source_provide
297313
}
298314
}
299315
}
300316
(Some(backtrace_field), _) => {
301317
let backtrace = &backtrace_field.member;
302318
let body = if type_is_option(backtrace_field.ty) {
303-
quote!(backtrace.as_ref())
319+
quote! {
320+
if let std::option::Option::Some(backtrace) = backtrace {
321+
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
322+
}
323+
}
304324
} else {
305-
quote!(std::option::Option::Some(backtrace))
325+
quote! {
326+
#demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
327+
}
306328
};
307329
quote! {
308-
#ty::#ident {#backtrace: backtrace, ..} => #body,
330+
#ty::#ident {#backtrace: backtrace, ..} => {
331+
#body
332+
}
309333
}
310334
}
311335
(None, _) => quote! {
312-
#ty::#ident {..} => std::option::Option::None,
336+
#ty::#ident {..} => {}
313337
},
314338
}
315339
});
316340
Some(quote! {
317-
fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
341+
fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) {
318342
#[allow(deprecated)]
319343
match self {
320344
#(#arms)*
@@ -420,7 +444,7 @@ fn impl_enum(input: Enum) -> TokenStream {
420444
#[allow(unused_qualifications)]
421445
impl #impl_generics #error_trait for #ty #ty_generics #error_where_clause {
422446
#source_method
423-
#backtrace_method
447+
#provide_method
424448
}
425449
#display_impl
426450
#(#from_impls)*

tests/test_backtrace.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// FIXME: needs to be updated to provide_ref/request_ref API.
2-
#![cfg(any())]
3-
#![cfg_attr(thiserror_nightly_testing, feature(backtrace))]
1+
#![cfg_attr(
2+
thiserror_nightly_testing,
3+
feature(error_generic_member_access, provide_any)
4+
)]
45

56
use thiserror::Error;
67

@@ -18,8 +19,8 @@ pub struct InnerBacktrace {
1819
#[cfg(thiserror_nightly_testing)]
1920
pub mod structs {
2021
use super::{Inner, InnerBacktrace};
22+
use std::any;
2123
use std::backtrace::Backtrace;
22-
use std::error::Error;
2324
use std::sync::Arc;
2425
use thiserror::Error;
2526

@@ -90,44 +91,44 @@ pub mod structs {
9091
let error = PlainBacktrace {
9192
backtrace: Backtrace::capture(),
9293
};
93-
assert!(error.backtrace().is_some());
94+
assert!(any::request_ref::<Backtrace>(&error).is_some());
9495

9596
let error = ExplicitBacktrace {
9697
backtrace: Backtrace::capture(),
9798
};
98-
assert!(error.backtrace().is_some());
99+
assert!(any::request_ref::<Backtrace>(&error).is_some());
99100

100101
let error = OptBacktrace {
101102
backtrace: Some(Backtrace::capture()),
102103
};
103-
assert!(error.backtrace().is_some());
104+
assert!(any::request_ref::<Backtrace>(&error).is_some());
104105

105106
let error = ArcBacktrace {
106107
backtrace: Arc::new(Backtrace::capture()),
107108
};
108-
assert!(error.backtrace().is_some());
109+
assert!(any::request_ref::<Backtrace>(&error).is_some());
109110

110111
let error = BacktraceFrom::from(Inner);
111-
assert!(error.backtrace().is_some());
112+
assert!(any::request_ref::<Backtrace>(&error).is_some());
112113

113114
let error = CombinedBacktraceFrom::from(InnerBacktrace {
114115
backtrace: Backtrace::capture(),
115116
});
116-
assert!(error.backtrace().is_some());
117+
assert!(any::request_ref::<Backtrace>(&error).is_some());
117118

118119
let error = OptBacktraceFrom::from(Inner);
119-
assert!(error.backtrace().is_some());
120+
assert!(any::request_ref::<Backtrace>(&error).is_some());
120121

121122
let error = ArcBacktraceFrom::from(Inner);
122-
assert!(error.backtrace().is_some());
123+
assert!(any::request_ref::<Backtrace>(&error).is_some());
123124
}
124125
}
125126

126127
#[cfg(thiserror_nightly_testing)]
127128
pub mod enums {
128129
use super::{Inner, InnerBacktrace};
130+
use std::any;
129131
use std::backtrace::Backtrace;
130-
use std::error::Error;
131132
use std::sync::Arc;
132133
use thiserror::Error;
133134

@@ -212,36 +213,36 @@ pub mod enums {
212213
let error = PlainBacktrace::Test {
213214
backtrace: Backtrace::capture(),
214215
};
215-
assert!(error.backtrace().is_some());
216+
assert!(any::request_ref::<Backtrace>(&error).is_some());
216217

217218
let error = ExplicitBacktrace::Test {
218219
backtrace: Backtrace::capture(),
219220
};
220-
assert!(error.backtrace().is_some());
221+
assert!(any::request_ref::<Backtrace>(&error).is_some());
221222

222223
let error = OptBacktrace::Test {
223224
backtrace: Some(Backtrace::capture()),
224225
};
225-
assert!(error.backtrace().is_some());
226+
assert!(any::request_ref::<Backtrace>(&error).is_some());
226227

227228
let error = ArcBacktrace::Test {
228229
backtrace: Arc::new(Backtrace::capture()),
229230
};
230-
assert!(error.backtrace().is_some());
231+
assert!(any::request_ref::<Backtrace>(&error).is_some());
231232

232233
let error = BacktraceFrom::from(Inner);
233-
assert!(error.backtrace().is_some());
234+
assert!(any::request_ref::<Backtrace>(&error).is_some());
234235

235236
let error = CombinedBacktraceFrom::from(InnerBacktrace {
236237
backtrace: Backtrace::capture(),
237238
});
238-
assert!(error.backtrace().is_some());
239+
assert!(any::request_ref::<Backtrace>(&error).is_some());
239240

240241
let error = OptBacktraceFrom::from(Inner);
241-
assert!(error.backtrace().is_some());
242+
assert!(any::request_ref::<Backtrace>(&error).is_some());
242243

243244
let error = ArcBacktraceFrom::from(Inner);
244-
assert!(error.backtrace().is_some());
245+
assert!(any::request_ref::<Backtrace>(&error).is_some());
245246
}
246247
}
247248

tests/test_option.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// FIXME: needs to be updated to provide_ref/request_ref API.
2-
#![cfg(any())]
3-
#![cfg_attr(thiserror_nightly_testing, feature(backtrace))]
1+
#![cfg_attr(
2+
thiserror_nightly_testing,
3+
feature(error_generic_member_access, provide_any)
4+
)]
45

56
#[cfg(thiserror_nightly_testing)]
67
pub mod structs {

0 commit comments

Comments
 (0)