manyhow-0.11.4/.cargo_vcs_info.json0000644000000001360000000000100126010ustar { "git": { "sha1": "47e52ac6043a81db7c90ff4021fd700dcf2e8cbf" }, "path_in_vcs": "" }manyhow-0.11.4/.github/workflows/docs.yml000064400000000000000000000021101046102023000164330ustar 00000000000000name: Git Docs on: push: branches: - main workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: pages cancel-in-progress: true jobs: docs: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/cache@v3 with: path: | ~/.cargo/ target/ key: docs-${{ hashFiles('Cargo.toml') }} restore-keys: | docs- - uses: hecrj/setup-rust-action@v1 with: rust-version: nightly - name: Generate Docs (reference docs.rs) run: | cargo rustdoc -- --cfg docsrs -Z unstable-options $(cargo metadata --format-version 1 | jq --raw-output '.packages | map("--extern-html-root-url=\(.name)=https://docs.rs/\(.name)/\(.version)") | join(" ")') - uses: actions/upload-pages-artifact@v1 with: path: 'target/doc' - id: deployment uses: actions/deploy-pages@v1 manyhow-0.11.4/.github/workflows/test.yaml000064400000000000000000000024121046102023000166300ustar 00000000000000name: Test on: push: null pull_request: null schedule: - cron: '0 12 * * *' jobs: test: strategy: fail-fast: false matrix: os: - ubuntu-latest - windows-latest - macos-latest rust: - stable - nightly include: - rust: nightly cargo_flags: -Z minimal-versions runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - uses: actions/cache@v3 with: path: | ~/.cargo/ target key: ${{ matrix.os }}-${{ matrix.rust }}-${{ hashFiles('**/Cargo.toml') }} restore-keys: | ${{ matrix.os }}-${{ matrix.rust }}- - uses: hecrj/setup-rust-action@v1 with: rust-version: ${{ matrix.rust }} - uses: bruxisma/setup-cargo-hack@v1 with: cargo-hack-version: "0.5" - name: Build run: cargo hack build --feature-powerset ${{ matrix.cargo_flags }} - name: Test run: cargo hack test --feature-powerset --all-targets --no-fail-fast --workspace - name: Doc Test run: cargo test --all-features --doc --no-fail-fast --workspace - name: Build Docs run: cargo doc --all-features --workspace manyhow-0.11.4/.gitignore000064400000000000000000000000221046102023000133530ustar 00000000000000target Cargo.lock manyhow-0.11.4/CHANGELOG.md000064400000000000000000000113561046102023000132100ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.11.4] - 2024-08-25 - Updated `proc-macro-utils` ## [0.11.3] - 2024-07-30 ### Added - implementations of `Add` and `AddAssign` to `manyhow::Error`/`manyhow::ErrorMessage` ## [0.11.2] - 2024-07-20 ### Fixed - adjusted spans for `#[manyhow]` on `use` items to make go-to-definition work better. ## [0.11.1] - 2024-03-16 ### Fixed - fix `item_as_dummy` for attribute macros ## [0.11.0] - 2024-01-27 ### Added - `{Function,Attribute,Derive}MacroHandler`, ### Removed - **Breaking Change** `MacroHandler` was replaced by dedicated traits for each macro type, this probably doesn't affect any usages ### Fixed - `syn::Result` could not be used as return type of macro handlers ## [0.10.4] - 2023-11-24 ### Changed - Allow parsing of types that do not implement `ToTokens` ## [0.10.3] - 2023-11-23 ### Added - `impl SpanRanged for Range` ## [0.10.2] - 2023-11-20 ### Added - `SpanRanged::span_joined` a function to return joined spans on nightly (to replace `SpanRanged::joined`). ## [0.10.1] - 2023-11-20 ### Added - `SpanRanged::joined` a function to return joined spans on nightly. ## [0.10.0] - 2023-11-13 ### Added - support `(impl ToTokens, impl ToTokens)` tuples for span range ## [0.9.0] - 2023-11-06 ### Added - support `impl Parse` inputs and `impl ToTokens` outputs. - added macro alternatives to the `function()`, `derive()` and `attribute()` functions to support `impl Parse/ToTokens`. ## [0.8.1] - 2023-09-17 ### Fixed - `ensure!(let...)` had compile error in its expansion. ## [0.8.0] - 2023-09-17 ### Changed - `ensure!` now supports `let ... = ...` as condition. ## [0.7.0] - 2023-09-17 ### Added - `ensure!` macro. ## [0.6.0] - 2023-09-09 ### Added - Support attribute on use statement of function. - Support `#[manyhow(proc_macro*)]` to specify proc-macro kind ## [0.5.1] - 2023-07-21 Something went wrong with previous release. ## [0.5.0] - 2023-07-20 ### Added - `Emitter::new()` and `Emitter::into_error()` to enable using the Emitter manually. - Implemented `Extend` for `Emitter` and `Error`. - Added `emit!` macro for adding errors to `Emitter`. - Added support for converting `darling::Error` to `manyhow::Error` (available via `darling` feature). ### Changed - **Breaking Change** replaced `Emitter::fail_if_dirty` with `Emitter::into_result`. ## [0.4.2] - 2023-05-15 ### Fixed - `ToTokens`' `SpanRange` conversion should work without `proc_macro`. ## [0.4.1] - 2023-05-14 ### Fixed - `manyhow_macros` version ## [0.4.0] - 2023-05-14 ### Added - `impl_fn` flag to create separate implementation function types. ## [0.3.0] - 2023-05-02 ### Added - `SpanRanged` implementation for `Option`. ## [0.2.0] - 2023-04-19 ### Changed - Moved `Error::join` to `JoinToTokensError` trait. ## [0.1.1] - 2023-04-16 Only documentation changes. ## [v0.1.0] **Initial Release** [unreleased]: https://github.com/ModProg/manyhow/compare/v0.11.4...HEAD [0.11.4]: https://github.com/ModProg/manyhow/compare/v0.11.3...v0.11.4 [0.11.3]: https://github.com/ModProg/manyhow/compare/v0.11.2...v0.11.3 [0.11.2]: https://github.com/ModProg/manyhow/compare/v0.11.1...v0.11.2 [0.11.1]: https://github.com/ModProg/manyhow/compare/v0.11.0...v0.11.1 [0.11.0]: https://github.com/ModProg/manyhow/compare/v0.10.4...v0.11.0 [0.10.4]: https://github.com/ModProg/manyhow/compare/v0.10.3...v0.10.4 [0.10.3]: https://github.com/ModProg/manyhow/compare/v0.10.2...v0.10.3 [0.10.2]: https://github.com/ModProg/manyhow/compare/v0.10.1...v0.10.2 [0.10.1]: https://github.com/ModProg/manyhow/compare/v0.10.0...v0.10.1 [0.10.0]: https://github.com/ModProg/manyhow/compare/v0.9.0...v0.10.0 [0.9.0]: https://github.com/ModProg/manyhow/compare/v0.8.1...v0.9.0 [0.8.1]: https://github.com/ModProg/manyhow/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/ModProg/manyhow/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/ModProg/manyhow/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/ModProg/manyhow/compare/v0.5.1...v0.6.0 [0.5.1]: https://github.com/ModProg/manyhow/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/ModProg/manyhow/compare/v0.4.2...v0.5.0 [0.4.2]: https://github.com/ModProg/manyhow/compare/v0.4.1...v0.4.2 [0.4.1]: https://github.com/ModProg/manyhow/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/ModProg/manyhow/compare/v0.3.0...v0.4.0 [0.3.0]: https://github.com/ModProg/manyhow/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/ModProg/manyhow/compare/v0.1.1...v0.2.0 [0.1.1]: https://github.com/ModProg/manyhow/compare/v0.1.0...v0.1.1 [v0.1.0]: https://github.com/ModProg/manyhow/tree/v0.1.0 manyhow-0.11.4/Cargo.toml0000644000000044660000000000100106110ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "manyhow" version = "0.11.4" build = false autobins = false autoexamples = false autotests = false autobenches = false description = "proc macro error handling à la anyhow x proc-macro-error" documentation = "https://docs.rs/manyhow" readme = "README.md" keywords = [ "proc-macro", "error", "error-handling", ] categories = [ "development-tools::procedural-macro-helpers", "rust-patterns", ] license = "MIT OR Apache-2.0" repository = "https://github.com/ModProg/manyhow" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [package.metadata.release] shared-version = true [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" replace = """ ## [{{version}}] - {{date}}""" search = '## \[Unreleased\]' [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" replace = """ [unreleased]: $1/{{tag_name}}...HEAD [{{version}}]: $1/$2...{{tag_name}}""" search = '\[unreleased\]: (.*)/(v.*)\.\.\.HEAD' [lib] name = "manyhow" path = "src/lib.rs" [dependencies.darling_core] version = "0.20.1" optional = true [dependencies.macros] version = "0.11.4" optional = true package = "manyhow-macros" [dependencies.proc-macro2] version = "1.0.60" [dependencies.quote] version = "1" [dependencies.syn1] version = "1" features = ["printing"] optional = true default-features = false package = "syn" [dependencies.syn2] version = "2" features = [ "printing", "parsing", ] optional = true default-features = false package = "syn" [dev-dependencies.proc-macro-utils] version = "0.8.0" [dev-dependencies.proc-macro2] version = "1" features = ["span-locations"] [dev-dependencies.syn2] version = "2" features = ["full"] package = "syn" [features] darling = ["darling_core"] default = [ "syn", "macros", ] syn = ["syn2"] manyhow-0.11.4/Cargo.toml.orig000064400000000000000000000034061046102023000142630ustar 00000000000000[package] name = "manyhow" version = "0.11.4" edition = "2021" categories = ["development-tools::procedural-macro-helpers", "rust-patterns"] description = "proc macro error handling à la anyhow x proc-macro-error" keywords = ["proc-macro", "error", "error-handling"] license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/ModProg/manyhow" documentation = "https://docs.rs/manyhow" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] members = [ "macros", ".", "examples/macro", "examples/no_macro" ] [dependencies] macros = { package = "manyhow-macros", path = "macros", version = "0.11.4", optional = true} proc-macro2 = "1.0.60" quote = "1" syn1 = { package = "syn", version = "1", default-features = false, optional = true, features = ["printing"] } syn2 = { package = "syn", version = "2", default-features = false, optional = true, features = ["printing", "parsing"] } darling_core = { version = "0.20.1", optional = true } [features] default = ["syn", "macros"] syn = ["syn2"] darling = ["darling_core"] [dev-dependencies] proc-macro-utils = "0.8.0" proc-macro2 = { version = "1", features = ["span-locations"] } syn2 = {package = "syn", version = "2", features = ["full"]} [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [package.metadata.release] shared-version = true [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '## \[Unreleased\]' replace = """ ## [{{version}}] - {{date}}\ """ [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '\[unreleased\]: (.*)/(v.*)\.\.\.HEAD' replace = """ [unreleased]: $1/{{tag_name}}...HEAD [{{version}}]: $1/$2...{{tag_name}}\ """ manyhow-0.11.4/LICENSE-APACHE000064400000000000000000000261351046102023000133240ustar 00000000000000 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 peretual, worldwide, non-exclusive, no-cpharge, 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 it s 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. manyhow-0.11.4/LICENSE-MIT000064400000000000000000000020631046102023000130260ustar 00000000000000MIT License Copyright (c) 2023 Roland Fredenhagen 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. manyhow-0.11.4/README.md000064400000000000000000000113241046102023000126510ustar 00000000000000# manyhow ## anyhow for proc macros [![CI Status](https://github.com/ModProg/manyhow/actions/workflows/test.yaml/badge.svg)](https://github.com/ModProg/manyhow/actions/workflows/test.yaml) [![Crates.io](https://img.shields.io/crates/v/manyhow)](https://crates.io/crates/manyhow) [![Docs.rs](https://img.shields.io/crates/v/template?color=informational&label=docs.rs)](https://docs.rs/manyhow) [![Documentation for `main`](https://img.shields.io/badge/docs-main-informational)](https://modprog.github.io/manyhow/manyhow/) Proc **m**acro **anyhow**, a combination of ideas from [`anyhow`](https://docs.rs/anyhow) and [`proc-macro-error`](https://docs.rs/proc-macro-error) to improve proc macro development, especially focused on the error handling. ## Motivation Error handling in proc-macros is unideal, as the top level functions of proc macros can only return `TokenStreams` both in success and failure case. This means that I often write code like this, moving the actual implementation in a separate function to be able to use the ergonomic rust error handling with e.g., `?`. ```rust use proc_macro2::TokenStream as TokenStream2; #[proc_macro] pub fn my_macro(input: TokenStream) -> TokenStream { match actual_implementation(input.into()) { Ok(output) => output, Err(error) => error.into_compile_error(), } .into() } fn actual_implementation(input: TokenStream2) -> syn::Result { // .. } ``` ## Using the `#[manyhow]` macro To activate the error handling, just add `#[manyhow]` above any proc macro implementation, reducing the above example to: ```rust use manyhow::manyhow; use proc_macro2::TokenStream as TokenStream2; #[manyhow] #[proc_macro] fn my_macro(input: TokenStream2) -> syn::Result { // .. } ``` See [Without macros](#without-macros) to see what this expands to under the hood. A proc macro function marked as `#[manyhow]` can take and return any `TokenStream` and can also return `Result` where `E` implements `ToTokensError`. As additional parameters a [dummy](#dummy-mut-tokenstream) and/or [emitter](#emitter-mut-emitter) can be specified. The `manyhow` attribute takes one optional flag when used for `proc_macro` and `proc_macro_attribute`. `#[manyhow(input_as_dummy)]` will take the input of a function like `proc_macro` to initialize the [dummy `&mut TokenStream`](#dummy-mut-tokenstream) while `#[manyhow(item_as_dummy)]` on `proc_macro_attribute` will initialize the dummy with the annotated item. ## Without macros `manyhow` can be used without proc macros, and they can be disabled by adding `manyhow` with `default-features=false`. The usage is more or less the same, though with some added boilerplate from needing to invoke one of `function`, `attribute` or `derive` directly. While the examples use closures, functions can be passed in as well. The above example would then change to: ```rust use proc_macro2::TokenStream as TokenStream2; #[proc_macro] pub fn my_macro(input: TokenStream) -> TokenStream { manyhow::function( input, false, |input: TokenStream2| -> syn::Result { // .. }, ) } ``` [`Emitter`](#emitter-mut-emitter) and [dummy `TokenStream`](#dummy-mut-tokenstream) can also be used. `function` and `attribute` take an additional boolean parameter controlling whether the input/item will be used as initial dummy. ## `emitter: &mut Emitter` `MacroHandler`s (the trait defining what closures/functions can be used with `manyhow`) can take a mutable reference to an `Emitter`. This allows to collect errors, but not fail immediately. `Emitter::into_result` can be used to return if an `Emitter` contains any values. ```rust use manyhow::{manyhow, Emitter, ErrorMessage}; use proc_macro2::TokenStream as TokenStream2; #[manyhow] #[proc_macro] fn my_macro(input: TokenStream2, emitter: &mut Emitter) -> manyhow::Result { // .. emitter.emit(ErrorMessage::call_site("A fun error!")); emitter.into_result()?; // .. } ``` ## `dummy: &mut TokenStream` `MacroHandler`s also take a mutable reference to a `TokenStream`, to enable emitting some dummy code to be used in case the macro errors. This allows either appending tokens e.g., with `ToTokens::to_tokens` or directly setting the dummy code e.g., `*dummy = quote!{some tokens}`. manyhow-0.11.4/rustfmt.toml000064400000000000000000000006461046102023000140000ustar 00000000000000condense_wildcard_suffixes = true format_code_in_doc_comments = true format_macro_matchers = true format_strings = true hex_literal_case = "Upper" imports_granularity = "Module" normalize_comments = true normalize_doc_attributes = true overflow_delimited_expr = true reorder_impl_items = true group_imports = "StdExternalCrate" unstable_features = true use_field_init_shorthand = true version = "Two" wrap_comments = true manyhow-0.11.4/src/error.rs000064400000000000000000000355761046102023000136770ustar 00000000000000#![allow(clippy::missing_errors_doc)] use std::convert::Infallible; use std::fmt::{Debug, Display}; use std::mem; use std::ops::{Add, AddAssign, Range}; #[cfg(feature = "darling")] use darling_core::Error as DarlingError; use proc_macro2::{Span, TokenStream}; use quote::{quote_spanned, ToTokens}; #[cfg(feature = "syn1")] use syn1::Error as Syn1Error; #[cfg(feature = "syn2")] use syn2::Error as Syn2Error; #[cfg(doc)] use crate::MacroOutput; use crate::{to_tokens_span_range, SpanRanged}; /// An alias for [`Result`](std::result::Result) suited for use with this crate pub type Result = std::result::Result; /// Error that does not expand to any [`compile_error!`] and therefor does not /// cause compilation to fail. #[derive(Debug)] pub struct SilentError; /// This crates Error type #[derive(Debug)] #[must_use] pub struct Error(Vec>); #[cfg(feature = "syn1")] impl From for Error { fn from(error: Syn1Error) -> Self { Self::from(error) } } #[cfg(feature = "syn2")] impl From for Error { fn from(error: Syn2Error) -> Self { Self::from(error) } } #[cfg(feature = "darling")] impl From for Error { fn from(error: DarlingError) -> Self { Self::from(error) } } impl From for Error { fn from(error: ErrorMessage) -> Self { Self::from(error) } } impl From for Error { fn from(_: SilentError) -> Self { Self(Vec::new()) } } impl Add for Error { type Output = Error; fn add(self, rhs: T) -> Self::Output { self.join(rhs) } } impl AddAssign for Error { fn add_assign(&mut self, rhs: T) { self.push(rhs); } } impl Error { /// Mimics [`From for Error`](From) implementation to /// not conflict std's `From for T` pub fn from(error: impl ToTokensError + 'static) -> Self { Self(vec![Box::new(error)]) } /// Pushes an additional `Error` /// /// Alternatively errors can also be "added": /// /// ``` /// use manyhow::{Error, error_message}; /// let mut error = Error::from(error_message!("Hello Rust!")); /// error += error_message!("Hello 🦀!"); /// # use manyhow::ToTokensError; /// # proc_macro_utils::assert_tokens!(error.into_token_stream(), /// # { ::core::compile_error!{"Hello Rust!"} ::core::compile_error!{"Hello 🦀!"} }); /// ``` pub fn push(&mut self, error: impl ToTokensError + 'static) { self.0.push(Box::new(error)); } } impl Extend for Error { fn extend>(&mut self, iter: T) { self.0.extend( iter.into_iter() .map(|i| Box::new(i) as Box), ); } } /// A single error message /// /// Can take additional attachments like [`help`](Self::help) or /// [`note`](Self::note). /// /// Implements `ToTokensError` and can therefore be used with /// [`MacroOutput`]s. #[derive(Debug)] #[must_use] pub struct ErrorMessage { span: Range, msg: String, attachments: Vec<(&'static str, String)>, } impl Display for ErrorMessage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.msg.trim_end())?; if !self.attachments.is_empty() { write!(f, "\n\n")?; } for (label, attachment) in &self.attachments { let mut attachment = attachment.lines(); writeln!( f, " = {label}: {}", attachment.next().expect("should return at least one line") )?; for line in attachment { // `labels` should always be one char per cell writeln!(f, " {1:2$} {}", line, "", label.len())?; } } Ok(()) } } impl ToTokensError for ErrorMessage { fn to_tokens(&self, tokens: &mut TokenStream) { let msg = self.to_string(); let msg = quote_spanned!(self.span.end => {#msg}); quote_spanned! {self.span.start => ::core::compile_error! #msg } .to_tokens(tokens); } } #[cfg(feature = "syn1")] impl From for Syn1Error { fn from(value: ErrorMessage) -> Self { Self::new_spanned(value.to_token_stream(), value) } } #[cfg(feature = "syn2")] impl From for Syn2Error { fn from(value: ErrorMessage) -> Self { Self::new_spanned(value.to_token_stream(), value) } } impl Add for ErrorMessage { type Output = Error; fn add(self, rhs: T) -> Self::Output { self.join(rhs) } } impl ErrorMessage { /// Creates a new error message at the specified span /// /// This function takes a [`SpanRanged`] meaning you can also pass a /// [`Range`](Range)[``](Span) (i.e. `first..last`) for better error /// messages on multi token values, for details see /// [SpanRanged#motivation](SpanRanged#motivation) /// /// If your type implements [`ToTokens`] use [`ErrorMessage::spanned`] /// instead. pub fn new(span: impl SpanRanged, msg: impl Display) -> Self { Self { span: span.span_range(), msg: msg.to_string(), attachments: Vec::new(), } } /// Creates an error message pointing to the complete token stream `tokens` /// expands to pub fn spanned(tokens: impl ToTokens, msg: impl Display) -> Self { Self { span: to_tokens_span_range(tokens), msg: msg.to_string(), attachments: Vec::new(), } } /// Creates a new error message at [`Span::call_site`] prefer /// [`ErrorMessage::new`] or [`ErrorMessage::spanned`] with the correct span /// for a more helpful output. pub fn call_site(msg: impl Display) -> Self { Self::new(Span::call_site(), msg) } /// Attaches an additional message to `self` reusing the same /// span, and the specified `label`. pub fn attachment(mut self, label: &'static str, msg: impl Display) -> Self { self.attachments.push((label, msg.to_string())); self } /// Attaches a new `error` message to `self` reusing the same span pub fn error(self, msg: impl Display) -> Self { self.attachment("error", msg) } /// Attaches a new `warning` message to `self` reusing the same span pub fn warning(self, msg: impl Display) -> Self { self.attachment("warning", msg) } /// Attaches a new `note` message to `self` reusing the same span pub fn note(self, msg: impl Display) -> Self { self.attachment("note", msg) } /// Attaches a new `help` message to `self` reusing the same span pub fn help(self, msg: impl Display) -> Self { self.attachment("help", msg) } } /// Exposes [`ErrorMessage::attachment`] as a trait to allow /// [`ResultExt::attachment`]. pub trait Attachment: Sized { /// Attaches an additional message to `self` reusing the same /// span, and the specified `label`. #[must_use] fn attachment(self, label: &'static str, msg: impl Display) -> Self; } impl Attachment for ErrorMessage { fn attachment(mut self, label: &'static str, msg: impl Display) -> Self { self.attachments.push((label, msg.to_string())); self } } /// Allows emitting errors without returning. #[derive(Default, Debug)] pub struct Emitter(Vec>); impl AddAssign for Emitter { fn add_assign(&mut self, rhs: T) { self.emit(rhs); } } impl Emitter { /// Creates an `Emitter`, this can be used to collect errors than can later /// be converted with [`Emitter::into_result()`]. #[must_use] pub fn new() -> Self { Emitter(Vec::new()) } pub(crate) fn to_tokens(&self, tokens: &mut TokenStream) { for error in &self.0 { error.to_tokens(tokens); } } /// Emits an error /// /// Alternatively errors can also be "added": /// /// ``` /// let mut emitter = manyhow::Emitter::new(); /// emitter += manyhow::error_message!("Hello World!"); /// # use manyhow::ToTokensError; /// # proc_macro_utils::assert_tokens!(emitter.into_result().unwrap_err().into_token_stream(), { ::core::compile_error!{"Hello World!"} }); /// ``` pub fn emit(&mut self, error: impl ToTokensError + 'static) { self.0.push(Box::new(error)); } /// Checks if any errors were emitted #[must_use] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Removes all emitted errors pub fn clear(&mut self) { self.0.clear(); } /// Returns emitted errors if not [`Self::is_empty`]. /// /// If no errors where emitted, returns `Ok(())`. /// /// *Note:* This clears the emitter to avoid returning duplicate errors. pub fn into_result(&mut self) -> Result<(), Error> { if self.is_empty() { Ok(()) } else { Err(Error(mem::take(&mut self.0))) } } } impl Extend for Emitter { fn extend>(&mut self, iter: T) { self.0.extend( iter.into_iter() .map(|i| Box::new(i) as Box), ); } } /// Error that can be converted to a [`TokenStream`] required to be used with /// [`MacroOutput`] /// /// This trait is equivalent to [`ToTokens`]. pub trait ToTokensError: Debug { /// Equivalent to [`ToTokens::to_tokens`] fn to_tokens(&self, tokens: &mut TokenStream); /// Equivalent to [`ToTokens::to_token_stream`] fn to_token_stream(&self) -> TokenStream { let mut tokens = TokenStream::new(); self.to_tokens(&mut tokens); tokens } /// Equivalent to [`ToTokens::into_token_stream`] fn into_token_stream(self) -> TokenStream where Self: Sized, { self.to_token_stream() } } /// Allows to call `.join(..)` on any `impl ToTokensError` pub trait JoinToTokensError { /// Joins two `Error`s /// /// ``` /// use manyhow::error_message; /// # use crate::manyhow::JoinToTokensError; /// /// error_message!("test").join(error_message!("another")); /// ``` /// /// Some errors like [`manyhow::Error`] and [`manyhow::ErrorMessage`] can /// also be "added": /// /// ``` /// # use manyhow::ToTokensError; /// use manyhow::{error_message, Error}; /// # proc_macro_utils::assert_tokens!(( /// error_message!("Hello Rust!") + error_message!("Hello 🦀!") /// # ).into_token_stream(), { ::core::compile_error!{"Hello Rust!"} ::core::compile_error!{"Hello 🦀!"} }); /// # proc_macro_utils::assert_tokens!(( /// Error::from(error_message!("Hello Rust!")) + error_message!("Hello 🦀!") /// # ).into_token_stream(), { ::core::compile_error!{"Hello Rust!"} ::core::compile_error!{"Hello 🦀!"} }); /// ``` fn join(self, error: impl ToTokensError + 'static) -> Error; } impl JoinToTokensError for T { fn join(self, error: impl ToTokensError + 'static) -> Error { let mut this = Error::from(self); this.push(error); this } } impl ToTokensError for Infallible { fn to_tokens(&self, _: &mut TokenStream) { unreachable!() } } #[cfg(feature = "syn1")] impl ToTokensError for Syn1Error { fn to_tokens(&self, tokens: &mut TokenStream) { self.to_compile_error().to_tokens(tokens); } } #[cfg(feature = "syn")] impl ToTokensError for Syn2Error { fn to_tokens(&self, tokens: &mut TokenStream) { self.to_compile_error().to_tokens(tokens); } } #[cfg(feature = "darling")] impl ToTokensError for DarlingError { fn to_tokens(&self, tokens: &mut TokenStream) { self.clone().write_errors().to_tokens(tokens); } } impl ToTokensError for Error { fn to_tokens(&self, tokens: &mut TokenStream) { for error in &self.0 { error.to_tokens(tokens); } } } impl ToTokensError for SilentError { fn to_tokens(&self, _: &mut TokenStream) {} } /// Some utilities on [`Result`](ToTokensError) pub trait ResultExt: Sized { /// If self is error, attaches another error fn context(self, error: impl ToTokensError + 'static) -> Result { self.context_with(|| error) } /// If self is error, attaches another error, closure is only executed if /// the `Result` is `Err` fn context_with( self, error: impl FnOnce() -> C, ) -> Result; /// If self is error, extend error message /// /// Only works if `E` implements [`Attachment`] which is the case for /// [`ErrorMessage`] #[must_use] fn attachment(self, label: &'static str, msg: impl Display) -> Self where E: Attachment; /// Attaches a new `error` message to `self` reusing the same span #[must_use] fn error(self, msg: impl Display) -> Self where E: Attachment, { self.attachment("error", msg) } /// Attaches a new `warning` message to `self` reusing the same span #[must_use] fn warning(self, msg: impl Display) -> Self where E: Attachment, { self.attachment("warning", msg) } /// Attaches a new `note` message to `self` reusing the same span #[must_use] fn note(self, msg: impl Display) -> Self where E: Attachment, { self.attachment("note", msg) } /// Attaches a new `help` message to `self` reusing the same span #[must_use] fn help(self, msg: impl Display) -> Self where E: Attachment, { self.attachment("help", msg) } } impl ResultExt for Result { fn context_with( self, error: impl FnOnce() -> C, ) -> Result { self.map_err(|e| { let mut e = Error::from(e); e.push(error()); e }) } fn attachment(self, label: &'static str, msg: impl Display) -> Result where E: Attachment, { self.map_err(|e| e.attachment(label, msg)) } } #[cfg(test)] mod test { use proc_macro_utils::assert_tokens; use super::*; #[test] fn error_message() { let error_message = ErrorMessage::new(Span::call_site(), "test message") .help("try to call your dog") .note("you could use the banana phone") .warning("be careful") .error("you cannot reach them"); assert_tokens! {error_message.to_token_stream(), { ::core::compile_error! { "test message\n\n = help: try to call your dog\n = note: you could use the banana phone\n = warning: be careful\n = error: you cannot reach them\n" } }} } } manyhow-0.11.4/src/lib.rs000064400000000000000000000704441046102023000133050ustar 00000000000000#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![warn(clippy::pedantic, missing_docs)] #![allow(clippy::module_name_repetitions)] //! Proc **m**acro **anyhow**, a combination of ideas from //! [`anyhow`](docs.rs/anyhow) and //! [`proc-macro-error`](docs.rs/proc-macro-error) to improve proc macro //! development, especially focused on the error handling. //! //! # Motivation //! Error handling in proc-macros is unideal, as the top level functions of //! proc-macros can only return `TokenStreams` both in success and failure case. //! This means that I often write code like this, moving the actual //! implementation in a separate function to be able to use the ergonomic rust //! error handling with e.g., `?`. //! ``` //! # use proc_macro2::TokenStream; //! # use quote::quote; //! # use syn2 as syn; //! use proc_macro2::TokenStream as TokenStream2; //! //! # let _ = quote!{ //! #[proc_macro] //! # }; //! pub fn my_macro(input: TokenStream) -> TokenStream { //! match actual_implementation(input.into()) { //! Ok(output) => output, //! Err(error) => error.into_compile_error(), //! } //! .into() //! } //! //! fn actual_implementation(input: TokenStream2) -> syn::Result { //! // .. //! # Ok(quote!()) //! } //! ``` //! //! # Using the `#[manyhow]` macro //! To activate the error handling, just add [`#[manyhow]`](manyhow) above any //! proc-macro implementation, reducing the above example to: //! //! ``` //! # use quote::quote; //! # use syn2 as syn; //! use manyhow::manyhow; //! use proc_macro2::TokenStream as TokenStream2; //! //! # let _ = quote!{ //! #[manyhow] //! #[proc_macro] //! # }; //! // You can also merge the two attributes: #[manyhow(proc_macro)] //! fn my_macro(input: TokenStream2) -> syn::Result { //! // .. //! # Ok(quote!()) //! } //! //! // On top of the TokenStreams any type that implements [`Parse`] is supported //! # let _ = quote!{ //! #[manyhow(proc_macro_derive(MyMacro))] //! #[proc_macro] //! # }; //! // The output can also be anything that implements `quote::ToTokens` //! fn my_derive_macro(input: syn::DeriveInput) -> manyhow::Result { //! // .. //! # manyhow::bail!("hello") //! } //! ``` //! //! See [Without macros](#without-macros) to see what this expands to under the //! hood. //! //! You can also use the `#[manyhow]` attributes on a use statement, useful when //! moving your proc-macro implementations in separate modules. //! //! ``` //! # use quote::quote; //! use manyhow::manyhow; //! //! mod module { //! # use quote::quote; //! # use syn2 as syn; //! use proc_macro2::TokenStream as TokenStream2; //! //! pub fn my_macro(input: TokenStream2) -> syn::Result { //! // .. //! # Ok(quote!()) //! } //! } //! //! # let _ = quote!{ //! #[manyhow] //! #[proc_macro] //! # }; //! // You can also merge the two attributes: #[manyhow(proc_macro)] //! pub use module::my_macro; //! ``` //! //! A proc macro function marked as `#[manyhow]` can take and return any //! [`TokenStream`](AnyTokenStream), and can also return `Result` where `E` implements [`ToTokensError`]. As additional parameters a //! [dummy](#dummy-mut-tokenstream) and/or [emitter](#emitter-mut-emitter) can //! be specified. //! //! The `manyhow` attribute takes optional flags to configure its behavior. //! //! When used for `proc_macro` and `proc_macro_attribute`, //! `#[manyhow(input_as_dummy, ...)]` will take the input of a function like //! `proc_macro` to initialize the [dummy `&mut TokenStream`](# //! dummy-mut-tokenstream) while `#[manyhow(item_as_dummy, ...)]` on //! `proc_macro_attribute` will initialize the dummy with the annotated item. //! //! You can merge the `#[proc_macro*]` attribute inside the manyhow flags e.g., //! `#[manyhow(proc_macro)]` or `#[manyhow(proc_macro_derive(SomeTrait, ...))]`. //! //! The `#[manyhow(impl_fn, ...)]` flag will put the actual macro implementation //! in a separate function. Making it available for e.g., unit testing with //! [`proc_macro_utils::assert_expansion!`](https://docs.rs/proc-macro-utils/latest/proc_macro_utils/macro.assert_expansion.html). //! //! ```ignore //! #[manyhow(impl_fn)] //! #[proc_macro] //! pub fn actual_macro(input: TokenStream2) -> TokenStream2 { //! // ... //! } //! // would roughly expand to //! #[proc_macro] //! pub fn actual_macro(input: TokenStream) -> TokenStream { //! actual_macro_impl(input.into()).into() //! } //! fn actual_macro_impl(input: TokenStream2) -> TokenStream2 { //! // ... //! } //! ``` //! //! # Without macros //! `manyhow` can be used without proc macros, and they can be disabled by //! adding `manyhow` with `default-features=false`. //! //! The usage is more or less the same, though with some added boilerplate from //! needing to invoke one of [`function()`] ([`function!`]), [`attribute()`] //! ([`attribute!`]) or [`derive()`] ([`derive!`]) directly. For each version //! there exists a function and a `macro_rules` macro, while the function only //! supports [`proc_macro::TokenStream`] and [`proc_macro2::TokenStream`], the //! macro versions also support any type that implements [`Parse`] //! and [`ToTokens`] respectively. //! //! While the examples use closures, functions can be passed in as well. The //! above example would then change to: //! ``` //! # use proc_macro2::TokenStream; //! # use quote::quote; //! # use syn2 as syn; //! use proc_macro2::TokenStream as TokenStream2; //! //! # let _ = quote!{ //! #[proc_macro] //! # }; //! pub fn my_macro(input: TokenStream) -> TokenStream { //! # let tmp = input.clone(); //! # let output: TokenStream = //! manyhow::function( //! input, //! false, //! |input: TokenStream2| -> syn::Result { //! // .. //! # Ok(quote!()) //! }, //! ) //! # ; //! # let input = tmp; //! // Or //! manyhow::function!( //! input, //! |input: syn::DeriveInput| -> manyhow::Result { //! // .. //! # manyhow::bail!("error") //! }, //! ) //! } //! ``` //! [`Emitter`](#emitter-mut-emitter) and [dummy //! `TokenStream`](#dummy-mut-tokenstream) can also be used. [`function()`] //! ([`function!`]) and [`attribute()`] ([`attribute!`]) take an additional //! boolean parameter controlling whether the input/item will be used as initial //! dummy. //! //! # `emitter: &mut Emitter` //! [`*MacroHandler`](FunctionMacroHandler)s (the traits defining what //! closures/functions can be used with `manyhow`) can take a mutable reference //! to an [`Emitter`]. This allows collecting errors, but not fail immediately. //! //! [`Emitter::into_result`] can be used to return if an [`Emitter`] contains //! any values. //! //! ``` //! # use quote::quote; //! # use syn2 as syn; //! use manyhow::{manyhow, Emitter, ErrorMessage}; //! use proc_macro2::TokenStream as TokenStream2; //! //! # let _ = quote!{ //! #[manyhow] //! #[proc_macro] //! # }; //! fn my_macro(input: TokenStream2, emitter: &mut Emitter) -> manyhow::Result { //! // .. //! emitter.emit(ErrorMessage::call_site("A fun error!")); //! emitter.into_result()?; //! // .. //! # Ok(quote!()) //! } //! ``` //! //! # `dummy: &mut TokenStream` //! [`*MacroHandler`](FunctionMacroHandler)s can also take a mutable reference //! to a `TokenStream`, to enable emitting some dummy code to be used in case //! the macro errors. //! //! This allows either appending tokens e.g., with [`ToTokens::to_tokens`] or //! directly setting the dummy code e.g., `*dummy = quote!{some tokens}`. //! //! # Crate features //! //! - `macros` **default** Enables [`#[manyhow]`](macros::manyhow) attribute //! macro. //! - `syn`/`syn2` **default** Enables errors for [`syn` 2.x](https://docs.rs/syn/latest/syn/). //! - `syn1` Enables errors for [`syn` 1.x](https://docs.rs/syn/1.0.109/syn/index.html). //! - `darling` Enables errors for [`darling`](https://docs.rs/darling/latest/index.html). #[cfg(feature = "macros")] pub use macros::manyhow; use proc_macro2::TokenStream; #[cfg(doc)] use {quote::ToTokens, syn2::parse::Parse}; extern crate proc_macro; #[macro_use] mod span_ranged; pub use span_ranged::{to_tokens_span_range, SpanRanged}; #[macro_use] mod macro_rules; mod error; pub use error::*; mod parse_to_tokens; #[doc(hidden)] pub mod __private { pub use std::prelude::rust_2021::*; use proc_macro2::TokenStream; pub use quote; pub use crate::span_ranged::*; pub type Dummy = Option; pub use crate::parse_to_tokens::*; } /// Marker trait for [`proc_macro::TokenStream`] and /// [`proc_macro2::TokenStream`] pub trait AnyTokenStream: Clone + From + Into + Default {} impl AnyTokenStream for TokenStream {} impl AnyTokenStream for proc_macro::TokenStream {} #[macro_export] #[doc(hidden)] macro_rules! __macro_handler { ($name:ident; $($(#attr=$attr:tt)? $n:ident: $input:expr),+; $impl:expr$(; dummy:$dummy:expr)?) => { $crate::__macro_handler! {! $name; $($(#attr=$attr)? $n: $input.clone()),+; $impl $(; $crate::__private::Some($dummy))?} }; ($name:ident; $($(#attr=$attr:tt)? $n:ident: $input:expr),+; $impl:expr; dummy) => { $crate::__macro_handler! {! $name; $($(#attr=$attr)? $n: $input),+; $impl; $crate::__private::Dummy::None} }; (! $name:ident; $($(#attr=$attr:tt)? $n:ident: $input:expr),+; $impl:expr $(; $dummy:expr)?) => {{ use $crate::__private::{ManyhowParse, ManyhowToTokens, ManyhowTry}; let implementation = $impl; $(let $n = &$crate::__private::WhatType::new();)+ if false { _ = $crate::__private::$name($($n.identify(),)+ $($dummy,)? implementation); unreachable!(); } else { match $crate::__private::$name($( {#[allow(unused)] let attr = false; $(let attr = $attr;)? $n.manyhow_parse($input, attr)}, )+ $($dummy,)? implementation) { Err(tokens) => tokens.into(), Ok((output, mut tokens, mut dummy)) => { match (&$crate::__private::WhatType::from(&output)).manyhow_try(output) { Err(error) => { dummy.extend(tokens); tokens = dummy; (&$crate::__private::WhatType::from(&error)).manyhow_to_tokens(error, &mut tokens); }, Ok(output) => (&$crate::__private::WhatType::from(&output)).manyhow_to_tokens(output, &mut tokens), }; tokens.into() } } } }}; } /// Handles [`proc_macro_attribute`](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros) /// implementation /// /// Takes any `TokenStream` for `input` and `item` and returns any /// `TokenStream`. If `item_as_dummy = true` the item input will be used as /// default dummy code on error. `body` takes a [`AttributeMacroHandler`] with /// two `TokenStream` parameters. And an optional [`&mut Emitter`](Emitter) and /// a `&mut TokenStream` for storing a dummy output. /// /// ``` /// # use proc_macro_utils::assert_tokens; /// # use quote::{quote, ToTokens}; /// use manyhow::{attribute, Emitter, Result}; /// use proc_macro2::TokenStream; /// # let input = quote!(); /// # let item = quote!(); /// # let output: TokenStream = /// attribute( /// input, /// item, /// false, /// |input: TokenStream, /// item: TokenStream, /// dummy: &mut TokenStream, /// emitter: &mut Emitter| /// -> Result { /// // .. /// # Ok(quote!()) /// }, /// ); /// ``` /// /// *Note:* When `item_as_dummy = true` the `dummy: &mut TokenStream` will be /// initialized with `item`. To override assign a new `TokenStream`: /// ``` /// # use proc_macro_utils::assert_tokens; /// use manyhow::{attribute, Result, SilentError}; /// use proc_macro2::TokenStream; /// use quote::{quote, ToTokens}; /// # let input = quote!(input); /// let item = quote!( /// struct Struct; /// ); /// let output: TokenStream = attribute( /// input, /// item, /// true, /// |input: TokenStream, /// item: TokenStream, /// dummy: &mut TokenStream| /// -> Result { /// assert_tokens!(dummy.to_token_stream(), { /// struct Struct; /// }); /// *dummy = quote! { /// struct Struct(HelloWorld); /// }; /// // .. /// Err(SilentError) /// }, /// ); /// /// assert_tokens! {output, {struct Struct(HelloWorld);}}; /// ``` pub fn attribute< Input: AnyTokenStream, Item: AnyTokenStream, Dummy: AnyTokenStream, Output: MacroOutput, Return: AnyTokenStream, Function, >( input: impl AnyTokenStream, item: impl AnyTokenStream, item_as_dummy: bool, body: impl AttributeMacroHandler< Function, Item = Item, Input = Input, Dummy = Dummy, Output = Output, >, ) -> Return { #[allow(unused_mut)] let mut tokens = Dummy::default(); let mut tokens = if item_as_dummy { item.clone().into().into() } else { tokens }; let mut emitter = Emitter::new(); let output = body.call( input.into().into(), item.into().into(), &mut tokens, &mut emitter, ); let mut tokens = tokens.into(); let mut tokens = match output.convert() { Ok(tokens) => tokens, Err(error) => { error.to_tokens(&mut tokens); tokens } }; emitter.to_tokens(&mut tokens); tokens.into() } /// Handles [`proc_macro_attribute`](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros) /// implementation /// /// Takes any `TokenStream` for `input` and `item` and its return value. If /// `#[as_dummy]` is specified on item, it will be used as default /// dummy code on error. `body` takes a [`AttributeMacroHandler`] with two /// `TokenStream`s or types implementing [`Parse`] parameters and returning a /// `TokenStream` or type implementing [`ToTokens`]. And an optional [`&mut /// Emitter`](Emitter) and a `&mut TokenStream` for storing a dummy output. /// /// /// ``` /// # use proc_macro_utils::assert_tokens; /// # use quote::{quote, ToTokens}; /// use manyhow::{attribute, Emitter, Result}; /// use proc_macro2::TokenStream; /// # let input = quote!(); /// # let item = quote!(); /// # let output: TokenStream = /// attribute!(input, item, |input: TokenStream, /// item: TokenStream, /// dummy: &mut TokenStream, /// emitter: &mut Emitter| /// -> Result { /// // .. /// # Ok(quote!()) /// }); /// ``` /// /// *Note:* When `#[as_dummy]` is specified the `dummy: &mut TokenStream` will /// be initialized with `item`. To override assign a new `TokenStream`: /// ``` /// # use proc_macro_utils::assert_tokens; /// # use syn2 as syn; /// use manyhow::{attribute, Result, SilentError}; /// use proc_macro2::TokenStream; /// use quote::{quote, ToTokens}; /// # let input = quote!(input); /// let item = quote!( /// struct Struct; /// ); /// let output: TokenStream = attribute!( /// input, /// #[as_dummy] /// item, /// |input: TokenStream, /// item: syn::ItemStruct, /// dummy: &mut TokenStream| /// -> Result { /// assert_tokens!(dummy.to_token_stream(), { /// struct Struct; /// }); /// *dummy = quote! { /// struct Struct(HelloWorld); /// }; /// // .. /// Err(SilentError) /// }, /// ); /// /// assert_tokens! {output, {struct Struct(HelloWorld);}}; /// ``` #[macro_export] macro_rules! attribute { ($input:expr, #[as_dummy] $item:expr, $impl:expr $(,)?) => { $crate::__macro_handler!{attribute_transparent; #attr=true input: $input, item: $item.clone(); $impl; dummy: $item} }; ($input:expr, $item:expr, $impl:expr $(,)?) => { $crate::__macro_handler!{attribute_transparent; #attr=true input: $input, item: $item; $impl; dummy} }; } /// Handles [`proc_macro_derive`](https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros) /// implementation. /// /// Use [`derive!`] to support [`Parse`] and [`ToTokens`] as well. /// /// Takes any `TokenStream` for `item` and returns any `TokenStream`. `body` /// takes a [`DeriveMacroHandler`] with one `TokenStream` parameter. And an /// optional [`&mut Emitter`](Emitter) and `&mut TokenStream` for storing a /// dummy output. /// /// ``` /// # use proc_macro_utils::assert_tokens; /// # use quote::{quote, ToTokens}; /// use manyhow::{derive, Emitter, Result}; /// use proc_macro2::TokenStream; /// # let item = quote!(); /// # let output: TokenStream = /// derive( /// item, /// |item: TokenStream, dummy: &mut TokenStream, emitter: &mut Emitter| -> Result { /// // .. /// # Ok(quote!()) /// }, /// ); /// ``` pub fn derive< Item: AnyTokenStream, Dummy: AnyTokenStream, Output: MacroOutput, Return: AnyTokenStream, Function, >( item: impl AnyTokenStream, body: impl DeriveMacroHandler, ) -> Return { let mut tokens = Dummy::default(); let mut emitter = Emitter::new(); let output = body.call(item.into().into(), &mut tokens, &mut emitter); let mut tokens = tokens.into(); let mut tokens = match output.convert() { Ok(tokens) => tokens, Err(error) => { error.to_tokens(&mut tokens); tokens } }; emitter.to_tokens(&mut tokens); tokens.into() } /// Handles [`proc_macro_derive`](https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros) /// implementation. /// /// Takes any `TokenStream` for `item` and returns any `TokenStream`. `body` /// takes a [`DeriveMacroHandler`] with one `TokenStream` or type implementing /// [`Parse`] parameter and returns a `TokenStream` or type implementing /// [`ToTokens`]. And an optional [`&mut Emitter`](Emitter) and `&mut /// TokenStream` for storing a dummy output. /// /// ``` /// # use proc_macro_utils::assert_tokens; /// # use quote::{quote, ToTokens}; /// # use syn2 as syn; /// use manyhow::{derive, Emitter, Result}; /// use proc_macro2::TokenStream; /// # let item = quote!(); /// # let output: TokenStream = /// derive!(item, |item: syn::DeriveInput, /// dummy: &mut TokenStream, /// emitter: &mut Emitter| /// -> Result { /// // .. /// # Ok(quote!()) /// }); /// ``` #[macro_export] macro_rules! derive { ($item:expr, $impl:expr $(,)?) => { $crate::__macro_handler! {derive_transparent; item: $item; $impl} }; } /// Handles function like [`proc_macro`](https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros) /// implementation /// /// Use [`function!`] to support [`Parse`] and [`ToTokens`] as well. /// /// Takes any `TokenStream` for `input` and returns any /// `TokenStream`. If `input_as_dummy = true` the item input will be used as /// default dummy code on error. `body` takes a [`FunctionMacroHandler`] with /// one `TokenStream` parameter. And an optional [`&mut Emitter`](Emitter) and a /// `&mut TokenStream` for storing a dummy output. /// /// ``` /// # use proc_macro_utils::assert_tokens; /// # use quote::{quote, ToTokens}; /// use manyhow::{function, Emitter, Result}; /// use proc_macro2::TokenStream; /// # let input = quote!(); /// # let output: TokenStream = /// function( /// input, /// false, /// |input: TokenStream, dummy: &mut TokenStream, emitter: &mut Emitter| -> Result { /// // .. /// # Ok(quote!()) /// }, /// ); /// ``` /// /// *Note:* When `input_as_dummy = true` the `dummy: &mut TokenStream` will be /// initialized with `input`. To override assign a new `TokenStream`: /// ``` /// # use proc_macro_utils::assert_tokens; /// use manyhow::{function, Result, SilentError}; /// use proc_macro2::TokenStream; /// use quote::{quote, ToTokens}; /// let input = quote!(some input); /// let output: TokenStream = function( /// input, /// true, /// |input: TokenStream, /// dummy: &mut TokenStream| /// -> Result { /// assert_tokens!(dummy.to_token_stream(), { /// some input /// }); /// *dummy = quote! { /// another input /// }; /// // .. /// Err(SilentError) /// }, /// ); /// /// assert_tokens! {output, {another input}}; /// ``` pub fn function< Input: AnyTokenStream, Dummy: AnyTokenStream, Output: MacroOutput, Return: AnyTokenStream, Function, >( input: impl AnyTokenStream, input_as_dummy: bool, body: impl FunctionMacroHandler, ) -> Return { let mut tokens = if input_as_dummy { input.clone().into().into() } else { Dummy::default() }; let mut emitter = Emitter::new(); let output = body.call(input.into().into(), &mut tokens, &mut emitter); let mut tokens = tokens.into(); let mut tokens = match output.convert() { Ok(tokens) => tokens, Err(error) => { error.to_tokens(&mut tokens); tokens } }; emitter.to_tokens(&mut tokens); tokens.into() } /// Handles function like [`proc_macro`](https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros) /// implementation /// /// Takes any `TokenStream` for `input` and returns any `TokenStream`. If /// `#[as_dummy]` is specified on input, it will be used as default /// dummy code on error. `body` takes a [`FunctionMacroHandler`] with one /// `TokenStream` or type implementing [`Parse`] parameter and returns a /// `TokenStream` or type implementing [`ToTokens`]. And an optional [`&mut /// Emitter`](Emitter) and a `&mut TokenStream` for storing a dummy output. /// /// ``` /// # use proc_macro_utils::assert_tokens; /// # use quote::{quote, ToTokens}; /// # use syn2 as syn; /// use manyhow::{function, Emitter, Result}; /// use proc_macro2::TokenStream; /// # let input = quote!(); /// # let output: TokenStream = /// function!(input, |input: syn::Item, /// dummy: &mut TokenStream, /// emitter: &mut Emitter| /// -> Result { /// // .. /// # manyhow::bail!("unimplemented") /// }); /// ``` /// /// *Note:* When `#[as_dummy]` is specified on the input, the `dummy: &mut /// TokenStream` will be initialized with `input`. To override assign a new /// `TokenStream`: /// /// ``` /// use proc_macro_utils::assert_tokens; /// use manyhow::{function, Result, SilentError}; /// use proc_macro2::TokenStream; /// use quote::{quote, ToTokens}; /// /// let input = quote!(some input); /// let output: TokenStream = function!( /// #[as_dummy] input, /// |input: TokenStream, dummy: &mut TokenStream| /// -> Result { /// assert_tokens!(dummy.to_token_stream(), { /// some input /// }); /// *dummy = quote! { /// another input /// }; /// // .. /// Err(SilentError) /// }, /// ); /// /// assert_tokens! {output, {another input}}; /// ``` #[macro_export] macro_rules! function { (#[as_dummy] $input:expr, $impl:expr $(,)?) => { $crate::__macro_handler! {function_transparent; input: $input; $impl; dummy: $input} }; ($input:expr, $impl:expr $(,)?) => { $crate::__macro_handler! {function_transparent; input: $input; $impl; dummy} }; } #[test] fn function_macro() { use proc_macro::TokenStream as TokenStream1; use quote::quote; // proc_macro2::TokenStream let output: TokenStream = function!(quote!(hello), |input: TokenStream| -> TokenStream { input }); assert_eq!(output.to_string(), "hello"); // proc_macro::TokenStream do not run :D if false { let _: TokenStream1 = function!( TokenStream1::from(quote!(hello)), |input: TokenStream1| -> TokenStream1 { input } ); } #[cfg(feature = "syn2")] { use quote::ToTokens; let output: TokenStream = function!( #[as_dummy] quote!(hello;), |input: syn2::LitInt| -> TokenStream { input.into_token_stream() } ); assert_eq!( output.to_string(), quote!(hello; ::core::compile_error! { "expected integer literal" }).to_string() ); let output: TokenStream = function!(quote!(20), |_input: syn2::LitInt| -> syn2::Ident { syn2::parse_quote!(hello) }); assert_eq!(output.to_string(), "hello"); } } macro_rules! macro_input { ($MacroInput:ident; $($input:ident: $Input:ident),+; $a:literal; $name:literal; $token_streams:literal) => { /// Input of #[doc = $a] #[doc = $name] /// proc-macro /// /// Note: for `TokenStream` either [`proc_macro::TokenStream`] or /// [`proc_macro2::TokenStream`] can be used. /// /// Trait is implemented for any [`function`](FnOnce), taking in #[doc = concat!($token_streams, ".")] /// Additionally, they can take optionally in any order a [`&mut /// Emitter`](Emitter) which allows emitting errors without returning early. And /// a `&mut TokenStream` to return a dummy `TokenStream` on failure. /// /// When used with #[doc = concat!("[`", $name, "()`]")] /// it must return a type implementing [`MacroOutput`], with #[doc = concat!("[`", $name, "!`]")] /// it can accept types implementing [`Parse`] and return types /// implementing [`ToTokens`](quote::ToTokens). #[allow(missing_docs)] pub trait $MacroInput { $(type $Input;)+ type Dummy; type Output; #[allow(clippy::missing_errors_doc)] fn call( self, $(item: Self::$Input,)+ dummy: &mut Self::Dummy, emitter: &mut Emitter, ) -> Self::Output; } macro_input_impl!([$($Input,)+ Dummy: Clone]; $MacroInput; $($input: $Input),*; &mut Dummy, &mut Emitter; dummy: Dummy dummy; emitter emitter); macro_input_impl!([$($Input,)+ Dummy: Clone]; $MacroInput; $($input: $Input),*; &mut Dummy; dummy: Dummy dummy; _emitter); macro_input_impl!([$($Input),+]; $MacroInput; $($input: $Input),*; &mut Emitter; _dummy: TokenStream; emitter emitter); macro_input_impl!([$($Input),+]; $MacroInput; $($input: $Input),*; ; _dummy: TokenStream; _emitter); }; } macro_rules! macro_input_impl { ([$($gen:tt)*]; $MacroInput:ident; $($input:ident: $Input:ident),+; $($Extra:ty),*; $dummy1:ident: $Dummy:ident $($dummy2:ident)?; $emitter1:ident $($emitter2:ident)?) => { impl<$($gen)*, Output, Function> $MacroInput<($($Input,)+ $($Extra,)* Output)> for Function where Function: Fn($($Input,)+ $($Extra,)*) -> Output, { type Dummy = $Dummy; $(type $Input = $Input;)* type Output = Output; fn call( self, $($input: Self::$Input,)* $dummy1: &mut Self::Dummy, $emitter1: &mut Emitter, ) -> Self::Output { self($($input,)+ $($dummy2,)? $($emitter2,)?) } } } } macro_input!(FunctionMacroHandler; input: Input; "a"; "function"; "one `TokenStream`"); macro_input!(DeriveMacroHandler; item: Item; "a"; "derive"; "one `TokenStream`"); macro_input!(AttributeMacroHandler; input: Input, item: Item; "an"; "attribute"; "two `TokenStream`s"); #[rustfmt::skip] #[allow(clippy::doc_markdown)] /// Output of a macro handler. /// /// Enables support for returning any [`TokenStream`](AnyTokenStream) or /// [Result]<[TokenStream](AnyTokenStream), [impl ToTokensError](ToTokensError)> /// from a proc-macro implementation. pub trait MacroOutput { /// Handles conversion into a [Result]<[TokenStream](AnyTokenStream), [Error]>. #[allow(clippy::missing_errors_doc)] fn convert(self) -> Result; } impl MacroOutput for T { fn convert(self) -> Result { Ok(self.into()) } } impl MacroOutput for Result { fn convert(self) -> Result { self.map_err(Error::from).and_then(MacroOutput::convert) } } manyhow-0.11.4/src/macro_rules.rs000064400000000000000000000241571046102023000150520ustar 00000000000000#[cfg(doc)] use proc_macro2::Span; #[cfg(doc)] use quote::ToTokens; #[cfg(doc)] use crate::{Emitter, Error, ErrorMessage, SpanRanged}; #[doc(hidden)] #[macro_export] macro_rules! __error_message_internal { ((cs($($fmt:tt)*)$(.$fn:ident($($fmt_fn:tt)*))*), (), ()) => { $crate::ErrorMessage::call_site($($fmt)*) $(.attachment(::core::stringify!($fn), $($fmt_fn)*))* }; ((new($span:expr)($($fmt:tt)*)$(.$fn:ident($($fmt_fn:tt)*))*), (), ()) => { $crate::ErrorMessage::new( $crate::span_range!($span), $($fmt)* ) $(.attachment(::core::stringify!($fn), $($fmt_fn)*))* }; // ident = expr ($head:tt, ($($fmt:tt)*), (, $ident:ident = $expr:expr, $($tail:tt)*)) => { $crate::__error_message_internal!($head, ($($fmt)*, $ident = $expr), (, $($tail)*)) }; ($head:tt, ($($fmt:tt)*), (, $ident:ident = $expr:expr; $($tail:tt)*)) => { $crate::__error_message_internal!($head, ($($fmt)*, $ident = $expr), (; $($tail)*)) }; ($head:tt, ($($fmt:tt)*), (, $ident:ident = $expr:expr)) => { $crate::__error_message_internal!($head, ($($fmt)*, $ident = $expr), ()) }; // expr, ($head:tt, ($($fmt:tt)*), (, $expr:expr, $($tail:tt)*)) => { $crate::__error_message_internal!($head, ($($fmt)*, $expr), (, $($tail)*)) }; ($head:tt, ($($fmt:tt)*), (, $expr:expr; $($tail:tt)*)) => { $crate::__error_message_internal!($head, ($($fmt)*, $expr), (; $($tail)*)) }; ($head:tt, ($($fmt:tt)*), (, $expr:expr)) => { $crate::__error_message_internal!($head, ($($fmt)*, $expr), ()) }; // ; ident = "format", arguments (($($head:tt)*), $fmt:tt, ($(,)?$(;)?)) => { $crate::__error_message_internal!(($($head)*(::core::format_args!$fmt)), (), ()) }; (($($head:tt)*), $fmt:tt, ($(,)?; $attachment:ident = $fmt_str:literal $($tail:tt)*)) => { $crate::__error_message_internal!(($($head)*(::core::format_args!$fmt).$attachment), ($fmt_str), ($($tail)*)) }; } /// Creates an [`ErrorMessage`], comparable to the [`anyhow!`](https://docs.rs/anyhow/latest/anyhow/macro.anyhow.html) macro /// /// If the first argument is not a literal it is taken as the span of the error. /// The span expression can **either** implement [`SpanRanged`] or implement /// [`ToTokens`]. Otherwise, [`Span::call_site`] is used. /// /// ``` /// # use proc_macro2::Span; /// # use quote::quote; /// # use manyhow::error_message; /// assert_eq!( /// error_message!("format {} string{named}", "<3", named = "!").to_string(), /// "format <3 string!" /// ); /// // Span can either be `proc_macro::Span` or `proc_macro2::Span` /// assert_eq!( /// error_message!(Span::call_site(), "spanned error").to_string(), /// "spanned error" /// ); /// # if false { /// // Or any expression implementing `quote::ToTokens` /// assert_eq!( /// error_message!(quote!(some tokens), "spanned error").to_string(), /// "spanned error" /// ); /// # } /// ``` /// /// On top of the standard [`format_args!`] parameters additional attachments /// can be specified delimited with `;`. /// /// ``` /// # use proc_macro2::Span; /// # use quote::quote; /// # use manyhow::error_message; /// assert_eq!( /// error_message!( /// "format {} string{named}", "<3", named = "!"; /// error = "some additional error"; /// info = "some info as well"; /// custom_attachment = "amazing" /// ).to_string(), /// "format <3 string! /// /// = error: some additional error /// = info: some info as well /// = custom_attachment: amazing /// " /// ); /// ``` #[macro_export] macro_rules! error_message { ($fmt:literal $($tt:tt)*) => { $crate::__error_message_internal!((cs), ($fmt), ($($tt)*)) }; ($span:expr, $fmt:literal $($tt:tt)*) => { $crate::__error_message_internal!((new($span)), ($fmt), ($($tt)*)) }; } /// Exit by returning error, matching [`anyhow::bail!`](https://docs.rs/anyhow/latest/anyhow/macro.bail.html). /// /// The syntax is identical to [`error_message!`], the only difference is, that /// a single expression with an error is supported as well. /// ```should_panic /// # use manyhow::bail; /// # use proc_macro2::Span; /// # use syn2 as syn; /// bail!("an error message"; error = "with attachments"); /// let span = Span::call_site(); /// bail!(span, "error message"); /// let error = syn::Error::new(Span::call_site(), "an error"); /// bail!(error); /// # Ok::<_, manyhow::Error>(()) /// ``` #[macro_export] macro_rules! bail { ($msg:literal) => { return ::core::result::Result::Err($crate::error_message!($msg).into()); }; ($error:expr) => { return ::core::result::Result::Err($error.into()); }; ($($tt:tt)*) => { return ::core::result::Result::Err($crate::error_message!($($tt)*).into()); }; } /// Return early with an error, if a condition is not satisfied, matching /// [`anyhow::ensure!`](https://docs.rs/anyhow/latest/anyhow/macro.ensure.html). /// /// The syntax is identical to [`bail!`], with an additional leading condition. /// /// Additional to a boolean expression, the expression can also be a `let ... = /// ...` pattern matching, and will expand to `let ... else`. /// ``` /// # use manyhow::ensure; /// ensure!(true, "an error message"; help = "with attachments"); /// /// ensure!(let Some(a) = Some(1), "error"); /// assert_eq!(a, 1); /// /// # Ok::<_, manyhow::Error>(()) /// ``` /// ```should_panic /// # use manyhow::ensure; /// # use proc_macro2::Span; /// # use syn2 as syn; /// let span = Span::call_site(); /// ensure!(false, span, "error message"); /// let error = syn::Error::new(Span::call_site(), "an error"); /// ensure!(false, error); /// # Ok::<_, manyhow::Error>(()) /// ``` #[macro_export] macro_rules! ensure { ($cond:expr, $($bail_args:tt)*) => { if !$cond { $crate::bail!($($bail_args)*); } }; (let $pat:pat = $expr:expr, $($bail_args:tt)*) => { let $pat = $expr else { $crate::bail!($($bail_args)*); }; }; } /// Push an error to an emitter. /// /// The syntax is identical to [`error_message!`] and [`bail!`], but the first /// argument is the [`Emitter`]. /// ``` /// # use manyhow::{emit, Emitter}; /// # use proc_macro2::Span; /// # use syn2 as syn; /// let mut emitter = Emitter::new(); /// emit!(emitter, "an error message"); /// emit!(emitter, "an error message"; error = "with attachments"); /// let span = Span::call_site(); /// emit!(emitter, span, "error message"); /// let error = syn::Error::new(Span::call_site(), "an error"); /// emit!(emitter, error); /// ``` /// /// It can also be used with [`Error`]. /// ``` /// # use manyhow::{emit, error_message, Error}; /// # use proc_macro2::Span; /// # use syn2 as syn; /// let mut error: Error = error_message!("initial error").into(); /// emit!(error, "an error message"); /// ``` /// /// Or any collection implementing [`Extend`]. /// ``` /// # use manyhow::emit; /// # use proc_macro2::Span; /// # use syn2 as syn; /// let mut errors = Vec::new(); /// emit!(errors, "an error message"); /// ``` #[macro_export] macro_rules! emit { ($emitter:expr, $msg:literal) => { $emitter.extend(::core::iter::once::<$crate::ErrorMessage>($crate::error_message!($msg))); }; ($emitter:expr, $error:expr) => { $emitter.extend(::core::iter::once($error)); }; ($emitter:expr, $($tt:tt)*) => { $emitter.extend(::core::iter::once::<$crate::ErrorMessage>($crate::error_message!($($tt)*).into())); }; } #[cfg(test)] mod test { use proc_macro::Span; use quote::quote; use crate::{Emitter, ErrorMessage}; macro_rules! returned { ($ty:ty, $expr:expr) => { #[allow(unreachable_code)] (|| -> $ty { $expr; unreachable!(); })() }; } #[test] fn bail() { assert_eq!( returned!(Result<(), ErrorMessage>, bail!("format")) .unwrap_err() .to_string(), "format" ); assert_eq!( returned!(Result<(), ErrorMessage>, bail!("format {}", 1)) .unwrap_err() .to_string(), "format 1" ); let b = "ho"; assert_eq!( returned!(Result<(), ErrorMessage>, bail!("format {} {a} {} {b}", 1, 2, a = 4)) .unwrap_err() .to_string(), "format 1 4 2 ho" ); } #[test] fn error_message() { assert_eq!(error_message!("test").to_string(), "test"); assert_eq!(error_message!("test";).to_string(), "test"); assert_eq!( error_message!( "test"; error = "hello {} {a}", 1 + 4, a = "" ) .to_string(), "test\n\n = error: hello 5 \n" ); assert_eq!( error_message!( "test"; error = "hello {} {a}", 1 + 4, a = ""; ) .to_string(), "test\n\n = error: hello 5 \n" ); assert_eq!( error_message!( "test"; error = "hello {} {a}", 1 + 4, a = "",; hint = "a hint" ) .to_string(), "test\n\n = error: hello 5 \n = hint: a hint\n" ); } #[test] fn emit() { let mut emitter = Emitter::new(); emit!(emitter, "an error message"; error = "with attachments"); let span = proc_macro2::Span::call_site(); emit!(emitter, span, "error message"); #[cfg(feature = "syn2")] { let error = syn2::Error::new(proc_macro2::Span::call_site(), "an error"); emit!(emitter, error); } } // Only tests that it compiles fn _error_message_spanned() { let span = Span::call_site(); _ = error_message!(span, "test"); let span = proc_macro::Span::call_site(); _ = error_message!(span, "test"); let tokens = quote!(test); _ = error_message!(tokens, "an error message",); _ = error_message!(tokens, "an error message",;); _ = error_message!(tokens, "an error message",; warning="and a warning";); } } manyhow-0.11.4/src/parse_to_tokens.rs000064400000000000000000000125631046102023000157340ustar 00000000000000#![allow(missing_docs, clippy::pedantic)] use std::convert::Infallible; use std::marker::PhantomData; use proc_macro2::TokenStream; use crate::{ AnyTokenStream, AttributeMacroHandler, DeriveMacroHandler, Emitter, FunctionMacroHandler, ToTokensError, }; pub trait ManyhowParse { fn manyhow_parse(&self, input: impl AnyTokenStream, attr: bool) -> Result; } pub trait ManyhowToTokens { fn manyhow_to_tokens(&self, input: T, tokens: &mut TokenStream); } pub trait ManyhowTry { type Ok; type Err; fn manyhow_try(&self, value: T) -> Result; } pub struct WhatType(PhantomData); impl WhatType { /// Always panics pub fn identify(&self) -> Result { unimplemented!("DON'T YOU DARE CALL ME") } pub fn from(_ty: &T) -> Self { Self(PhantomData) } #[allow(clippy::new_without_default)] pub fn new() -> Self { Self(PhantomData) } } impl Clone for WhatType { fn clone(&self) -> Self { *self } } impl Copy for WhatType {} impl + From> ManyhowParse for WhatType { fn manyhow_parse(&self, input: impl AnyTokenStream, _attr: bool) -> Result { Ok(input.into().into()) } } impl ManyhowToTokens for WhatType { fn manyhow_to_tokens(&self, input: TokenStream, tokens: &mut TokenStream) { tokens.extend(input); } } impl ManyhowToTokens for WhatType { fn manyhow_to_tokens(&self, input: proc_macro::TokenStream, tokens: &mut TokenStream) { tokens.extend(TokenStream::from(input)); } } impl ManyhowToTokens for WhatType { fn manyhow_to_tokens(&self, input: E, tokens: &mut TokenStream) { input.to_tokens(tokens); } } impl ManyhowTry> for WhatType> { type Err = E; type Ok = T; fn manyhow_try(&self, value: Result) -> Result { value } } impl ManyhowTry for &WhatType { type Err = Infallible; type Ok = T; fn manyhow_try(&self, value: T) -> Result { Ok(value) } } #[cfg(feature = "syn2")] impl ManyhowParse for &WhatType { fn manyhow_parse(&self, input: impl AnyTokenStream, attr: bool) -> Result { let input = input.into(); let empty = input.is_empty(); syn2::parse2(input).map_err(|e| { let mut e = e.into_compile_error(); if attr && empty { error_message!("while parsing attribute argument (`#[... (...)]`)") .to_tokens(&mut e) } e }) } } #[cfg(feature = "syn2")] impl ManyhowToTokens for &WhatType { fn manyhow_to_tokens(&self, input: T, tokens: &mut TokenStream) { input.to_tokens(tokens); } } #[cfg(feature = "syn2")] #[test] #[allow(unused)] fn test_inference() { use syn2::parse::Parse; if false { let wt = &WhatType::new(); let ts: proc_macro::TokenStream = wt.manyhow_parse(quote::quote!(test), false).unwrap(); let wt = &WhatType::new(); if false { let wt: Result = wt.identify(); } let ts: syn2::Ident = wt.manyhow_parse(quote::quote!(test), false).unwrap(); struct Parsable; impl Parse for Parsable { fn parse(input: syn2::parse::ParseStream) -> syn2::Result { todo!() } } let wt = &WhatType::new(); let _: Result = wt.identify(); let ts = wt.manyhow_parse(quote::quote!(test), false).unwrap(); } } macro_rules! transparent_handlers { ($name:ident; $MacroInput:ident; $($input:ident: $Input:ident $($context:expr)?),*; $($dummy:ident)?) => { /// Internal implementation for macro. pub fn $name<$($Input,)* Dummy: AnyTokenStream, Output, Function,>( $($input: Result<$Input, TokenStream>,)* $($dummy: Option,)? body: impl $MacroInput, ) -> Result<(Output, TokenStream, TokenStream), TokenStream> { // use $crate::ToTokensError as _; #[allow(unused)] let mut dummy = TokenStream::new(); $(let mut dummy = $dummy.unwrap_or_default().into();)? $(let $input = match $input { Ok($input) => $input, Err(tokens) => { dummy.extend(tokens); $($crate::error_message!($context).to_tokens(&mut dummy);)? return Err(dummy); } };)* let mut dummy = dummy.into(); let mut emitter = Emitter::new(); let output = body.call($($input,)+ &mut dummy, &mut emitter); let mut tokens = TokenStream::new(); emitter.to_tokens(&mut tokens); Ok((output, tokens, dummy.into())) } }; } transparent_handlers! { function_transparent; FunctionMacroHandler; input: Input; dummy } transparent_handlers! { derive_transparent; DeriveMacroHandler; item: Item;} transparent_handlers! { attribute_transparent; AttributeMacroHandler; input: Input, item: Item; dummy } manyhow-0.11.4/src/span_ranged.rs000064400000000000000000000134011046102023000150060ustar 00000000000000use std::ops::Range; use proc_macro2::Span; use quote::ToTokens; #[cfg(doc)] use crate::ErrorMessage; /// Get a [`Range`](std::ops::Range)[``](proc_macro2::Span) from a /// type that **either** implements [`SpanRanged`] or [`ToTokens`] (**NOT** /// both). #[macro_export] macro_rules! span_range { ($span:expr) => {{ // Warning is triggered if span is incorrect type #[allow(unused_imports)] use $crate::__private::*; ($span).FIRST_ARG_MUST_IMPLEMENT_SpanRanged_OR_ToTokens() }}; } /// Returns the [`Range`](Range)[``](Span) from the start to the end of /// multi-token structures. /// /// `start` and `end` can be the same when called on single Tokens or [`Span`]. /// /// Due to compiler limitations, it is currently not possible to implement /// `SpanRanged for T: ToTokens`, therefor there is /// [`to_tokens_span_range()`]. /// /// For types that **either** implement [`SpanRanged`] or [`ToTokens`] (but /// **NOT** both) the [`span_range!`] macro can be used as well. /// /// # Motivation /// This is superior to a normal [`Span`] (at least until [`Span::join`] works /// on stable), because it leads to better error messages: /// /// Given the following expression /// ``` /// let a = |something: usize| something; /// ``` /// /// [`ErrorMessage::new(first_pipe_span, "error message")`](ErrorMessage::new) /// would result in something like /// /// ```text /// error: error message /// /// let a = |something: usize| something; /// ^ /// ``` /// /// While [`ErrorMessage::new(first_pipe_span..something_span, /// "error message")`](ErrorMessage::new) would improve the error message to: /// ```text /// error: error message /// /// let a = |something: usize| something; /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// ``` pub trait SpanRanged { /// Returns the [`Range`](Range)[``](Span) fully encompasing `self` fn span_range(&self) -> Range; /// Returns [`Self::span_range`] as a single span if possible, currently /// only possible on nightly. [more](proc_macro2::Span::join) fn span_joined(&self) -> Option { let range = self.span_range(); range.start.join(range.end) } #[doc(hidden)] #[deprecated] fn joined(&self) -> Option { self.span_joined() } } impl SpanRanged for &T { fn span_range(&self) -> Range { (*self).span_range() } } impl SpanRanged for Option { fn span_range(&self) -> Range { self.as_ref() .map_or_else(|| Span::call_site().span_range(), SpanRanged::span_range) } } impl SpanRanged for (A, B) { fn span_range(&self) -> Range { (self.0.span_range().start)..(self.1.span_range().end) } } impl SpanRanged for Span { fn span_range(&self) -> Range { *self..*self } } impl SpanRanged for proc_macro::Span { fn span_range(&self) -> Range { (*self).into()..(*self).into() } } impl SpanRanged for Range { fn span_range(&self) -> Range { self.start.span_range().start..self.end.span_range().end } } impl SpanRanged for proc_macro::TokenStream { fn span_range(&self) -> Range { let mut this = self.clone().into_iter(); let first = this .next() .as_ref() .map_or_else(proc_macro::Span::call_site, proc_macro::TokenTree::span); let last = this .last() .as_ref() .map_or(first, proc_macro::TokenTree::span); first.into()..last.into() } } /// Implementation of [`SpanRanged`](SpanRanged)` for T: `[`ToTokens`] /// /// This is necessary to put in a standalone function due to compiler /// limitations. pub fn to_tokens_span_range(tokens: impl ToTokens) -> Range { proc_macro::TokenStream::from(tokens.to_token_stream()).span_range() } #[doc(hidden)] pub trait SpanRangedToSpanRange { #[allow(non_snake_case)] fn FIRST_ARG_MUST_IMPLEMENT_SpanRanged_OR_ToTokens(&self) -> Range; } impl SpanRangedToSpanRange for T { #[allow(non_snake_case)] fn FIRST_ARG_MUST_IMPLEMENT_SpanRanged_OR_ToTokens(&self) -> Range { self.span_range() } } #[doc(hidden)] pub trait ToTokensToSpanRange { #[allow(non_snake_case)] fn FIRST_ARG_MUST_IMPLEMENT_SpanRanged_OR_ToTokens(&self) -> Range; } impl ToTokensToSpanRange for T { #[allow(non_snake_case)] fn FIRST_ARG_MUST_IMPLEMENT_SpanRanged_OR_ToTokens(&self) -> Range { let mut this = self.to_token_stream().into_iter(); let first = this .next() .as_ref() .map_or_else(proc_macro2::Span::call_site, proc_macro2::TokenTree::span); let last = this .last() .as_ref() .map_or(first, proc_macro2::TokenTree::span); first..last } } #[doc(hidden)] pub trait ToTokensTupleToSpanRange { #[allow(non_snake_case)] fn FIRST_ARG_MUST_IMPLEMENT_SpanRanged_OR_ToTokens(&self) -> Range; } impl ToTokensTupleToSpanRange for (A, B) { #[allow(non_snake_case)] fn FIRST_ARG_MUST_IMPLEMENT_SpanRanged_OR_ToTokens(&self) -> Range { let first = self .0 .to_token_stream() .into_iter() .next() .as_ref() .map_or_else(proc_macro2::Span::call_site, proc_macro2::TokenTree::span); let last = self .1 .to_token_stream() .into_iter() .last() .as_ref() .map_or(first, proc_macro2::TokenTree::span); first..last } } #[cfg(test)] mod test { #[test] fn test() { span_range!(1); span_range!((1, 2)); } }