diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/anyhow | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/anyhow')
31 files changed, 3451 insertions, 0 deletions
diff --git a/third_party/rust/anyhow/.cargo-checksum.json b/third_party/rust/anyhow/.cargo-checksum.json new file mode 100644 index 0000000000..015dd54c4a --- /dev/null +++ b/third_party/rust/anyhow/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"76e6e0a729a4038b0d8bcdd36a054e96f3e93dc1922813f512d28cdf8d21e516","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"0d9c5facc3db17123752af8c3dbcdb9bb3785c8ed046b27de26669e4e87f131c","build.rs":"d8b58acc2cd88d627763135b3b25b46f276e2672ef740efb7fb5b1404fb230d8","src/backtrace.rs":"a82a8ffae2c68ee385dc78d8ec8cb6f3351234f0ad6af7e87df2371593e0f6aa","src/chain.rs":"1627608ce95c3484d26e1742a1b8b533b74c6159916b717bacffae3ae53731f9","src/context.rs":"f2be36af9588c924ed087bcb7bd681d330889e54b8f98cdc2aba93512a28762e","src/error.rs":"2c6a51880c7379265f7f7b6557e7bed808ff0f3267337a0c77dbbc1d2c4662e7","src/fmt.rs":"079d7b4faaa23f42423e0bb6b4e8a80d7d6d45c38c0d46bebd7d647c8679469f","src/kind.rs":"8481a8b7835eebb3859a8c32c217bf9c73543cfc62e3916b98d39af8b063125c","src/lib.rs":"119a39d1062a4b99a9f888a9f595a48fabacba6c8f2c3c59361ce2065d938ef6","src/macros.rs":"77722190b58a6106b21aefd3b5d4f136a076afcdbc0fae21562d99e2c22912e1","src/wrapper.rs":"1229beca67dbd95ca77c9ecce282272acc55276c267c58cb73a75388b4693dda","tests/common/mod.rs":"f9088c2d7afafa64ff730b629272045b776bfafc2f5957508242da630635f2e1","tests/compiletest.rs":"0a52a44786aea1c299c695bf948b2ed2081e4cc344e5c2cadceab4eb03d0010d","tests/drop/mod.rs":"464bc1ddeae307eac906928286ec3edb77057c5c1302e02150d3649e2b861f1a","tests/test_autotrait.rs":"981e792db353be2f14c7a1cabe43b5f1329c168cb7679077cc2be786a0920d48","tests/test_backtrace.rs":"0e50edbb33b6bd07ba89ff3db72fb7c688ba2a4371fccdbbb20309ab02948b6a","tests/test_boxed.rs":"98a45325b1e86d4c5d3094ab99cd1ada1f771c505d2d7322f0afcbe7bdb71cfa","tests/test_chain.rs":"f28efeae7395d1c395e6f1a647b4199c25a00410ade45248c145c6fcf2fb448a","tests/test_context.rs":"f82c915b182df1a604a4cd558a03b1a821414983d6f6af6822398104cea70676","tests/test_convert.rs":"62840be1ee8022ba5e8c0d3fc1752a1526b2c47d4cceecff2b86790524c3b3ea","tests/test_downcast.rs":"253d6f54e554965023b378b037827ec6289c4779a7a7c12706e19c2731d219fe","tests/test_fmt.rs":"17572596f257aac9aa2ec4620e292ca6a954128b94772bb948399fab53832e70","tests/test_macros.rs":"c7d3d5e0b756f59d4858035025fb341d031369c88486fd9f961ee16bae6c78bf","tests/test_repr.rs":"dbb9b04ddbe1ab31eb5331ea69f05bb3a147299da2275a3d4dcc92947b5591b9","tests/test_source.rs":"b80723cf635a4f8c4df21891b34bfab9ed2b2aa407e7a2f826d24e334cd5f88e","tests/ui/no-impl.rs":"fab6cbf2f6ea510b86f567dfb3b7c31250a9fd71ae5d110dbb9188be569ec593","tests/ui/no-impl.stderr":"7c2c3f46c266a437300591f10be330f937ac6a0a2213ed5030a9fbc895e2d100"},"package":"2494382e9ba43995f3c56359e518641f450f5c36feeb4632a75cde2ec297c867"}
\ No newline at end of file diff --git a/third_party/rust/anyhow/Cargo.toml b/third_party/rust/anyhow/Cargo.toml new file mode 100644 index 0000000000..5ae385777f --- /dev/null +++ b/third_party/rust/anyhow/Cargo.toml @@ -0,0 +1,43 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "anyhow" +version = "1.0.30" +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"] +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"] +[dev-dependencies.futures] +version = "0.3" +default-features = false + +[dev-dependencies.rustversion] +version = "1.0" + +[dev-dependencies.thiserror] +version = "1.0" + +[dev-dependencies.trybuild] +version = "1.0.19" +features = ["diff"] + +[features] +default = ["std"] +std = [] diff --git a/third_party/rust/anyhow/LICENSE-APACHE b/third_party/rust/anyhow/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/anyhow/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + 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/third_party/rust/anyhow/LICENSE-MIT b/third_party/rust/anyhow/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/third_party/rust/anyhow/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/anyhow/README.md b/third_party/rust/anyhow/README.md new file mode 100644 index 0000000000..87b41d7958 --- /dev/null +++ b/third_party/rust/anyhow/README.md @@ -0,0 +1,176 @@ +Anyhow ¯\\\_(ツ)\_/¯ +========================= + +[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/anyhow-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/anyhow) +[<img alt="crates.io" src="https://img.shields.io/crates/v/anyhow.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/anyhow) +[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-anyhow-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/anyhow) +[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/anyhow/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/anyhow/actions?query=branch%3Amaster) + +This library provides [`anyhow::Error`][Error], a trait object based error type +for easy idiomatic error handling in Rust applications. + +[Error]: https://docs.rs/anyhow/1.0/anyhow/struct.Error.html + +```toml +[dependencies] +anyhow = "1.0" +``` + +*Compiler support: requires rustc 1.34+* + +<br> + +## Details + +- Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as the + return type of any fallible function. + + Within the function, use `?` to easily propagate any error that implements the + `std::error::Error` trait. + + ```rust + use anyhow::Result; + + fn get_cluster_info() -> Result<ClusterMap> { + let config = std::fs::read_to_string("cluster.json")?; + let map: ClusterMap = serde_json::from_str(&config)?; + Ok(map) + } + ``` + +- Attach context to help the person troubleshooting the error understand where + things went wrong. A low-level error like "No such file or directory" can be + annoying to debug without more context about what higher level step the + application was in the middle of. + + ```rust + use anyhow::{Context, Result}; + + fn main() -> Result<()> { + ... + it.detach().context("Failed to detach the important thing")?; + + let content = std::fs::read(path) + .with_context(|| format!("Failed to read instrs from {}", path))?; + ... + } + ``` + + ```console + Error: Failed to read instrs from ./path/to/instrs.json + + Caused by: + No such file or directory (os error 2) + ``` + +- Downcasting is supported and can be by value, by shared reference, or by + mutable reference as needed. + + ```rust + // If the error was caused by redaction, then return a + // tombstone instead of the content. + match root_cause.downcast_ref::<DataStoreError>() { + Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), + None => Err(error), + } + ``` + +- If using the nightly channel, 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`]: + + - If you want panics and errors to both have backtraces, set + `RUST_BACKTRACE=1`; + - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; + - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and + `RUST_LIB_BACKTRACE=0`. + + The tracking issue for this feature is [rust-lang/rust#53487]. + + [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables + [rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487 + +- Anyhow works with any error type that has an impl of `std::error::Error`, + including ones defined in your crate. We do not bundle a `derive(Error)` macro + but you can write the impls yourself or use a standalone macro like + [thiserror]. + + ```rust + use thiserror::Error; + + #[derive(Error, Debug)] + pub enum FormatError { + #[error("Invalid header (expected {expected:?}, got {found:?})")] + InvalidHeader { + expected: String, + found: String, + }, + #[error("Missing attribute: {0}")] + MissingAttribute(String), + } + ``` + +- One-off error messages can be constructed using the `anyhow!` macro, which + supports string interpolation and produces an `anyhow::Error`. + + ```rust + return Err(anyhow!("Missing attribute: {}", missing)); + ``` + +<br> + +## No-std support + +In no_std mode, the same API is almost all available and works the same way. To +depend on Anyhow in no_std mode, disable our default enabled "std" feature in +Cargo.toml. A global allocator is required. + +```toml +[dependencies] +anyhow = { version = "1.0", default-features = false } +``` + +Since the `?`-based error conversions would normally rely on the +`std::error::Error` trait which is only available through std, no_std mode 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. + +<br> + +## Comparison to failure + +The `anyhow::Error` type works something like `failure::Error`, but unlike +failure ours is built around the standard library's `std::error::Error` trait +rather than a separate trait `failure::Fail`. The standard library has adopted +the necessary improvements for this to be possible as part of [RFC 2504]. + +[RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md + +<br> + +## Comparison to thiserror + +Use Anyhow if you don't care what error type your functions return, you just +want it to be easy. This is common in application code. Use [thiserror] if you +are a library that wants to design your own dedicated error type(s) so that on +failures the caller gets exactly the information that you choose. + +[thiserror]: https://github.com/dtolnay/thiserror + +<br> + +#### License + +<sup> +Licensed under either of <a href="LICENSE-APACHE">Apache License, Version +2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. +</sup> + +<br> + +<sub> +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. +</sub> diff --git a/third_party/rust/anyhow/build.rs b/third_party/rust/anyhow/build.rs new file mode 100644 index 0000000000..d20b0721db --- /dev/null +++ b/third_party/rust/anyhow/build.rs @@ -0,0 +1,63 @@ +use std::env; +use std::fs; +use std::path::Path; +use std::process::{Command, ExitStatus, Stdio}; + +// This code exercises the surface area that we expect of the std Backtrace +// 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)] + + use std::backtrace::{Backtrace, BacktraceStatus}; + use std::error::Error; + use std::fmt::{self, Display}; + + #[derive(Debug)] + struct E; + + impl Display for E { + fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } + } + + impl Error for E { + fn backtrace(&self) -> Option<&Backtrace> { + let backtrace = Backtrace::capture(); + match backtrace.status() { + BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} + } + unimplemented!() + } + } +"#; + +fn main() { + if !cfg!(feature = "std") { + return; + } + match compile_probe() { + Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"), + _ => {} + } +} + +fn compile_probe() -> Option<ExitStatus> { + let rustc = env::var_os("RUSTC")?; + let out_dir = env::var_os("OUT_DIR")?; + let probefile = Path::new(&out_dir).join("probe.rs"); + fs::write(&probefile, PROBE).ok()?; + Command::new(rustc) + .stderr(Stdio::null()) + .arg("--edition=2018") + .arg("--crate-name=anyhow_build") + .arg("--crate-type=lib") + .arg("--emit=metadata") + .arg("--out-dir") + .arg(out_dir) + .arg(probefile) + .status() + .ok() +} diff --git a/third_party/rust/anyhow/src/backtrace.rs b/third_party/rust/anyhow/src/backtrace.rs new file mode 100644 index 0000000000..01e33cb237 --- /dev/null +++ b/third_party/rust/anyhow/src/backtrace.rs @@ -0,0 +1,36 @@ +#[cfg(backtrace)] +pub(crate) use std::backtrace::Backtrace; + +#[cfg(not(backtrace))] +pub(crate) enum Backtrace {} + +#[cfg(backtrace)] +macro_rules! backtrace { + () => { + Some(Backtrace::capture()) + }; +} + +#[cfg(not(backtrace))] +macro_rules! backtrace { + () => { + None + }; +} + +#[cfg(backtrace)] +macro_rules! backtrace_if_absent { + ($err:expr) => { + match $err.backtrace() { + Some(_) => None, + None => Some(Backtrace::capture()), + } + }; +} + +#[cfg(all(feature = "std", not(backtrace)))] +macro_rules! backtrace_if_absent { + ($err:expr) => { + None + }; +} diff --git a/third_party/rust/anyhow/src/chain.rs b/third_party/rust/anyhow/src/chain.rs new file mode 100644 index 0000000000..207e4f0ca9 --- /dev/null +++ b/third_party/rust/anyhow/src/chain.rs @@ -0,0 +1,101 @@ +use self::ChainState::*; +use crate::StdError; + +#[cfg(feature = "std")] +use std::vec; + +#[cfg(feature = "std")] +pub(crate) use crate::Chain; + +#[cfg(not(feature = "std"))] +pub(crate) struct Chain<'a> { + state: ChainState<'a>, +} + +#[derive(Clone)] +pub(crate) enum ChainState<'a> { + Linked { + next: Option<&'a (dyn StdError + 'static)>, + }, + #[cfg(feature = "std")] + Buffered { + rest: vec::IntoIter<&'a (dyn StdError + 'static)>, + }, +} + +impl<'a> Chain<'a> { + pub fn new(head: &'a (dyn StdError + 'static)) -> Self { + Chain { + state: ChainState::Linked { next: Some(head) }, + } + } +} + +impl<'a> Iterator for Chain<'a> { + type Item = &'a (dyn StdError + 'static); + + fn next(&mut self) -> Option<Self::Item> { + match &mut self.state { + Linked { next } => { + let error = (*next)?; + *next = error.source(); + Some(error) + } + #[cfg(feature = "std")] + Buffered { rest } => rest.next(), + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let len = self.len(); + (len, Some(len)) + } +} + +#[cfg(feature = "std")] +impl DoubleEndedIterator for Chain<'_> { + fn next_back(&mut self) -> Option<Self::Item> { + match &mut self.state { + Linked { mut next } => { + let mut rest = Vec::new(); + while let Some(cause) = next { + next = cause.source(); + rest.push(cause); + } + let mut rest = rest.into_iter(); + let last = rest.next_back(); + self.state = Buffered { rest }; + last + } + Buffered { rest } => rest.next_back(), + } + } +} + +impl ExactSizeIterator for Chain<'_> { + fn len(&self) -> usize { + match &self.state { + Linked { mut next } => { + let mut len = 0; + while let Some(cause) = next { + next = cause.source(); + len += 1; + } + len + } + #[cfg(feature = "std")] + Buffered { rest } => rest.len(), + } + } +} + +#[cfg(feature = "std")] +impl Default for Chain<'_> { + fn default() -> Self { + Chain { + state: ChainState::Buffered { + rest: Vec::new().into_iter(), + }, + } + } +} diff --git a/third_party/rust/anyhow/src/context.rs b/third_party/rust/anyhow/src/context.rs new file mode 100644 index 0000000000..25d34114c1 --- /dev/null +++ b/third_party/rust/anyhow/src/context.rs @@ -0,0 +1,177 @@ +use crate::error::ContextError; +use crate::{Context, Error, StdError}; +use core::convert::Infallible; +use core::fmt::{self, Debug, Display, Write}; + +#[cfg(backtrace)] +use std::backtrace::Backtrace; + +mod ext { + use super::*; + + pub trait StdError { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static; + } + + #[cfg(feature = "std")] + impl<E> StdError for E + where + E: std::error::Error + Send + Sync + 'static, + { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static, + { + let backtrace = backtrace_if_absent!(self); + Error::from_context(context, self, backtrace) + } + } + + impl StdError for Error { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static, + { + self.context(context) + } + } +} + +impl<T, E> Context<T, E> for Result<T, E> +where + E: ext::StdError + Send + Sync + 'static, +{ + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + { + self.map_err(|error| error.ext_context(context)) + } + + fn with_context<C, F>(self, context: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + self.map_err(|error| error.ext_context(context())) + } +} + +/// ``` +/// # type T = (); +/// # +/// use anyhow::{Context, Result}; +/// +/// fn maybe_get() -> Option<T> { +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// +/// fn demo() -> Result<()> { +/// let t = maybe_get().context("there is no T")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// ``` +impl<T> Context<T, Infallible> for Option<T> { + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + { + self.ok_or_else(|| Error::from_display(context, backtrace!())) + } + + fn with_context<C, F>(self, context: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + self.ok_or_else(|| Error::from_display(context(), backtrace!())) + } +} + +impl<C, E> Debug for ContextError<C, E> +where + C: Display, + E: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Error") + .field("context", &Quoted(&self.context)) + .field("source", &self.error) + .finish() + } +} + +impl<C, E> Display for ContextError<C, E> +where + C: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.context, f) + } +} + +impl<C, E> StdError for ContextError<C, E> +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) + } +} + +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(self.error.inner.error()) + } +} + +struct Quoted<C>(C); + +impl<C> Debug for Quoted<C> +where + C: Display, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_char('"')?; + Quoted(&mut *formatter).write_fmt(format_args!("{}", self.0))?; + formatter.write_char('"')?; + Ok(()) + } +} + +impl Write for Quoted<&mut fmt::Formatter<'_>> { + fn write_str(&mut self, s: &str) -> fmt::Result { + Display::fmt(&s.escape_debug(), self.0) + } +} + +pub(crate) mod private { + use super::*; + + pub trait Sealed {} + + impl<T, E> Sealed for Result<T, E> where E: ext::StdError {} + impl<T> Sealed for Option<T> {} +} diff --git a/third_party/rust/anyhow/src/error.rs b/third_party/rust/anyhow/src/error.rs new file mode 100644 index 0000000000..80d879f84e --- /dev/null +++ b/third_party/rust/anyhow/src/error.rs @@ -0,0 +1,802 @@ +use crate::alloc::Box; +use crate::backtrace::Backtrace; +use crate::chain::Chain; +use crate::{Error, StdError}; +use core::any::TypeId; +use core::fmt::{self, Debug, Display}; +use core::mem::{self, ManuallyDrop}; +use core::ptr::{self, NonNull}; + +#[cfg(feature = "std")] +use core::ops::{Deref, DerefMut}; + +impl Error { + /// Create a new error object from any error type. + /// + /// The error type must be threadsafe and `'static`, so that the `Error` + /// will be as well. + /// + /// If the error type does not provide a backtrace, a backtrace will be + /// created here to ensure that a backtrace exists. + #[cfg(feature = "std")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + pub fn new<E>(error: E) -> Self + where + E: StdError + Send + Sync + 'static, + { + let backtrace = backtrace_if_absent!(error); + Error::from_std(error, backtrace) + } + + /// Create a new error object from a printable error message. + /// + /// If the argument implements std::error::Error, prefer `Error::new` + /// instead which preserves the underlying error's cause chain and + /// backtrace. If the argument may or may not implement std::error::Error + /// now or in the future, use `anyhow!(err)` which handles either way + /// correctly. + /// + /// `Error::msg("...")` is equivalent to `anyhow!("...")` but occasionally + /// convenient in places where a function is preferable over a macro, such + /// as iterator or stream combinators: + /// + /// ``` + /// # mod ffi { + /// # pub struct Input; + /// # pub struct Output; + /// # pub async fn do_some_work(_: Input) -> Result<Output, &'static str> { + /// # unimplemented!() + /// # } + /// # } + /// # + /// # use ffi::{Input, Output}; + /// # + /// use anyhow::{Error, Result}; + /// use futures::stream::{Stream, StreamExt, TryStreamExt}; + /// + /// async fn demo<S>(stream: S) -> Result<Vec<Output>> + /// where + /// S: Stream<Item = Input>, + /// { + /// stream + /// .then(ffi::do_some_work) // returns Result<Output, &str> + /// .map_err(Error::msg) + /// .try_collect() + /// .await + /// } + /// ``` + pub fn msg<M>(message: M) -> Self + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } + + #[cfg(feature = "std")] + pub(crate) fn from_std<E>(error: E, backtrace: Option<Backtrace>) -> Self + where + E: StdError + Send + Sync + 'static, + { + let vtable = &ErrorVTable { + object_drop: object_drop::<E>, + object_ref: object_ref::<E>, + #[cfg(feature = "std")] + object_mut: object_mut::<E>, + object_boxed: object_boxed::<E>, + object_downcast: object_downcast::<E>, + object_drop_rest: object_drop_front::<E>, + }; + + // Safety: passing vtable that operates on the right type E. + unsafe { Error::construct(error, vtable, backtrace) } + } + + pub(crate) fn from_adhoc<M>(message: M, backtrace: Option<Backtrace>) -> Self + where + M: Display + Debug + Send + Sync + 'static, + { + use crate::wrapper::MessageError; + let error: MessageError<M> = MessageError(message); + let vtable = &ErrorVTable { + object_drop: object_drop::<MessageError<M>>, + object_ref: object_ref::<MessageError<M>>, + #[cfg(feature = "std")] + object_mut: object_mut::<MessageError<M>>, + object_boxed: object_boxed::<MessageError<M>>, + object_downcast: object_downcast::<M>, + object_drop_rest: object_drop_front::<M>, + }; + + // Safety: MessageError is repr(transparent) so it is okay for the + // vtable to allow casting the MessageError<M> to M. + unsafe { Error::construct(error, vtable, backtrace) } + } + + pub(crate) fn from_display<M>(message: M, backtrace: Option<Backtrace>) -> Self + where + M: Display + Send + Sync + 'static, + { + use crate::wrapper::DisplayError; + let error: DisplayError<M> = DisplayError(message); + let vtable = &ErrorVTable { + object_drop: object_drop::<DisplayError<M>>, + object_ref: object_ref::<DisplayError<M>>, + #[cfg(feature = "std")] + object_mut: object_mut::<DisplayError<M>>, + object_boxed: object_boxed::<DisplayError<M>>, + object_downcast: object_downcast::<M>, + object_drop_rest: object_drop_front::<M>, + }; + + // Safety: DisplayError is repr(transparent) so it is okay for the + // vtable to allow casting the DisplayError<M> to M. + unsafe { Error::construct(error, vtable, backtrace) } + } + + #[cfg(feature = "std")] + pub(crate) fn from_context<C, E>(context: C, error: E, backtrace: Option<Backtrace>) -> Self + where + C: Display + Send + Sync + 'static, + E: StdError + Send + Sync + 'static, + { + let error: ContextError<C, E> = ContextError { context, error }; + + let vtable = &ErrorVTable { + object_drop: object_drop::<ContextError<C, E>>, + object_ref: object_ref::<ContextError<C, E>>, + #[cfg(feature = "std")] + object_mut: object_mut::<ContextError<C, E>>, + object_boxed: object_boxed::<ContextError<C, E>>, + object_downcast: context_downcast::<C, E>, + object_drop_rest: context_drop_rest::<C, E>, + }; + + // Safety: passing vtable that operates on the right type. + unsafe { Error::construct(error, vtable, backtrace) } + } + + #[cfg(feature = "std")] + pub(crate) fn from_boxed( + error: Box<dyn StdError + Send + Sync>, + backtrace: Option<Backtrace>, + ) -> Self { + use crate::wrapper::BoxedError; + let error = BoxedError(error); + let vtable = &ErrorVTable { + object_drop: object_drop::<BoxedError>, + object_ref: object_ref::<BoxedError>, + #[cfg(feature = "std")] + object_mut: object_mut::<BoxedError>, + object_boxed: object_boxed::<BoxedError>, + object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>, + object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>, + }; + + // Safety: BoxedError is repr(transparent) so it is okay for the vtable + // to allow casting to Box<dyn StdError + Send + Sync>. + unsafe { Error::construct(error, vtable, backtrace) } + } + + // Takes backtrace as argument rather than capturing it here so that the + // user sees one fewer layer of wrapping noise in the backtrace. + // + // Unsafe because the given vtable must have sensible behavior on the error + // value of type E. + unsafe fn construct<E>( + error: E, + vtable: &'static ErrorVTable, + backtrace: Option<Backtrace>, + ) -> Self + where + E: StdError + Send + Sync + 'static, + { + let inner = Box::new(ErrorImpl { + vtable, + backtrace, + _object: error, + }); + // Erase the concrete type of E from the compile-time type system. This + // is equivalent to the safe unsize coersion from Box<ErrorImpl<E>> to + // Box<ErrorImpl<dyn StdError + Send + Sync + 'static>> except that the + // result is a thin pointer. The necessary behavior for manipulating the + // underlying ErrorImpl<E> is preserved in the vtable provided by the + // caller rather than a builtin fat pointer vtable. + let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner); + let inner = ManuallyDrop::new(erased); + Error { inner } + } + + /// Wrap the error value with additional context. + /// + /// For attaching context to a `Result` as it is propagated, the + /// [`Context`][crate::Context] extension trait may be more convenient than + /// this function. + /// + /// The primary reason to use `error.context(...)` instead of + /// `result.context(...)` via the `Context` trait would be if the context + /// needs to depend on some data held by the underlying error: + /// + /// ``` + /// # use std::fmt::{self, Debug, Display}; + /// # + /// # type T = (); + /// # + /// # impl std::error::Error for ParseError {} + /// # impl Debug for ParseError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # impl Display for ParseError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # + /// use anyhow::Result; + /// use std::fs::File; + /// use std::path::Path; + /// + /// struct ParseError { + /// line: usize, + /// column: usize, + /// } + /// + /// fn parse_impl(file: File) -> Result<T, ParseError> { + /// # const IGNORE: &str = stringify! { + /// ... + /// # }; + /// # unimplemented!() + /// } + /// + /// pub fn parse(path: impl AsRef<Path>) -> Result<T> { + /// let file = File::open(&path)?; + /// parse_impl(file).map_err(|error| { + /// let context = format!( + /// "only the first {} lines of {} are valid", + /// error.line, path.as_ref().display(), + /// ); + /// anyhow::Error::new(error).context(context) + /// }) + /// } + /// ``` + pub fn context<C>(self, context: C) -> Self + where + C: Display + Send + Sync + 'static, + { + let error: ContextError<C, Error> = ContextError { + context, + error: self, + }; + + let vtable = &ErrorVTable { + object_drop: object_drop::<ContextError<C, Error>>, + object_ref: object_ref::<ContextError<C, Error>>, + #[cfg(feature = "std")] + object_mut: object_mut::<ContextError<C, Error>>, + object_boxed: object_boxed::<ContextError<C, Error>>, + object_downcast: context_chain_downcast::<C>, + object_drop_rest: context_chain_drop_rest::<C>, + }; + + // As the cause is anyhow::Error, we already have a backtrace for it. + let backtrace = None; + + // Safety: passing vtable that operates on the right type. + unsafe { Error::construct(error, vtable, backtrace) } + } + + /// Get the backtrace for this Error. + /// + /// Backtraces are only available on the nightly channel. Tracking issue: + /// [rust-lang/rust#53487][tracking]. + /// + /// In order for the backtrace to be meaningful, one of the two environment + /// variables `RUST_LIB_BACKTRACE=1` or `RUST_BACKTRACE=1` must be defined + /// and `RUST_LIB_BACKTRACE` must not be `0`. Backtraces are somewhat + /// expensive to capture in Rust, so we don't necessarily want to be + /// capturing them all over the place all the time. + /// + /// - If you want panics and errors to both have backtraces, set + /// `RUST_BACKTRACE=1`; + /// - If you want only errors to have backtraces, set + /// `RUST_LIB_BACKTRACE=1`; + /// - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and + /// `RUST_LIB_BACKTRACE=0`. + /// + /// [tracking]: https://github.com/rust-lang/rust/issues/53487 + #[cfg(backtrace)] + pub fn backtrace(&self) -> &Backtrace { + self.inner.backtrace() + } + + /// An iterator of the chain of source errors contained by this Error. + /// + /// This iterator will visit every error in the cause chain of this error + /// object, beginning with the error that this error object was created + /// from. + /// + /// # Example + /// + /// ``` + /// use anyhow::Error; + /// use std::io; + /// + /// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> { + /// for cause in error.chain() { + /// if let Some(io_error) = cause.downcast_ref::<io::Error>() { + /// return Some(io_error.kind()); + /// } + /// } + /// None + /// } + /// ``` + #[cfg(feature = "std")] + pub fn chain(&self) -> Chain { + self.inner.chain() + } + + /// The lowest level cause of this error — this error's cause's + /// cause's cause etc. + /// + /// The root cause is the last error in the iterator produced by + /// [`chain()`][Error::chain]. + #[cfg(feature = "std")] + pub fn root_cause(&self) -> &(dyn StdError + 'static) { + let mut chain = self.chain(); + let mut root_cause = chain.next().unwrap(); + for cause in chain { + root_cause = cause; + } + root_cause + } + + /// Returns true if `E` is the type held by this error object. + /// + /// For errors with context, this method returns true if `E` matches the + /// type of the context `C` **or** the type of the error on which the + /// context has been attached. For details about the interaction between + /// context and downcasting, [see here]. + /// + /// [see here]: trait.Context.html#effect-on-downcasting + pub fn is<E>(&self) -> bool + where + E: Display + Debug + Send + Sync + 'static, + { + self.downcast_ref::<E>().is_some() + } + + /// Attempt to downcast the error object to a concrete type. + pub fn downcast<E>(self) -> Result<E, Self> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = match (self.inner.vtable.object_downcast)(&self.inner, target) { + Some(addr) => addr, + None => return Err(self), + }; + + // Prepare to read E out of the data structure. We'll drop the rest + // of the data structure separately so that E is not dropped. + let outer = ManuallyDrop::new(self); + + // Read E from where the vtable found it. + let error = ptr::read(addr.cast::<E>().as_ptr()); + + // Read Box<ErrorImpl<()>> from self. Can't move it out because + // Error has a Drop impl which we want to not run. + let inner = ptr::read(&outer.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Drop rest of the data structure outside of E. + (erased.vtable.object_drop_rest)(erased, target); + + Ok(error) + } + } + + /// Downcast this error object by reference. + /// + /// # Example + /// + /// ``` + /// # use anyhow::anyhow; + /// # use std::fmt::{self, Display}; + /// # use std::task::Poll; + /// # + /// # #[derive(Debug)] + /// # enum DataStoreError { + /// # Censored(()), + /// # } + /// # + /// # impl Display for DataStoreError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # + /// # impl std::error::Error for DataStoreError {} + /// # + /// # const REDACTED_CONTENT: () = (); + /// # + /// # let error = anyhow!("..."); + /// # let root_cause = &error; + /// # + /// # let ret = + /// // If the error was caused by redaction, then return a tombstone instead + /// // of the content. + /// match root_cause.downcast_ref::<DataStoreError>() { + /// Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), + /// None => Err(error), + /// } + /// # ; + /// ``` + pub fn downcast_ref<E>(&self) -> Option<&E> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&*addr.cast::<E>().as_ptr()) + } + } + + /// Downcast this error object by mutable reference. + pub fn downcast_mut<E>(&mut self) -> Option<&mut E> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&mut *addr.cast::<E>().as_ptr()) + } + } +} + +#[cfg(feature = "std")] +impl<E> From<E> for Error +where + E: StdError + Send + Sync + 'static, +{ + fn from(error: E) -> Self { + let backtrace = backtrace_if_absent!(error); + Error::from_std(error, backtrace) + } +} + +#[cfg(feature = "std")] +impl Deref for Error { + type Target = dyn StdError + Send + Sync + 'static; + + fn deref(&self) -> &Self::Target { + self.inner.error() + } +} + +#[cfg(feature = "std")] +impl DerefMut for Error { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.error_mut() + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.inner.display(formatter) + } +} + +impl Debug for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.inner.debug(formatter) + } +} + +impl Drop for Error { + fn drop(&mut self) { + unsafe { + // Read Box<ErrorImpl<()>> from self. + let inner = ptr::read(&self.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Invoke the vtable's drop behavior. + (erased.vtable.object_drop)(erased); + } + } +} + +struct ErrorVTable { + object_drop: unsafe fn(Box<ErrorImpl<()>>), + object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static), + #[cfg(feature = "std")] + object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static), + object_boxed: unsafe fn(Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>, + object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option<NonNull<()>>, + object_drop_rest: unsafe fn(Box<ErrorImpl<()>>, TypeId), +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) { + // Cast back to ErrorImpl<E> so that the allocator receives the correct + // Layout to deallocate the Box's memory. + let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e); + drop(unerased); +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) { + // Drop the fields of ErrorImpl other than E as well as the Box allocation, + // without dropping E itself. This is used by downcast after doing a + // ptr::read to take ownership of the E. + let _ = target; + let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<ManuallyDrop<E>>>>(e); + drop(unerased); +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_ref<E>(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static) +where + E: StdError + Send + Sync + 'static, +{ + // Attach E's native StdError vtable onto a pointer to self._object. + &(*(e as *const ErrorImpl<()> as *const ErrorImpl<E>))._object +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +#[cfg(feature = "std")] +unsafe fn object_mut<E>(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static) +where + E: StdError + Send + Sync + 'static, +{ + // Attach E's native StdError vtable onto a pointer to self._object. + &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl<E>))._object +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_boxed<E>(e: Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static> +where + E: StdError + Send + Sync + 'static, +{ + // Attach ErrorImpl<E>'s native StdError vtable. The StdError impl is below. + mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e) +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_downcast<E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + E: 'static, +{ + if TypeId::of::<E>() == target { + // Caller is looking for an E pointer and e is ErrorImpl<E>, take a + // pointer to its E field. + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<E>; + let addr = &(*unerased)._object as *const E as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + None + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>. +#[cfg(feature = "std")] +unsafe fn context_downcast<C, E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + C: 'static, + E: 'static, +{ + if TypeId::of::<C>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>; + let addr = &(*unerased)._object.context as *const C as *mut (); + Some(NonNull::new_unchecked(addr)) + } else if TypeId::of::<E>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>; + let addr = &(*unerased)._object.error as *const E as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + None + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>. +#[cfg(feature = "std")] +unsafe fn context_drop_rest<C, E>(e: Box<ErrorImpl<()>>, target: TypeId) +where + C: 'static, + E: 'static, +{ + // Called after downcasting by value to either the C or the E and doing a + // ptr::read to take ownership of that value. + if TypeId::of::<C>() == target { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<ManuallyDrop<C>, E>>>, + >(e); + drop(unerased); + } else { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<C, ManuallyDrop<E>>>>, + >(e); + drop(unerased); + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>. +unsafe fn context_chain_downcast<C>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + C: 'static, +{ + if TypeId::of::<C>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>; + let addr = &(*unerased)._object.context as *const C as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + // Recurse down the context chain per the inner error's vtable. + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>; + let source = &(*unerased)._object.error; + (source.inner.vtable.object_downcast)(&source.inner, target) + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>. +unsafe fn context_chain_drop_rest<C>(e: Box<ErrorImpl<()>>, target: TypeId) +where + C: 'static, +{ + // Called after downcasting by value to either the C or one of the causes + // and doing a ptr::read to take ownership of that value. + if TypeId::of::<C>() == target { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<ManuallyDrop<C>, Error>>>, + >(e); + // Drop the entire rest of the data structure rooted in the next Error. + drop(unerased); + } else { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<C, ManuallyDrop<Error>>>>, + >(e); + // Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error. + let inner = ptr::read(&unerased._object.error.inner); + drop(unerased); + let erased = ManuallyDrop::into_inner(inner); + // Recursively drop the next error using the same target typeid. + (erased.vtable.object_drop_rest)(erased, target); + } +} + +// repr C to ensure that E remains in the final position. +#[repr(C)] +pub(crate) struct ErrorImpl<E> { + vtable: &'static ErrorVTable, + backtrace: Option<Backtrace>, + // NOTE: Don't use directly. Use only through vtable. Erased type may have + // different alignment. + _object: E, +} + +// repr C to ensure that ContextError<C, E> has the same layout as +// ContextError<ManuallyDrop<C>, E> and ContextError<C, ManuallyDrop<E>>. +#[repr(C)] +pub(crate) struct ContextError<C, E> { + pub context: C, + pub error: E, +} + +impl<E> ErrorImpl<E> { + fn erase(&self) -> &ErrorImpl<()> { + // Erase the concrete type of E but preserve the vtable in self.vtable + // for manipulating the resulting thin pointer. This is analogous to an + // unsize coersion. + unsafe { &*(self as *const ErrorImpl<E> as *const ErrorImpl<()>) } + } +} + +impl ErrorImpl<()> { + pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) { + // Use vtable to attach E's native StdError vtable for the right + // original type E. + unsafe { &*(self.vtable.object_ref)(self) } + } + + #[cfg(feature = "std")] + pub(crate) fn error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) { + // Use vtable to attach E's native StdError vtable for the right + // original type E. + unsafe { &mut *(self.vtable.object_mut)(self) } + } + + #[cfg(backtrace)] + pub(crate) fn backtrace(&self) -> &Backtrace { + // This unwrap can only panic if the underlying error's backtrace method + // is nondeterministic, which would only happen in maliciously + // constructed code. + self.backtrace + .as_ref() + .or_else(|| self.error().backtrace()) + .expect("backtrace capture failed") + } + + pub(crate) fn chain(&self) -> Chain { + Chain::new(self.error()) + } +} + +impl<E> StdError for ErrorImpl<E> +where + E: StdError, +{ + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> { + Some(self.erase().backtrace()) + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.erase().error().source() + } +} + +impl<E> Debug for ErrorImpl<E> +where + E: Debug, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.erase().debug(formatter) + } +} + +impl<E> Display for ErrorImpl<E> +where + E: Display, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.erase().error(), formatter) + } +} + +impl From<Error> for Box<dyn StdError + Send + Sync + 'static> { + fn from(error: Error) -> Self { + let outer = ManuallyDrop::new(error); + unsafe { + // Read Box<ErrorImpl<()>> from error. Can't move it out because + // Error has a Drop impl which we want to not run. + let inner = ptr::read(&outer.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Use vtable to attach ErrorImpl<E>'s native StdError vtable for + // the right original type E. + (erased.vtable.object_boxed)(erased) + } + } +} + +impl From<Error> for Box<dyn StdError + 'static> { + fn from(error: Error) -> Self { + Box::<dyn StdError + Send + Sync>::from(error) + } +} + +#[cfg(feature = "std")] +impl AsRef<dyn StdError + Send + Sync> for Error { + fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { + &**self + } +} + +#[cfg(feature = "std")] +impl AsRef<dyn StdError> for Error { + fn as_ref(&self) -> &(dyn StdError + 'static) { + &**self + } +} diff --git a/third_party/rust/anyhow/src/fmt.rs b/third_party/rust/anyhow/src/fmt.rs new file mode 100644 index 0000000000..16a82ba8d8 --- /dev/null +++ b/third_party/rust/anyhow/src/fmt.rs @@ -0,0 +1,149 @@ +use crate::chain::Chain; +use crate::error::ErrorImpl; +use core::fmt::{self, Debug, Write}; + +impl ErrorImpl<()> { + pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.error())?; + + if f.alternate() { + for cause in self.chain().skip(1) { + write!(f, ": {}", cause)?; + } + } + + Ok(()) + } + + pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { + let error = self.error(); + + if f.alternate() { + return Debug::fmt(error, f); + } + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + let multiple = cause.source().is_some(); + for (n, error) in Chain::new(cause).enumerate() { + writeln!(f)?; + let mut indented = Indented { + inner: f, + number: if multiple { Some(n) } else { None }, + started: false, + }; + write!(indented, "{}", error)?; + } + } + + #[cfg(backtrace)] + { + use std::backtrace::BacktraceStatus; + + let backtrace = self.backtrace(); + if let BacktraceStatus::Captured = backtrace.status() { + let mut backtrace = backtrace.to_string(); + if backtrace.starts_with("stack backtrace:") { + // Capitalize to match "Caused by:" + backtrace.replace_range(0..1, "S"); + } + backtrace.truncate(backtrace.trim_end().len()); + write!(f, "\n\n{}", backtrace)?; + } + } + + Ok(()) + } +} + +struct Indented<'a, D> { + inner: &'a mut D, + number: Option<usize>, + started: bool, +} + +impl<T> Write for Indented<'_, T> +where + T: Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if !self.started { + self.started = true; + match self.number { + Some(number) => write!(self.inner, "{: >5}: ", number)?, + None => self.inner.write_str(" ")?, + } + } else if i > 0 { + self.inner.write_char('\n')?; + if self.number.is_some() { + self.inner.write_str(" ")?; + } else { + self.inner.write_str(" ")?; + } + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn one_digit() { + let input = "verify\nthis"; + let expected = " 2: verify\n this"; + let mut output = String::new(); + + Indented { + inner: &mut output, + number: Some(2), + started: false, + } + .write_str(input) + .unwrap(); + + assert_eq!(expected, output); + } + + #[test] + fn two_digits() { + let input = "verify\nthis"; + let expected = " 12: verify\n this"; + let mut output = String::new(); + + Indented { + inner: &mut output, + number: Some(12), + started: false, + } + .write_str(input) + .unwrap(); + + assert_eq!(expected, output); + } + + #[test] + fn no_digits() { + let input = "verify\nthis"; + let expected = " verify\n this"; + let mut output = String::new(); + + Indented { + inner: &mut output, + number: None, + started: false, + } + .write_str(input) + .unwrap(); + + assert_eq!(expected, output); + } +} diff --git a/third_party/rust/anyhow/src/kind.rs b/third_party/rust/anyhow/src/kind.rs new file mode 100644 index 0000000000..fdeb060ad8 --- /dev/null +++ b/third_party/rust/anyhow/src/kind.rs @@ -0,0 +1,116 @@ +// Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`. +// +// When anyhow! is given a single expr argument to turn into anyhow::Error, we +// want the resulting Error to pick up the input's implementation of source() +// and backtrace() if it has a std::error::Error impl, otherwise require nothing +// more than Display and Debug. +// +// Expressed in terms of specialization, we want something like: +// +// trait AnyhowNew { +// fn new(self) -> Error; +// } +// +// impl<T> AnyhowNew for T +// where +// T: Display + Debug + Send + Sync + 'static, +// { +// default fn new(self) -> Error { +// /* no std error impl */ +// } +// } +// +// impl<T> AnyhowNew for T +// where +// T: std::error::Error + Send + Sync + 'static, +// { +// fn new(self) -> Error { +// /* use std error's source() and backtrace() */ +// } +// } +// +// Since specialization is not stable yet, instead we rely on autoref behavior +// of method resolution to perform tagged dispatch. Here we have two traits +// AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is +// implemented whether or not the caller's type has a std error impl, while +// TraitKind is implemented only when a std error impl does exist. The ambiguity +// is resolved by AdhocKind requiring an extra autoref so that it has lower +// precedence. +// +// The anyhow! macro will set up the call in this form: +// +// #[allow(unused_imports)] +// use $crate::private::{AdhocKind, TraitKind}; +// let error = $msg; +// (&error).anyhow_kind().new(error) + +use crate::Error; +use core::fmt::{Debug, Display}; + +#[cfg(feature = "std")] +use crate::StdError; + +#[cfg(backtrace)] +use std::backtrace::Backtrace; + +pub struct Adhoc; + +pub trait AdhocKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Adhoc { + Adhoc + } +} + +impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} + +impl Adhoc { + pub fn new<M>(self, message: M) -> Error + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } +} + +pub struct Trait; + +pub trait TraitKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Trait { + Trait + } +} + +impl<E> TraitKind for E where E: Into<Error> {} + +impl Trait { + pub fn new<E>(self, error: E) -> Error + where + E: Into<Error>, + { + error.into() + } +} + +#[cfg(feature = "std")] +pub struct Boxed; + +#[cfg(feature = "std")] +pub trait BoxedKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Boxed { + Boxed + } +} + +#[cfg(feature = "std")] +impl BoxedKind for Box<dyn StdError + Send + Sync> {} + +#[cfg(feature = "std")] +impl Boxed { + pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error { + let backtrace = backtrace_if_absent!(error); + Error::from_boxed(error, backtrace) + } +} diff --git a/third_party/rust/anyhow/src/lib.rs b/third_party/rust/anyhow/src/lib.rs new file mode 100644 index 0000000000..91d4c4652e --- /dev/null +++ b/third_party/rust/anyhow/src/lib.rs @@ -0,0 +1,603 @@ +//! [![github]](https://github.com/dtolnay/anyhow) [![crates-io]](https://crates.io/crates/anyhow) [![docs-rs]](https://docs.rs/anyhow) +//! +//! [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 +//! +//! <br> +//! +//! This library provides [`anyhow::Error`][Error], a trait object based error +//! type for easy idiomatic error handling in Rust applications. +//! +//! <br> +//! +//! # Details +//! +//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as +//! the return type of any fallible function. +//! +//! Within the function, use `?` to easily propagate any error that implements +//! the `std::error::Error` trait. +//! +//! ``` +//! # pub trait Deserialize {} +//! # +//! # mod serde_json { +//! # use super::Deserialize; +//! # use std::io; +//! # +//! # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { +//! # unimplemented!() +//! # } +//! # } +//! # +//! # struct ClusterMap; +//! # +//! # impl Deserialize for ClusterMap {} +//! # +//! use anyhow::Result; +//! +//! fn get_cluster_info() -> Result<ClusterMap> { +//! let config = std::fs::read_to_string("cluster.json")?; +//! let map: ClusterMap = serde_json::from_str(&config)?; +//! Ok(map) +//! } +//! # +//! # fn main() {} +//! ``` +//! +//! - Attach context to help the person troubleshooting the error understand +//! where things went wrong. A low-level error like "No such file or +//! directory" can be annoying to debug without more context about what higher +//! level step the application was in the middle of. +//! +//! ``` +//! # struct It; +//! # +//! # impl It { +//! # fn detach(&self) -> Result<()> { +//! # unimplemented!() +//! # } +//! # } +//! # +//! use anyhow::{Context, Result}; +//! +//! fn main() -> Result<()> { +//! # return Ok(()); +//! # +//! # const _: &str = stringify! { +//! ... +//! # }; +//! # +//! # let it = It; +//! # let path = "./path/to/instrs.json"; +//! # +//! it.detach().context("Failed to detach the important thing")?; +//! +//! let content = std::fs::read(path) +//! .with_context(|| format!("Failed to read instrs from {}", path))?; +//! # +//! # const _: &str = stringify! { +//! ... +//! # }; +//! # +//! # Ok(()) +//! } +//! ``` +//! +//! ```console +//! Error: Failed to read instrs from ./path/to/instrs.json +//! +//! Caused by: +//! No such file or directory (os error 2) +//! ``` +//! +//! - Downcasting is supported and can be by value, by shared reference, or by +//! mutable reference as needed. +//! +//! ``` +//! # use anyhow::anyhow; +//! # use std::fmt::{self, Display}; +//! # use std::task::Poll; +//! # +//! # #[derive(Debug)] +//! # enum DataStoreError { +//! # Censored(()), +//! # } +//! # +//! # impl Display for DataStoreError { +//! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +//! # unimplemented!() +//! # } +//! # } +//! # +//! # impl std::error::Error for DataStoreError {} +//! # +//! # const REDACTED_CONTENT: () = (); +//! # +//! # let error = anyhow!("..."); +//! # let root_cause = &error; +//! # +//! # let ret = +//! // If the error was caused by redaction, then return a +//! // tombstone instead of the content. +//! match root_cause.downcast_ref::<DataStoreError>() { +//! Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), +//! None => Err(error), +//! } +//! # ; +//! ``` +//! +//! - If using the nightly channel, 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`]: +//! +//! - If you want panics and errors to both have backtraces, set +//! `RUST_BACKTRACE=1`; +//! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; +//! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and +//! `RUST_LIB_BACKTRACE=0`. +//! +//! The tracking issue for this feature is [rust-lang/rust#53487]. +//! +//! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables +//! [rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487 +//! +//! - Anyhow works with any error type that has an impl of `std::error::Error`, +//! including ones defined in your crate. We do not bundle a `derive(Error)` +//! macro but you can write the impls yourself or use a standalone macro like +//! [thiserror]. +//! +//! [thiserror]: https://github.com/dtolnay/thiserror +//! +//! ``` +//! use thiserror::Error; +//! +//! #[derive(Error, Debug)] +//! pub enum FormatError { +//! #[error("Invalid header (expected {expected:?}, got {found:?})")] +//! InvalidHeader { +//! expected: String, +//! found: String, +//! }, +//! #[error("Missing attribute: {0}")] +//! MissingAttribute(String), +//! } +//! ``` +//! +//! - One-off error messages can be constructed using the `anyhow!` macro, which +//! supports string interpolation and produces an `anyhow::Error`. +//! +//! ``` +//! # use anyhow::{anyhow, Result}; +//! # +//! # fn demo() -> Result<()> { +//! # let missing = "..."; +//! return Err(anyhow!("Missing attribute: {}", missing)); +//! # Ok(()) +//! # } +//! ``` +//! +//! <br> +//! +//! # No-std support +//! +//! In no_std mode, the same API is almost all available and works the same way. +//! To depend on Anyhow in no_std mode, disable our default enabled "std" +//! feature in Cargo.toml. A global allocator is required. +//! +//! ```toml +//! [dependencies] +//! anyhow = { version = "1.0", default-features = false } +//! ``` +//! +//! Since the `?`-based error conversions would normally rely on the +//! `std::error::Error` trait which is only available through std, no_std mode +//! 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.30")] +#![cfg_attr(backtrace, feature(backtrace))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow( + clippy::needless_doctest_main, + clippy::new_ret_no_self, + 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; +} + +#[macro_use] +mod backtrace; +mod chain; +mod context; +mod error; +mod fmt; +mod kind; +mod macros; +mod wrapper; + +use crate::alloc::Box; +use crate::error::ErrorImpl; +use core::fmt::Display; +use core::mem::ManuallyDrop; + +#[cfg(not(feature = "std"))] +use core::fmt::Debug; + +#[cfg(feature = "std")] +use std::error::Error as StdError; + +#[cfg(not(feature = "std"))] +trait StdError: Debug + Display { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + None + } +} + +pub use anyhow as format_err; + +/// The `Error` type, a wrapper around a dynamic error type. +/// +/// `Error` works a lot like `Box<dyn std::error::Error>`, but with these +/// differences: +/// +/// - `Error` requires that the error is `Send`, `Sync`, and `'static`. +/// - `Error` guarantees that a backtrace is available, even if the underlying +/// error type does not provide one. +/// - `Error` is represented as a narrow pointer — exactly one word in +/// size instead of two. +/// +/// <br> +/// +/// # Display representations +/// +/// When you print an error object using "{}" or to_string(), only the outermost +/// underlying error or context is printed, not any of the lower level causes. +/// This is exactly as if you had called the Display impl of the error from +/// which you constructed your anyhow::Error. +/// +/// ```console +/// Failed to read instrs from ./path/to/instrs.json +/// ``` +/// +/// To print causes as well using anyhow's default formatting of causes, use the +/// alternate selector "{:#}". +/// +/// ```console +/// Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2) +/// ``` +/// +/// The Debug format "{:?}" includes your backtrace if one was captured. Note +/// that this is the representation you get by default if you return an error +/// from `fn main` instead of printing it explicitly yourself. +/// +/// ```console +/// Error: Failed to read instrs from ./path/to/instrs.json +/// +/// Caused by: +/// No such file or directory (os error 2) +/// +/// Stack backtrace: +/// 0: <E as anyhow::context::ext::StdError>::ext_context +/// at /git/anyhow/src/backtrace.rs:26 +/// 1: core::result::Result<T,E>::map_err +/// at /git/rustc/src/libcore/result.rs:596 +/// 2: anyhow::context::<impl anyhow::Context<T,E> for core::result::Result<T,E>>::with_context +/// at /git/anyhow/src/context.rs:58 +/// 3: testing::main +/// at src/main.rs:5 +/// 4: std::rt::lang_start +/// at /git/rustc/src/libstd/rt.rs:61 +/// 5: main +/// 6: __libc_start_main +/// 7: _start +/// ``` +/// +/// To see a conventional struct-style Debug representation, use "{:#?}". +/// +/// ```console +/// Error { +/// context: "Failed to read instrs from ./path/to/instrs.json", +/// source: Os { +/// code: 2, +/// kind: NotFound, +/// message: "No such file or directory", +/// }, +/// } +/// ``` +/// +/// If none of the built-in representations are appropriate and you would prefer +/// to render the error and its cause chain yourself, it can be done something +/// like this: +/// +/// ``` +/// use anyhow::{Context, Result}; +/// +/// fn main() { +/// if let Err(err) = try_main() { +/// eprintln!("ERROR: {}", err); +/// err.chain().skip(1).for_each(|cause| eprintln!("because: {}", cause)); +/// std::process::exit(1); +/// } +/// } +/// +/// fn try_main() -> Result<()> { +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # Ok(()) +/// } +/// ``` +pub struct Error { + inner: ManuallyDrop<Box<ErrorImpl<()>>>, +} + +/// Iterator of a chain of source errors. +/// +/// This type is the iterator returned by [`Error::chain`]. +/// +/// # Example +/// +/// ``` +/// use anyhow::Error; +/// use std::io; +/// +/// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> { +/// for cause in error.chain() { +/// if let Some(io_error) = cause.downcast_ref::<io::Error>() { +/// return Some(io_error.kind()); +/// } +/// } +/// None +/// } +/// ``` +#[cfg(feature = "std")] +#[derive(Clone)] +pub struct Chain<'a> { + state: crate::chain::ChainState<'a>, +} + +/// `Result<T, Error>` +/// +/// This is a reasonable return type to use throughout your application but also +/// for `fn main`; if you do, failures will be printed along with any +/// [context][Context] and a backtrace if one was captured. +/// +/// `anyhow::Result` may be used with one *or* two type parameters. +/// +/// ```rust +/// use anyhow::Result; +/// +/// # const IGNORE: &str = stringify! { +/// fn demo1() -> Result<T> {...} +/// // ^ equivalent to std::result::Result<T, anyhow::Error> +/// +/// fn demo2() -> Result<T, OtherError> {...} +/// // ^ equivalent to std::result::Result<T, OtherError> +/// # }; +/// ``` +/// +/// # Example +/// +/// ``` +/// # pub trait Deserialize {} +/// # +/// # mod serde_json { +/// # use super::Deserialize; +/// # use std::io; +/// # +/// # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { +/// # unimplemented!() +/// # } +/// # } +/// # +/// # #[derive(Debug)] +/// # struct ClusterMap; +/// # +/// # impl Deserialize for ClusterMap {} +/// # +/// use anyhow::Result; +/// +/// fn main() -> Result<()> { +/// # return Ok(()); +/// let config = std::fs::read_to_string("cluster.json")?; +/// let map: ClusterMap = serde_json::from_str(&config)?; +/// println!("cluster info: {:#?}", map); +/// Ok(()) +/// } +/// ``` +pub type Result<T, E = Error> = core::result::Result<T, E>; + +/// Provides the `context` method for `Result`. +/// +/// This trait is sealed and cannot be implemented for types outside of +/// `anyhow`. +/// +/// <br> +/// +/// # Example +/// +/// ``` +/// use anyhow::{Context, Result}; +/// use std::fs; +/// use std::path::PathBuf; +/// +/// pub struct ImportantThing { +/// path: PathBuf, +/// } +/// +/// impl ImportantThing { +/// # const IGNORE: &'static str = stringify! { +/// pub fn detach(&mut self) -> Result<()> {...} +/// # }; +/// # fn detach(&mut self) -> Result<()> { +/// # unimplemented!() +/// # } +/// } +/// +/// pub fn do_it(mut it: ImportantThing) -> Result<Vec<u8>> { +/// it.detach().context("Failed to detach the important thing")?; +/// +/// let path = &it.path; +/// let content = fs::read(path) +/// .with_context(|| format!("Failed to read instrs from {}", path.display()))?; +/// +/// Ok(content) +/// } +/// ``` +/// +/// When printed, the outermost context would be printed first and the lower +/// level underlying causes would be enumerated below. +/// +/// ```console +/// Error: Failed to read instrs from ./path/to/instrs.json +/// +/// Caused by: +/// No such file or directory (os error 2) +/// ``` +/// +/// <br> +/// +/// # Effect on downcasting +/// +/// After attaching context of type `C` onto an error of type `E`, the resulting +/// `anyhow::Error` may be downcast to `C` **or** to `E`. +/// +/// That is, in codebases that rely on downcasting, Anyhow's context supports +/// both of the following use cases: +/// +/// - **Attaching context whose type is insignificant onto errors whose type +/// is used in downcasts.** +/// +/// In other error libraries whose context is not designed this way, it can +/// be risky to introduce context to existing code because new context might +/// break existing working downcasts. In Anyhow, any downcast that worked +/// before adding context will continue to work after you add a context, so +/// you should freely add human-readable context to errors wherever it would +/// be helpful. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct SuspiciousError; +/// # +/// # fn helper() -> Result<()> { +/// # bail!(SuspiciousError); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context("Failed to complete the work")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::<SuspiciousError>() { +/// // If helper() returned SuspiciousError, this downcast will +/// // correctly succeed even with the context in between. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` +/// +/// - **Attaching context whose type is used in downcasts onto errors whose +/// type is insignificant.** +/// +/// Some codebases prefer to use machine-readable context to categorize +/// lower level errors in a way that will be actionable to higher levels of +/// the application. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct HelperFailed; +/// # +/// # fn helper() -> Result<()> { +/// # bail!("no such file or directory"); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context(HelperFailed)?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::<HelperFailed>() { +/// // If helper failed, this downcast will succeed because +/// // HelperFailed is the context that has been attached to +/// // that error. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` +pub trait Context<T, E>: context::private::Sealed { + /// Wrap the error value with additional context. + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static; + + /// Wrap the error value with additional context that is evaluated lazily + /// only once an error does occur. + fn with_context<C, F>(self, f: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C; +} + +// Not public API. Referenced by macro-generated code. +#[doc(hidden)] +pub mod private { + use crate::Error; + use core::fmt::{Debug, Display}; + + #[cfg(backtrace)] + use std::backtrace::Backtrace; + + pub use core::result::Result::Err; + + #[doc(hidden)] + pub mod kind { + pub use crate::kind::{AdhocKind, TraitKind}; + + #[cfg(feature = "std")] + pub use crate::kind::BoxedKind; + } + + pub fn new_adhoc<M>(message: M) -> Error + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } +} diff --git a/third_party/rust/anyhow/src/macros.rs b/third_party/rust/anyhow/src/macros.rs new file mode 100644 index 0000000000..15a920810c --- /dev/null +++ b/third_party/rust/anyhow/src/macros.rs @@ -0,0 +1,163 @@ +/// Return early with an error. +/// +/// This macro is equivalent to `return Err(From::from($err))`. +/// +/// # Example +/// +/// ``` +/// # use anyhow::{bail, Result}; +/// # +/// # fn has_permission(user: usize, resource: usize) -> bool { +/// # true +/// # } +/// # +/// # fn main() -> Result<()> { +/// # let user = 0; +/// # let resource = 0; +/// # +/// if !has_permission(user, resource) { +/// bail!("permission denied for accessing {}", resource); +/// } +/// # Ok(()) +/// # } +/// ``` +/// +/// ``` +/// # use anyhow::{bail, Result}; +/// # use thiserror::Error; +/// # +/// # const MAX_DEPTH: usize = 1; +/// # +/// #[derive(Error, Debug)] +/// enum ScienceError { +/// #[error("recursion limit exceeded")] +/// RecursionLimitExceeded, +/// # #[error("...")] +/// # More = (stringify! { +/// ... +/// # }, 1).1, +/// } +/// +/// # fn main() -> Result<()> { +/// # let depth = 0; +/// # +/// if depth > MAX_DEPTH { +/// bail!(ScienceError::RecursionLimitExceeded); +/// } +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! bail { + ($msg:literal $(,)?) => { + return $crate::private::Err($crate::anyhow!($msg)); + }; + ($err:expr $(,)?) => { + return $crate::private::Err($crate::anyhow!($err)); + }; + ($fmt:expr, $($arg:tt)*) => { + return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + }; +} + +/// Return early with an error if a condition is not satisfied. +/// +/// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`. +/// +/// Analogously to `assert!`, `ensure!` takes a condition and exits the function +/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error` +/// rather than panicking. +/// +/// # Example +/// +/// ``` +/// # use anyhow::{ensure, Result}; +/// # +/// # fn main() -> Result<()> { +/// # let user = 0; +/// # +/// ensure!(user == 0, "only user 0 is allowed"); +/// # Ok(()) +/// # } +/// ``` +/// +/// ``` +/// # use anyhow::{ensure, Result}; +/// # use thiserror::Error; +/// # +/// # const MAX_DEPTH: usize = 1; +/// # +/// #[derive(Error, Debug)] +/// enum ScienceError { +/// #[error("recursion limit exceeded")] +/// RecursionLimitExceeded, +/// # #[error("...")] +/// # More = (stringify! { +/// ... +/// # }, 1).1, +/// } +/// +/// # fn main() -> Result<()> { +/// # let depth = 0; +/// # +/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded); +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! ensure { + ($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)*)); + } + }; +} + +/// Construct an ad-hoc error from a string. +/// +/// This evaluates to an `Error`. It can take either just a string, or a format +/// string with arguments. It also can take any custom type which implements +/// `Debug` and `Display`. +/// +/// # Example +/// +/// ``` +/// # type V = (); +/// # +/// use anyhow::{anyhow, Result}; +/// +/// fn lookup(key: &str) -> Result<V> { +/// if key.len() != 16 { +/// return Err(anyhow!("key length must be 16 characters, got {:?}", key)); +/// } +/// +/// // ... +/// # Ok(()) +/// } +/// ``` +#[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) + }; + ($err:expr $(,)?) => ({ + use $crate::private::kind::*; + let error = $err; + (&error).anyhow_kind().new(error) + }); + ($fmt:expr, $($arg:tt)*) => { + $crate::private::new_adhoc(format!($fmt, $($arg)*)) + }; +} diff --git a/third_party/rust/anyhow/src/wrapper.rs b/third_party/rust/anyhow/src/wrapper.rs new file mode 100644 index 0000000000..3ebe51a883 --- /dev/null +++ b/third_party/rust/anyhow/src/wrapper.rs @@ -0,0 +1,78 @@ +use crate::StdError; +use core::fmt::{self, Debug, Display}; + +#[repr(transparent)] +pub struct MessageError<M>(pub M); + +impl<M> Debug for MessageError<M> +where + M: Display + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl<M> Display for MessageError<M> +where + M: Display + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {} + +#[repr(transparent)] +pub struct DisplayError<M>(pub M); + +impl<M> Debug for DisplayError<M> +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> Display for DisplayError<M> +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> StdError for DisplayError<M> where M: Display + 'static {} + +#[cfg(feature = "std")] +#[repr(transparent)] +pub struct BoxedError(pub Box<dyn StdError + Send + Sync>); + +#[cfg(feature = "std")] +impl Debug for BoxedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +#[cfg(feature = "std")] +impl Display for BoxedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +#[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() + } +} diff --git a/third_party/rust/anyhow/tests/common/mod.rs b/third_party/rust/anyhow/tests/common/mod.rs new file mode 100644 index 0000000000..fc165a5be9 --- /dev/null +++ b/third_party/rust/anyhow/tests/common/mod.rs @@ -0,0 +1,14 @@ +use anyhow::{bail, Result}; +use std::io; + +pub fn bail_literal() -> Result<()> { + bail!("oh no!"); +} + +pub fn bail_fmt() -> Result<()> { + bail!("{} {}!", "oh", "no"); +} + +pub fn bail_error() -> Result<()> { + bail!(io::Error::new(io::ErrorKind::Other, "oh no!")); +} diff --git a/third_party/rust/anyhow/tests/compiletest.rs b/third_party/rust/anyhow/tests/compiletest.rs new file mode 100644 index 0000000000..f9aea23b51 --- /dev/null +++ b/third_party/rust/anyhow/tests/compiletest.rs @@ -0,0 +1,6 @@ +#[rustversion::attr(not(nightly), ignore)] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/third_party/rust/anyhow/tests/drop/mod.rs b/third_party/rust/anyhow/tests/drop/mod.rs new file mode 100644 index 0000000000..c9d7144479 --- /dev/null +++ b/third_party/rust/anyhow/tests/drop/mod.rs @@ -0,0 +1,52 @@ +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::Arc; + +#[derive(Debug)] +pub struct Flag { + atomic: Arc<AtomicBool>, +} + +impl Flag { + pub fn new() -> Self { + Flag { + atomic: Arc::new(AtomicBool::new(false)), + } + } + + pub fn get(&self) -> bool { + self.atomic.load(SeqCst) + } +} + +#[derive(Debug)] +pub struct DetectDrop { + has_dropped: Flag, +} + +impl DetectDrop { + pub fn new(has_dropped: &Flag) -> Self { + DetectDrop { + has_dropped: Flag { + atomic: Arc::clone(&has_dropped.atomic), + }, + } + } +} + +impl StdError for DetectDrop {} + +impl Display for DetectDrop { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "oh no!") + } +} + +impl Drop for DetectDrop { + fn drop(&mut self) { + let already_dropped = self.has_dropped.atomic.swap(true, SeqCst); + assert!(!already_dropped); + } +} diff --git a/third_party/rust/anyhow/tests/test_autotrait.rs b/third_party/rust/anyhow/tests/test_autotrait.rs new file mode 100644 index 0000000000..0c9326dad6 --- /dev/null +++ b/third_party/rust/anyhow/tests/test_autotrait.rs @@ -0,0 +1,13 @@ +use anyhow::Error; + +#[test] +fn test_send() { + fn assert_send<T: Send>() {} + assert_send::<Error>(); +} + +#[test] +fn test_sync() { + fn assert_sync<T: Sync>() {} + assert_sync::<Error>(); +} diff --git a/third_party/rust/anyhow/tests/test_backtrace.rs b/third_party/rust/anyhow/tests/test_backtrace.rs new file mode 100644 index 0000000000..ce385f5011 --- /dev/null +++ b/third_party/rust/anyhow/tests/test_backtrace.rs @@ -0,0 +1,13 @@ +#[rustversion::not(nightly)] +#[ignore] +#[test] +fn test_backtrace() {} + +#[rustversion::nightly] +#[test] +fn test_backtrace() { + use anyhow::anyhow; + + let error = anyhow!("oh no!"); + let _ = error.backtrace(); +} diff --git a/third_party/rust/anyhow/tests/test_boxed.rs b/third_party/rust/anyhow/tests/test_boxed.rs new file mode 100644 index 0000000000..38a568fb73 --- /dev/null +++ b/third_party/rust/anyhow/tests/test_boxed.rs @@ -0,0 +1,40 @@ +use anyhow::anyhow; +use std::error::Error as StdError; +use std::io; +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("outer")] +struct MyError { + source: io::Error, +} + +#[test] +fn test_boxed_str() { + let error = Box::<dyn StdError + Send + Sync>::from("oh no!"); + let error = anyhow!(error); + assert_eq!("oh no!", error.to_string()); + assert_eq!( + "oh no!", + error + .downcast_ref::<Box<dyn StdError + Send + Sync>>() + .unwrap() + .to_string() + ); +} + +#[test] +fn test_boxed_thiserror() { + let error = MyError { + source: io::Error::new(io::ErrorKind::Other, "oh no!"), + }; + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} + +#[test] +fn test_boxed_anyhow() { + let error = anyhow!("oh no!").context("it failed"); + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} diff --git a/third_party/rust/anyhow/tests/test_chain.rs b/third_party/rust/anyhow/tests/test_chain.rs new file mode 100644 index 0000000000..b1c5a3daaa --- /dev/null +++ b/third_party/rust/anyhow/tests/test_chain.rs @@ -0,0 +1,45 @@ +use anyhow::{anyhow, Error}; + +fn error() -> Error { + anyhow!(0).context(1).context(2).context(3) +} + +#[test] +fn test_iter() { + let e = error(); + let mut chain = e.chain(); + assert_eq!("3", chain.next().unwrap().to_string()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!("1", chain.next().unwrap().to_string()); + assert_eq!("0", chain.next().unwrap().to_string()); + assert!(chain.next().is_none()); + assert!(chain.next_back().is_none()); +} + +#[test] +fn test_rev() { + let e = error(); + let mut chain = e.chain().rev(); + assert_eq!("0", chain.next().unwrap().to_string()); + assert_eq!("1", chain.next().unwrap().to_string()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!("3", chain.next().unwrap().to_string()); + assert!(chain.next().is_none()); + assert!(chain.next_back().is_none()); +} + +#[test] +fn test_len() { + let e = error(); + let mut chain = e.chain(); + assert_eq!(4, chain.len()); + assert_eq!("3", chain.next().unwrap().to_string()); + assert_eq!(3, chain.len()); + assert_eq!("0", chain.next_back().unwrap().to_string()); + assert_eq!(2, chain.len()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!(1, chain.len()); + assert_eq!("1", chain.next_back().unwrap().to_string()); + assert_eq!(0, chain.len()); + assert!(chain.next().is_none()); +} diff --git a/third_party/rust/anyhow/tests/test_context.rs b/third_party/rust/anyhow/tests/test_context.rs new file mode 100644 index 0000000000..44c1c7036e --- /dev/null +++ b/third_party/rust/anyhow/tests/test_context.rs @@ -0,0 +1,159 @@ +mod drop; + +use crate::drop::{DetectDrop, Flag}; +use anyhow::{Context, Error, Result}; +use std::fmt::{self, Display}; +use thiserror::Error; + +// https://github.com/dtolnay/anyhow/issues/18 +#[test] +fn test_inference() -> Result<()> { + let x = "1"; + let y: u32 = x.parse().context("...")?; + assert_eq!(y, 1); + Ok(()) +} + +macro_rules! context_type { + ($name:ident) => { + #[derive(Debug)] + struct $name { + message: &'static str, + drop: DetectDrop, + } + + impl Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.message) + } + } + }; +} + +context_type!(HighLevel); +context_type!(MidLevel); + +#[derive(Error, Debug)] +#[error("{message}")] +struct LowLevel { + message: &'static str, + drop: DetectDrop, +} + +struct Dropped { + low: Flag, + mid: Flag, + high: Flag, +} + +impl Dropped { + fn none(&self) -> bool { + !self.low.get() && !self.mid.get() && !self.high.get() + } + + fn all(&self) -> bool { + self.low.get() && self.mid.get() && self.high.get() + } +} + +fn make_chain() -> (Error, Dropped) { + let dropped = Dropped { + low: Flag::new(), + mid: Flag::new(), + high: Flag::new(), + }; + + let low = LowLevel { + message: "no such file or directory", + drop: DetectDrop::new(&dropped.low), + }; + + // impl Context for Result<T, E> + let mid = Err::<(), LowLevel>(low) + .context(MidLevel { + message: "failed to load config", + drop: DetectDrop::new(&dropped.mid), + }) + .unwrap_err(); + + // impl Context for Result<T, Error> + let high = Err::<(), Error>(mid) + .context(HighLevel { + message: "failed to start server", + drop: DetectDrop::new(&dropped.high), + }) + .unwrap_err(); + + (high, dropped) +} + +#[test] +fn test_downcast_ref() { + let (err, dropped) = make_chain(); + + assert!(!err.is::<String>()); + assert!(err.downcast_ref::<String>().is_none()); + + assert!(err.is::<HighLevel>()); + let high = err.downcast_ref::<HighLevel>().unwrap(); + assert_eq!(high.to_string(), "failed to start server"); + + assert!(err.is::<MidLevel>()); + let mid = err.downcast_ref::<MidLevel>().unwrap(); + assert_eq!(mid.to_string(), "failed to load config"); + + assert!(err.is::<LowLevel>()); + let low = err.downcast_ref::<LowLevel>().unwrap(); + assert_eq!(low.to_string(), "no such file or directory"); + + assert!(dropped.none()); + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_high() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<HighLevel>().unwrap(); + assert!(!dropped.high.get()); + assert!(dropped.low.get() && dropped.mid.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_mid() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<MidLevel>().unwrap(); + assert!(!dropped.mid.get()); + assert!(dropped.low.get() && dropped.high.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_low() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<LowLevel>().unwrap(); + assert!(!dropped.low.get()); + assert!(dropped.mid.get() && dropped.high.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_unsuccessful_downcast() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<String>().unwrap_err(); + assert!(dropped.none()); + + drop(err); + assert!(dropped.all()); +} diff --git a/third_party/rust/anyhow/tests/test_convert.rs b/third_party/rust/anyhow/tests/test_convert.rs new file mode 100644 index 0000000000..72da020be6 --- /dev/null +++ b/third_party/rust/anyhow/tests/test_convert.rs @@ -0,0 +1,24 @@ +mod drop; + +use self::drop::{DetectDrop, Flag}; +use anyhow::{Error, Result}; +use std::error::Error as StdError; + +#[test] +fn test_convert() { + let has_dropped = Flag::new(); + let error = Error::new(DetectDrop::new(&has_dropped)); + let box_dyn = Box::<dyn StdError + Send + Sync>::from(error); + assert_eq!("oh no!", box_dyn.to_string()); + drop(box_dyn); + assert!(has_dropped.get()); +} + +#[test] +fn test_question_mark() -> Result<(), Box<dyn StdError>> { + fn f() -> Result<()> { + Ok(()) + } + f()?; + Ok(()) +} diff --git a/third_party/rust/anyhow/tests/test_downcast.rs b/third_party/rust/anyhow/tests/test_downcast.rs new file mode 100644 index 0000000000..c2c3e129ff --- /dev/null +++ b/third_party/rust/anyhow/tests/test_downcast.rs @@ -0,0 +1,106 @@ +mod common; +mod drop; + +use self::common::*; +use self::drop::{DetectDrop, Flag}; +use anyhow::Error; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +#[test] +fn test_downcast() { + assert_eq!( + "oh no!", + bail_literal().unwrap_err().downcast::<&str>().unwrap(), + ); + assert_eq!( + "oh no!", + bail_fmt().unwrap_err().downcast::<String>().unwrap(), + ); + assert_eq!( + "oh no!", + bail_error() + .unwrap_err() + .downcast::<io::Error>() + .unwrap() + .to_string(), + ); +} + +#[test] +fn test_downcast_ref() { + assert_eq!( + "oh no!", + *bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(), + ); + assert_eq!( + "oh no!", + bail_fmt().unwrap_err().downcast_ref::<String>().unwrap(), + ); + assert_eq!( + "oh no!", + bail_error() + .unwrap_err() + .downcast_ref::<io::Error>() + .unwrap() + .to_string(), + ); +} + +#[test] +fn test_downcast_mut() { + assert_eq!( + "oh no!", + *bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(), + ); + assert_eq!( + "oh no!", + bail_fmt().unwrap_err().downcast_mut::<String>().unwrap(), + ); + assert_eq!( + "oh no!", + bail_error() + .unwrap_err() + .downcast_mut::<io::Error>() + .unwrap() + .to_string(), + ); +} + +#[test] +fn test_drop() { + let has_dropped = Flag::new(); + let error = Error::new(DetectDrop::new(&has_dropped)); + drop(error.downcast::<DetectDrop>().unwrap()); + assert!(has_dropped.get()); +} + +#[test] +fn test_large_alignment() { + #[repr(align(64))] + #[derive(Debug)] + struct LargeAlignedError(&'static str); + + impl Display for LargeAlignedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.0) + } + } + + impl StdError for LargeAlignedError {} + + let error = Error::new(LargeAlignedError("oh no!")); + assert_eq!( + "oh no!", + error.downcast_ref::<LargeAlignedError>().unwrap().0 + ); +} + +#[test] +fn test_unsuccessful_downcast() { + let mut error = bail_error().unwrap_err(); + assert!(error.downcast_ref::<&str>().is_none()); + assert!(error.downcast_mut::<&str>().is_none()); + assert!(error.downcast::<&str>().is_err()); +} diff --git a/third_party/rust/anyhow/tests/test_fmt.rs b/third_party/rust/anyhow/tests/test_fmt.rs new file mode 100644 index 0000000000..cc4929197d --- /dev/null +++ b/third_party/rust/anyhow/tests/test_fmt.rs @@ -0,0 +1,94 @@ +use anyhow::{bail, Context, Result}; +use std::io; + +fn f() -> Result<()> { + bail!(io::Error::new(io::ErrorKind::PermissionDenied, "oh no!")); +} + +fn g() -> Result<()> { + f().context("f failed") +} + +fn h() -> Result<()> { + g().context("g failed") +} + +const EXPECTED_ALTDISPLAY_F: &str = "oh no!"; + +const EXPECTED_ALTDISPLAY_G: &str = "f failed: oh no!"; + +const EXPECTED_ALTDISPLAY_H: &str = "g failed: f failed: oh no!"; + +const EXPECTED_DEBUG_F: &str = "oh no!"; + +const EXPECTED_DEBUG_G: &str = "\ +f failed + +Caused by: + oh no!\ +"; + +const EXPECTED_DEBUG_H: &str = "\ +g failed + +Caused by: + 0: f failed + 1: oh no!\ +"; + +const EXPECTED_ALTDEBUG_F: &str = "\ +Custom { + kind: PermissionDenied, + error: \"oh no!\", +}\ +"; + +const EXPECTED_ALTDEBUG_G: &str = "\ +Error { + context: \"f failed\", + source: Custom { + kind: PermissionDenied, + error: \"oh no!\", + }, +}\ +"; + +const EXPECTED_ALTDEBUG_H: &str = "\ +Error { + context: \"g failed\", + source: Error { + context: \"f failed\", + source: Custom { + kind: PermissionDenied, + error: \"oh no!\", + }, + }, +}\ +"; + +#[test] +fn test_display() { + assert_eq!("g failed", h().unwrap_err().to_string()); +} + +#[test] +fn test_altdisplay() { + assert_eq!(EXPECTED_ALTDISPLAY_F, format!("{:#}", f().unwrap_err())); + assert_eq!(EXPECTED_ALTDISPLAY_G, format!("{:#}", g().unwrap_err())); + assert_eq!(EXPECTED_ALTDISPLAY_H, format!("{:#}", h().unwrap_err())); +} + +#[test] +#[cfg_attr(not(backtrace), ignore)] +fn test_debug() { + assert_eq!(EXPECTED_DEBUG_F, format!("{:?}", f().unwrap_err())); + assert_eq!(EXPECTED_DEBUG_G, format!("{:?}", g().unwrap_err())); + assert_eq!(EXPECTED_DEBUG_H, format!("{:?}", h().unwrap_err())); +} + +#[test] +fn test_altdebug() { + assert_eq!(EXPECTED_ALTDEBUG_F, format!("{:#?}", f().unwrap_err())); + assert_eq!(EXPECTED_ALTDEBUG_G, format!("{:#?}", g().unwrap_err())); + assert_eq!(EXPECTED_ALTDEBUG_H, format!("{:#?}", h().unwrap_err())); +} diff --git a/third_party/rust/anyhow/tests/test_macros.rs b/third_party/rust/anyhow/tests/test_macros.rs new file mode 100644 index 0000000000..c6888b6b63 --- /dev/null +++ b/third_party/rust/anyhow/tests/test_macros.rs @@ -0,0 +1,33 @@ +mod common; + +use self::common::*; +use anyhow::ensure; + +#[test] +fn test_messages() { + assert_eq!("oh no!", bail_literal().unwrap_err().to_string()); + assert_eq!("oh no!", bail_fmt().unwrap_err().to_string()); + assert_eq!("oh no!", bail_error().unwrap_err().to_string()); +} + +#[test] +fn test_ensure() { + let f = || { + ensure!(1 + 1 == 2, "This is correct"); + Ok(()) + }; + assert!(f().is_ok()); + + let v = 1; + let f = || { + ensure!(v + v == 2, "This is correct, v: {}", v); + Ok(()) + }; + assert!(f().is_ok()); + + let f = || { + ensure!(v + v == 1, "This is not correct, v: {}", v); + Ok(()) + }; + assert!(f().is_err()); +} diff --git a/third_party/rust/anyhow/tests/test_repr.rs b/third_party/rust/anyhow/tests/test_repr.rs new file mode 100644 index 0000000000..72f5002ae1 --- /dev/null +++ b/third_party/rust/anyhow/tests/test_repr.rs @@ -0,0 +1,29 @@ +mod drop; + +use self::drop::{DetectDrop, Flag}; +use anyhow::Error; +use std::marker::Unpin; +use std::mem; + +#[test] +fn test_error_size() { + assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>()); +} + +#[test] +fn test_null_pointer_optimization() { + assert_eq!(mem::size_of::<Result<(), Error>>(), mem::size_of::<usize>()); +} + +#[test] +fn test_autotraits() { + fn assert<E: Unpin + Send + Sync + 'static>() {} + assert::<Error>(); +} + +#[test] +fn test_drop() { + let has_dropped = Flag::new(); + drop(Error::new(DetectDrop::new(&has_dropped))); + assert!(has_dropped.get()); +} diff --git a/third_party/rust/anyhow/tests/test_source.rs b/third_party/rust/anyhow/tests/test_source.rs new file mode 100644 index 0000000000..018267d315 --- /dev/null +++ b/third_party/rust/anyhow/tests/test_source.rs @@ -0,0 +1,62 @@ +use anyhow::anyhow; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +#[derive(Debug)] +enum TestError { + Io(io::Error), +} + +impl Display for TestError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + TestError::Io(e) => Display::fmt(e, formatter), + } + } +} + +impl StdError for TestError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + TestError::Io(io) => Some(io), + } + } +} + +#[test] +fn test_literal_source() { + let error = anyhow!("oh no!"); + assert!(error.source().is_none()); +} + +#[test] +fn test_variable_source() { + let msg = "oh no!"; + let error = anyhow!(msg); + assert!(error.source().is_none()); + + let msg = msg.to_owned(); + let error = anyhow!(msg); + assert!(error.source().is_none()); +} + +#[test] +fn test_fmt_source() { + let error = anyhow!("{} {}!", "oh", "no"); + assert!(error.source().is_none()); +} + +#[test] +fn test_io_source() { + let io = io::Error::new(io::ErrorKind::Other, "oh no!"); + let error = anyhow!(TestError::Io(io)); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} + +#[test] +fn test_anyhow_from_anyhow() { + let error = anyhow!("oh no!").context("context"); + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} diff --git a/third_party/rust/anyhow/tests/ui/no-impl.rs b/third_party/rust/anyhow/tests/ui/no-impl.rs new file mode 100644 index 0000000000..d2e89afc1b --- /dev/null +++ b/third_party/rust/anyhow/tests/ui/no-impl.rs @@ -0,0 +1,8 @@ +use anyhow::anyhow; + +#[derive(Debug)] +struct Error; + +fn main() { + let _ = anyhow!(Error); +} diff --git a/third_party/rust/anyhow/tests/ui/no-impl.stderr b/third_party/rust/anyhow/tests/ui/no-impl.stderr new file mode 100644 index 0000000000..be957370d3 --- /dev/null +++ b/third_party/rust/anyhow/tests/ui/no-impl.stderr @@ -0,0 +1,21 @@ +error[E0599]: no method named `anyhow_kind` found for reference `&Error` in the current scope + --> $DIR/no-impl.rs:7:13 + | +4 | struct Error; + | ------------- + | | + | doesn't satisfy `Error: anyhow::kind::TraitKind` + | doesn't satisfy `Error: std::convert::Into<anyhow::Error>` + | doesn't satisfy `Error: std::fmt::Display` +... +7 | let _ = anyhow!(Error); + | ^^^^^^^^^^^^^^ method not found in `&Error` + | + = note: the method `anyhow_kind` exists but the following trait bounds were not satisfied: + `Error: std::convert::Into<anyhow::Error>` + which is required by `Error: anyhow::kind::TraitKind` + `Error: std::fmt::Display` + which is required by `&Error: anyhow::kind::AdhocKind` + `&Error: std::convert::Into<anyhow::Error>` + which is required by `&Error: anyhow::kind::TraitKind` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) |