diff options
31 files changed, 429 insertions, 252 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index cafa20e..302d141 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "672e9525bbc2e5682c380d36974f34716b963591" + "sha1": "74bfe75eb25ba9d39b0ae5b570d611855cbc5086" }, "path_in_vcs": "" }
\ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61714b6..e1db2ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,12 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + jobs: test: name: Rust ${{matrix.rust}} @@ -12,41 +18,61 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.36.0] + rust: [beta, stable, 1.56.0] include: - rust: nightly rustflags: --cfg thiserror_nightly_testing + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} components: rust-src - run: cargo test --all env: - RUSTFLAGS: ${{matrix.rustflags}} + RUSTFLAGS: ${{matrix.rustflags}} ${{env.RUSTFLAGS}} msrv: name: Rust 1.31.0 runs-on: ubuntu-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@1.31.0 + with: + components: rust-src - run: cargo check clippy: name: Clippy runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@clippy + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy, rust-src - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic + miri: + name: Miri + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@miri + - run: cargo miri test + env: + MIRIFLAGS: -Zmiri-strict-provenance + outdated: name: Outdated runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 - - run: cargo outdated --exit-code 1 + - uses: actions/checkout@v3 + - uses: dtolnay/install@cargo-outdated + - run: cargo outdated --workspace --exit-code 1 @@ -42,18 +42,20 @@ rust_library { host_supported: true, crate_name: "thiserror", cargo_env_compat: true, - cargo_pkg_version: "1.0.30", + cargo_pkg_version: "1.0.38", srcs: ["src/lib.rs"], edition: "2018", + cfgs: ["provide_any"], proc_macros: ["libthiserror_impl"], apex_available: [ "//apex_available:platform", - "com.android.bluetooth", + "com.android.btservices", "com.android.compos", "com.android.resolv", "com.android.uwb", "com.android.virt", ], + product_available: true, vendor_available: true, min_sdk_version: "29", } @@ -13,20 +13,28 @@ edition = "2018" rust-version = "1.31" name = "thiserror" -version = "1.0.30" +version = "1.0.38" authors = ["David Tolnay <dtolnay@gmail.com>"] description = "derive(Error)" documentation = "https://docs.rs/thiserror" readme = "README.md" +keywords = [ + "error", + "error-handling", + "derive", +] categories = ["rust-patterns"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/thiserror" + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] + [dependencies.thiserror-impl] -version = "=1.0.30" +version = "=1.0.38" + [dev-dependencies.anyhow] -version = "1.0" +version = "1.0.65" [dev-dependencies.ref-cast] version = "1.0" @@ -35,5 +43,5 @@ version = "1.0" version = "1.0" [dev-dependencies.trybuild] -version = "1.0.49" +version = "1.0.66" features = ["diff"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index efa9af1..82ef03d 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,24 +1,24 @@ [package] name = "thiserror" -version = "1.0.30" +version = "1.0.38" authors = ["David Tolnay <dtolnay@gmail.com>"] +categories = ["rust-patterns"] +description = "derive(Error)" +documentation = "https://docs.rs/thiserror" edition = "2018" -rust-version = "1.31" +keywords = ["error", "error-handling", "derive"] license = "MIT OR Apache-2.0" -description = "derive(Error)" repository = "https://github.com/dtolnay/thiserror" -documentation = "https://docs.rs/thiserror" -categories = ["rust-patterns"] -readme = "README.md" +rust-version = "1.31" [dependencies] -thiserror-impl = { version = "=1.0.30", path = "impl" } +thiserror-impl = { version = "=1.0.38", path = "impl" } [dev-dependencies] -anyhow = "1.0" +anyhow = "1.0.65" ref-cast = "1.0" rustversion = "1.0" -trybuild = { version = "1.0.49", features = ["diff"] } +trybuild = { version = "1.0.66", features = ["diff"] } [workspace] members = ["impl"] @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/thiserror +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "thiserror" description: "derive(Error)" third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/thiserror/thiserror-1.0.30.crate" + value: "https://static.crates.io/crates/thiserror/thiserror-1.0.38.crate" } - version: "1.0.30" + version: "1.0.38" license_type: NOTICE last_upgrade_date { year: 2022 - month: 3 - day: 1 + month: 12 + day: 19 } } @@ -3,8 +3,8 @@ derive(Error) [<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) [<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) -[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-thiserror-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/thiserror) -[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/thiserror/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/thiserror/actions?query=branch%3Amaster) +[<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) +[<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) This library provides a convenient derive macro for the standard library's [`std::error::Error`] trait. @@ -124,8 +124,8 @@ pub enum DataStoreError { } ``` -- The Error trait's `backtrace()` method is implemented to return whichever - field has a type named `Backtrace`, if any. +- The Error trait's `provide()` method is implemented to provide whichever field + has a type named `Backtrace`, if any, as a `std::backtrace::Backtrace`. ```rust use std::backtrace::Backtrace; @@ -138,8 +138,9 @@ pub enum DataStoreError { ``` - If a field is both a source (named `source`, or has `#[source]` or `#[from]` - attribute) *and* is marked `#[backtrace]`, then the Error trait's - `backtrace()` method is forwarded to the source's backtrace. + attribute) *and* is marked `#[backtrace]`, then the Error trait's `provide()` + method is forwarded to the source's `provide` so that both layers of the error + share the same backtrace. ```rust #[derive(Error, Debug)] @@ -165,6 +166,27 @@ pub enum DataStoreError { } ``` + Another use case is hiding implementation details of an error representation + behind an opaque error type, so that the representation is able to evolve + without breaking the crate's public API. + + ```rust + // PublicError is public, but opaque and easy to keep compatible. + #[derive(Error, Debug)] + #[error(transparent)] + pub struct PublicError(#[from] ErrorRepr); + + impl PublicError { + // Accessors for anything we do want to expose publicly. + } + + // Private and free to change across minor version of the crate. + #[derive(Error, Debug)] + enum ErrorRepr { + ... + } + ``` + - See also the [`anyhow`] library for a convenient single error type to use in application code. diff --git a/TEST_MAPPING b/TEST_MAPPING index eea7991..6b4c996 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -9,142 +9,60 @@ }, { "path": "external/rust/crates/serde-xml-rs" - } - ], - "presubmit": [ - { - "name": "ZipFuseTest" - }, - { - "name": "apkdmverity.test" - }, - { - "name": "authfs_device_test_src_lib" - }, - { - "name": "diced_open_dice_cbor_test" - }, - { - "name": "diced_sample_inputs_test" - }, - { - "name": "diced_test" - }, - { - "name": "diced_utils_test" - }, - { - "name": "diced_vendor_test" - }, - { - "name": "doh_unit_test" - }, - { - "name": "keystore2_crypto_test_rust" - }, - { - "name": "keystore2_selinux_concurrency_test" - }, - { - "name": "keystore2_selinux_test" - }, - { - "name": "keystore2_test" - }, - { - "name": "keystore2_test_utils_test" - }, - { - "name": "keystore2_vintf_test" - }, - { - "name": "legacykeystore_test" - }, - { - "name": "libapkverify.integration_test" - }, - { - "name": "libapkverify.test" - }, - { - "name": "libcert_request_validator_tests" - }, - { - "name": "librustutils_test" - }, - { - "name": "microdroid_manager_test" - }, - { - "name": "virtualizationservice_device_test" - } - ], - "presubmit-rust": [ - { - "name": "ZipFuseTest" - }, - { - "name": "apkdmverity.test" - }, - { - "name": "authfs_device_test_src_lib" - }, - { - "name": "diced_open_dice_cbor_test" }, { - "name": "diced_sample_inputs_test" + "path": "external/uwb/src" }, { - "name": "diced_test" + "path": "packages/modules/DnsResolver" }, { - "name": "diced_utils_test" + "path": "packages/modules/Virtualization/apkdmverity" }, { - "name": "diced_vendor_test" + "path": "packages/modules/Virtualization/authfs" }, { - "name": "doh_unit_test" + "path": "packages/modules/Virtualization/avmd" }, { - "name": "keystore2_crypto_test_rust" + "path": "packages/modules/Virtualization/encryptedstore" }, { - "name": "keystore2_selinux_concurrency_test" + "path": "packages/modules/Virtualization/libs/apexutil" }, { - "name": "keystore2_selinux_test" + "path": "packages/modules/Virtualization/libs/devicemapper" }, { - "name": "keystore2_test" + "path": "packages/modules/Virtualization/microdroid_manager" }, { - "name": "keystore2_test_utils_test" + "path": "packages/modules/Virtualization/virtualizationmanager" }, { - "name": "keystore2_vintf_test" + "path": "packages/modules/Virtualization/vm" }, { - "name": "legacykeystore_test" + "path": "packages/modules/Virtualization/zipfuse" }, { - "name": "libapkverify.integration_test" + "path": "system/keymint/hal" }, { - "name": "libapkverify.test" + "path": "system/security/diced" }, { - "name": "libcert_request_validator_tests" + "path": "system/security/keystore2" }, { - "name": "librustutils_test" + "path": "system/security/keystore2/legacykeystore" }, { - "name": "microdroid_manager_test" + "path": "system/security/keystore2/selinux" }, { - "name": "virtualizationservice_device_test" + "path": "system/security/keystore2/src/crypto" } ] } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..004dfb0 --- /dev/null +++ b/build.rs @@ -0,0 +1,66 @@ +use std::env; +use std::fs; +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. +const PROBE: &str = r#" + #![feature(provide_any)] + + use std::any::{Demand, Provider}; + + fn _f<'a, P: Provider>(p: &'a P, demand: &mut Demand<'a>) { + p.provide(demand); + } +"#; + +fn main() { + match compile_probe() { + Some(status) if status.success() => println!("cargo:rustc-cfg=provide_any"), + _ => {} + } +} + +fn compile_probe() -> Option<ExitStatus> { + let rustc = env::var_os("RUSTC")?; + let out_dir = env::var_os("OUT_DIR")?; + let probefile = Path::new(&out_dir).join("probe.rs"); + fs::write(&probefile, PROBE).ok()?; + + // Make sure to pick up Cargo rustc configuration. + let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") { + let mut cmd = Command::new(wrapper); + // The wrapper's first argument is supposed to be the path to rustc. + cmd.arg(rustc); + cmd + } else { + Command::new(rustc) + }; + + cmd.stderr(Stdio::null()) + .arg("--edition=2018") + .arg("--crate-name=thiserror_build") + .arg("--crate-type=lib") + .arg("--emit=metadata") + .arg("--out-dir") + .arg(out_dir) + .arg(probefile); + + if let Some(target) = env::var_os("TARGET") { + cmd.arg("--target").arg(target); + } + + // If Cargo wants to set RUSTFLAGS, use that. + if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") { + if !rustflags.is_empty() { + for arg in rustflags.split('\x1f') { + cmd.arg(arg); + } + } + } + + cmd.status().ok() +} diff --git a/cargo2android.json b/cargo2android.json index 9a52406..7d5c2b4 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -1,7 +1,7 @@ { "apex-available": [ "//apex_available:platform", - "com.android.bluetooth", + "com.android.btservices", "com.android.compos", "com.android.resolv", "com.android.uwb", @@ -11,5 +11,6 @@ "device": true, "min-sdk-version": "29", "run": true, - "vendor-available": true + "vendor-available": true, + "product-available": true } diff --git a/src/aserror.rs b/src/aserror.rs index c036b7b..5fea84e 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::panic::UnwindSafe; -pub trait AsDynError<'a> { +pub trait AsDynError<'a>: Sealed { fn as_dyn_error(&self) -> &(dyn Error + 'a); } @@ -39,3 +39,10 @@ impl<'a> AsDynError<'a> for dyn Error + Send + Sync + UnwindSafe + 'a { self } } + +pub trait Sealed {} +impl<'a, T: Error + 'a> Sealed for T {} +impl<'a> Sealed for dyn Error + 'a {} +impl<'a> Sealed for dyn Error + Send + 'a {} +impl<'a> Sealed for dyn Error + Send + Sync + 'a {} +impl<'a> Sealed for dyn Error + Send + Sync + UnwindSafe + 'a {} @@ -2,7 +2,7 @@ //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust -//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs //! //! <br> //! @@ -146,8 +146,9 @@ //! # } //! ``` //! -//! - The Error trait's `backtrace()` method is implemented to return whichever -//! field has a type named `Backtrace`, if any. +//! - The Error trait's `provide()` method is implemented to provide whichever +//! field has a type named `Backtrace`, if any, as a +//! `std::backtrace::Backtrace`. //! //! ```rust //! # const IGNORE: &str = stringify! { @@ -163,7 +164,8 @@ //! //! - If a field is both a source (named `source`, or has `#[source]` or //! `#[from]` attribute) *and* is marked `#[backtrace]`, then the Error -//! trait's `backtrace()` method is forwarded to the source's backtrace. +//! trait's `provide()` method is forwarded to the source's `provide` so that +//! both layers of the error share the same backtrace. //! //! ```rust //! # const IGNORE: &str = stringify! { @@ -196,6 +198,31 @@ //! } //! ``` //! +//! Another use case is hiding implementation details of an error +//! representation behind an opaque error type, so that the representation is +//! able to evolve without breaking the crate's public API. +//! +//! ``` +//! # use thiserror::Error; +//! # +//! // PublicError is public, but opaque and easy to keep compatible. +//! #[derive(Error, Debug)] +//! #[error(transparent)] +//! pub struct PublicError(#[from] ErrorRepr); +//! +//! impl PublicError { +//! // Accessors for anything we do want to expose publicly. +//! } +//! +//! // Private and free to change across minor version of the crate. +//! #[derive(Error, Debug)] +//! enum ErrorRepr { +//! # /* +//! ... +//! # */ +//! } +//! ``` +//! //! - See also the [`anyhow`] library for a convenient single error type to use //! in application code. //! @@ -205,16 +232,23 @@ // Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7421 clippy::doc_markdown, clippy::module_name_repetitions, + clippy::return_self_not_must_use, + clippy::wildcard_imports, )] +#![cfg_attr(provide_any, feature(provide_any))] mod aserror; mod display; +#[cfg(provide_any)] +mod provide; pub use thiserror_impl::*; // Not public API. #[doc(hidden)] -pub mod private { +pub mod __private { pub use crate::aserror::AsDynError; pub use crate::display::{DisplayAsDisplay, PathAsDisplay}; + #[cfg(provide_any)] + pub use crate::provide::ThiserrorProvide; } diff --git a/src/provide.rs b/src/provide.rs new file mode 100644 index 0000000..524e743 --- /dev/null +++ b/src/provide.rs @@ -0,0 +1,15 @@ +use std::any::{Demand, Provider}; + +pub trait ThiserrorProvide: Sealed { + fn thiserror_provide<'a>(&'a self, demand: &mut Demand<'a>); +} + +impl<T: Provider + ?Sized> ThiserrorProvide for T { + #[inline] + fn thiserror_provide<'a>(&'a self, demand: &mut Demand<'a>) { + self.provide(demand); + } +} + +pub trait Sealed {} +impl<T: Provider + ?Sized> Sealed for T {} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 641d03c..7974a62 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,6 +1,5 @@ -#![deny(clippy::all, clippy::pedantic)] - #[rustversion::attr(not(nightly), ignore)] +#[cfg_attr(miri, ignore)] #[test] fn ui() { let t = trybuild::TestCases::new(); diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 42e37ca..43f68b8 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -1,4 +1,7 @@ -#![cfg_attr(thiserror_nightly_testing, feature(backtrace))] +#![cfg_attr( + thiserror_nightly_testing, + feature(error_generic_member_access, provide_any) +)] use thiserror::Error; @@ -16,6 +19,7 @@ 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; @@ -83,49 +87,94 @@ pub mod structs { backtrace: Arc<Backtrace>, } + #[derive(Error, Debug)] + #[error("...")] + pub struct AnyhowBacktrace { + #[backtrace] + source: anyhow::Error, + } + + #[derive(Error, Debug)] + #[error("...")] + pub struct BoxDynErrorBacktrace { + #[backtrace] + source: Box<dyn Error>, + } + #[test] fn test_backtrace() { let error = PlainBacktrace { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = ExplicitBacktrace { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = OptBacktrace { backtrace: Some(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = ArcBacktrace { backtrace: Arc::new(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = BacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = CombinedBacktraceFrom::from(InnerBacktrace { backtrace: Backtrace::capture(), }); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = OptBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = ArcBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); + + let error = AnyhowBacktrace { + source: anyhow::Error::msg("..."), + }; + assert!(any::request_ref::<Backtrace>(&error).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; } } #[cfg(thiserror_nightly_testing)] pub mod enums { use super::{Inner, InnerBacktrace}; + use std::any; use std::backtrace::Backtrace; - use std::error::Error; use std::sync::Arc; use thiserror::Error; @@ -210,36 +259,36 @@ pub mod enums { let error = PlainBacktrace::Test { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = ExplicitBacktrace::Test { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = OptBacktrace::Test { backtrace: Some(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = ArcBacktrace::Test { backtrace: Arc::new(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = BacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = CombinedBacktraceFrom::from(InnerBacktrace { backtrace: Backtrace::capture(), }); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = OptBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); let error = ArcBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::<Backtrace>(&error).is_some()); } } diff --git a/tests/test_deprecated.rs b/tests/test_deprecated.rs new file mode 100644 index 0000000..5524666 --- /dev/null +++ b/tests/test_deprecated.rs @@ -0,0 +1,10 @@ +#![deny(deprecated, clippy::all, clippy::pedantic)] + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[deprecated] + #[error("...")] + Deprecated, +} diff --git a/tests/test_display.rs b/tests/test_display.rs index 949d9ed..99ce2fd 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -1,6 +1,4 @@ -#![deny(clippy::all, clippy::pedantic)] - -use std::fmt::Display; +use std::fmt::{self, Display}; use thiserror::Error; fn assert<T: Display>(expected: &str, value: T) { @@ -144,6 +142,35 @@ fn test_match() { } #[test] +fn test_nested_display() { + // Same behavior as the one in `test_match`, but without String allocations. + #[derive(Error, Debug)] + #[error("{}", { + struct Msg<'a>(&'a String, &'a Option<usize>); + impl<'a> Display for Msg<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self.1 { + Some(n) => write!(formatter, "error occurred with {}", n), + None => write!(formatter, "there was an empty error"), + }?; + write!(formatter, ": {}", self.0) + } + } + Msg(.0, .1) + })] + struct Error(String, Option<usize>); + + assert( + "error occurred with 1: ...", + Error("...".to_owned(), Some(1)), + ); + assert( + "there was an empty error: ...", + Error("...".to_owned(), None), + ); +} + +#[test] fn test_void() { #[allow(clippy::empty_enum)] #[derive(Error, Debug)] diff --git a/tests/test_error.rs b/tests/test_error.rs index ece7f91..fab934d 100644 --- a/tests/test_error.rs +++ b/tests/test_error.rs @@ -1,4 +1,3 @@ -#![deny(clippy::all, clippy::pedantic)] #![allow(dead_code)] use std::fmt::{self, Display}; diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 87a56cf..34de560 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,5 +1,4 @@ -#![deny(clippy::all, clippy::pedantic)] -#![allow(clippy::option_if_let_else)] +#![allow(clippy::iter_cloned_collect, clippy::option_if_let_else)] use std::fmt::Display; use thiserror::Error; diff --git a/tests/test_from.rs b/tests/test_from.rs index a25ce35..1f38705 100644 --- a/tests/test_from.rs +++ b/tests/test_from.rs @@ -1,5 +1,3 @@ -#![deny(clippy::all, clippy::pedantic)] - use std::io; use thiserror::Error; diff --git a/tests/test_generics.rs b/tests/test_generics.rs index f5e1de2..4ab9f37 100644 --- a/tests/test_generics.rs +++ b/tests/test_generics.rs @@ -1,4 +1,4 @@ -#![deny(clippy::all, clippy::pedantic)] +#![allow(clippy::needless_late_init)] use std::fmt::{self, Debug, Display}; use thiserror::Error; diff --git a/tests/test_option.rs b/tests/test_option.rs index ca21713..ed5287d 100644 --- a/tests/test_option.rs +++ b/tests/test_option.rs @@ -1,5 +1,7 @@ -#![cfg_attr(thiserror_nightly_testing, feature(backtrace))] -#![deny(clippy::all, clippy::pedantic)] +#![cfg_attr( + thiserror_nightly_testing, + feature(error_generic_member_access, provide_any) +)] #[cfg(thiserror_nightly_testing)] pub mod structs { diff --git a/tests/test_path.rs b/tests/test_path.rs index a10b1f3..a34a3d7 100644 --- a/tests/test_path.rs +++ b/tests/test_path.rs @@ -1,5 +1,3 @@ -#![deny(clippy::all, clippy::pedantic)] - use ref_cast::RefCast; use std::fmt::Display; use std::path::{Path, PathBuf}; diff --git a/tests/test_source.rs b/tests/test_source.rs index ab16097..637f4ac 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,5 +1,3 @@ -#![deny(clippy::all, clippy::pedantic)] - use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 84d7c91..6f3c03e 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -1,5 +1,3 @@ -#![deny(clippy::all, clippy::pedantic)] - use anyhow::anyhow; use std::error::Error as _; use std::io; diff --git a/tests/ui/concat-display.stderr b/tests/ui/concat-display.stderr index 6ab4048..dbecd69 100644 --- a/tests/ui/concat-display.stderr +++ b/tests/ui/concat-display.stderr @@ -5,6 +5,6 @@ error: expected string literal | ^^^^^^ ... 13 | error_type!(Error, "foo"); - | -------------------------- in this macro invocation + | ------------------------- in this macro invocation | = note: this error originates in the macro `error_type` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/from-backtrace-backtrace.rs b/tests/ui/from-backtrace-backtrace.rs new file mode 100644 index 0000000..8f411bf --- /dev/null +++ b/tests/ui/from-backtrace-backtrace.rs @@ -0,0 +1,10 @@ +// https://github.com/dtolnay/thiserror/issues/163 + +use std::backtrace::Backtrace; +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("...")] +pub struct Error(#[from] #[backtrace] std::io::Error, Backtrace); + +fn main() {} diff --git a/tests/ui/from-backtrace-backtrace.stderr b/tests/ui/from-backtrace-backtrace.stderr new file mode 100644 index 0000000..55d647b --- /dev/null +++ b/tests/ui/from-backtrace-backtrace.stderr @@ -0,0 +1,5 @@ +error: deriving From requires no fields other than source and backtrace + --> tests/ui/from-backtrace-backtrace.rs:8:18 + | +8 | pub struct Error(#[from] #[backtrace] std::io::Error, Backtrace); + | ^^^^^^^ diff --git a/tests/ui/no-display.stderr b/tests/ui/no-display.stderr index e6a52f5..76818e1 100644 --- a/tests/ui/no-display.stderr +++ b/tests/ui/no-display.stderr @@ -1,23 +1,17 @@ error[E0599]: the method `as_display` exists for reference `&NoDisplay`, but its trait bounds were not satisfied - --> tests/ui/no-display.rs:7:9 - | -4 | struct NoDisplay; - | ----------------- doesn't satisfy `NoDisplay: std::fmt::Display` + --> tests/ui/no-display.rs:7:9 + | +4 | struct NoDisplay; + | ---------------- doesn't satisfy `NoDisplay: std::fmt::Display` ... -7 | #[error("thread: {thread}")] - | ^^^^^^^^^^^^^^^^^^ method cannot be called on `&NoDisplay` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `NoDisplay: std::fmt::Display` - which is required by `&NoDisplay: DisplayAsDisplay` -note: the following trait must be implemented - --> $RUST/core/src/fmt/mod.rs - | - | / pub trait Display { - | | /// Formats the value using the given formatter. - | | /// - | | /// # Examples -... | - | | fn fmt(&self, f: &mut Formatter<'_>) -> Result; - | | } - | |_^ +7 | #[error("thread: {thread}")] + | ^^^^^^^^^^^^^^^^^^ method cannot be called on `&NoDisplay` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `NoDisplay: std::fmt::Display` + which is required by `&NoDisplay: DisplayAsDisplay` +note: the trait `std::fmt::Display` must be implemented + --> $RUST/core/src/fmt/mod.rs + | + | pub trait Display { + | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/source-enum-not-error.stderr b/tests/ui/source-enum-not-error.stderr index d01cba5..750c69e 100644 --- a/tests/ui/source-enum-not-error.stderr +++ b/tests/ui/source-enum-not-error.stderr @@ -1,28 +1,22 @@ error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but its trait bounds were not satisfied - --> tests/ui/source-enum-not-error.rs:10:9 - | -4 | pub struct NotError; - | -------------------- - | | - | doesn't satisfy `NotError: AsDynError` - | doesn't satisfy `NotError: std::error::Error` + --> tests/ui/source-enum-not-error.rs:10:9 + | +4 | pub struct NotError; + | ------------------- + | | + | doesn't satisfy `NotError: AsDynError<'_>` + | doesn't satisfy `NotError: std::error::Error` ... -10 | source: NotError, - | ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `NotError: std::error::Error` - which is required by `NotError: AsDynError` - `&NotError: std::error::Error` - which is required by `&NotError: AsDynError` -note: the following trait must be implemented - --> $RUST/std/src/error.rs - | - | / pub trait Error: Debug + Display { - | | /// The lower-level source of this error, if any. - | | /// - | | /// # Examples -... | - | | } - | | } - | |_^ +10 | source: NotError, + | ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `NotError: std::error::Error` + which is required by `NotError: AsDynError<'_>` + `&NotError: std::error::Error` + which is required by `&NotError: AsDynError<'_>` +note: the trait `std::error::Error` must be implemented + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/source-struct-not-error.stderr b/tests/ui/source-struct-not-error.stderr index be1331a..b98460f 100644 --- a/tests/ui/source-struct-not-error.stderr +++ b/tests/ui/source-struct-not-error.stderr @@ -1,27 +1,21 @@ error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its trait bounds were not satisfied - --> tests/ui/source-struct-not-error.rs:9:5 - | -4 | struct NotError; - | ---------------- - | | - | method `as_dyn_error` not found for this - | doesn't satisfy `NotError: AsDynError` - | doesn't satisfy `NotError: std::error::Error` + --> tests/ui/source-struct-not-error.rs:9:5 + | +4 | struct NotError; + | --------------- + | | + | method `as_dyn_error` not found for this struct + | doesn't satisfy `NotError: AsDynError<'_>` + | doesn't satisfy `NotError: std::error::Error` ... -9 | source: NotError, - | ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `NotError: std::error::Error` - which is required by `NotError: AsDynError` -note: the following trait must be implemented - --> $RUST/std/src/error.rs - | - | / pub trait Error: Debug + Display { - | | /// The lower-level source of this error, if any. - | | /// - | | /// # Examples -... | - | | } - | | } - | |_^ +9 | source: NotError, + | ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `NotError: std::error::Error` + which is required by `NotError: AsDynError<'_>` +note: the trait `std::error::Error` must be implemented + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |