aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:02:47 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:02:47 +0000
commit1a2f904fb04f5431f388c01a4d20f570f8347950 (patch)
tree3f8485458be804c8c9ca12fff724617701513d03
parent2f4eb578444734dd5d6968ff613a6219b1379cee (diff)
parent52eb4adffa2688928e7bf037045c570899b864ba (diff)
downloadanyhow-aml_wif_341610000.tar.gz
Change-Id: I6464419eaf66aa518a46343797168f97c514eb10
-rw-r--r--.cargo_vcs_info.json7
-rw-r--r--.clippy.toml2
-rw-r--r--.github/workflows/ci.yml88
-rw-r--r--Android.bp13
-rw-r--r--Cargo.toml27
-rw-r--r--Cargo.toml.orig18
-rw-r--r--LICENSE-APACHE25
-rw-r--r--METADATA14
-rw-r--r--README.md12
-rw-r--r--TEST_MAPPING143
-rw-r--r--build.rs46
-rw-r--r--cargo2android.json18
-rw-r--r--rust-toolchain.toml2
-rw-r--r--src/backtrace.rs6
-rw-r--r--src/context.rs48
-rw-r--r--src/ensure.rs834
-rw-r--r--src/error.rs45
-rw-r--r--src/kind.rs4
-rw-r--r--src/lib.rs101
-rw-r--r--src/macros.rs82
-rw-r--r--src/ptr.rs2
-rw-r--r--src/wrapper.rs13
-rw-r--r--tests/drop/mod.rs7
-rw-r--r--tests/test_chain.rs3
-rw-r--r--tests/test_context.rs1
-rw-r--r--tests/test_downcast.rs2
-rw-r--r--tests/test_ensure.rs718
-rw-r--r--tests/test_macros.rs42
-rw-r--r--tests/ui/chained-comparison.rs8
-rw-r--r--tests/ui/chained-comparison.stderr10
-rw-r--r--tests/ui/empty-ensure.rs6
-rw-r--r--tests/ui/empty-ensure.stderr12
-rw-r--r--tests/ui/must-use.rs11
-rw-r--r--tests/ui/must-use.stderr12
-rw-r--r--tests/ui/no-impl.stderr22
-rw-r--r--tests/ui/temporary-value.stderr7
-rw-r--r--tests/ui/wrong-interpolation.rs5
-rw-r--r--tests/ui/wrong-interpolation.stderr5
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
diff --git a/Android.bp b/Android.bp
index 3df2e9b..17f59e3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
}
diff --git a/Cargo.toml b/Cargo.toml
index 881fc23..d42c470 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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.
diff --git a/METADATA b/METADATA
index f97422f..cb503ee 100644
--- a/METADATA
+++ b/METADATA
@@ -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
}
}
diff --git a/README.md b/README.md
index e10063f..6380c1c 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@ Anyhow&ensp;¯\\\_(°ペ)\_/¯
[<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"
}
]
}
diff --git a/build.rs b/build.rs
index 968e68f..3800683 100644
--- a/build.rs
+++ b/build.rs
@@ -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)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 2b7a0b1..e570d6e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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 &mdash; 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)*))
};
}
diff --git a/src/ptr.rs b/src/ptr.rs
index 6840833..c7fe488 100644
--- a/src/ptr.rs
+++ b/src/ptr.rs
@@ -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");
+ | ^^