Skip to content

Commit 96904a7

Browse files
committed
Fork thiserror as wherror with std::panic::Location support
- Rebrand from thiserror to wherror across all packages - Add comprehensive README documentation for location feature - Update all imports and references throughout codebase - Add location support implementation with automatic call site capture - Include proper attribution to original thiserror and PR dtolnay#291 authors
1 parent 36dc8b0 commit 96904a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+170
-135
lines changed

Cargo.toml

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[package]
2-
name = "thiserror"
2+
name = "wherror"
33
version = "2.0.12"
4-
authors = ["David Tolnay <dtolnay@gmail.com>"]
4+
authors = ["Tom Grushka <tom@dra11y.com>", "David Tolnay <dtolnay@gmail.com>"]
55
categories = ["rust-patterns"]
6-
description = "derive(Error)"
7-
documentation = "https://docs.rs/thiserror"
6+
description = "Fork of thiserror derive(Error) with std::panic::Location support"
7+
documentation = "https://docs.rs/wherror"
88
edition = "2021"
9-
keywords = ["error", "error-handling", "derive"]
9+
keywords = ["error", "error-handling", "derive", "location", "panic"]
1010
license = "MIT OR Apache-2.0"
11-
repository = "https://github.com/dtolnay/thiserror"
11+
repository = "https://github.com/dra11y/wherror"
1212
rust-version = "1.61"
1313

1414
[features]
@@ -28,7 +28,7 @@ default = ["std"]
2828
std = []
2929

3030
[dependencies]
31-
thiserror-impl = { version = "=2.0.12", path = "impl" }
31+
wherror-impl = { version = "=2.0.12", path = "impl", package = "wherror-impl" }
3232

3333
[dev-dependencies]
3434
anyhow = "1.0.73"
@@ -41,9 +41,4 @@ members = ["impl", "tests/no-std"]
4141

