diff options
38 files changed, 2085 insertions, 336 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index e50dc09..4958ce0 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "6c97c59375b24dad08c624cf61d06338f4dc9c3e" - } -} + "sha1": "58377abfea67601caf6a7051de082484717fe79f" + }, + "path_in_vcs": "" +}
\ No newline at end of file diff --git a/.clippy.toml b/.clippy.toml index b32f6d5..78eb145 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.34.0" +msrv = "1.38.0" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e685ee..198a836 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,69 +5,99 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + jobs: + pre_ci: + uses: dtolnay/.github/.github/workflows/pre_ci.yml@master + test: name: Rust ${{matrix.rust}} + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.51.0, 1.50.0] + rust: [nightly, beta, stable, 1.60.0] + 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 - run: cargo check --no-default-features - run: cargo check --features backtrace - backtrace: - name: Rust 1.42.0 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@1.42.0 - - run: cargo check --features backtrace - - nostd: - name: Rust 1.36.0 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@1.36.0 - - run: cargo check --no-default-features - - msrv: - name: Rust 1.34.0 + build: + name: Rust ${{matrix.rust}} + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust: [1.52.0, 1.51.0, 1.50.0, 1.42.0, 1.39.0] + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@1.34.0 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + components: rust-src - run: cargo check + - run: cargo check --no-default-features windows: name: Windows + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: windows-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable + with: + components: rust-src - run: cargo check --features backtrace 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 + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@nightly - with: - components: miri + - 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@v3 + - uses: dtolnay/install@cargo-outdated + - run: cargo outdated --workspace --exit-code 1 @@ -42,7 +42,7 @@ rust_test { host_supported: true, crate_name: "anyhow", cargo_env_compat: true, - cargo_pkg_version: "1.0.44", + cargo_pkg_version: "1.0.69", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -64,7 +64,7 @@ rust_defaults { name: "anyhow_test_defaults", crate_name: "anyhow", cargo_env_compat: true, - cargo_pkg_version: "1.0.44", + cargo_pkg_version: "1.0.69", test_suites: ["general-tests"], auto_gen_config: true, edition: "2018", @@ -184,7 +184,7 @@ rust_library { host_supported: true, crate_name: "anyhow", cargo_env_compat: true, - cargo_pkg_version: "1.0.44", + cargo_pkg_version: "1.0.69", srcs: ["src/lib.rs"], edition: "2018", features: [ @@ -193,12 +193,9 @@ rust_library { ], apex_available: [ "//apex_available:platform", - "com.android.bluetooth", - "com.android.compos", - "com.android.resolv", - "com.android.uwb", - "com.android.virt", + "//apex_available:anyapex", ], + product_available: true, vendor_available: true, min_sdk_version: "29", } @@ -11,27 +11,44 @@ [package] edition = "2018" +rust-version = "1.39" name = "anyhow" -version = "1.0.44" +version = "1.0.69" authors = ["David Tolnay <dtolnay@gmail.com>"] description = "Flexible concrete Error type built on std::error::Error" documentation = "https://docs.rs/anyhow" readme = "README.md" -categories = ["rust-patterns"] +keywords = [ + "error", + "error-handling", +] +categories = [ + "rust-patterns", + "no-std", +] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/anyhow" + [package.metadata.docs.rs] -rustdoc-args = ["--cfg", "doc_cfg"] targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = [ + "--cfg", + "doc_cfg", +] + +[lib] +doc-scrape-examples = false + [dependencies.backtrace] version = "0.3.51" optional = true + [dev-dependencies.futures] version = "0.3" default-features = false [dev-dependencies.rustversion] -version = "1.0" +version = "1.0.6" [dev-dependencies.syn] version = "1.0" @@ -41,7 +58,7 @@ features = ["full"] version = "1.0" [dev-dependencies.trybuild] -version = "1.0.19" +version = "1.0.66" features = ["diff"] [features] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 97fe475..b16323b 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,14 +1,15 @@ [package] name = "anyhow" -version = "1.0.44" # remember to update html_root_url +version = "1.0.69" # remember to update html_root_url authors = ["David Tolnay <dtolnay@gmail.com>"] +categories = ["rust-patterns", "no-std"] +description = "Flexible concrete Error type built on std::error::Error" +documentation = "https://docs.rs/anyhow" edition = "2018" +keywords = ["error", "error-handling"] license = "MIT OR Apache-2.0" -description = "Flexible concrete Error type built on std::error::Error" repository = "https://github.com/dtolnay/anyhow" -documentation = "https://docs.rs/anyhow" -readme = "README.md" -categories = ["rust-patterns"] +rust-version = "1.39" [features] default = ["std"] @@ -19,10 +20,13 @@ backtrace = { version = "0.3.51", optional = true } [dev-dependencies] futures = { version = "0.3", default-features = false } -rustversion = "1.0" +rustversion = "1.0.6" syn = { version = "1.0", features = ["full"] } thiserror = "1.0" -trybuild = { version = "1.0.19", features = ["diff"] } +trybuild = { version = "1.0.66", features = ["diff"] } + +[lib] +doc-scrape-examples = false [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 16fe87b..1b5ec8b 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -174,28 +174,3 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/anyhow +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "anyhow" description: "Flexible concrete Error type built on std::error::Error" third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/anyhow/anyhow-1.0.44.crate" + value: "https://static.crates.io/crates/anyhow/anyhow-1.0.69.crate" } - version: "1.0.44" + version: "1.0.69" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 9 - day: 22 + year: 2023 + month: 2 + day: 15 } } @@ -3,8 +3,8 @@ Anyhow ¯\\\_(°ペ)\_/¯ [<img alt="github" src="https://img.shields.io/badge/github-dtolnay/anyhow-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/anyhow) [<img alt="crates.io" src="https://img.shields.io/crates/v/anyhow.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/anyhow) -[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-anyhow-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/anyhow) -[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/anyhow/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/anyhow/actions?query=branch%3Amaster) +[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-anyhow-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/anyhow) +[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/anyhow/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/anyhow/actions?query=branch%3Amaster) This library provides [`anyhow::Error`][Error], a trait object based error type for easy idiomatic error handling in Rust applications. @@ -16,7 +16,7 @@ for easy idiomatic error handling in Rust applications. anyhow = "1.0" ``` -*Compiler support: requires rustc 1.34+* +*Compiler support: requires rustc 1.39+* <br> @@ -76,9 +76,9 @@ anyhow = "1.0" ``` - If using the nightly channel, or stable with `features = ["backtrace"]`, a - a backtrace is captured and printed with the error if the underlying error - type does not already provide its own. In order to see backtraces, they must - be enabled through the environment variables described in [`std::backtrace`]: + backtrace is captured and printed with the error if the underlying error type + does not already provide its own. In order to see backtraces, they must be + enabled through the environment variables described in [`std::backtrace`]: - If you want panics and errors to both have backtraces, set `RUST_BACKTRACE=1`; diff --git a/TEST_MAPPING b/TEST_MAPPING index bfcb3e9..f983c0e 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,111 +1,95 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { - "presubmit": [ + "imports": [ { - "name": "ZipFuseTest" + "path": "packages/modules/DnsResolver" }, { - "name": "anyhow_test_src_lib" + "path": "packages/modules/Virtualization/apkdmverity" }, { - "name": "anyhow_test_tests_test_autotrait" + "path": "packages/modules/Virtualization/authfs" }, { - "name": "anyhow_test_tests_test_boxed" + "path": "packages/modules/Virtualization/avmd" }, { - "name": "anyhow_test_tests_test_chain" + "path": "packages/modules/Virtualization/encryptedstore" }, { - "name": "anyhow_test_tests_test_context" + "path": "packages/modules/Virtualization/libs/apkverify" }, { - "name": "anyhow_test_tests_test_convert" + "path": "packages/modules/Virtualization/libs/capabilities" }, { - "name": "anyhow_test_tests_test_downcast" + "path": "packages/modules/Virtualization/libs/devicemapper" }, { - "name": "anyhow_test_tests_test_fmt" + "path": "packages/modules/Virtualization/microdroid_manager" }, { - "name": "anyhow_test_tests_test_macros" + "path": "packages/modules/Virtualization/virtualizationmanager" }, { - "name": "anyhow_test_tests_test_repr" + "path": "packages/modules/Virtualization/vm" }, { - "name": "anyhow_test_tests_test_source" - }, - { - "name": "apkdmverity.test" - }, - { - "name": "authfs_device_test_src_lib" + "path": "packages/modules/Virtualization/zipfuse" }, { - "name": "diced_open_dice_cbor_test" + "path": "system/keymint/hal" }, { - "name": "diced_sample_inputs_test" + "path": "system/security/diced" }, { - "name": "diced_test" + "path": "system/security/keystore2" }, { - "name": "diced_utils_test" + "path": "system/security/keystore2/legacykeystore" }, { - "name": "diced_vendor_test" - }, - { - "name": "doh_unit_test" - }, - { - "name": "keystore2_selinux_concurrency_test" - }, - { - "name": "keystore2_selinux_test" - }, + "path": "system/security/keystore2/selinux" + } + ], + "presubmit": [ { - "name": "keystore2_test" + "name": "anyhow_test_src_lib" }, { - "name": "keystore2_test_utils_test" + "name": "anyhow_test_tests_test_autotrait" }, { - "name": "keystore2_vintf_test" + "name": "anyhow_test_tests_test_boxed" }, { - "name": "legacykeystore_test" + "name": "anyhow_test_tests_test_chain" }, { - "name": "libapkverify.integration_test" + "name": "anyhow_test_tests_test_context" }, { - "name": "libapkverify.test" + "name": "anyhow_test_tests_test_convert" }, { - "name": "libcert_request_validator_tests" + "name": "anyhow_test_tests_test_downcast" }, { - "name": "libidsig.test" + "name": "anyhow_test_tests_test_fmt" }, { - "name": "librustutils_test" + "name": "anyhow_test_tests_test_macros" }, { - "name": "microdroid_manager_test" + "name": "anyhow_test_tests_test_repr" }, { - "name": "virtualizationservice_device_test" + "name": "anyhow_test_tests_test_source" } ], "presubmit-rust": [ { - "name": "ZipFuseTest" - }, - { "name": "anyhow_test_src_lib" }, { @@ -137,69 +121,6 @@ }, { "name": "anyhow_test_tests_test_source" - }, - { - "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_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": "libidsig.test" - }, - { - "name": "librustutils_test" - }, - { - "name": "microdroid_manager_test" - }, - { - "name": "virtualizationservice_device_test" } ] } @@ -15,15 +15,17 @@ compile_error! { // type. If the current toolchain is able to compile it, we go ahead and use // backtrace in anyhow. const PROBE: &str = r#" - #![feature(backtrace)] - #![allow(dead_code)] + #![feature(error_generic_member_access, provide_any)] + use std::any::{Demand, Provider}; use std::backtrace::{Backtrace, BacktraceStatus}; use std::error::Error; use std::fmt::{self, Display}; #[derive(Debug)] - struct E; + struct E { + backtrace: Backtrace, + } impl Display for E { fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { @@ -32,14 +34,26 @@ const PROBE: &str = r#" } impl Error for E { - fn backtrace(&self) -> Option<&Backtrace> { - let backtrace = Backtrace::capture(); - match backtrace.status() { - BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} - } - unimplemented!() + fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + demand.provide_ref(&self.backtrace); } } + + struct P; + + impl Provider for P { + fn provide<'a>(&'a self, _demand: &mut Demand<'a>) {} + } + + const _: fn() = || { + let backtrace: Backtrace = Backtrace::capture(); + let status: BacktraceStatus = backtrace.status(); + match status { + BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} + } + }; + + const _: fn(&dyn Error) -> Option<&Backtrace> = |err| err.request_ref::<Backtrace>(); "#; fn main() { @@ -55,13 +69,13 @@ fn main() { None => return, }; - if rustc < 38 { - println!("cargo:rustc-cfg=anyhow_no_macro_reexport"); - } - if rustc < 51 { println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of"); } + + if rustc < 52 { + println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str"); + } } fn compile_probe() -> Option<ExitStatus> { @@ -71,7 +85,7 @@ fn compile_probe() -> Option<ExitStatus> { fs::write(&probefile, PROBE).ok()?; // Make sure to pick up Cargo rustc configuration. - let mut cmd = if let Some(wrapper) = env::var_os("CARGO_RUSTC_WRAPPER") { + 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); @@ -89,6 +103,10 @@ fn compile_probe() -> Option<ExitStatus> { .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() { diff --git a/cargo2android.json b/cargo2android.json index 0e4d86d..6198806 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -1,14 +1,7 @@ { - "apex-available": [ - "//apex_available:platform", - "com.android.bluetooth", - "com.android.compos", - "com.android.resolv", - "com.android.uwb", - "com.android.virt" - ], "dependencies": true, "dependency-blocklist": [ + "backtrace", "rustversion", "syn", "trybuild" @@ -18,8 +11,13 @@ "run": true, "test-blocklist": [ "tests/compiletest.rs", - "tests/test_backtrace.rs" + "tests/test_backtrace.rs", + "tests/test_ensure.rs" ], "tests": true, - "vendor-available": true + "vendor-available": true, + "product-available": true, + "cfg-blocklist": [ + "backtrace" + ] } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..20fe888 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +components = ["rust-src"] diff --git a/src/backtrace.rs b/src/backtrace.rs index 1fa20a6..23c0c85 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -38,7 +38,7 @@ macro_rules! backtrace { #[cfg(backtrace)] macro_rules! backtrace_if_absent { ($err:expr) => { - match $err.backtrace() { + match ($err as &dyn std::error::Error).request_ref::<std::backtrace::Backtrace>() { Some(_) => None, None => backtrace!(), } @@ -182,7 +182,7 @@ mod capture { impl Backtrace { fn enabled() -> bool { static ENABLED: AtomicUsize = AtomicUsize::new(0); - match ENABLED.load(Ordering::SeqCst) { + match ENABLED.load(Ordering::Relaxed) { 0 => {} 1 => return false, _ => return true, @@ -194,7 +194,7 @@ mod capture { None => false, }, }; - ENABLED.store(enabled as usize + 1, Ordering::SeqCst); + ENABLED.store(enabled as usize + 1, Ordering::Relaxed); enabled } diff --git a/src/context.rs b/src/context.rs index c228413..9df8693 100644 --- a/src/context.rs +++ b/src/context.rs @@ -4,7 +4,7 @@ use core::convert::Infallible; use core::fmt::{self, Debug, Display, Write}; #[cfg(backtrace)] -use std::backtrace::Backtrace; +use std::any::{Demand, Provider}; mod ext { use super::*; @@ -24,7 +24,7 @@ mod ext { where C: Display + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(self); + let backtrace = backtrace_if_absent!(&self); Error::from_context(context, self, backtrace) } } @@ -47,7 +47,12 @@ where where C: Display + Send + Sync + 'static, { - self.map_err(|error| error.ext_context(context)) + // Not using map_err to save 2 useless frames off the captured backtrace + // in ext_context. + match self { + Ok(ok) => Ok(ok), + Err(error) => Err(error.ext_context(context)), + } } fn with_context<C, F>(self, context: F) -> Result<T, Error> @@ -55,7 +60,10 @@ where C: Display + Send + Sync + 'static, F: FnOnce() -> C, { - self.map_err(|error| error.ext_context(context())) + match self { + Ok(ok) => Ok(ok), + Err(error) => Err(error.ext_context(context())), + } } } @@ -84,7 +92,12 @@ impl<T> Context<T, Infallible> for Option<T> { where C: Display + Send + Sync + 'static, { - self.ok_or_else(|| Error::from_display(context, backtrace!())) + // Not using ok_or_else to save 2 useless frames off the captured + // backtrace. + match self { + Some(ok) => Ok(ok), + None => Err(Error::from_display(context, backtrace!())), + } } fn with_context<C, F>(self, context: F) -> Result<T, Error> @@ -92,7 +105,10 @@ impl<T> Context<T, Infallible> for Option<T> { C: Display + Send + Sync + 'static, F: FnOnce() -> C, { - self.ok_or_else(|| Error::from_display(context(), backtrace!())) + match self { + Some(ok) => Ok(ok), + None => Err(Error::from_display(context(), backtrace!())), + } } } @@ -123,28 +139,28 @@ where C: Display, E: StdError + 'static, { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> { - self.error.backtrace() - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.error) } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + StdError::provide(&self.error, demand); + } } impl<C> StdError for ContextError<C, Error> where C: Display, { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> { - Some(self.error.backtrace()) - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(unsafe { crate::ErrorImpl::error(self.error.inner.by_ref()) }) } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + Provider::provide(&self.error, demand); + } } struct Quoted<C>(C); diff --git a/src/ensure.rs b/src/ensure.rs new file mode 100644 index 0000000..0ab4471 --- /dev/null +++ b/src/ensure.rs @@ -0,0 +1,834 @@ +use crate::Error; +use alloc::string::String; +use core::fmt::{self, Debug, Write}; +use core::mem::MaybeUninit; +use core::ptr; +use core::slice; +use core::str; + +#[doc(hidden)] +pub trait BothDebug { + fn __dispatch_ensure(self, msg: &'static str) -> Error; +} + +impl<A, B> BothDebug for (A, B) +where + A: Debug, + B: Debug, +{ + fn __dispatch_ensure(self, msg: &'static str) -> Error { + render(msg, &self.0, &self.1) + } +} + +#[doc(hidden)] +pub trait NotBothDebug { + fn __dispatch_ensure(self, msg: &'static str) -> Error; +} + +impl<A, B> NotBothDebug for &(A, B) { + fn __dispatch_ensure(self, msg: &'static str) -> Error { + Error::msg(msg) + } +} + +struct Buf { + bytes: [MaybeUninit<u8>; 40], + written: usize, +} + +impl Buf { + fn new() -> Self { + Buf { + bytes: [MaybeUninit::uninit(); 40], + written: 0, + } + } + + fn as_str(&self) -> &str { + unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + self.bytes.as_ptr().cast::<u8>(), + self.written, + )) + } + } +} + +impl Write for Buf { + fn write_str(&mut self, s: &str) -> fmt::Result { + if s.bytes().any(|b| b == b' ' || b == b'\n') { + return Err(fmt::Error); + } + + let remaining = self.bytes.len() - self.written; + if s.len() > remaining { + return Err(fmt::Error); + } + + unsafe { + ptr::copy_nonoverlapping( + s.as_ptr(), + self.bytes.as_mut_ptr().add(self.written).cast::<u8>(), + s.len(), + ); + } + self.written += s.len(); + Ok(()) + } +} + +fn render(msg: &'static str, lhs: &dyn Debug, rhs: &dyn Debug) -> Error { + let mut lhs_buf = Buf::new(); + if fmt::write(&mut lhs_buf, format_args!("{:?}", lhs)).is_ok() { + let mut rhs_buf = Buf::new(); + if fmt::write(&mut rhs_buf, format_args!("{:?}", rhs)).is_ok() { + let lhs_str = lhs_buf.as_str(); + let rhs_str = rhs_buf.as_str(); + // "{msg} ({lhs} vs {rhs})" + let len = msg.len() + 2 + lhs_str.len() + 4 + rhs_str.len() + 1; + let mut string = String::with_capacity(len); + string.push_str(msg); + string.push_str(" ("); + string.push_str(lhs_str); + string.push_str(" vs "); + string.push_str(rhs_str); + string.push(')'); + return Error::msg(string); + } + } + Error::msg(msg) +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __parse_ensure { + (atom () $bail:tt $fuel:tt {($($rhs:tt)+) ($($lhs:tt)+) $op:tt} $dup:tt $(,)?) => { + $crate::__fancy_ensure!($($lhs)+, $op, $($rhs)+) + }; + + // low precedence control flow constructs + + (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt return $($rest:tt)*) => { + $crate::__fallback_ensure!($($bail)*) + }; + + (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt break $($rest:tt)*) => { + $crate::__fallback_ensure!($($bail)*) + }; + + (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt continue $($rest:tt)*) => { + $crate::__fallback_ensure!($($bail)*) + }; + + (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt yield $($rest:tt)*) => { + $crate::__fallback_ensure!($($bail)*) + }; + + (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt move $($rest:tt)*) => { + $crate::__fallback_ensure!($($bail)*) + }; + + // unary operators + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($deref:tt $($dup:tt)*) * $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $deref) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($not:tt $($dup:tt)*) ! $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $not) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($neg:tt $($dup:tt)*) - $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $neg) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($let:tt $($dup:tt)*) let $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $let) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($life:tt $colon:tt $($dup:tt)*) $label:lifetime : $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $life $colon) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $mut:tt $($dup:tt)*) &mut $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $and $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $($dup:tt)*) & $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $and) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($andand:tt $mut:tt $($dup:tt)*) &&mut $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $andand $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($andand:tt $($dup:tt)*) && $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $andand) $($parse)*} ($($rest)*) $($rest)*) + }; + + // control flow constructs + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($if:tt $($dup:tt)*) if $($rest:tt)*) => { + $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $if) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($match:tt $($dup:tt)*) match $($rest:tt)*) => { + $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $match) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($while:tt $($dup:tt)*) while $($rest:tt)*) => { + $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $while) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($for:tt $($dup:tt)*) for $($rest:tt)*) => { + $crate::__parse_ensure!(pat (cond $stack) $bail ($($fuel)*) {($($buf)* $for) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom (cond $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($block:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(cond $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*) + }; + + (cond $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($else:tt $if:tt $($dup:tt)*) else if $($rest:tt)*) => { + $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $else $if) $($parse)*} ($($rest)*) $($rest)*) + }; + + (cond $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($else:tt $brace:tt $($dup:tt)*) else {$($block:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $else $brace) $($parse)*} ($($rest)*) $($rest)*) + }; + + (cond $stack:tt $bail:tt (~$($fuel:tt)*) $parse:tt $dup:tt $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) $parse $dup $($rest)*) + }; + + // atomic expressions + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $($dup:tt)*) ($($content:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $paren) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bracket:tt $($dup:tt)*) [$($array:tt)*] $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $bracket) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($block:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($loop:tt $block:tt $($dup:tt)*) loop {$($body:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $loop $block) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($async:tt $block:tt $($dup:tt)*) async {$($body:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $async $block) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($async:tt $move:tt $block:tt $($dup:tt)*) async move {$($body:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $async $move $block) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($unsafe:tt $block:tt $($dup:tt)*) unsafe {$($body:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $unsafe $block) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $lit:literal $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $lit) $($parse)*} ($($rest)*) $($rest)*) + }; + + // path expressions + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(epath (atom $stack) $bail ($($fuel)*) {($($buf)* $colons $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(epath (atom $stack) $bail ($($fuel)*) {($($buf)* $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($langle:tt $($dup:tt)*) < $($rest:tt)*) => { + $crate::__parse_ensure!(type (qpath (epath (atom $stack))) $bail ($($fuel)*) {($($buf)* $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (epath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $langle:tt $($dup:tt)*) :: < $($rest:tt)*) => { + $crate::__parse_ensure!(generic (epath $stack) $bail ($($fuel)*) {($($buf)* $colons $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (epath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: << $($rest:tt)*) => { + $crate::__parse_ensure!(generic (epath $stack) $bail ($($fuel)*) {($($buf)* $colons <) $($parse)*} (< $($rest)*) < $($rest)*) + }; + + (epath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: <- $($rest:tt)*) => { + $crate::__parse_ensure!(generic (epath $stack) $bail ($($fuel)*) {($($buf)* $colons <) $($parse)*} (- $($rest)*) - $($rest)*) + }; + + (epath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(epath $stack $bail ($($fuel)*) {($($buf)* $colons $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (epath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! ($($mac:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*) + }; + + (epath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! [$($mac:tt)*] $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*) + }; + + (epath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! {$($mac:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*) + }; + + (epath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) $parse:tt $dup:tt $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) $parse $dup $($rest)*) + }; + + // trailer expressions + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $($dup:tt)*) ($($call:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $paren) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bracket:tt $($dup:tt)*) [$($index:tt)*] $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $bracket) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($init:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($question:tt $($dup:tt)*) ? $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $question) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $ident:tt $colons:tt $langle:tt $($dup:tt)*) . $i:ident :: < $($rest:tt)*) => { + $crate::__parse_ensure!(generic (atom $stack) $bail ($($fuel)*) {($($buf)* $dot $ident $colons $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $ident:tt $colons:tt $($dup:tt)*) . $i:ident :: << $($rest:tt)*) => { + $crate::__parse_ensure!(generic (atom $stack) $bail ($($fuel)*) {($($buf)* $dot $ident $colons <) $($parse)*} (< $($rest)*) < $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $ident:tt $colons:tt $($dup:tt)*) . $i:ident :: <- $($rest:tt)*) => { + $crate::__parse_ensure!(generic (atom $stack) $bail ($($fuel)*) {($($buf)* $dot $ident $colons <) $($parse)*} (- $($rest)*) - $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $($dup:tt)*) . $field:ident $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $dot $field) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $index:tt $($dup:tt)*) . $lit:literal $($rest:tt)*) => { + $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $dot $index) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($as:tt $($dup:tt)*) as $($rest:tt)*) => { + $crate::__parse_ensure!(type (atom $stack) $bail ($($fuel)*) {($($buf)* $as) $($parse)*} ($($rest)*) $($rest)*) + }; + + // types + + (type ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bracket:tt $($dup:tt)*) [$($content:tt)*] $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $bracket) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $($dup:tt)*) ($($content:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $paren) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($star:tt $const:tt $($dup:tt)*) *const $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $star $const) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($star:tt $mut:tt $($dup:tt)*) *mut $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $star $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $life:lifetime $mut:tt $($dup:tt)*) & $l:lifetime mut $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and $life $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $mut:tt $($dup:tt)*) & mut $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $life:lifetime $($dup:tt)*) & $l:lifetime $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and $life) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $($dup:tt)*) & $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $life:lifetime $mut:tt $($dup:tt)*) && $l:lifetime mut $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and $life $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $mut:tt $($dup:tt)*) && mut $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $life:lifetime $($dup:tt)*) && $l:lifetime $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and $life) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $($dup:tt)*) && $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $and) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($unsafe:tt $(extern $($abi:literal)?)? fn $($dup:tt)*) unsafe $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $unsafe) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($extern:tt $abi:literal fn $($dup:tt)*) extern $lit:literal $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $extern $abi) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($extern:tt fn $($dup:tt)*) extern $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $extern) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($fn:tt $paren:tt $arrow:tt $($dup:tt)*) fn ($($args:tt)*) -> $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $fn $paren $arrow) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($fn:tt $paren:tt $($dup:tt)*) fn ($($args:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $fn $paren) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($impl:tt $($dup:tt)*) impl $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $impl) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dyn:tt $($dup:tt)*) dyn $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $dyn) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($wild:tt $($dup:tt)*) _ $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $wild) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($never:tt $($dup:tt)*) ! $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $never) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($for:tt $langle:tt $($dup:tt)*) for < $($rest:tt)*) => { + $crate::__parse_ensure!(generic (type $stack) $bail ($($fuel)*) {($($buf)* $for $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + // path types + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(tpath $stack $bail ($($fuel)*) {($($buf)* $colons $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(tpath $stack $bail ($($fuel)*) {($($buf)* $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (type $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($langle:tt $($dup:tt)*) < $($rest:tt)*) => { + $crate::__parse_ensure!(type (qpath (tpath $stack)) $bail ($($fuel)*) {($($buf)* $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($langle:tt $($dup:tt)*) < $($rest:tt)*) => { + $crate::__parse_ensure!(generic (tpath $stack) $bail ($($fuel)*) {($($buf)* $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt << $($rest:tt)*) => { + $crate::__parse_ensure!(generic (tpath $stack) $bail ($($fuel)*) {($($buf)* <) $($parse)*} (< $($rest)*) < $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt <- $($rest:tt)*) => { + $crate::__parse_ensure!(generic (tpath $stack) $bail ($($fuel)*) {($($buf)* <) $($parse)*} (- $($rest)*) - $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $langle:tt $($dup:tt)*) :: < $($rest:tt)*) => { + $crate::__parse_ensure!(generic (tpath $stack) $bail ($($fuel)*) {($($buf)* $colons $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: << $($rest:tt)*) => { + $crate::__parse_ensure!(generic (tpath $stack) $bail ($($fuel)*) {($($buf)* $colons <) $($parse)*} (< $($rest)*) < $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: <- $($rest:tt)*) => { + $crate::__parse_ensure!(generic (tpath $stack) $bail ($($fuel)*) {($($buf)* $colons <) $($parse)*} (- $($rest)*) - $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(tpath $stack $bail ($($fuel)*) {($($buf)* $colons $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $arrow:tt $($dup:tt)*) ($($args:tt)*) -> $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $paren $arrow) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $($dup:tt)*) ($($args:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!(object $stack $bail ($($fuel)*) {($($buf)* $paren) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $paren:tt $arrow:tt $($dup:tt)*) :: ($($args:tt)*) -> $($rest:tt)*) => { + $crate::__parse_ensure!(type $stack $bail ($($fuel)*) {($($buf)* $colons $paren $arrow) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $paren:tt $($dup:tt)*) :: ($($args:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!(object $stack $bail ($($fuel)*) {($($buf)* $colons $paren) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! ($($mac:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! [$($mac:tt)*] $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! {$($mac:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*) + }; + + (tpath $stack:tt $bail:tt (~$($fuel:tt)*) $parse:tt $dup:tt $($rest:tt)*) => { + $crate::__parse_ensure!(object $stack $bail ($($fuel)*) $parse $dup $($rest)*) + }; + + // qualified paths + + (qpath ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($rangle:tt $colons:tt $($dup:tt)*) > :: $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $rangle $colons $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (qpath $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($as:tt $($dup:tt)*) as $($rest:tt)*) => { + $crate::__parse_ensure!(type (qpath $stack) $bail ($($fuel)*) {($($buf)* $as) $($parse)*} ($($rest)*) $($rest)*) + }; + + // trait objects + + (object (arglist $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($plus:tt $colons:tt $($dup:tt)*) + :: $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(tpath (arglist $stack) $bail ($($fuel)*) {($($buf)* $plus $colons $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (object (arglist $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($plus:tt $($dup:tt)*) + $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(tpath (arglist $stack) $bail ($($fuel)*) {($($buf)* $plus $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (object ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) $parse:tt $dup:tt $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) $parse $dup $($rest)*) + }; + + // angle bracketed generic arguments + + (generic ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($rangle:tt $($dup:tt)*) > $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $rangle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (generic ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt >> $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* >) $($parse)*} (> $($rest)*) > $($rest)*) + }; + + (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $lit:literal $($rest:tt)*) => { + $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $lit) $($parse)*} ($($rest)*) $($rest)*) + }; + + (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($block:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*) + }; + + (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $life:lifetime $($rest:tt)*) => { + $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $life) $($parse)*} ($($rest)*) $($rest)*) + }; + + (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($assoc:ident $eq:tt $($dup:tt)*) $ident:ident = $($rest:tt)*) => { + $crate::__parse_ensure!(type (arglist $stack) $bail ($($fuel)*) {($($buf)* $assoc $eq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (generic $stack:tt $bail:tt (~$($fuel:tt)*) $parse:tt $dup:tt $($rest:tt)*) => { + $crate::__parse_ensure!(type (arglist $stack) $bail ($($fuel)*) $parse $dup $($rest)*) + }; + + (arglist $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($comma:tt $($dup:tt)*) , $($rest:tt)*) => { + $crate::__parse_ensure!(generic $stack $bail ($($fuel)*) {($($buf)* $comma) $($parse)*} ($($rest)*) $($rest)*) + }; + + (arglist ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($rangle:tt $($dup:tt)*) > $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $rangle) $($parse)*} ($($rest)*) $($rest)*) + }; + + (arglist ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt >> $($rest:tt)*) => { + $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* >) $($parse)*} (> $($rest)*) > $($rest)*) + }; + + // patterns + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($pipe:tt $($dup:tt)*) | $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $pipe) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($eq:tt $($dup:tt)*) = $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $eq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($in:tt $($dup:tt)*) in $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $in) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($ref:tt $($dup:tt)*) ref $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $ref) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($mut:tt $($dup:tt)*) mut $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $mut) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($at:tt $($dup:tt)*) @ $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $at) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $lit:literal $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $lit) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($range:tt $($dup:tt)*) .. $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $range) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($range:tt $($dup:tt)*) ..= $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $range) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $($dup:tt)*) & $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $and) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($andand:tt $($dup:tt)*) && $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $andand) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $($dup:tt)*) ($($content:tt)*) $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $paren) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bracket:tt $($dup:tt)*) [$($content:tt)*] $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $bracket) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($content:tt)*} $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($wild:tt $($dup:tt)*) _ $($rest:tt)*) => { + $crate::__parse_ensure!(pat $stack $bail ($($fuel)*) {($($buf)* $wild) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(epath (pat $stack) $bail ($($fuel)*) {($($buf)* $colons $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $ident:ident $($rest:tt)*) => { + $crate::__parse_ensure!(epath (pat $stack) $bail ($($fuel)*) {($($buf)* $ident) $($parse)*} ($($rest)*) $($rest)*) + }; + + (pat $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($langle:tt $($dup:tt)*) < $($rest:tt)*) => { + $crate::__parse_ensure!(type (qpath (epath (pat $stack))) $bail ($($fuel)*) {($($buf)* $langle) $($parse)*} ($($rest)*) $($rest)*) + }; + + // high precedence binary operators + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($add:tt $($dup:tt)*) + $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $add) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($sub:tt $($dup:tt)*) - $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $sub) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($mul:tt $($dup:tt)*) * $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $mul) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($div:tt $($dup:tt)*) / $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $div) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($rem:tt $($dup:tt)*) % $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $rem) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitxor:tt $($dup:tt)*) ^ $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $bitxor) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitand:tt $($dup:tt)*) & $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $bitand) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitor:tt $($dup:tt)*) | $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $bitor) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shl:tt $($dup:tt)*) << $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $shl) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shr:tt $($dup:tt)*) >> $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $shr) $($parse)*} ($($rest)*) $($rest)*) + }; + + // comparison binary operators + + (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($eq:tt $($dup:tt)*) == $($rest:tt)*) => { + $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $eq} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($eq:tt $($dup:tt)*) == $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $eq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($le:tt $($dup:tt)*) <= $($rest:tt)*) => { + $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $le} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($le:tt $($dup:tt)*) <= $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $le) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($lt:tt $($dup:tt)*) < $($rest:tt)*) => { + $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $lt} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($lt:tt $($dup:tt)*) < $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $lt) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($ne:tt $($dup:tt)*) != $($rest:tt)*) => { + $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $ne} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($ne:tt $($dup:tt)*) != $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $ne) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($ge:tt $($dup:tt)*) >= $($rest:tt)*) => { + $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $ge} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($ge:tt $($dup:tt)*) >= $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $ge) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($gt:tt $($dup:tt)*) > $($rest:tt)*) => { + $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $gt} ($($rest)*) $($rest)*) + }; + + (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($gt:tt $($dup:tt)*) > $($rest:tt)*) => { + $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $gt) $($parse)*} ($($rest)*) $($rest)*) + }; + + // low precedence binary operators + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $($dup:tt)*) && $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $and) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($or:tt $($dup:tt)*) || $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $or) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($assign:tt $($dup:tt)*) = $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $assign) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($addeq:tt $($dup:tt)*) += $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $addeq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($subeq:tt $($dup:tt)*) -= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $subeq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($muleq:tt $($dup:tt)*) *= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $muleq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($diveq:tt $($dup:tt)*) /= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $diveq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($remeq:tt $($dup:tt)*) %= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $remeq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitxoreq:tt $($dup:tt)*) ^= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $bitxoreq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitandeq:tt $($dup:tt)*) &= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $bitandeq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitoreq:tt $($dup:tt)*) |= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $bitoreq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shleq:tt $($dup:tt)*) <<= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $shleq) $($parse)*} ($($rest)*) $($rest)*) + }; + + (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shreq:tt $($dup:tt)*) >>= $($rest:tt)*) => { + $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $shreq) $($parse)*} ($($rest)*) $($rest)*) + }; + + // unrecognized expression + + ($state:tt $stack:tt ($($bail:tt)*) $($rest:tt)*) => { + $crate::__fallback_ensure!($($bail)*) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __fancy_ensure { + ($lhs:expr, $op:tt, $rhs:expr) => { + match (&$lhs, &$rhs) { + (lhs, rhs) => { + if !(lhs $op rhs) { + #[allow(unused_imports)] + use $crate::__private::{BothDebug, NotBothDebug}; + return Err((lhs, rhs).__dispatch_ensure( + $crate::__private::concat!( + "Condition failed: `", + $crate::__private::stringify!($lhs), + " ", + $crate::__private::stringify!($op), + " ", + $crate::__private::stringify!($rhs), + "`", + ), + )); + } + } + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __fallback_ensure { + ($cond:expr $(,)?) => { + if !$cond { + return $crate::__private::Err($crate::Error::msg( + $crate::__private::concat!("Condition failed: `", $crate::__private::stringify!($cond), "`") + )); + } + }; + ($cond:expr, $msg:literal $(,)?) => { + if !$cond { + return $crate::__private::Err($crate::__anyhow!($msg)); + } + }; + ($cond:expr, $err:expr $(,)?) => { + if !$cond { + return $crate::__private::Err($crate::__anyhow!($err)); + } + }; + ($cond:expr, $fmt:expr, $($arg:tt)*) => { + if !$cond { + return $crate::__private::Err($crate::__anyhow!($fmt, $($arg)*)); + } + }; +} diff --git a/src/error.rs b/src/error.rs index 3fa0835..9f6ce8c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,12 @@ -use crate::alloc::Box; use crate::backtrace::Backtrace; use crate::chain::Chain; #[cfg(any(feature = "std", anyhow_no_ptr_addr_of))] use crate::ptr::Mut; use crate::ptr::{Own, Ref}; use crate::{Error, StdError}; +use alloc::boxed::Box; +#[cfg(backtrace)] +use core::any::Demand; use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::ManuallyDrop; @@ -26,11 +28,12 @@ impl Error { #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] #[cold] + #[must_use] pub fn new<E>(error: E) -> Self where E: StdError + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(error); + let backtrace = backtrace_if_absent!(&error); Error::from_std(error, backtrace) } @@ -72,6 +75,7 @@ impl Error { /// } /// ``` #[cold] + #[must_use] pub fn msg<M>(message: M) -> Self where M: Display + Debug + Send + Sync + 'static, @@ -293,6 +297,7 @@ impl Error { /// } /// ``` #[cold] + #[must_use] pub fn context<C>(self, context: C) -> Self where C: Display + Send + Sync + 'static, @@ -519,6 +524,18 @@ impl Error { } } +#[cfg(backtrace)] +impl std::any::Provider for Error { + // Called by thiserror when you have `#[source] anyhow::Error`. This provide + // implementation includes the anyhow::Error's Backtrace if any, unlike + // deref'ing to dyn Error where the provide implementation would include + // only the original error's Backtrace from before it got wrapped into an + // anyhow::Error. + fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + unsafe { ErrorImpl::provide(self.inner.by_ref(), demand) } + } +} + #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] impl<E> From<E> for Error @@ -527,7 +544,7 @@ where { #[cold] fn from(error: E) -> Self { - let backtrace = backtrace_if_absent!(error); + let backtrace = backtrace_if_absent!(&error); Error::from_std(error, backtrace) } } @@ -883,13 +900,21 @@ impl ErrorImpl { .as_ref() .or_else(|| { #[cfg(backtrace)] - return Self::error(this).backtrace(); - #[cfg(all(not(backtrace), feature = "backtrace"))] + return Self::error(this).request_ref::<Backtrace>(); + #[cfg(not(backtrace))] return (vtable(this.ptr).object_backtrace)(this); }) .expect("backtrace capture failed") } + #[cfg(backtrace)] + unsafe fn provide<'a>(this: Ref<'a, Self>, demand: &mut Demand<'a>) { + if let Some(backtrace) = &this.deref().backtrace { + demand.provide_ref(backtrace); + } + Self::error(this).provide(demand); + } + #[cold] pub(crate) unsafe fn chain(this: Ref<Self>) -> Chain { Chain::new(Self::error(this)) @@ -900,14 +925,14 @@ impl<E> StdError for ErrorImpl<E> where E: StdError, { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> { - Some(unsafe { ErrorImpl::backtrace(self.erase()) }) - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { unsafe { ErrorImpl::error(self.erase()).source() } } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + unsafe { ErrorImpl::provide(self.erase(), demand) } + } } impl<E> Debug for ErrorImpl<E> diff --git a/src/kind.rs b/src/kind.rs index 5985705..f47fe44 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -40,7 +40,7 @@ // The anyhow! macro will set up the call in this form: // // #[allow(unused_imports)] -// use $crate::private::{AdhocKind, TraitKind}; +// use $crate::__private::{AdhocKind, TraitKind}; // let error = $msg; // (&error).anyhow_kind().new(error) @@ -110,7 +110,7 @@ impl BoxedKind for Box<dyn StdError + Send + Sync> {} impl Boxed { #[cold] pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error { - let backtrace = backtrace_if_absent!(error); + let backtrace = backtrace_if_absent!(&*error); Error::from_boxed(error, backtrace) } } @@ -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> //! @@ -210,14 +210,15 @@ //! will require an explicit `.map_err(Error::msg)` when working with a //! non-Anyhow error type inside a function that returns Anyhow's error type. -#![doc(html_root_url = "https://docs.rs/anyhow/1.0.44")] -#![cfg_attr(backtrace, feature(backtrace))] +#![doc(html_root_url = "https://docs.rs/anyhow/1.0.69")] +#![cfg_attr(backtrace, feature(error_generic_member_access, provide_any))] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![cfg_attr(not(feature = "std"), no_std)] #![deny(dead_code, unused_imports, unused_mut)] #![allow( clippy::doc_markdown, clippy::enum_glob_use, + clippy::explicit_auto_deref, clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::module_name_repetitions, @@ -225,27 +226,20 @@ clippy::needless_doctest_main, clippy::new_ret_no_self, clippy::redundant_else, + clippy::return_self_not_must_use, clippy::unused_self, clippy::used_underscore_binding, clippy::wildcard_imports, clippy::wrong_self_convention )] -mod alloc { - #[cfg(not(feature = "std"))] - extern crate alloc; - - #[cfg(not(feature = "std"))] - pub use alloc::boxed::Box; - - #[cfg(feature = "std")] - pub use std::boxed::Box; -} +extern crate alloc; #[macro_use] mod backtrace; mod chain; mod context; +mod ensure; mod error; mod fmt; mod kind; @@ -373,7 +367,7 @@ pub use anyhow as format_err; /// # Ok(()) /// } /// ``` -#[repr(transparent)] +#[cfg_attr(not(doc), repr(transparent))] pub struct Error { inner: Own<ErrorImpl>, } @@ -503,6 +497,11 @@ pub type Result<T, E = Error> = core::result::Result<T, E>; /// No such file or directory (os error 2) /// ``` /// +/// Refer to the [Display representations] documentation for other forms in +/// which this context chain can be rendered. +/// +/// [Display representations]: Error#display-representations +/// /// <br> /// /// # Effect on downcasting @@ -610,13 +609,40 @@ pub trait Context<T, E>: context::private::Sealed { F: FnOnce() -> C; } +/// Equivalent to Ok::<_, anyhow::Error>(value). +/// +/// This simplifies creation of an anyhow::Result in places where type inference +/// cannot deduce the `E` type of the result — without needing to write +/// `Ok::<_, anyhow::Error>(value)`. +/// +/// One might think that `anyhow::Result::Ok(value)` would work in such cases +/// but it does not. +/// +/// ```console +/// error[E0282]: type annotations needed for `std::result::Result<i32, E>` +/// --> src/main.rs:11:13 +/// | +/// 11 | let _ = anyhow::Result::Ok(1); +/// | - ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the enum `Result` +/// | | +/// | consider giving this pattern the explicit type `std::result::Result<i32, E>`, where the type parameter `E` is specified +/// ``` +#[allow(non_snake_case)] +pub fn Ok<T>(t: T) -> Result<T> { + Result::Ok(t) +} + // Not public API. Referenced by macro-generated code. #[doc(hidden)] -pub mod private { +pub mod __private { use crate::Error; - use core::fmt::{Debug, Display}; + use alloc::fmt; + use core::fmt::Arguments; + pub use crate::ensure::{BothDebug, NotBothDebug}; + pub use alloc::format; pub use core::result::Result::Err; + pub use core::{concat, format_args, stringify}; #[doc(hidden)] pub mod kind { @@ -626,34 +652,29 @@ pub mod private { pub use crate::kind::BoxedKind; } + #[doc(hidden)] + #[inline] #[cold] - pub fn new_adhoc<M>(message: M) -> Error - where - M: Display + Debug + Send + Sync + 'static, - { - Error::from_adhoc(message, backtrace!()) - } - - #[cfg(anyhow_no_macro_reexport)] - pub use crate::{__anyhow_concat as concat, __anyhow_stringify as stringify}; - #[cfg(not(anyhow_no_macro_reexport))] - pub use core::{concat, stringify}; + pub fn format_err(args: Arguments) -> Error { + #[cfg(anyhow_no_fmt_arguments_as_str)] + let fmt_arguments_as_str = None::<&str>; + #[cfg(not(anyhow_no_fmt_arguments_as_str))] + let fmt_arguments_as_str = args.as_str(); - #[cfg(anyhow_no_macro_reexport)] - #[doc(hidden)] - #[macro_export] - macro_rules! __anyhow_concat { - ($($tt:tt)*) => { - concat!($($tt)*) - }; + if let Some(message) = fmt_arguments_as_str { + // anyhow!("literal"), can downcast to &'static str + Error::msg(message) + } else { + // anyhow!("interpolate {var}"), can downcast to String + Error::msg(fmt::format(args)) + } } - #[cfg(anyhow_no_macro_reexport)] #[doc(hidden)] - #[macro_export] - macro_rules! __anyhow_stringify { - ($($tt:tt)*) => { - stringify!($($tt)*) - }; + #[inline] + #[cold] + #[must_use] + pub fn must_use(error: Error) -> Error { + error } } diff --git a/src/macros.rs b/src/macros.rs index a0f4cae..6dd2274 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,6 +5,8 @@ /// The surrounding function's or closure's return value is required to be /// `Result<_,`[`anyhow::Error`][crate::Error]`>`. /// +/// [anyhow!]: crate::anyhow +/// /// # Example /// /// ``` @@ -53,13 +55,13 @@ #[macro_export] macro_rules! bail { ($msg:literal $(,)?) => { - return $crate::private::Err($crate::anyhow!($msg)) + return $crate::__private::Err($crate::__anyhow!($msg)) }; ($err:expr $(,)?) => { - return $crate::private::Err($crate::anyhow!($err)) + return $crate::__private::Err($crate::__anyhow!($err)) }; ($fmt:expr, $($arg:tt)*) => { - return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)) + return $crate::__private::Err($crate::__anyhow!($fmt, $($arg)*)) }; } @@ -75,6 +77,8 @@ macro_rules! bail { /// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error` /// rather than panicking. /// +/// [anyhow!]: crate::anyhow +/// /// # Example /// /// ``` @@ -111,31 +115,49 @@ macro_rules! bail { /// # Ok(()) /// # } /// ``` +#[cfg(doc)] #[macro_export] macro_rules! ensure { ($cond:expr $(,)?) => { - $crate::ensure!( - $cond, - $crate::private::concat!("Condition failed: `", $crate::private::stringify!($cond), "`"), - ) + if !$cond { + return $crate::__private::Err($crate::Error::msg( + $crate::__private::concat!("Condition failed: `", $crate::__private::stringify!($cond), "`") + )); + } }; ($cond:expr, $msg:literal $(,)?) => { if !$cond { - return $crate::private::Err($crate::anyhow!($msg)); + return $crate::__private::Err($crate::__anyhow!($msg)); } }; ($cond:expr, $err:expr $(,)?) => { if !$cond { - return $crate::private::Err($crate::anyhow!($err)); + return $crate::__private::Err($crate::__anyhow!($err)); } }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { if !$cond { - return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + return $crate::__private::Err($crate::__anyhow!($fmt, $($arg)*)); } }; } +#[cfg(not(doc))] +#[macro_export] +macro_rules! ensure { + ($($tt:tt)*) => { + $crate::__parse_ensure!( + /* state */ 0 + /* stack */ () + /* bail */ ($($tt)*) + /* fuel */ (~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~) + /* parse */ {()} + /* dup */ ($($tt)*) + /* rest */ $($tt)* + ) + }; +} + /// Construct an ad-hoc error from a string or existing non-`anyhow` error /// value. /// @@ -167,17 +189,43 @@ macro_rules! ensure { #[macro_export] macro_rules! anyhow { ($msg:literal $(,)?) => { - // Handle $:literal as a special case to make cargo-expanded code more - // concise in the common case. - $crate::private::new_adhoc($msg) + $crate::__private::must_use({ + let error = $crate::__private::format_err($crate::__private::format_args!($msg)); + error + }) + }; + ($err:expr $(,)?) => { + $crate::__private::must_use({ + use $crate::__private::kind::*; + let error = match $err { + error => (&error).anyhow_kind().new(error), + }; + error + }) + }; + ($fmt:expr, $($arg:tt)*) => { + $crate::Error::msg($crate::__private::format!($fmt, $($arg)*)) }; +} + +// Not public API. This is used in the implementation of some of the other +// macros, in which the must_use call is not needed because the value is known +// to be used. +#[doc(hidden)] +#[macro_export] +macro_rules! __anyhow { + ($msg:literal $(,)?) => ({ + let error = $crate::__private::format_err($crate::__private::format_args!($msg)); + error + }); ($err:expr $(,)?) => ({ - use $crate::private::kind::*; - match $err { + use $crate::__private::kind::*; + let error = match $err { error => (&error).anyhow_kind().new(error), - } + }; + error }); ($fmt:expr, $($arg:tt)*) => { - $crate::private::new_adhoc(format!($fmt, $($arg)*)) + $crate::Error::msg($crate::__private::format!($fmt, $($arg)*)) }; } @@ -1,4 +1,4 @@ -use crate::alloc::Box; +use alloc::boxed::Box; use core::marker::PhantomData; use core::ptr::NonNull; diff --git a/src/wrapper.rs b/src/wrapper.rs index 3ebe51a..5f18a50 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -1,6 +1,9 @@ use crate::StdError; use core::fmt::{self, Debug, Display}; +#[cfg(backtrace)] +use std::any::Demand; + #[repr(transparent)] pub struct MessageError<M>(pub M); @@ -67,12 +70,12 @@ impl Display for BoxedError { #[cfg(feature = "std")] impl StdError for BoxedError { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> { - self.0.backtrace() - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { self.0.source() } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + self.0.provide(demand); + } } diff --git a/tests/drop/mod.rs b/tests/drop/mod.rs index ba2cc5e..7da4bf5 100644 --- a/tests/drop/mod.rs +++ b/tests/drop/mod.rs @@ -2,8 +2,7 @@ use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering::SeqCst; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; #[derive(Debug)] @@ -19,7 +18,7 @@ impl Flag { } pub fn get(&self) -> bool { - self.atomic.load(SeqCst) + self.atomic.load(Ordering::Relaxed) } } @@ -48,7 +47,7 @@ impl Display for DetectDrop { impl Drop for DetectDrop { fn drop(&mut self) { - let already_dropped = self.has_dropped.atomic.swap(true, SeqCst); + let already_dropped = self.has_dropped.atomic.swap(true, Ordering::Relaxed); assert!(!already_dropped); } } diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 12fd472..c8b901a 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Chain, Error}; fn error() -> Error { - anyhow!(0).context(1).context(2).context(3) + anyhow!({ 0 }).context(1).context(2).context(3) } #[test] @@ -56,6 +56,7 @@ fn test_default() { } #[test] +#[allow(clippy::redundant_clone)] fn test_clone() { let e = error(); let mut chain = e.chain().clone(); diff --git a/tests/test_context.rs b/tests/test_context.rs index e2d4a29..2053fc9 100644 --- a/tests/test_context.rs +++ b/tests/test_context.rs @@ -24,6 +24,7 @@ macro_rules! context_type { #[derive(Debug)] struct $name { message: &'static str, + #[allow(dead_code)] drop: DetectDrop, } diff --git a/tests/test_downcast.rs b/tests/test_downcast.rs index 7fb063f..b4470d5 100644 --- a/tests/test_downcast.rs +++ b/tests/test_downcast.rs @@ -1,4 +1,4 @@ -#![allow(clippy::wildcard_imports)] +#![allow(clippy::assertions_on_result_states, clippy::wildcard_imports)] mod common; mod drop; diff --git a/tests/test_ensure.rs b/tests/test_ensure.rs new file mode 100644 index 0000000..de867f7 --- /dev/null +++ b/tests/test_ensure.rs @@ -0,0 +1,718 @@ +#![allow( + clippy::bool_to_int_with_if, + clippy::diverging_sub_expression, + clippy::if_same_then_else, + clippy::ifs_same_cond, + clippy::items_after_statements, + clippy::let_and_return, + clippy::match_bool, + clippy::never_loop, + clippy::overly_complex_bool_expr, + clippy::redundant_closure_call, + clippy::redundant_pattern_matching, + clippy::too_many_lines, + clippy::unit_arg, + clippy::while_immutable_condition, + clippy::zero_ptr, + irrefutable_let_patterns +)] + +use self::Enum::Generic; +use anyhow::{anyhow, ensure, Chain, Error, Result}; +use std::fmt::{self, Debug}; +use std::iter; +use std::marker::{PhantomData, PhantomData as P}; +use std::mem; +use std::ops::Add; +use std::ptr; + +struct S; + +impl<T> Add<T> for S { + type Output = bool; + fn add(self, rhs: T) -> Self::Output { + let _ = rhs; + false + } +} + +trait Trait: Sized { + const V: usize = 0; + fn t(self, i: i32) -> i32 { + i + } +} + +impl<T> Trait for T {} + +enum Enum<T: ?Sized> { + #[allow(dead_code)] + Thing(PhantomData<T>), + Generic, +} + +impl<T: ?Sized> PartialEq for Enum<T> { + fn eq(&self, rhs: &Self) -> bool { + mem::discriminant(self) == mem::discriminant(rhs) + } +} + +impl<T: ?Sized> Debug for Enum<T> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Generic") + } +} + +#[track_caller] +fn assert_err<T: Debug>(result: impl FnOnce() -> Result<T>, expected: &'static str) { + let actual = result().unwrap_err().to_string(); + + // In general different rustc versions will format the interpolated lhs and + // rhs $:expr fragment with insignificant differences in whitespace or + // punctuation, so we check the message in full against nightly and do just + // a cursory test on older toolchains. + if rustversion::cfg!(nightly) && !cfg!(miri) { + assert_eq!(actual, expected); + } else { + assert_eq!(actual.contains(" vs "), expected.contains(" vs ")); + } +} + +#[test] +fn test_recursion() { + // Must not blow the default #[recursion_limit], which is 128. + #[rustfmt::skip] + let test = || Ok(ensure!( + false | false | false | false | false | false | false | false | false | + false | false | false | false | false | false | false | false | false | + false | false | false | false | false | false | false | false | false | + false | false | false | false | false | false | false | false | false | + false | false | false | false | false | false | false | false | false | + false | false | false | false | false | false | false | false | false | + false | false | false | false | false | false | false | false | false + )); + + test().unwrap_err(); +} + +#[test] +fn test_low_precedence_control_flow() { + #[allow(unreachable_code)] + let test = || { + let val = loop { + // Break has lower precedence than the comparison operators so the + // expression here is `S + (break (1 == 1))`. It would be bad if the + // ensure macro partitioned this input into `(S + break 1) == (1)` + // because that means a different thing than what was written. + ensure!(S + break 1 == 1); + }; + Ok(val) + }; + + assert!(test().unwrap()); +} + +#[test] +fn test_low_precedence_binary_operator() { + // Must not partition as `false == (true && false)`. + let test = || Ok(ensure!(false == true && false)); + assert_err(test, "Condition failed: `false == true && false`"); + + // But outside the root level, it is fine. + let test = || Ok(ensure!(while false == true && false {} < ())); + assert_err( + test, + "Condition failed: `while false == true && false {} < ()` (() vs ())", + ); +} + +#[test] +fn test_closure() { + // Must not partition as `(S + move) || (1 == 1)` by treating move as an + // identifier, nor as `(S + move || 1) == (1)` by misinterpreting the + // closure precedence. + let test = || Ok(ensure!(S + move || 1 == 1)); + assert_err(test, "Condition failed: `S + (move || 1 == 1)`"); + + let test = || Ok(ensure!(S + || 1 == 1)); + assert_err(test, "Condition failed: `S + (|| 1 == 1)`"); + + // Must not partition as `S + ((move | ()) | 1) == 1` by treating those + // pipes as bitwise-or. + let test = || Ok(ensure!(S + move |()| 1 == 1)); + assert_err(test, "Condition failed: `S + (move |()| 1 == 1)`"); + + let test = || Ok(ensure!(S + |()| 1 == 1)); + assert_err(test, "Condition failed: `S + (|()| 1 == 1)`"); +} + +#[test] +fn test_unary() { + let mut x = &1; + let test = || Ok(ensure!(*x == 2)); + assert_err(test, "Condition failed: `*x == 2` (1 vs 2)"); + + let test = || Ok(ensure!(!x == 1)); + assert_err(test, "Condition failed: `!x == 1` (-2 vs 1)"); + + let test = || Ok(ensure!(-x == 1)); + assert_err(test, "Condition failed: `-x == 1` (-1 vs 1)"); + + let test = || Ok(ensure!(&x == &&2)); + assert_err(test, "Condition failed: `&x == &&2` (1 vs 2)"); + + let test = || Ok(ensure!(&mut x == *&&mut &2)); + assert_err(test, "Condition failed: `&mut x == *&&mut &2` (1 vs 2)"); +} + +#[test] +fn test_if() { + #[rustfmt::skip] + let test = || Ok(ensure!(if false {}.t(1) == 2)); + assert_err(test, "Condition failed: `if false {}.t(1) == 2` (1 vs 2)"); + + #[rustfmt::skip] + let test = || Ok(ensure!(if false {} else {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `if false {} else {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(if false {} else if false {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `if false {} else if false {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(if let 1 = 2 {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `if let 1 = 2 {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(if let 1 | 2 = 2 {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `if let 1 | 2 = 2 {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(if let | 1 | 2 = 2 {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `if let 1 | 2 = 2 {}.t(1) == 2` (1 vs 2)", + ); +} + +#[test] +fn test_loop() { + #[rustfmt::skip] + let test = || Ok(ensure!(1 + loop { break 1 } == 1)); + assert_err( + test, + "Condition failed: `1 + loop { break 1 } == 1` (2 vs 1)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(1 + 'a: loop { break 'a 1 } == 1)); + assert_err( + test, + "Condition failed: `1 + 'a: loop { break 'a 1 } == 1` (2 vs 1)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(while false {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `while false {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(while let None = Some(1) {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `while let None = Some(1) {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(for _x in iter::once(0) {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `for _x in iter::once(0) {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(for | _x in iter::once(0) {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `for _x in iter::once(0) {}.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(for true | false in iter::empty() {}.t(1) == 2)); + assert_err( + test, + "Condition failed: `for true | false in iter::empty() {}.t(1) == 2` (1 vs 2)", + ); +} + +#[test] +fn test_match() { + #[rustfmt::skip] + let test = || Ok(ensure!(match 1 == 1 { true => 1, false => 0 } == 2)); + assert_err( + test, + "Condition failed: `match 1 == 1 { true => 1, false => 0, } == 2` (1 vs 2)", + ); +} + +#[test] +fn test_atom() { + let test = || Ok(ensure!([false, false].len() > 3)); + assert_err( + test, + "Condition failed: `[false, false].len() > 3` (2 vs 3)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!({ let x = 1; x } >= 3)); + assert_err(test, "Condition failed: `{ let x = 1; x } >= 3` (1 vs 3)"); + + let test = || Ok(ensure!(S + async { 1 } == true)); + assert_err( + test, + "Condition failed: `S + async { 1 } == true` (false vs true)", + ); + + let test = || Ok(ensure!(S + async move { 1 } == true)); + assert_err( + test, + "Condition failed: `S + async move { 1 } == true` (false vs true)", + ); + + let x = &1; + let test = || Ok(ensure!(S + unsafe { ptr::read(x) } == true)); + assert_err( + test, + "Condition failed: `S + unsafe { ptr::read(x) } == true` (false vs true)", + ); +} + +#[test] +fn test_path() { + let test = || Ok(ensure!(crate::S.t(1) == 2)); + assert_err(test, "Condition failed: `crate::S.t(1) == 2` (1 vs 2)"); + + let test = || Ok(ensure!(::anyhow::Error::root_cause.t(1) == 2)); + assert_err( + test, + "Condition failed: `::anyhow::Error::root_cause.t(1) == 2` (1 vs 2)", + ); + + let test = || Ok(ensure!(Error::msg::<&str>.t(1) == 2)); + assert_err( + test, + "Condition failed: `Error::msg::<&str>.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(Error::msg::<&str,>.t(1) == 2)); + assert_err( + test, + "Condition failed: `Error::msg::<&str>.t(1) == 2` (1 vs 2)", + ); + + let test = || Ok(ensure!(Error::msg::<<str as ToOwned>::Owned>.t(1) == 2)); + assert_err( + test, + "Condition failed: `Error::msg::<<str as ToOwned>::Owned>.t(1) == 2` (1 vs 2)", + ); + + let test = || Ok(ensure!(Chain::<'static>::new.t(1) == 2)); + assert_err( + test, + "Condition failed: `Chain::<'static>::new.t(1) == 2` (1 vs 2)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(Chain::<'static,>::new.t(1) == 2)); + assert_err( + test, + "Condition failed: `Chain::<'static>::new.t(1) == 2` (1 vs 2)", + ); + + fn f<const I: isize>() {} + let test = || Ok(ensure!(f::<1>() != ())); + assert_err(test, "Condition failed: `f::<1>() != ()` (() vs ())"); + let test = || Ok(ensure!(f::<-1>() != ())); + assert_err(test, "Condition failed: `f::<-1>() != ()` (() vs ())"); + + fn g<T, const I: isize>() {} + let test = || Ok(ensure!(g::<u8, 1>() != ())); + assert_err(test, "Condition failed: `g::<u8, 1>() != ()` (() vs ())"); + let test = || Ok(ensure!(g::<u8, -1>() != ())); + assert_err(test, "Condition failed: `g::<u8, -1>() != ()` (() vs ())"); + + #[derive(PartialOrd, PartialEq, Debug)] + enum E<'a, T> { + #[allow(dead_code)] + T(&'a T), + U, + } + + #[rustfmt::skip] + let test = || Ok(ensure!(E::U::<>>E::U::<u8>)); + assert_err(test, "Condition failed: `E::U::<> > E::U::<u8>` (U vs U)"); + + #[rustfmt::skip] + let test = || Ok(ensure!(E::U::<u8>>E::U)); + assert_err(test, "Condition failed: `E::U::<u8> > E::U` (U vs U)"); + + #[rustfmt::skip] + let test = || Ok(ensure!(E::U::<u8,>>E::U)); + assert_err(test, "Condition failed: `E::U::<u8> > E::U` (U vs U)"); + + let test = || Ok(ensure!(Generic::<dyn Debug + Sync> != Generic)); + assert_err( + test, + "Condition failed: `Generic::<dyn Debug + Sync> != Generic` (Generic vs Generic)", + ); + + let test = || Ok(ensure!(Generic::<dyn Fn() + Sync> != Generic)); + assert_err( + test, + "Condition failed: `Generic::<dyn Fn() + Sync> != Generic` (Generic vs Generic)", + ); + + #[rustfmt::skip] + let test = || { + Ok(ensure!( + Generic::<dyn Fn::() + ::std::marker::Sync> != Generic + )) + }; + assert_err( + test, + "Condition failed: `Generic::<dyn Fn() + ::std::marker::Sync> != Generic` (Generic vs Generic)", + ); +} + +#[test] +fn test_macro() { + let test = || Ok(ensure!(anyhow!("...").to_string().len() <= 1)); + assert_err( + test, + "Condition failed: `anyhow!(\"...\").to_string().len() <= 1` (3 vs 1)", + ); + + let test = || Ok(ensure!(vec![1].len() < 1)); + assert_err(test, "Condition failed: `vec![1].len() < 1` (1 vs 1)"); + + let test = || Ok(ensure!(stringify! {} != "")); + assert_err( + test, + "Condition failed: `stringify! {} != \"\"` (\"\" vs \"\")", + ); +} + +#[test] +fn test_trailer() { + let test = || Ok(ensure!((|| 1)() == 2)); + assert_err(test, "Condition failed: `(|| 1)() == 2` (1 vs 2)"); + + let test = || Ok(ensure!(b"hmm"[1] == b'c')); + assert_err(test, "Condition failed: `b\"hmm\"[1] == b'c'` (109 vs 99)"); + + let test = || Ok(ensure!(PhantomData::<u8> {} != PhantomData)); + assert_err( + test, + "Condition failed: `PhantomData::<u8> {} != PhantomData` (PhantomData<u8> vs PhantomData<u8>)", + ); + + let result = Ok::<_, Error>(1); + let test = || Ok(ensure!(result? == 2)); + assert_err(test, "Condition failed: `result? == 2` (1 vs 2)"); + + let test = || Ok(ensure!((2, 3).1 == 2)); + assert_err(test, "Condition failed: `(2, 3).1 == 2` (3 vs 2)"); + + #[rustfmt::skip] + let test = || Ok(ensure!((2, (3, 4)). 1.1 == 2)); + assert_err(test, "Condition failed: `(2, (3, 4)).1.1 == 2` (4 vs 2)"); + + let err = anyhow!(""); + let test = || Ok(ensure!(err.is::<&str>() == false)); + assert_err( + test, + "Condition failed: `err.is::<&str>() == false` (true vs false)", + ); + + let test = || Ok(ensure!(err.is::<<str as ToOwned>::Owned>() == true)); + assert_err( + test, + "Condition failed: `err.is::<<str as ToOwned>::Owned>() == true` (false vs true)", + ); +} + +#[test] +fn test_whitespace() { + #[derive(Debug)] + pub struct Point { + pub x: i32, + pub y: i32, + } + + let point = Point { x: 0, y: 0 }; + let test = || Ok(ensure!("" == format!("{:#?}", point))); + assert_err( + test, + "Condition failed: `\"\" == format!(\"{:#?}\", point)`", + ); +} + +#[test] +fn test_too_long() { + let test = || Ok(ensure!("" == "x".repeat(10))); + assert_err( + test, + "Condition failed: `\"\" == \"x\".repeat(10)` (\"\" vs \"xxxxxxxxxx\")", + ); + + let test = || Ok(ensure!("" == "x".repeat(80))); + assert_err(test, "Condition failed: `\"\" == \"x\".repeat(80)`"); +} + +#[test] +fn test_as() { + let test = || Ok(ensure!('\0' as u8 > 1)); + assert_err(test, "Condition failed: `'\\0' as u8 > 1` (0 vs 1)"); + + let test = || Ok(ensure!('\0' as ::std::primitive::u8 > 1)); + assert_err( + test, + "Condition failed: `'\\0' as ::std::primitive::u8 > 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(&[0] as &[i32] == [1])); + assert_err( + test, + "Condition failed: `&[0] as &[i32] == [1]` ([0] vs [1])", + ); + + let test = || Ok(ensure!(0 as *const () as *mut _ == 1 as *mut ())); + assert_err( + test, + "Condition failed: `0 as *const () as *mut _ == 1 as *mut ()` (0x0 vs 0x1)", + ); + + let s = ""; + let test = || Ok(ensure!(s as &str != s)); + assert_err(test, "Condition failed: `s as &str != s` (\"\" vs \"\")"); + + let test = || Ok(ensure!(&s as &&str != &s)); + assert_err(test, "Condition failed: `&s as &&str != &s` (\"\" vs \"\")"); + + let test = || Ok(ensure!(s as &'static str != s)); + assert_err( + test, + "Condition failed: `s as &'static str != s` (\"\" vs \"\")", + ); + + let test = || Ok(ensure!(&s as &&'static str != &s)); + assert_err( + test, + "Condition failed: `&s as &&'static str != &s` (\"\" vs \"\")", + ); + + let m: &mut str = Default::default(); + let test = || Ok(ensure!(m as &mut str != s)); + assert_err( + test, + "Condition failed: `m as &mut str != s` (\"\" vs \"\")", + ); + + let test = || Ok(ensure!(&m as &&mut str != &s)); + assert_err( + test, + "Condition failed: `&m as &&mut str != &s` (\"\" vs \"\")", + ); + + let test = || Ok(ensure!(&m as &&'static mut str != &s)); + assert_err( + test, + "Condition failed: `&m as &&'static mut str != &s` (\"\" vs \"\")", + ); + + let f = || {}; + let test = || Ok(ensure!(f as fn() as usize * 0 != 0)); + assert_err( + test, + "Condition failed: `f as fn() as usize * 0 != 0` (0 vs 0)", + ); + + let test = || Ok(ensure!(f as fn() -> () as usize * 0 != 0)); + assert_err( + test, + "Condition failed: `f as fn() -> () as usize * 0 != 0` (0 vs 0)", + ); + + let test = || Ok(ensure!(f as for<'a> fn() as usize * 0 != 0)); + assert_err( + test, + "Condition failed: `f as for<'a> fn() as usize * 0 != 0` (0 vs 0)", + ); + + let test = || Ok(ensure!(f as unsafe fn() as usize * 0 != 0)); + assert_err( + test, + "Condition failed: `f as unsafe fn() as usize * 0 != 0` (0 vs 0)", + ); + + #[rustfmt::skip] + let test = || Ok(ensure!(f as extern "Rust" fn() as usize * 0 != 0)); + assert_err( + test, + "Condition failed: `f as extern \"Rust\" fn() as usize * 0 != 0` (0 vs 0)", + ); + + extern "C" fn extern_fn() {} + #[rustfmt::skip] + let test = || Ok(ensure!(extern_fn as extern fn() as usize * 0 != 0)); + assert_err( + test, + "Condition failed: `extern_fn as extern fn() as usize * 0 != 0` (0 vs 0)", + ); + + let f = || -> ! { panic!() }; + let test = || Ok(ensure!(f as fn() -> ! as usize * 0 != 0)); + assert_err( + test, + "Condition failed: `f as fn() -> ! as usize * 0 != 0` (0 vs 0)", + ); + + trait EqDebug<T>: PartialEq<T> + Debug { + type Assoc; + } + + impl<S, T> EqDebug<T> for S + where + S: PartialEq<T> + Debug, + { + type Assoc = bool; + } + + let test = || Ok(ensure!(&0 as &dyn EqDebug<i32, Assoc = bool> != &0)); + assert_err( + test, + "Condition failed: `&0 as &dyn EqDebug<i32, Assoc = bool> != &0` (0 vs 0)", + ); + + let test = || { + Ok(ensure!( + PhantomData as PhantomData<<i32 as ToOwned>::Owned> != PhantomData + )) + }; + assert_err( + test, + "Condition failed: `PhantomData as PhantomData<<i32 as ToOwned>::Owned> != PhantomData` (PhantomData<i32> vs PhantomData<i32>)", + ); + + macro_rules! int { + (...) => { + u8 + }; + } + + let test = || Ok(ensure!(0 as int!(...) != 0)); + assert_err(test, "Condition failed: `0 as int!(...) != 0` (0 vs 0)"); + + let test = || Ok(ensure!(0 as int![...] != 0)); + assert_err(test, "Condition failed: `0 as int![...] != 0` (0 vs 0)"); + + let test = || Ok(ensure!(0 as int! {...} != 0)); + assert_err(test, "Condition failed: `0 as int! { ... } != 0` (0 vs 0)"); +} + +#[test] +fn test_pat() { + let test = || Ok(ensure!(if let ref mut _x @ 0 = 0 { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let ref mut _x @ 0 = 0 { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let -1..=1 = 0 { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let -1..=1 = 0 { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let &0 = &0 { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let &0 = &0 { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let &&0 = &&0 { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let &&0 = &&0 { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let &mut 0 = &mut 0 { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let &mut 0 = &mut 0 { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let &&mut 0 = &&mut 0 { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let &&mut 0 = &&mut 0 { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let (0, 1) = (0, 1) { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let (0, 1) = (0, 1) { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let [0] = b"\0" { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let [0] = b\"\\0\" { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let p = PhantomData::<u8>; + let test = || Ok(ensure!(if let P::<u8> {} = p { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let P::<u8> {} = p { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(if let ::std::marker::PhantomData = p {} != ())); + assert_err( + test, + "Condition failed: `if let ::std::marker::PhantomData = p {} != ()` (() vs ())", + ); + + let test = || Ok(ensure!(if let <S as Trait>::V = 0 { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let <S as Trait>::V = 0 { 0 } else { 1 } == 1` (0 vs 1)", + ); + + let test = || Ok(ensure!(for _ in iter::once(()) {} != ())); + assert_err( + test, + "Condition failed: `for _ in iter::once(()) {} != ()` (() vs ())", + ); + + let test = || Ok(ensure!(if let stringify!(x) = "x" { 0 } else { 1 } == 1)); + assert_err( + test, + "Condition failed: `if let stringify!(x) = \"x\" { 0 } else { 1 } == 1` (0 vs 1)", + ); +} diff --git a/tests/test_macros.rs b/tests/test_macros.rs index cd75d50..a3342ab 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -1,9 +1,18 @@ -#![allow(clippy::eq_op, clippy::shadow_unrelated, clippy::wildcard_imports)] +#![allow( + clippy::assertions_on_result_states, + clippy::eq_op, + clippy::items_after_statements, + clippy::needless_pass_by_value, + clippy::shadow_unrelated, + clippy::wildcard_imports +)] mod common; use self::common::*; -use anyhow::ensure; +use anyhow::{anyhow, ensure}; +use std::cell::Cell; +use std::future; #[test] fn test_messages() { @@ -39,6 +48,33 @@ fn test_ensure() { }; assert_eq!( f().unwrap_err().to_string(), - "Condition failed: `v + v == 1`", + "Condition failed: `v + v == 1` (2 vs 1)", ); } + +#[test] +fn test_temporaries() { + fn require_send_sync(_: impl Send + Sync) {} + + require_send_sync(async { + // If anyhow hasn't dropped any temporary format_args it creates by the + // time it's done evaluating, those will stick around until the + // semicolon, which is on the other side of the await point, making the + // enclosing future non-Send. + future::ready(anyhow!("...")).await; + }); + + fn message(cell: Cell<&str>) -> &str { + cell.get() + } + + require_send_sync(async { + future::ready(anyhow!(message(Cell::new("...")))).await; + }); +} + +#[test] +fn test_brace_escape() { + let err = anyhow!("unterminated ${{..}} expression"); + assert_eq!("unterminated ${..} expression", err.to_string()); +} diff --git a/tests/ui/chained-comparison.rs b/tests/ui/chained-comparison.rs new file mode 100644 index 0000000..4521b51 --- /dev/null +++ b/tests/ui/chained-comparison.rs @@ -0,0 +1,8 @@ +use anyhow::{ensure, Result}; + +fn main() -> Result<()> { + // `ensure!` must not partition this into `(false) == (false == true)` + // because Rust doesn't ordinarily allow this form of expression. + ensure!(false == false == true); + Ok(()) +} diff --git a/tests/ui/chained-comparison.stderr b/tests/ui/chained-comparison.stderr new file mode 100644 index 0000000..2a4c665 --- /dev/null +++ b/tests/ui/chained-comparison.stderr @@ -0,0 +1,10 @@ +error: comparison operators cannot be chained + --> tests/ui/chained-comparison.rs:6:19 + | +6 | ensure!(false == false == true); + | ^^ ^^ + | +help: split the comparison into two + | +6 | ensure!(false == false && false == true); + | ++++++++ diff --git a/tests/ui/empty-ensure.rs b/tests/ui/empty-ensure.rs new file mode 100644 index 0000000..139b743 --- /dev/null +++ b/tests/ui/empty-ensure.rs @@ -0,0 +1,6 @@ +use anyhow::{ensure, Result}; + +fn main() -> Result<()> { + ensure!(); + Ok(()) +} diff --git a/tests/ui/empty-ensure.stderr b/tests/ui/empty-ensure.stderr new file mode 100644 index 0000000..bf0229a --- /dev/null +++ b/tests/ui/empty-ensure.stderr @@ -0,0 +1,12 @@ +error: unexpected end of macro invocation + --> tests/ui/empty-ensure.rs:4:5 + | +4 | ensure!(); + | ^^^^^^^^^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$cond:expr` + --> src/ensure.rs + | + | ($cond:expr $(,)?) => { + | ^^^^^^^^^^ + = note: this error originates in the macro `$crate::__parse_ensure` which comes from the expansion of the macro `ensure` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/must-use.rs b/tests/ui/must-use.rs new file mode 100644 index 0000000..ea4e58f --- /dev/null +++ b/tests/ui/must-use.rs @@ -0,0 +1,11 @@ +#![deny(unused_must_use)] + +use anyhow::anyhow; + +fn main() -> anyhow::Result<()> { + if true { + // meant to write bail! + anyhow!("it failed"); + } + Ok(()) +} diff --git a/tests/ui/must-use.stderr b/tests/ui/must-use.stderr new file mode 100644 index 0000000..e10bde4 --- /dev/null +++ b/tests/ui/must-use.stderr @@ -0,0 +1,12 @@ +error: unused return value of `anyhow::__private::must_use` that must be used + --> tests/ui/must-use.rs:8:9 + | +8 | anyhow!("it failed"); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/must-use.rs:1:9 + | +1 | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ + = note: this error originates in the macro `anyhow` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/no-impl.stderr b/tests/ui/no-impl.stderr index 976b62c..1ddf768 100644 --- a/tests/ui/no-impl.stderr +++ b/tests/ui/no-impl.stderr @@ -1,11 +1,11 @@ error[E0599]: the method `anyhow_kind` exists for reference `&Error`, but its trait bounds were not satisfied - --> $DIR/no-impl.rs:7:13 + --> tests/ui/no-impl.rs:7:13 | 4 | struct Error; - | ------------- + | ------------ | | | doesn't satisfy `Error: Into<anyhow::Error>` - | doesn't satisfy `Error: anyhow::private::kind::TraitKind` + | doesn't satisfy `Error: anyhow::kind::TraitKind` | doesn't satisfy `Error: std::fmt::Display` ... 7 | let _ = anyhow!(Error); @@ -13,9 +13,19 @@ error[E0599]: the method `anyhow_kind` exists for reference `&Error`, but its tr | = note: the following trait bounds were not satisfied: `Error: Into<anyhow::Error>` - which is required by `Error: anyhow::private::kind::TraitKind` + which is required by `Error: anyhow::kind::TraitKind` `Error: std::fmt::Display` - which is required by `&Error: anyhow::private::kind::AdhocKind` + which is required by `&Error: anyhow::kind::AdhocKind` `&Error: Into<anyhow::Error>` - which is required by `&Error: anyhow::private::kind::TraitKind` + which is required by `&Error: anyhow::kind::TraitKind` +note: the traits `Into` and `std::fmt::Display` must be implemented + --> $RUST/core/src/fmt/mod.rs + | + | pub trait Display { + | ^^^^^^^^^^^^^^^^^ + | + ::: $RUST/core/src/convert/mod.rs + | + | pub trait Into<T>: Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `anyhow` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/temporary-value.stderr b/tests/ui/temporary-value.stderr index fa753d2..dc27c49 100644 --- a/tests/ui/temporary-value.stderr +++ b/tests/ui/temporary-value.stderr @@ -1,8 +1,9 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/temporary-value.rs:4:22 + --> tests/ui/temporary-value.rs:4:22 | 4 | let _ = anyhow!(&String::new()); - | ---------^^^^^^^^^^^^^-- temporary value is freed at the end of this statement + | ---------^^^^^^^^^^^^^- | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use + | temporary value is freed at the end of this statement | argument requires that borrow lasts for `'static` diff --git a/tests/ui/wrong-interpolation.rs b/tests/ui/wrong-interpolation.rs new file mode 100644 index 0000000..b870ca7 --- /dev/null +++ b/tests/ui/wrong-interpolation.rs @@ -0,0 +1,5 @@ +use anyhow::{bail, Result}; + +fn main() -> Result<()> { + bail!("{} not found"); +} diff --git a/tests/ui/wrong-interpolation.stderr b/tests/ui/wrong-interpolation.stderr new file mode 100644 index 0000000..55a2964 --- /dev/null +++ b/tests/ui/wrong-interpolation.stderr @@ -0,0 +1,5 @@ +error: 1 positional argument in format string, but no arguments were given + --> tests/ui/wrong-interpolation.rs:4:12 + | +4 | bail!("{} not found"); + | ^^ |