Skip to content

Commit 42a4b8c

Browse files
ilslvtyranron
andauthored
Replace fn backtrace() with Provider API in Error derive (#200)
Fixes #195 similar to dtolnay/thiserror#182 Co-authored-by: tyranron <[email protected]>
1 parent fe33b36 commit 42a4b8c

9 files changed

+1036
-160
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3232

3333
- Use a deterministic `HashSet` in all derives, this is needed for rust analyzer
3434
to work correctly.
35+
- Use `Provider` API for backtraces in `Error` derive.
3536

3637
## 0.99.10 - 2020-09-11
3738

doc/error.md

+30-15
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
# Using #[derive(Error)]
44
Deriving `Error` will generate an `Error` implementation, that contains
5-
(depending on the type) a `source()` and a `backtrace()` method. Please note,
6-
at the time of writing `backtrace` is only supported on nightly rust. So you
5+
(depending on the type) a `source()` and a `provide()` method. Please note,
6+
at the time of writing `provide()` is only supported on nightly rust. So you
77
have to use that to make use of it.
88

99
For a struct, these methods always do the same. For an `enum` they have separate
@@ -17,22 +17,22 @@ often [`From`] as well.
1717
[`Display`]: display.html
1818
[`From`]: from.html
1919

20-
## When and how does it derive `backtrace()`?
21-
22-
1. It's a struct/variant with named fields and one of the fields is
23-
called `backtrace`. Then it would return that field as the `backtrace`.
24-
2. It's a tuple struct/variant and the type of exactly one of the fields is
25-
called `Backtrace`. Then it would return that field as the `backtrace`.
26-
3. One of the fields is annotated with `#[error(backtrace)]`. Then it would
27-
return that field as the `backtrace`.
28-
2920
## When and how does it derive `source()`?
3021

3122
1. It's a struct/variant with named fields and one is the fields is
3223
called `source`. Then it would return that field as the `source`.
3324
2. It's a tuple struct/variant and there's exactly one field that is not used as
3425
the `backtrace`. So either a tuple struct with one field, or one with two where one
3526
is the `backtrace`. Then it returns this field as the `source`.
27+
3. One of the fields is annotated with `#[error(source)]`. Then it would
28+
return that field as the `source`.
29+
30+
## When and how does it derive `provide()`?
31+
32+
1. It's a struct/variant with named fields and one of the fields is
33+
called `backtrace`. Then it would return that field as the `backtrace`.
34+
2. It's a tuple struct/variant and the type of exactly one of the fields is
35+
called `Backtrace`. Then it would return that field as the `backtrace`.
3636
3. One of the fields is annotated with `#[error(backtrace)]`. Then it would
3737
return that field as the `backtrace`.
3838

@@ -47,10 +47,10 @@ ignored for one of these methods by using `#[error(not(backtrace))]` or
4747
# Example usage
4848

4949
```rust
50-
#![feature(backtrace)]
50+
#![feature(error_generic_member_access, provide_any)]
5151
# #[macro_use] extern crate derive_more;
5252
# use std::error::Error as _;
53-
use std::backtrace::Backtrace;
53+
use std::{any, backtrace::Backtrace};
5454

5555
// std::error::Error requires std::fmt::Debug and std::fmt::Display,
5656
// so we can also use derive_more::Display for fully declarative
@@ -90,17 +90,32 @@ enum CompoundError {
9090
WithSource {
9191
source: Simple,
9292
},
93+
#[from(ignore)]
94+
WithBacktraceFromSource {
95+
#[error(backtrace)]
96+
source: Simple,
97+
},
98+
#[display(fmt = "{source}")]
99+
WithDifferentBacktrace {
100+
source: Simple,
101+
backtrace: Backtrace,
102+
},
93103
WithExplicitSource {
94104
#[error(source)]
95105
explicit_source: WithSource,
96106
},
107+
#[from(ignore)]
108+
WithBacktraceFromExplicitSource {
109+
#[error(backtrace, source)]
110+
explicit_source: WithSource,
111+
},
97112
Tuple(WithExplicitSource),
98113
WithoutSource(#[error(not(source))] Tuple),
99114
}
100115

101116
fn main() {
102117
assert!(Simple.source().is_none());
103-
assert!(Simple.backtrace().is_none());
118+
assert!(any::request_ref::<Backtrace>(&Simple).is_none());
104119
assert!(WithSource::default().source().is_some());
105120
assert!(WithExplicitSource::default().source().is_some());
106121
assert!(Tuple::default().source().is_some());
@@ -110,7 +125,7 @@ fn main() {
110125
backtrace: Backtrace::capture(),
111126
};
112127
assert!(with_source_and_backtrace.source().is_some());
113-
assert!(with_source_and_backtrace.backtrace().is_some());
128+
assert!(any::request_ref::<Backtrace>(&with_source_and_backtrace).is_some());
114129

115130
assert!(CompoundError::Simple.source().is_none());
116131
assert!(CompoundError::from(Simple).source().is_some());

src/error.rs

+77-30
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub fn expand(
3232
})
3333
.collect();
3434

35-
let (bounds, source, backtrace) = match state.derive_type {
35+
let (bounds, source, provide) = match state.derive_type {
3636
DeriveType::Named | DeriveType::Unnamed => render_struct(&type_params, &state)?,
3737
DeriveType::Enum => render_enum(&type_params, &state)?,
3838
};
@@ -45,11 +45,11 @@ pub fn expand(
4545
}
4646
});
4747

48-
let backtrace = backtrace.map(|backtrace| {
48+
let provide = provide.map(|provide| {
4949
quote! {
50-
fn backtrace(&self) -> Option<&::std::backtrace::Backtrace> {
51-
#backtrace
52-
}
50+
fn provide<'_demand>(&'_demand self, demand: &mut ::std::any::Demand<'_demand>) {
51+
#provide
52+
}
5353
}
5454
});
5555

@@ -82,7 +82,7 @@ pub fn expand(
8282
let render = quote! {
8383
impl#impl_generics ::std::error::Error for #ident#ty_generics #where_clause {
8484
#source
85-
#backtrace
85+
#provide
8686
}
8787
};
8888

@@ -96,9 +96,9 @@ fn render_struct(
9696
let parsed_fields = parse_fields(type_params, state)?;
9797

9898
let source = parsed_fields.render_source_as_struct();
99-
let backtrace = parsed_fields.render_backtrace_as_struct();
99+
let provide = parsed_fields.render_provide_as_struct();
100100

101-
Ok((parsed_fields.bounds, source, backtrace))
101+
Ok((parsed_fields.bounds, source, provide))
102102
}
103103

104104
fn render_enum(
@@ -107,7 +107,7 @@ fn render_enum(
107107
) -> Result<(HashSet<syn::Type>, Option<TokenStream>, Option<TokenStream>)> {
108108
let mut bounds = HashSet::default();
109109
let mut source_match_arms = Vec::new();
110-
let mut backtrace_match_arms = Vec::new();
110+
let mut provide_match_arms = Vec::new();
111111

112112
for variant in state.enabled_variant_data().variants {
113113
let default_info = FullMetaInfo {
@@ -131,35 +131,31 @@ fn render_enum(
131131
source_match_arms.push(expr);
132132
}
133133

134-
if let Some(expr) = parsed_fields.render_backtrace_as_enum_variant_match_arm() {
135-
backtrace_match_arms.push(expr);
134+
if let Some(expr) = parsed_fields.render_provide_as_enum_variant_match_arm() {
135+
provide_match_arms.push(expr);
136136
}
137137

138138
bounds.extend(parsed_fields.bounds.into_iter());
139139
}
140140

141-
let render = |match_arms: &mut Vec<TokenStream>| {
141+
let render = |match_arms: &mut Vec<TokenStream>, unmatched| {
142142
if !match_arms.is_empty() && match_arms.len() < state.variants.len() {
143-
match_arms.push(quote!(_ => None));
143+
match_arms.push(quote!(_ => #unmatched));
144144
}
145145

146-
if !match_arms.is_empty() {
147-
let expr = quote! {
146+
(!match_arms.is_empty()).then(|| {
147+
quote! {
148148
match self {
149149
#(#match_arms),*
150150
}
151-
};
152-
153-
Some(expr)
154-
} else {
155-
None
156-
}
151+
}
152+
})
157153
};
158154

159-
let source = render(&mut source_match_arms);
160-
let backtrace = render(&mut backtrace_match_arms);
155+
let source = render(&mut source_match_arms, quote!(None));
156+
let provide = render(&mut provide_match_arms, quote!(()));
161157

162-
Ok((bounds, source, backtrace))
158+
Ok((bounds, source, provide))
163159
}
164160

165161
fn allowed_attr_params() -> AttrParams {
@@ -203,16 +199,67 @@ impl<'input, 'state> ParsedFields<'input, 'state> {
203199
Some(quote!(#pattern => #expr))
204200
}
205201

206-
fn render_backtrace_as_struct(&self) -> Option<TokenStream> {
202+
fn render_provide_as_struct(&self) -> Option<TokenStream> {
207203
let backtrace = self.backtrace?;
208-
let backtrace_expr = &self.data.members[backtrace];
209-
Some(quote!(Some(&#backtrace_expr)))
204+
205+
let source_provider = self.source.map(|source| {
206+
let source_expr = &self.data.members[source];
207+
quote! {
208+
::std::error::Error::provide(&#source_expr, demand);
209+
}
210+
});
211+
let backtrace_provider = self
212+
.source
213+
.filter(|source| *source == backtrace)
214+
.is_none()
215+
.then(|| {
216+
let backtrace_expr = &self.data.members[backtrace];
217+
quote! {
218+
demand.provide_ref::<std::backtrace::Backtrace>(&#backtrace_expr);
219+
}
220+
});
221+
222+
(source_provider.is_some() || backtrace_provider.is_some()).then(|| {
223+
quote! {
224+
#backtrace_provider
225+
#source_provider
226+
}
227+
})
210228
}
211229

212-
fn render_backtrace_as_enum_variant_match_arm(&self) -> Option<TokenStream> {
230+
fn render_provide_as_enum_variant_match_arm(&self) -> Option<TokenStream> {
213231
let backtrace = self.backtrace?;
214-
let pattern = self.data.matcher(&[backtrace], &[quote!(backtrace)]);
215-
Some(quote!(#pattern => Some(backtrace)))
232+
233+
match self.source {
234+
Some(source) if source == backtrace => {
235+
let pattern = self.data.matcher(&[source], &[quote!(source)]);
236+
Some(quote! {
237+
#pattern => {
238+
::std::error::Error::provide(source, demand);
239+
}
240+
})
241+
}
242+
Some(source) => {
243+
let pattern = self.data.matcher(
244+
&[source, backtrace],
245+
&[quote!(source), quote!(backtrace)],
246+
);
247+
Some(quote! {
248+
#pattern => {
249+
demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
250+
::std::error::Error::provide(source, demand);
251+
}
252+
})
253+
}
254+
None => {
255+
let pattern = self.data.matcher(&[backtrace], &[quote!(backtrace)]);
256+
Some(quote! {
257+
#pattern => {
258+
demand.provide_ref::<std::backtrace::Backtrace>(backtrace);
259+
}
260+
})
261+
}
262+
}
216263
}
217264
}
218265

0 commit comments

Comments
 (0)