4242
[package.metadata.docs.rs]
4343
targets = ["x86_64-unknown-linux-gnu"]
44-
rustdoc-args = [
45-
"--generate-link-to-definition",
46-
"--extern-html-root-url=core=https://doc.rust-lang.org",
47-
"--extern-html-root-url=alloc=https://doc.rust-lang.org",
48-
"--extern-html-root-url=std=https://doc.rust-lang.org",
49-
]
44+
rustdoc-args = ["--generate-link-to-definition", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org"]

README.md

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
1-
derive(Error)
2-
=============
1+
wherror
2+
========
33

4-
[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/thiserror-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/thiserror)
5-
[<img alt="crates.io" src="https://img.shields.io/crates/v/thiserror.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/thiserror)
6-
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-thiserror-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/thiserror)
7-
[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/thiserror/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/thiserror/actions?query=branch%3Amaster)
4+
[<img alt="github" src="https://img.shields.io/badge/github-dra11y/wherror-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dra11y/wherror)
5+
[<img alt="crates.io" src="https://img.shields.io/crates/v/wherror.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/wherror)
6+
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-wherror-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/wherror)
87

9-
This library provides a convenient derive macro for the standard library's
8+
Fork of [thiserror] `derive(Error)` with [`std::panic::Location`]
9+
support. This library provides a convenient derive macro for the standard library's
1010
[`std::error::Error`] trait.
1111

12+
This fork was created because the location support feature ([thiserror#291]) has been
13+
waiting for over a year to be merged.
14+
1215
[`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html
16+
[`std::panic::Location`]: https://doc.rust-lang.org/std/panic/struct.Location.html
17+
[thiserror]: https://github.com/dtolnay/thiserror
18+
[thiserror#291]: https://github.com/dtolnay/thiserror/pull/291
1319

1420
```toml
1521
[dependencies]
16-
thiserror = "2"
22+
wherror = "2"
1723
```
1824

19-
*Compiler support: requires rustc 1.61+*
25+
## Location Support
2026

21-
<br>
27+
Add a field of type `&'static std::panic::Location<'static>` to automatically capture where errors are created:
28+
29+
```rust
30+
use wherror::Error;
31+
32+
#[derive(Error, Debug)]
33+
#[error("Failed at {location}: {source}")]
34+
pub struct MyError {
35+
#[from]
36+
source: std::io::Error,
37+
location: &'static std::panic::Location<'static>, // Auto-populated
38+
}
39+
40+
// Location automatically captured when using `?`
41+
std::fs::read_to_string("file.txt")?;
42+
```
2243

2344
## Example
2445

2546
```rust
26-
use thiserror::Error;
47+
use wherror::Error;
2748

2849
#[derive(Error, Debug)]
2950
pub enum DataStoreError {
@@ -45,10 +66,10 @@ pub enum DataStoreError {
4566

4667
## Details
4768

48-
- Thiserror deliberately does not appear in your public API. You get the same
49-
thing as if you had written an implementation of `std::error::Error` by hand,
50-
and switching from handwritten impls to thiserror or vice versa is not a
51-
breaking change.
69+
Wherror deliberately does not appear in your public API. You get the same
70+
thing as if you had written an implementation of `std::error::Error` by hand,
71+
and switching from handwritten impls to thiserror or vice versa is not a
72+
breaking change.
5273

5374
- Errors may be enums, structs with named fields, tuple structs, or unit
5475
structs.
@@ -92,7 +113,7 @@ pub enum DataStoreError {
92113
attribute.
93114

94115
The variant using `#[from]` must not contain any other fields beyond the
95-
source error (and possibly a backtrace &mdash; see below). Usually `#[from]`
116+
source error (and possibly a location or backtrace &mdash; see below). Usually `#[from]`
96117
fields are unnamed, but `#[from]` is allowed on a named field too.
97118

98119
```rust
@@ -122,6 +143,20 @@ pub enum DataStoreError {
122143
}
123144
```
124145

146+
- Fields of type `&'static std::panic::Location<'static>` are automatically
147+
populated with the call site location when errors are created via `From` trait
148+
conversion. This works seamlessly with the `?` operator for precise error tracking.
149+
150+
```rust
151+
#[derive(Error, Debug)]
152+
#[error("Parse error at {location}: {source}")]
153+
pub struct ParseError {
154+
#[from]
155+
source: std::num::ParseIntError,
156+
location: &'static std::panic::Location<'static>, // automatically detected
157+
}
158+
```
159+
125160
- The Error trait's `provide()` method is implemented to provide whichever field
126161
has a type named `Backtrace`, if any, as a `std::backtrace::Backtrace`. Using
127162
`Backtrace` in errors requires a nightly compiler with Rust version 1.73 or
@@ -212,7 +247,7 @@ pub enum DataStoreError {
212247

213248
## Comparison to anyhow
214249

215-
Use thiserror if you care about designing your own dedicated error type(s) so
250+
Use wherror if you care about designing your own dedicated error type(s) so
216251
that the caller receives exactly the information that you choose in the event of
217252
failure. This most often applies to library-like code. Use [Anyhow] if you don't
218253
care what error type your functions return, you just want it to be easy. This is
@@ -236,3 +271,13 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
236271
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
237272
be dual licensed as above, without any additional terms or conditions.
238273
</sub>
274+
275+
<br>
276+
277+
#### Attribution
278+
279+
<sup>
280+
Fork of <a href="https://github.com/dtolnay/thiserror">thiserror</a> by David Tolnay,
281+
with location support by <a href="https://github.com/onlycs">Angad Tendulkar</a>
282+
from <a href="https://github.com/dtolnay/thiserror/pull/291">thiserror#291</a>.
283+
</sup>

impl/Cargo.toml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[package]
2-
name = "thiserror-impl"
2+
name = "wherror-impl"
33
version = "2.0.12"
4-
authors = ["David Tolnay <dtolnay@gmail.com>"]
5-
description = "Implementation detail of the `thiserror` crate"
4+
authors = ["Tom <tom@dra11y.com>", "David Tolnay <dtolnay@gmail.com>"]
5+
description = "Implementation detail of the `wherror` crate (fork of thiserror with Location support)"
66
edition = "2021"
77
license = "MIT OR Apache-2.0"
8-
repository = "https://github.com/dtolnay/thiserror"
8+
repository = "https://github.com/dra11y/wherror"
99
rust-version = "1.61"
1010

1111
[lib]
@@ -18,10 +18,4 @@ syn = "2.0.87"
1818

1919
[package.metadata.docs.rs]
2020
targets = ["x86_64-unknown-linux-gnu"]
21-
rustdoc-args = [
22-
"--generate-link-to-definition",
23-
"--extern-html-root-url=core=https://doc.rust-lang.org",
24-
"--extern-html-root-url=alloc=https://doc.rust-lang.org",
25-
"--extern-html-root-url=std=https://doc.rust-lang.org",
26-
"--extern-html-root-url=proc_macro=https://doc.rust-lang.org",
27-
]
21+
rustdoc-args = ["--generate-link-to-definition", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", "--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]

impl/src/expand.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@ fn impl_struct(input: Struct) -> TokenStream {
3535
let source_body = if let Some(transparent_attr) = &input.attrs.transparent {
3636
let only_field = &input.fields[0];
3737
if only_field.contains_generic {
38-
error_inferred_bounds.insert(only_field.ty, quote!(::thiserror::__private::Error));
38+
error_inferred_bounds.insert(only_field.ty, quote!(::wherror::__private::Error));
3939
}
4040
let member = &only_field.member;
4141
Some(quote_spanned! {transparent_attr.span=>
42-
::thiserror::__private::Error::source(self.#member.as_dyn_error())
42+
::wherror::__private::Error::source(self.#member.as_dyn_error())
4343
})
4444
} else if let Some(source_field) = input.source_field() {
4545
let source = &source_field.member;
4646
if source_field.contains_generic {
4747
let ty = unoptional_type(source_field.ty);
48-
error_inferred_bounds.insert(ty, quote!(::thiserror::__private::Error + 'static));
48+
error_inferred_bounds.insert(ty, quote!(::wherror::__private::Error + 'static));
4949
}
5050
let asref = if type_is_option(source_field.ty) {
5151
Some(quote_spanned!(source.span()=> .as_ref()?))
@@ -63,8 +63,8 @@ fn impl_struct(input: Struct) -> TokenStream {
6363
};
6464
let source_method = source_body.map(|body| {
6565
quote! {
66-
fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> {
67-
use ::thiserror::__private::AsDynError as _;
66+
fn source(&self) -> ::core::option::Option<&(dyn ::wherror::__private::Error + 'static)> {
67+
use ::wherror::__private::AsDynError as _;
6868
#body
6969
}
7070
}
@@ -91,28 +91,28 @@ fn impl_struct(input: Struct) -> TokenStream {
9191
} else if type_is_option(backtrace_field.ty) {
9292
Some(quote! {
9393
if let ::core::option::Option::Some(backtrace) = &self.#backtrace {
94-
#request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
94+
#request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
9595
}
9696
})
9797
} else {
9898
Some(quote! {
99-
#request.provide_ref::<::thiserror::__private::Backtrace>(&self.#backtrace);
99+
#request.provide_ref::<::wherror::__private::Backtrace>(&self.#backtrace);
100100
})
101101
};
102102
quote! {
103-
use ::thiserror::__private::ThiserrorProvide as _;
103+
use ::wherror::__private::ThiserrorProvide as _;
104104
#source_provide
105105
#self_provide
106106
}
107107
} else if type_is_option(backtrace_field.ty) {
108108
quote! {
109109
if let ::core::option::Option::Some(backtrace) = &self.#backtrace {
110-
#request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
110+
#request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
111111
}
112112
}
113113
} else {
114114
quote! {
115-
#request.provide_ref::<::thiserror::__private::Backtrace>(&self.#backtrace);
115+
#request.provide_ref::<::wherror::__private::Backtrace>(&self.#backtrace);
116116
}
117117
};
118118
quote! {
@@ -208,7 +208,7 @@ fn impl_struct(input: Struct) -> TokenStream {
208208
quote! {
209209
#[allow(unused_qualifications)]
210210
#[automatically_derived]
211-
impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #error_where_clause {
211+
impl #impl_generics ::wherror::__private::Error for #ty #ty_generics #error_where_clause {
212212
#source_method
213213
#provide_method
214214
}
@@ -228,11 +228,11 @@ fn impl_enum(input: Enum) -> TokenStream {
228228
if let Some(transparent_attr) = &variant.attrs.transparent {
229229
let only_field = &variant.fields[0];
230230
if only_field.contains_generic {
231-
error_inferred_bounds.insert(only_field.ty, quote!(::thiserror::__private::Error));
231+
error_inferred_bounds.insert(only_field.ty, quote!(::wherror::__private::Error));
232232
}
233233
let member = &only_field.member;
234234
let source = quote_spanned! {transparent_attr.span=>
235-
::thiserror::__private::Error::source(transparent.as_dyn_error())
235+
::wherror::__private::Error::source(transparent.as_dyn_error())
236236
};
237237
quote! {
238238
#ty::#ident {#member: transparent} => #source,
@@ -241,7 +241,7 @@ fn impl_enum(input: Enum) -> TokenStream {
241241
let source = &source_field.member;
242242
if source_field.contains_generic {
243243
let ty = unoptional_type(source_field.ty);
244-
error_inferred_bounds.insert(ty, quote!(::thiserror::__private::Error + 'static));
244+
error_inferred_bounds.insert(ty, quote!(::wherror::__private::Error + 'static));
245245
}
246246
let asref = if type_is_option(source_field.ty) {
247247
Some(quote_spanned!(source.span()=> .as_ref()?))
@@ -262,8 +262,8 @@ fn impl_enum(input: Enum) -> TokenStream {
262262
}
263263
});
264264
Some(quote! {
265-
fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> {
266-
use ::thiserror::__private::AsDynError as _;
265+
fn source(&self) -> ::core::option::Option<&(dyn ::wherror::__private::Error + 'static)> {
266+
use ::wherror::__private::AsDynError as _;
267267
#[allow(deprecated)]
268268
match self {
269269
#(#arms)*
@@ -299,12 +299,12 @@ fn impl_enum(input: Enum) -> TokenStream {
299299
let self_provide = if type_is_option(backtrace_field.ty) {
300300
quote! {
301301
if let ::core::option::Option::Some(backtrace) = backtrace {
302-
#request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
302+
#request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
303303
}
304304
}
305305
} else {
306306
quote! {
307-
#request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
307+
#request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
308308
}
309309
};
310310
quote! {
@@ -313,7 +313,7 @@ fn impl_enum(input: Enum) -> TokenStream {
313313
#source: #varsource,
314314
..
315315
} => {
316-
use ::thiserror::__private::ThiserrorProvide as _;
316+
use ::wherror::__private::ThiserrorProvide as _;
317317
#source_provide
318318
#self_provide
319319
}
@@ -337,7 +337,7 @@ fn impl_enum(input: Enum) -> TokenStream {
337337
};
338338
quote! {
339339
#ty::#ident {#backtrace: #varsource, ..} => {
340-
use ::thiserror::__private::ThiserrorProvide as _;
340+
use ::wherror::__private::ThiserrorProvide as _;
341341
#source_provide
342342
}
343343
}
@@ -347,12 +347,12 @@ fn impl_enum(input: Enum) -> TokenStream {
347347
let body = if type_is_option(backtrace_field.ty) {
348348
quote! {
349349
if let ::core::option::Option::Some(backtrace) = backtrace {
350-
#request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
350+
#request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
351351
}
352352
}
353353
} else {
354354
quote! {
355-
#request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
355+
#request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
356356
}
357357
};
358358
quote! {
@@ -486,7 +486,7 @@ fn impl_enum(input: Enum) -> TokenStream {
486486
quote! {
487487
#[allow(unused_qualifications)]
488488
#[automatically_derived]
489-
impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #error_where_clause {
489+
impl #impl_generics ::wherror::__private::Error for #ty #ty_generics #error_where_clause {
490490
#source_method
491491
#provide_method
492492
}
@@ -521,7 +521,7 @@ fn fields_pat(fields: &[Field]) -> TokenStream {
521521
fn use_as_display(needs_as_display: bool) -> Option<TokenStream> {
522522
if needs_as_display {
523523
Some(quote! {
524-
use ::thiserror::__private::AsDisplay as _;
524+
use ::wherror::__private::AsDisplay as _;
525525
})
526526
} else {
527527
None
@@ -544,11 +544,11 @@ fn from_initializer(
544544
let backtrace_member = &backtrace_field.member;
545545
if type_is_option(backtrace_field.ty) {
546546
quote! {
547-
#backtrace_member: ::core::option::Option::Some(::thiserror::__private::Backtrace::capture()),
547+
#backtrace_member: ::core::option::Option::Some(::wherror::__private::Backtrace::capture()),
548548
}
549549
} else {
550550
quote! {
551-
#backtrace_member: ::core::convert::From::from(::thiserror::__private::Backtrace::capture()),
551+
#backtrace_member: ::core::convert::From::from(::wherror::__private::Backtrace::capture()),
552552
}
553553
}
554554
});

impl/src/fallback.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub(crate) fn expand(input: &DeriveInput, error: syn::Error) -> TokenStream {
1414

1515
#[allow(unused_qualifications)]
1616
#[automatically_derived]
17-
impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #where_clause
17+
impl #impl_generics ::wherror::__private::Error for #ty #ty_generics #where_clause
1818
where
1919
// Work around trivial bounds being unstable.
2020
// https://github.com/rust-lang/rust/issues/48214

0 commit comments

Comments
 (0)