aboutsummaryrefslogtreecommitdiff
path: root/proc-macro-error
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2020-01-07 11:18:04 +0000
committerDaniel Mueller <deso@posteo.net>2020-01-08 09:20:25 -0800
commit5e20a29b4fdc8a2d442d1093681b396dcb4b816b (patch)
tree55ab083fa8999d2ccbb5e921c1ffe52560dca152 /proc-macro-error
parent203e691f46d591a2cc8acdfd850fa9f5b0fb8a98 (diff)
downloadnitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.gz
nitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.bz2
Add structopt dependency in version 0.3.7
This patch series replaces argparse with structopt in the argument handling code. As a first step, we need structopt as a dependency. Import subrepo structopt/:structopt at efbdda4753592e27bc430fb01f7b9650b2f3174d Import subrepo bitflags/:bitflags at 30668016aca6bd3b02c766e8347e0b4080d4c296 Import subrepo clap/:clap at 784524f7eb193e35f81082cc69454c8c21b948f7 Import subrepo heck/:heck at 093d56fbf001e1506e56dbfa38631d99b1066df1 Import subrepo proc-macro-error/:proc-macro-error at 6c4cfe79a622c5de8ae68557993542be46eacae2 Import subrepo proc-macro2/:proc-macro2 at d5d48eddca4566e5438e8a2cbed4a74e049544de Import subrepo quote/:quote at 727436c6c137b20f0f34dde5d8fda2679b9747ad Import subrepo rustversion/:rustversion at 0c5663313516263059ce9059ef81fc7a1cf655ca Import subrepo syn-mid/:syn-mid at 5d3d85414a9e6674e1857ec22a87b96e04a6851a Import subrepo syn/:syn at e87c27e87f6f4ef8919d0372bdb056d53ef0d8f3 Import subrepo textwrap/:textwrap at abcd618beae3f74841032aa5b53c1086b0a57ca2 Import subrepo unicode-segmentation/:unicode-segmentation at 637c9874c4fe0c205ff27787faf150a40295c6c3 Import subrepo unicode-width/:unicode-width at 3033826f8bf05e82724140a981d5941e48fce393 Import subrepo unicode-xid/:unicode-xid at 4baae9fffb156ba229665b972a9cd5991787ceb7
Diffstat (limited to 'proc-macro-error')
-rw-r--r--proc-macro-error/.gitignore4
-rw-r--r--proc-macro-error/.gitlab-ci.yml54
-rw-r--r--proc-macro-error/.travis.yml19
-rw-r--r--proc-macro-error/CHANGELOG.md86
-rw-r--r--proc-macro-error/Cargo.toml7
-rw-r--r--proc-macro-error/LICENSE-APACHE201
-rw-r--r--proc-macro-error/LICENSE-MIT21
-rw-r--r--proc-macro-error/README.md206
-rw-r--r--proc-macro-error/proc-macro-error-attr/Cargo.toml18
-rw-r--r--proc-macro-error/proc-macro-error-attr/src/lib.rs162
-rw-r--r--proc-macro-error/proc-macro-error/Cargo.toml26
-rw-r--r--proc-macro-error/proc-macro-error/build.rs11
-rw-r--r--proc-macro-error/proc-macro-error/src/dummy.rs136
-rw-r--r--proc-macro-error/proc-macro-error/src/lib.rs514
-rw-r--r--proc-macro-error/proc-macro-error/src/macros.rs257
-rw-r--r--proc-macro-error/proc-macro-error/src/nightly.rs49
-rw-r--r--proc-macro-error/proc-macro-error/src/stable.rs26
-rw-r--r--proc-macro-error/test-crate/Cargo.toml20
-rw-r--r--proc-macro-error/test-crate/src/lib.rs123
-rw-r--r--proc-macro-error/test-crate/tests/macro-errors.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ok.rs9
-rw-r--r--proc-macro-error/test-crate/tests/ui/abort.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/abort.stderr8
-rw-r--r--proc-macro-error/test-crate/tests/ui/call_site.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/call_site.stderr8
-rw-r--r--proc-macro-error/test-crate/tests/ui/direct_abort.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/direct_abort.stderr5
-rw-r--r--proc-macro-error/test-crate/tests/ui/dummy.rs16
-rw-r--r--proc-macro-error/test-crate/tests/ui/dummy.stderr5
-rw-r--r--proc-macro-error/test-crate/tests/ui/multi-error.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/multi-error.stderr32
-rw-r--r--proc-macro-error/test-crate/tests/ui/not_proc_macro.rs4
-rw-r--r--proc-macro-error/test-crate/tests/ui/not_proc_macro.stderr8
-rw-r--r--proc-macro-error/test-crate/tests/ui/option_expect.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/option_expect.stderr5
-rw-r--r--proc-macro-error/test-crate/tests/ui/result_expect.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/result_expect.stderr5
-rw-r--r--proc-macro-error/test-crate/tests/ui/result_unwrap.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/result_unwrap.stderr5
-rw-r--r--proc-macro-error/test-crate/tests/ui/unknown_setting.rs4
-rw-r--r--proc-macro-error/test-crate/tests/ui/unknown_setting.stderr5
-rw-r--r--proc-macro-error/test-crate/tests/ui/unrelated_panic.rs6
-rw-r--r--proc-macro-error/test-crate/tests/ui/unrelated_panic.stderr7
43 files changed, 2120 insertions, 0 deletions
diff --git a/proc-macro-error/.gitignore b/proc-macro-error/.gitignore
new file mode 100644
index 0000000..9d538f2
--- /dev/null
+++ b/proc-macro-error/.gitignore
@@ -0,0 +1,4 @@
+/target
+**/*.rs.bk
+Cargo.lock
+.fuse_hidden* \ No newline at end of file
diff --git a/proc-macro-error/.gitlab-ci.yml b/proc-macro-error/.gitlab-ci.yml
new file mode 100644
index 0000000..e36a714
--- /dev/null
+++ b/proc-macro-error/.gitlab-ci.yml
@@ -0,0 +1,54 @@
+stages:
+ - test
+
+
+.setup_template: &setup_template
+ stage: test
+ image: debian:stable-slim
+ before_script:
+ - export CARGO_HOME="$CI_PROJECT_DIR/.cargo"
+ - export PATH="$PATH:$CARGO_HOME/bin"
+ - export RUST_BACKTRACE=full
+ - apt-get update > /dev/null
+ - apt-get install -y curl build-essential > /dev/null
+ - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUST_VERSION
+ - rustup --version
+ - rustc --version
+ - cargo --version
+
+.test_all_template: &test_all_template
+ <<: *setup_template
+ script:
+ - cargo test --all
+
+
+test-stable:
+ <<: *test_all_template
+ variables:
+ RUST_VERSION: stable
+
+test-beta:
+ <<: *test_all_template
+ variables:
+ RUST_VERSION: beta
+
+test-nightly:
+ <<: *test_all_template
+ variables:
+ RUST_VERSION: nightly
+
+
+test-1.31.0:
+ <<: *setup_template
+ script:
+ - cargo test --tests # skip doctests
+ variables:
+ RUST_VERSION: 1.31.0
+
+test-fmt:
+ <<: *setup_template
+ script:
+ - cargo fmt --all -- --check
+ - RUTSFLAGS='--cfg nightly_fmt' cargo fmt --all -- --check
+ variables:
+ RUST_VERSION: stable
diff --git a/proc-macro-error/.travis.yml b/proc-macro-error/.travis.yml
new file mode 100644
index 0000000..362003f
--- /dev/null
+++ b/proc-macro-error/.travis.yml
@@ -0,0 +1,19 @@
+language: rust
+rust:
+ - stable
+ - beta
+ - nightly
+script:
+ - cargo test --all
+matrix:
+ include:
+ - rust: 1.31.0
+ script: cargo test --tests # skip doctests
+ allow_failures:
+ - rust: nightly
+ fast_finish: true
+
+
+notifications:
+ email:
+ on_success: never
diff --git a/proc-macro-error/CHANGELOG.md b/proc-macro-error/CHANGELOG.md
new file mode 100644
index 0000000..29043b5
--- /dev/null
+++ b/proc-macro-error/CHANGELOG.md
@@ -0,0 +1,86 @@
+# v0.4.4 (2019-11-13)
+* Fix `abort_if_dirty` + warnings bug
+* Allow trailing commas in macros
+
+# v0.4.2 (2019-11-7)
+* FINALLY fixed `__pme__suggestions not found` bug
+
+# v0.4.1 (2019-11-7) YANKED
+* Fixed `__pme__suggestions not found` bug
+* Documentation improvements, links checked
+
+# v0.4.0 (2019-11-6) YANKED
+
+## New features
+* "help" messages that can have their own span on nightly, they
+ inherit parent span on stable.
+ ```rust
+ let cond_help = if condition { Some("some help message") else { None } };
+ abort!(
+ span, // parent span
+ "something's wrong, {} wrongs in total", 10; // main message
+ help = "here's a help for you, {}", "take it"; // unconditional help message
+ help =? cond_help; // conditional help message, must be Option
+ note = note_span => "don't forget the note, {}", "would you?" // notes can have their own span but it's effective only on nightly
+ )
+ ```
+* Warnings via `emit_warning` and `emit_warning_call_site`. Nightly only, they're ignored on stable.
+* Now `proc-macro-error` delegates to `proc_macro::Diagnostic` on nightly.
+
+## Breaking changes
+* `MacroError` is now replaced by `Diagnostic`. Its API resembles `proc_macro::Diagnostic`.
+* `Diagnostic` does not implement `From<&str/String>` so `Result<T, &str/String>::abort_or_exit()`
+ won't work anymore (nobody used it anyway).
+* `macro_error!` macro is replaced with `diagnostic!`.
+
+## Improvements
+* Now `proc-macro-error` renders notes exactly just like rustc does.
+* We don't parse a body of a function annotated with `#[proc_macro_error]` anymore,
+ only looking at the signature. This should somewhat decrease expansion time for large functions.
+
+# v0.3.3 (2019-10-16)
+* Now you can use any word instead of "help", undocumented.
+
+# v0.3.2 (2019-10-16)
+* Introduced support for "help" messages, undocumented.
+
+# v0.3.0 (2019-10-8)
+
+## The crate has been completely rewritten from scratch!
+
+## Changes (most are breaking):
+* Renamed macros:
+ * `span_error` => `abort`
+ * `call_site_error` => `abort_call_site`
+* `filter_macro_errors` was replaced by `#[proc_macro_error]` attribute.
+* `set_dummy` now takes `TokenStream` instead of `Option<TokenStream>`
+* Support for multiple errors via `emit_error` and `emit_call_site_error`
+* New `macro_error` macro for building errors in format=like style.
+* `MacroError` API had been reconsidered. It also now implements `quote::ToTokens`.
+
+# v0.2.6 (2019-09-02)
+* Introduce support for dummy implementations via `dummy::set_dummy`
+* `multi::*` is now deprecated, will be completely rewritten in v0.3
+
+# v0.2.0 (2019-08-15)
+
+## Breaking changes
+* `trigger_error` replaced with `MacroError::trigger` and `filter_macro_error_panics`
+ is hidden from docs.
+ This is not quite a breaking change since users weren't supposed to use these functions directly anyway.
+* All dependencies are updated to `v1.*`.
+
+## New features
+* Ability to stack multiple errors via `multi::MultiMacroErrors` and emit them at once.
+
+## Improvements
+* Now `MacroError` implements `std::fmt::Display` instead of `std::string::ToString`.
+* `MacroError::span` inherent method.
+* `From<MacroError> for proc_macro/proc_macro2::TokenStream` implementations.
+* `AsRef/AsMut<String> for MacroError` implementations.
+
+# v0.1.x (2019-07-XX)
+
+## New features
+* An easy way to report errors inside within a proc-macro via `span_error`,
+ `call_site_error` and `filter_macro_errors`.
diff --git a/proc-macro-error/Cargo.toml b/proc-macro-error/Cargo.toml
new file mode 100644
index 0000000..6fa600e
--- /dev/null
+++ b/proc-macro-error/Cargo.toml
@@ -0,0 +1,7 @@
+[workspace]
+
+members = [
+ "proc-macro-error",
+ "proc-macro-error-attr",
+ "test-crate",
+]
diff --git a/proc-macro-error/LICENSE-APACHE b/proc-macro-error/LICENSE-APACHE
new file mode 100644
index 0000000..52ba334
--- /dev/null
+++ b/proc-macro-error/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 2019 CreepySkeleton <creepy-skeleton@yandex.ru>
+
+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/proc-macro-error/LICENSE-MIT b/proc-macro-error/LICENSE-MIT
new file mode 100644
index 0000000..d01f775
--- /dev/null
+++ b/proc-macro-error/LICENSE-MIT
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 CreepySkeleton
+
+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/proc-macro-error/README.md b/proc-macro-error/README.md
new file mode 100644
index 0000000..d6d1086
--- /dev/null
+++ b/proc-macro-error/README.md
@@ -0,0 +1,206 @@
+# proc-macro-error
+
+[![travis ci](https://travis-ci.org/CreepySkeleton/proc-macro-error.svg?branch=master)](https://travis-ci.org/CreepySkeleton/proc-macro-error)
+[![docs.rs](https://docs.rs/proc-macro-error/badge.svg)](https://docs.rs/proc-macro-error)
+
+This crate aims to make error reporting in proc-macros simple and easy to use.
+Migrate from `panic!`-based errors for as little effort as possible!
+
+Also, there's ability to [append a dummy token stream][crate::dummy] to your errors.
+
+```toml
+[dependencies]
+proc-macro-error = "0.4"
+```
+
+*Supports rustc 1.31 and up*
+
+[Documentation and guide][guide]
+
+## What emitted errors look like
+
+```
+error: multiple error part: multi2
+
+ = note: help message test
+ = help: Option help test
+ = note: I see what you did here...
+
+ --> $DIR/multi-error.rs:4:18
+ |
+4 | make_fn!(multi1, multi2, _, multi3);
+ | ^^^^^^
+```
+
+## Examples
+
+### Panic-like usage
+
+```rust
+use proc_macro_error::*;
+use proc_macro::TokenStream;
+use syn::{DeriveInput, parse_macro_input};
+use quote::quote;
+
+// This is your main entry point
+#[proc_macro]
+// this attribute *MUST* be placed on top of the #[proc_macro] function
+#[proc_macro_error]
+pub fn make_answer(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ if let Err(err) = some_logic(&input) {
+ // we've got a span to blame, let's use it
+ // This immediately aborts the proc-macro and shows the error
+ abort!(err.span, "You made an error, go fix it: {}", err.msg);
+ }
+
+ // `Result` has some handy shortcuts if your error type implements
+ // `Into<MacroError>`. `Option` has one unconditionally.
+ more_logic(&input).expect_or_abort("What a careless user, behave!");
+
+ if !more_logic_for_logic_god(&input) {
+ // We don't have an exact location this time,
+ // so just highlight the proc-macro invocation itself
+ abort_call_site!(
+ "Bad, bad user! Now go stand in the corner and think about what you did!");
+ }
+
+ // Now all the processing is done, return `proc_macro::TokenStream`
+ quote!(/* stuff */).into()
+}
+```
+
+### `proc_macro::Diagnostic`-like usage
+
+```rust
+use proc_macro_error::*;
+use proc_macro::TokenStream;
+use syn::{spanned::Spanned, DeriveInput, ItemStruct, Fields, Attribute , parse_macro_input};
+use quote::quote;
+
+fn process_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
+ attrs
+ .iter()
+ .filter_map(|attr| match process_attr(attr) {
+ Ok(res) => Some(res),
+ Err(msg) => {
+ emit_error!(attr.span(), "Invalid attribute: {}", msg);
+ None
+ }
+ })
+ .collect()
+}
+
+fn process_fields(_attrs: &Fields) -> Vec<TokenStream> {
+ // processing fields in pretty much the same way as attributes
+ unimplemented!()
+}
+
+#[proc_macro]
+#[proc_macro_error]
+pub fn make_answer(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as ItemStruct);
+ let attrs = process_attrs(&input.attrs);
+
+ // abort right now if some errors were encountered
+ // at the attributes processing stage
+ abort_if_dirty();
+
+ let fields = process_fields(&input.fields);
+
+ // no need to think about emitted errors
+ // #[proc_macro_error] will handle them for you
+ //
+ // just return a TokenStream as you normally would
+ quote!(/* stuff */).into()
+}
+```
+
+## Limitations
+
+- Warnings are emitted only on nightly, they're ignored on stable.
+- "help" suggestions cannot have their own span info on stable, (they inherit parent span).
+- If a panic occurs somewhere in your macro no errors will be displayed. This is not a
+ technical limitation but intentional design, `panic` is not for error reporting.
+
+## MSRV policy
+
+`proc_macro_error` will always be compatible with proc-macro holy trinity:
+`proc_macro2`, `syn`, `quote` crates. In other words, if the trinity is available
+to you than `proc_macro_error` is available too.
+
+## Motivation
+
+Error handling in proc-macros sucks. There's not much of a choice today:
+you either "bubble up" the error up to the top-level of your macro and convert it to
+a [`compile_error!`][compl_err] invocation or just use a good old panic. Both these ways suck:
+
+- Former sucks because it's quite redundant to unroll a proper error handling
+ just for critical errors that will crash the macro anyway so people mostly
+ choose not to bother with it at all and use panic. Almost nobody does it,
+ simple `.expect` is too tempting.
+
+ Also, if you do decide to implement this `Result`-based architecture in your macro
+ you're going to have to rewrite it entirely once [`proc_macro::Diagnostic`][] is finally stable.
+ Not cool.
+
+- Later sucks because there's no way to carry out span info via `panic!`. `rustc` will highlight
+ the whole invocation itself but not some specific token inside it.
+
+ Furthermore, panics aren't for error-reporting at all; panics are for bug-detecting
+ (like unwrapping on `None` or out-of-range indexing) or for early development stages
+ when you need a prototype ASAP and error handling can wait. Mixing these usages only
+ messes things up.
+
+- There is [`proc_macro::Diagnostic`][] which is awesome but it has been experimental
+ for more than a year and is unlikely to be stabilized any time soon.
+
+ This crate's API is intentionally designed to be compatible with `proc_macro::Diagnostic`
+ and delegates to it whenever possible. Once `Diagnostics` is stable this crate
+ will **always** delegate to it, no code changes will be required on user side.
+
+That said, we need a solution, but this solution must meet these conditions:
+
+- It must be better than `panic!`. The main point: it must offer a way to carry span information
+ over to user.
+- It must take as little effort as possible to migrate from `panic!`. Ideally, a new
+ macro with the same semantics plus ability to carry out span info. We should also keep
+ in mind the existence of [`proc_macro::Diagnostic`][] .
+- **It must be usable on stable**.
+
+This crate aims to provide such a mechanism. All you have to do is annotate your top-level
+`#[proc_macro]` function with `#[proc_macro_errors]` attribute and change panics to
+[`abort!`]/[`abort_call_site!`] where appropriate, see [the Guide][guide].
+
+## Disclaimer
+Please note that **this crate is not intended to be used in any way other
+than proc-macro error reporting**, use `Result` and `?` for anything else.
+
+<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>
+
+
+[compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html
+[`proc_macro::Diagnostic`]: https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html
+
+[crate::dummy]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/dummy/index.html
+[crate::multi]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/multi/index.html
+
+[`abort_call_site!`]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/macro.abort_call_site.html
+[`abort!`]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/macro.abort.html
+[guide]: https://docs.rs/proc-macro-error
diff --git a/proc-macro-error/proc-macro-error-attr/Cargo.toml b/proc-macro-error/proc-macro-error-attr/Cargo.toml
new file mode 100644
index 0000000..6dee68e
--- /dev/null
+++ b/proc-macro-error/proc-macro-error-attr/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "proc-macro-error-attr"
+version = "0.4.3"
+authors = ["CreepySkeleton <creepy-skeleton@yandex.ru>"]
+edition = "2018"
+description = "attribute macro for proc-macro-error crate"
+license = "MIT OR Apache-2.0"
+repository = "https://gitlab.com/CreepySkeleton/proc-macro-error"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "1"
+proc-macro2 = "1"
+syn = { version = "1", default-features = false, features = ["derive", "parsing", "proc-macro"] }
+syn-mid = "0.4"
+rustversion = "1.0"
diff --git a/proc-macro-error/proc-macro-error-attr/src/lib.rs b/proc-macro-error/proc-macro-error-attr/src/lib.rs
new file mode 100644
index 0000000..3c1013b
--- /dev/null
+++ b/proc-macro-error/proc-macro-error-attr/src/lib.rs
@@ -0,0 +1,162 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro2::Ident;
+use quote::quote;
+use std::iter::FromIterator;
+use syn::{
+ parse::{Parse, ParseStream},
+ parse_macro_input,
+ punctuated::Punctuated,
+ Attribute, Token,
+};
+use syn_mid::{Block, ItemFn};
+
+use self::Setting::*;
+
+#[proc_macro_attribute]
+pub fn proc_macro_error(attr: TokenStream, input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as ItemFn);
+ let mut settings = match syn::parse::<Settings>(attr) {
+ Ok(settings) => settings,
+ Err(err) => {
+ let err = err.to_compile_error();
+ return quote!(#input #err).into();
+ }
+ };
+
+ let is_proc_macro = is_proc_macro(&input.attrs);
+ if is_proc_macro {
+ settings.set(AssertUnwindSafe);
+ }
+
+ if detect_proc_macro_hack(&input.attrs) {
+ settings.set(ProcMacroHack);
+ }
+
+ if !(settings.is_set(AllowNotMacro) || is_proc_macro) {
+ return quote!(
+ #input
+ compile_error!("#[proc_macro_error] attribute can be used only with a proc-macro\n\n \
+ hint: if you are really sure that #[proc_macro_error] should be applied \
+ to this exact function use #[proc_macro_error(allow_not_macro)]\n");
+ )
+ .into();
+ }
+
+ let ItemFn {
+ attrs,
+ vis,
+ constness,
+ asyncness,
+ unsafety,
+ abi,
+ fn_token,
+ ident,
+ generics,
+ inputs,
+ output,
+ block,
+ ..
+ } = input;
+
+ let body = gen_body(block, settings);
+
+ quote!(
+ #(#attrs)*
+ #vis
+ #constness
+ #asyncness
+ #unsafety
+ #abi
+ #fn_token
+ #ident
+ #generics
+ (#inputs)
+ #output
+
+ { #body }
+ )
+ .into()
+}
+
+#[derive(PartialEq)]
+enum Setting {
+ AssertUnwindSafe,
+ AllowNotMacro,
+ ProcMacroHack,
+}
+
+impl Parse for Setting {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let ident: Ident = input.parse()?;
+ match &*ident.to_string() {
+ "assert_unwind_safe" => Ok(AssertUnwindSafe),
+ "allow_not_macro" => Ok(AllowNotMacro),
+ "proc_macro_hack" => Ok(ProcMacroHack),
+ _ => Err(syn::Error::new(
+ ident.span(),
+ format!(
+ "unknown setting `{}`, expected one of \
+ `assert_unwind_safe`, `allow_not_macro`, `proc_macro_hack`",
+ ident
+ ),
+ )),
+ }
+ }
+}
+
+struct Settings(Vec<Setting>);
+impl Parse for Settings {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let punct = Punctuated::<Setting, Token![,]>::parse_terminated(input)?;
+ Ok(Settings(Vec::from_iter(punct)))
+ }
+}
+
+impl Settings {
+ fn is_set(&self, setting: Setting) -> bool {
+ self.0.iter().any(|s| *s == setting)
+ }
+
+ fn set(&mut self, setting: Setting) {
+ self.0.push(setting)
+ }
+}
+
+#[rustversion::since(1.37)]
+fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream {
+ let is_proc_macro_hack = settings.is_set(ProcMacroHack);
+ let closure = if settings.is_set(AssertUnwindSafe) {
+ quote!(::std::panic::AssertUnwindSafe(|| #block ))
+ } else {
+ quote!(|| #block)
+ };
+
+ quote!( ::proc_macro_error::entry_point(#closure, #is_proc_macro_hack) )
+}
+
+// FIXME:
+// proc_macro::TokenStream does not implement UnwindSafe until 1.37.0.
+// Considering this is the closure's return type the unwind safety check would fail
+// for virtually every closure possible, the check is meaningless.
+#[rustversion::before(1.37)]
+fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream {
+ let is_proc_macro_hack = settings.is_set(ProcMacroHack);
+ let closure = quote!(::std::panic::AssertUnwindSafe(|| #block ));
+ quote!( ::proc_macro_error::entry_point(#closure, #is_proc_macro_hack) )
+}
+
+fn detect_proc_macro_hack(attrs: &[Attribute]) -> bool {
+ attrs
+ .iter()
+ .any(|attr| attr.path.is_ident("proc_macro_hack"))
+}
+
+fn is_proc_macro(attrs: &[Attribute]) -> bool {
+ attrs.iter().any(|attr| {
+ attr.path.is_ident("proc_macro")
+ || attr.path.is_ident("proc_macro_derive")
+ || attr.path.is_ident("proc_macro_attribute")
+ })
+}
diff --git a/proc-macro-error/proc-macro-error/Cargo.toml b/proc-macro-error/proc-macro-error/Cargo.toml
new file mode 100644
index 0000000..2da213a
--- /dev/null
+++ b/proc-macro-error/proc-macro-error/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "proc-macro-error"
+version = "0.4.4"
+authors = ["CreepySkeleton <creepy-skeleton@yandex.ru>"]
+description = "Almost drop-in replacement to panics in proc-macros"
+
+repository = "https://gitlab.com/CreepySkeleton/proc-macro-error"
+readme = "../README.md"
+keywords = ["proc-macro", "error", "errors"]
+categories = ["development-tools::procedural-macro-helpers"]
+license = "MIT OR Apache-2.0"
+
+edition = "2018"
+build = "build.rs"
+
+[badges]
+maintenance = { status = "actively-developed" }
+
+[dependencies]
+quote = "1"
+proc-macro2 = "1"
+syn = { version = "1", default-features = false }
+proc-macro-error-attr = { path = "../proc-macro-error-attr", version = "0.4.3"}
+
+[build-dependencies]
+rustversion = "1.0"
diff --git a/proc-macro-error/proc-macro-error/build.rs b/proc-macro-error/proc-macro-error/build.rs
new file mode 100644
index 0000000..04ddc11
--- /dev/null
+++ b/proc-macro-error/proc-macro-error/build.rs
@@ -0,0 +1,11 @@
+#[rustversion::nightly]
+fn nightly() {
+ println!("cargo:rustc-cfg=pme_nightly");
+}
+
+#[rustversion::not(nightly)]
+fn nightly() {}
+
+fn main() {
+ nightly()
+}
diff --git a/proc-macro-error/proc-macro-error/src/dummy.rs b/proc-macro-error/proc-macro-error/src/dummy.rs
new file mode 100644
index 0000000..f7443b3
--- /dev/null
+++ b/proc-macro-error/proc-macro-error/src/dummy.rs
@@ -0,0 +1,136 @@
+//! Facility to emit dummy implementations (or whatever) in case
+//! an error happen.
+//!
+//! `compile_error!` does not abort a compilation right away. This means
+//! `rustc` doesn't just show you the error and abort, it carries on the
+//! compilation process looking for other errors to report.
+//!
+//! Let's consider an example:
+//!
+//! ```rust,ignore
+//! use proc_macro::TokenStream;
+//! use proc_macro_error::*;
+//!
+//! trait MyTrait {
+//! fn do_thing();
+//! }
+//!
+//! // this proc macro is supposed to generate MyTrait impl
+//! #[proc_macro_derive(MyTrait)]
+//! #[proc_macro_error]
+//! fn example(input: TokenStream) -> TokenStream {
+//! // somewhere deep inside
+//! abort!(span, "something's wrong");
+//!
+//! // this implementation will be generated if no error happened
+//! quote! {
+//! impl MyTrait for #name {
+//! fn do_thing() {/* whatever */}
+//! }
+//! }
+//! }
+//!
+//! // ================
+//! // in main.rs
+//!
+//! // this derive triggers an error
+//! #[derive(MyTrait)] // first BOOM!
+//! struct Foo;
+//!
+//! fn main() {
+//! Foo::do_thing(); // second BOOM!
+//! }
+//! ```
+//!
+//! The problem is: the generated token stream contains only `compile_error!`
+//! invocation, the impl was not generated. That means user will see two compilation
+//! errors:
+//!
+//! ```text
+//! error: something's wrong
+//! --> $DIR/probe.rs:9:10
+//! |
+//! 9 |#[proc_macro_derive(MyTrait)]
+//! | ^^^^^^^
+//!
+//! error[E0599]: no function or associated item named `do_thing` found for type `Foo` in the current scope
+//! --> src\main.rs:3:10
+//! |
+//! 1 | struct Foo;
+//! | ----------- function or associated item `do_thing` not found for this
+//! 2 | fn main() {
+//! 3 | Foo::do_thing(); // second BOOM!
+//! | ^^^^^^^^ function or associated item not found in `Foo`
+//! ```
+//!
+//! But the second error is meaningless! We definitely need to fix this.
+//!
+//! Most used approach in cases like this is "dummy implementation" -
+//! omit `impl MyTrait for #name` and fill functions bodies with `unimplemented!()`.
+//!
+//! This is how you do it:
+//!
+//! ```rust,ignore
+//! use proc_macro::TokenStream;
+//! use proc_macro_error::*;
+//!
+//! trait MyTrait {
+//! fn do_thing();
+//! }
+//!
+//! // this proc macro is supposed to generate MyTrait impl
+//! #[proc_macro_derive(MyTrait)]
+//! #[proc_macro_error]
+//! fn example(input: TokenStream) -> TokenStream {
+//! // first of all - we set a dummy impl which will be appended to
+//! // `compile_error!` invocations in case a trigger does happen
+//! set_dummy(quote! {
+//! impl MyTrait for #name {
+//! fn do_thing() { unimplemented!() }
+//! }
+//! });
+//!
+//! // somewhere deep inside
+//! abort!(span, "something's wrong");
+//!
+//! // this implementation will be generated if no error happened
+//! quote! {
+//! impl MyTrait for #name {
+//! fn do_thing() {/* whatever */}
+//! }
+//! }
+//! }
+//!
+//! // ================
+//! // in main.rs
+//!
+//! // this derive triggers an error
+//! #[derive(MyTrait)] // first BOOM!
+//! struct Foo;
+//!
+//! fn main() {
+//! Foo::do_thing(); // no more errors!
+//! }
+//! ```
+
+use proc_macro2::TokenStream;
+use std::cell::Cell;
+
+use crate::check_correctness;
+
+thread_local! {
+ static DUMMY_IMPL: Cell<Option<TokenStream>> = Cell::new(None);
+}
+
+/// Sets dummy token stream which will be appended to `compile_error!(msg);...`
+/// invocations in case you'll emit any errors.
+///
+/// See [guide](../index.html#guide).
+pub fn set_dummy(dummy: TokenStream) -> Option<TokenStream> {
+ check_correctness();
+ DUMMY_IMPL.with(|old_dummy| old_dummy.replace(Some(dummy)))
+}
+
+pub(crate) fn cleanup() -> Option<TokenStream> {
+ DUMMY_IMPL.with(|old_dummy| old_dummy.replace(None))
+}
diff --git a/proc-macro-error/proc-macro-error/src/lib.rs b/proc-macro-error/proc-macro-error/src/lib.rs
new file mode 100644
index 0000000..5fb0223
--- /dev/null
+++ b/proc-macro-error/proc-macro-error/src/lib.rs
@@ -0,0 +1,514 @@
+//! # proc-macro-error
+//!
+//! This crate aims to make error reporting in proc-macros simple and easy to use.
+//! Migrate from `panic!`-based errors for as little effort as possible!
+//!
+//! Also, there's ability to [append a dummy token stream](dummy/index.html) to your errors.
+//!
+//! ## Limitations
+//!
+//! - Warnings are emitted only on nightly, they're ignored on stable.
+//! - "help" suggestions cannot have their own span info on stable, (they inherit parent span).
+//! - If a panic occurs somewhere in your macro no errors will be displayed. This is not a
+//! technical limitation but intentional design, `panic` is not for error reporting.
+//!
+//! ## Guide
+//!
+//! ### Macros
+//!
+//! First of all - **all the emitting-related API must be used within a function
+//! annotated with [`#[proc_macro_error]`](#proc_macro_error-attribute) attribute**. You'll just get a
+//! panic otherwise, no errors will be shown.
+//!
+//! For most of the time you will be using macros.
+//!
+//! - [`abort!`]:
+//!
+//! Very much panic-like usage - abort execution and show the error. Expands to [`!`] (never type).
+//!
+//! - [`abort_call_site!`]:
+//!
+//! Shortcut for `abort!(Span::call_site(), ...)`. Expands to [`!`] (never type).
+//!
+//! - [`emit_error!`]:
+//!
+//! [`proc_macro::Diagnostic`]-like usage - emit the error but do not abort the macro.
+//! The compilation will fail nonetheless. Expands to [`()`] (unit type).
+//!
+//! - [`emit_call_site_error!`]:
+//!
+//! Shortcut for `emit_error!(Span::call_site(), ...)`. Expands to [`()`] (unit type).
+//!
+//! - [`emit_warning!`]:
+//!
+//! Like `emit_error!` but emit a warning instead of error. The compilation won't fail
+//! because of warnings.
+//! Expands to [`()`] (unit type).
+//!
+//! **Beware**: warnings are nightly only, they are completely ignored on stable.
+//!
+//! - [`emit_call_site_warning!`]:
+//!
+//! Shortcut for `emit_warning!(Span::call_site(), ...)`. Expands to `()` (unit type).
+//!
+//! - [`diagnostic`]:
+//!
+//! Build instance of `Diagnostic` in format-like style.
+//!
+//! ### Syntax
+//!
+//! All the macros have pretty much the same syntax:
+//!
+//! 1. ```ignore
+//! abort!(single_expr)
+//! ```
+//! Shortcut for `Diagnostic::from().abort()`
+//!
+//! 2. ```ignore
+//! abort!(span, message)
+//! ```
+//! Shortcut for `Diagnostic::spanned(span, message.to_string()).abort()`
+//!
+//! 3. ```ignore
+//! abort!(span, format_literal, format_args...)
+//! ```
+//! Shortcut for `Diagnostic::spanned(span, format!(format_literal, format_args...)).abort()`
+//!
+//! That's it. `abort!`, `emit_warning`, `emit_error` share this exact syntax.
+//! `abort_call_site!`, `emit_call_site_warning`, `emit_call_site_error` lack 1 form
+//! and do not take span in 2 and 3 forms.
+//!
+//! `diagnostic!` require `Level` instance between `span` and second argument (1 form is the same).
+//!
+//! #### Note attachments
+//!
+//! 3. Every macro can have "note" attachments (only 2 and 3 form).
+//! ```ignore
+//! let opt_help = if have_some_info { Some("did you mean `this`?") } else { None };
+//!
+//! abort!(
+//! span, message; // <--- attachments start with `;` (semicolon)
+//!
+//! help = "format {} {}", "arg1", "arg2"; // <--- every attachment ends with `;`,
+//! // maybe except the last one
+//!
+//! note = "to_string"; // <--- one arg uses `.to_string()` instead of `format!()`
+//!
+//! yay = "I see what {} did here", "you"; // <--- "help =" and "hint =" are mapped to Diagnostic::help
+//! // anything else is Diagnostic::note
+//!
+//! wow = note_span => "custom span"; // <--- attachments can have their own span
+//! // it takes effect only on nightly though
+//!
+//! hint =? opt_help; // <-- "optional" attachment, get displayed only if `Some`
+//! // must be single `Option` expression
+//!
+//! note =? note_span => opt_help // <-- optional attachments can have custom spans too
+//! )
+//! ```
+//!
+//! ### `#[proc_macro_error]` attribute
+//!
+//! **This attribute MUST be present on the top level of your macro.**
+//!
+//! This attribute performs the setup and cleanup necessary to make things work.
+//!
+//! #### Syntax
+//!
+//! `#[proc_macro_error]` or `#[proc_macro_error(settings...)]`, where `settings...`
+//! is a comma-separated list of:
+//!
+//! - `proc_macro_hack`:
+//!
+//! To correctly cooperate with `#[proc_macro_hack]` `#[proc_macro_error]`
+//! attribute must be placed *before* (above) it, like this:
+//!
+//! ```ignore
+//! #[proc_macro_error]
+//! #[proc_macro_hack]
+//! #[proc_macro]
+//! fn my_macro(input: TokenStream) -> TokenStream {
+//! unimplemented!()
+//! }
+//! ```
+//!
+//! If, for some reason, you can't place it like that you can use
+//! `#[proc_macro_error(proc_macro_hack)]` instead.
+//!
+//! - `allow_not_macro`:
+//!
+//! By default, the attribute checks that it's applied to a proc-macro.
+//! If none of `#[proc_macro]`, `#[proc_macro_derive]` nor `#[proc_macro_attribute]` are
+//! present it will panic. It's the intention - this crate is supposed to be used only with
+//! proc-macros. This setting is made to bypass the check, useful in certain
+//! circumstances.
+//!
+//! Please note: the function this attribute is applied to must return `proc_macro::TokenStream`.
+//!
+//! - `assert_unwind_safe`:
+//!
+//! By default, your code must be [unwind safe]. If your code is not unwind safe but you believe
+//! it's correct you can use this setting to bypass the check. This is typically needed
+//! for code that uses `lazy_static` or `thread_local` with `Cell/RefCell` inside.
+//!
+//! This setting is implied if `#[proc_macro_error]` is applied to a function
+//! marked as `#[proc_macro]`, `#[proc_macro_derive]` or `#[proc_macro_attribute]`.
+//!
+//! ### Diagnostic type
+//!
+//! [`Diagnostic`] type is intentionally designed to be API compatible with [`proc_macro::Diagnostic`].
+//! Not all API is implemented, only the part that can be reasonably implemented on stable.
+//!
+//!
+//! [`abort!`]: macro.abort.html
+//! [`emit_warning!`]: macro.emit_warning.html
+//! [`emit_error!`]: macro.emit_error.html
+//! [`abort_call_site!`]: macro.abort_call_site.html
+//! [`emit_call_site_warning!`]: macro.emit_call_site_error.html
+//! [`emit_call_site_error!`]: macro.emit_call_site_warning.html
+//! [`diagnostic!`]: macro.diagnostic.html
+//! [proc_macro_error]: ./../proc_macro_error_attr/attr.proc_macro_error.html
+//! [`Diagnostic`]: struct.Diagnostic.html
+//! [`proc_macro::Diagnostic`]: https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html
+//! [unwind safe]: https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html#what-is-unwind-safety
+//! [`!`]: https://doc.rust-lang.org/std/primitive.never.html
+//! [`()`]: https://doc.rust-lang.org/std/primitive.unit.html
+
+#![cfg_attr(pme_nightly, feature(proc_macro_diagnostic))]
+#![forbid(unsafe_code)]
+
+// reexports for use in macros
+#[doc(hidden)]
+pub extern crate proc_macro;
+#[doc(hidden)]
+pub extern crate proc_macro2;
+
+pub use self::dummy::set_dummy;
+pub use proc_macro_error_attr::proc_macro_error;
+
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use quote::{quote_spanned, ToTokens};
+use std::cell::Cell;
+use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
+
+pub mod dummy;
+
+mod macros;
+
+#[cfg(not(any(pme_nightly, nightly_fmt)))]
+#[path = "stable.rs"]
+mod imp;
+
+#[cfg(any(pme_nightly, nightly_fmt))]
+#[path = "nightly.rs"]
+mod imp;
+
+/// Represents a diagnostic level
+///
+/// # Warnings
+///
+/// Warnings are ignored on stable/beta
+#[derive(Debug, PartialEq)]
+pub enum Level {
+ Error,
+ Warning,
+ #[doc(hidden)]
+ NonExhaustive,
+}
+
+/// Represents a single diagnostic message
+#[derive(Debug)]
+pub struct Diagnostic {
+ level: Level,
+ span: Span,
+ msg: String,
+ suggestions: Vec<(SuggestionKind, String, Option<Span>)>,
+}
+
+/// This traits expands `Result<T, Into<Diagnostic>>` with some handy shortcuts.
+pub trait ResultExt {
+ type Ok;
+
+ /// Behaves like `Result::unwrap`: if self is `Ok` yield the contained value,
+ /// otherwise abort macro execution via `abort!`.
+ fn unwrap_or_abort(self) -> Self::Ok;
+
+ /// Behaves like `Result::expect`: if self is `Ok` yield the contained value,
+ /// otherwise abort macro execution via `abort!`.
+ /// If it aborts then resulting error message will be preceded with `message`.
+ fn expect_or_abort(self, msg: &str) -> Self::Ok;
+}
+
+/// This traits expands `Option` with some handy shortcuts.
+pub trait OptionExt {
+ type Some;
+
+ /// Behaves like `Option::expect`: if self is `Some` yield the contained value,
+ /// otherwise abort macro execution via `abort_call_site!`.
+ /// If it aborts the `message` will be used for [`compile_error!`][compl_err] invocation.
+ ///
+ /// [compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html
+ fn expect_or_abort(self, msg: &str) -> Self::Some;
+}
+
+impl Diagnostic {
+ /// Create a new diagnostic message that points to `Span::call_site()`
+ pub fn new(level: Level, message: String) -> Self {
+ Diagnostic::spanned(Span::call_site(), level, message)
+ }
+
+ /// Create a new diagnostic message that points to the `span`
+ pub fn spanned(span: Span, level: Level, message: String) -> Self {
+ Diagnostic {
+ level,
+ span,
+ msg: message,
+ suggestions: vec![],
+ }
+ }
+
+ /// Attach a "help" note to your main message, note will have it's own span on nightly.
+ ///
+ /// # Span
+ ///
+ /// The span is ignored on stable, the note effectively inherits its parent's (main message) span
+ pub fn span_help(mut self, span: Span, msg: String) -> Self {
+ self.suggestions
+ .push((SuggestionKind::Help, msg, Some(span)));
+ self
+ }
+
+ /// Attach a "help" note to your main message,
+ pub fn help(mut self, msg: String) -> Self {
+ self.suggestions.push((SuggestionKind::Help, msg, None));
+ self
+ }
+
+ /// Attach a note to your main message, note will have it's own span on nightly.
+ ///
+ /// # Span
+ ///
+ /// The span is ignored on stable, the note effectively inherits its parent's (main message) span
+ pub fn span_note(mut self, span: Span, msg: String) -> Self {
+ self.suggestions
+ .push((SuggestionKind::Note, msg, Some(span)));
+ self
+ }
+
+ /// Attach a note to your main message
+ pub fn note(mut self, msg: String) -> Self {
+ self.suggestions.push((SuggestionKind::Note, msg, None));
+ self
+ }
+
+ /// The message of main warning/error (no notes attached)
+ pub fn message(&self) -> &str {
+ &self.msg
+ }
+
+ /// Abort the proc-macro's execution and display the diagnostic.
+ ///
+ /// # Warnings
+ ///
+ /// Warnings do not get emitted on stable/beta but this function will abort anyway.
+ pub fn abort(self) -> ! {
+ self.emit();
+ abort_now()
+ }
+
+ /// Display the diagnostic while not aborting macro execution.
+ ///
+ /// # Warnings
+ ///
+ /// Warnings are ignored on stable/beta
+ pub fn emit(self) {
+ imp::emit_diagnostic(self);
+ }
+}
+
+/// Abort macro execution and display all the emitted errors, if any.
+///
+/// Does nothing if no errors were emitted (warnings do not count).
+pub fn abort_if_dirty() {
+ imp::abort_if_dirty();
+}
+
+#[doc(hidden)]
+impl Diagnostic {
+ pub fn span_suggestion(self, span: Span, suggestion: &str, msg: String) -> Self {
+ match suggestion {
+ "help" | "hint" => self.span_help(span, msg),
+ _ => self.span_note(span, msg),
+ }
+ }
+
+ pub fn suggestion(self, suggestion: &str, msg: String) -> Self {
+ match suggestion {
+ "help" | "hint" => self.help(msg),
+ _ => self.note(msg),
+ }
+ }
+}
+
+impl ToTokens for Diagnostic {
+ fn to_tokens(&self, ts: &mut TokenStream) {
+ use std::borrow::Cow;
+
+ fn ensure_lf(buf: &mut String, s: &str) {
+ if s.ends_with('\n') {
+ buf.push_str(s);
+ } else {
+ buf.push_str(s);
+ buf.push('\n');
+ }
+ }
+
+ let Diagnostic {
+ ref msg,
+ ref suggestions,
+ ref level,
+ ..
+ } = *self;
+
+ if *level == Level::Warning {
+ return;
+ }
+
+ let message = if suggestions.is_empty() {
+ Cow::Borrowed(msg)
+ } else {
+ let mut message = String::new();
+ ensure_lf(&mut message, msg);
+ message.push('\n');
+
+ for (kind, note, _span) in suggestions {
+ message.push_str(" = ");
+ message.push_str(kind.name());
+ message.push_str(": ");
+ ensure_lf(&mut message, note);
+ }
+ message.push('\n');
+
+ Cow::Owned(message)
+ };
+
+ let span = &self.span;
+ let msg = syn::LitStr::new(&*message, *span);
+ ts.extend(quote_spanned!(*span=> compile_error!(#msg); ));
+ }
+}
+
+impl<T, E: Into<Diagnostic>> ResultExt for Result<T, E> {
+ type Ok = T;
+
+ fn unwrap_or_abort(self) -> T {
+ match self {
+ Ok(res) => res,
+ Err(e) => e.into().abort(),
+ }
+ }
+
+ fn expect_or_abort(self, message: &str) -> T {
+ match self {
+ Ok(res) => res,
+ Err(e) => {
+ let mut e = e.into();
+ e.msg = format!("{}: {}", message, e.msg);
+ e.abort()
+ }
+ }
+ }
+}
+
+impl<T> OptionExt for Option<T> {
+ type Some = T;
+
+ fn expect_or_abort(self, message: &str) -> T {
+ match self {
+ Some(res) => res,
+ None => abort_call_site!(message),
+ }
+ }
+}
+
+#[derive(Debug)]
+enum SuggestionKind {
+ Help,
+ Note,
+}
+
+impl SuggestionKind {
+ fn name(&self) -> &'static str {
+ match self {
+ SuggestionKind::Note => "note",
+ SuggestionKind::Help => "help",
+ }
+ }
+}
+
+impl From<syn::Error> for Diagnostic {
+ fn from(e: syn::Error) -> Self {
+ Diagnostic::spanned(e.span(), Level::Error, e.to_string())
+ }
+}
+
+/// This is the entry point for a proc-macro.
+///
+/// **NOT PUBLIC API, SUBJECT TO CHANGE WITHOUT ANY NOTICE**
+#[doc(hidden)]
+pub fn entry_point<F>(f: F, proc_macro_hack: bool) -> proc_macro::TokenStream
+where
+ F: FnOnce() -> proc_macro::TokenStream + UnwindSafe,
+{
+ ENTERED_ENTRY_POINT.with(|flag| flag.set(true));
+ let caught = catch_unwind(f);
+ let dummy = dummy::cleanup();
+ let err_storage = imp::cleanup();
+ ENTERED_ENTRY_POINT.with(|flag| flag.set(false));
+
+ let mut appendix = TokenStream::new();
+ if proc_macro_hack {
+ appendix.extend(quote! {
+ #[allow(unused)]
+ macro_rules! proc_macro_call {
+ () => ( unimplemented!() )
+ }
+ });
+ }
+
+ match caught {
+ Ok(ts) => {
+ if err_storage.is_empty() {
+ ts
+ } else {
+ quote!( #(#err_storage)* #dummy #appendix ).into()
+ }
+ }
+
+ Err(boxed) => match boxed.downcast::<AbortNow>() {
+ Ok(_) => quote!( #(#err_storage)* #dummy #appendix ).into(),
+ Err(boxed) => resume_unwind(boxed),
+ },
+ }
+}
+
+fn abort_now() -> ! {
+ check_correctness();
+ panic!(AbortNow)
+}
+
+thread_local! {
+ static ENTERED_ENTRY_POINT: Cell<bool> = Cell::new(false);
+}
+
+struct AbortNow;
+
+fn check_correctness() {
+ if !ENTERED_ENTRY_POINT.with(|flag| flag.get()) {
+ panic!(
+ "proc-macro-error API cannot be used outside of `entry_point` invocation, \
+ perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]"
+ );
+ }
+}
diff --git a/proc-macro-error/proc-macro-error/src/macros.rs b/proc-macro-error/proc-macro-error/src/macros.rs
new file mode 100644
index 0000000..117612c
--- /dev/null
+++ b/proc-macro-error/proc-macro-error/src/macros.rs
@@ -0,0 +1,257 @@
+// FIXME: this can be greatly simplified via $()?
+// as soon as MRSV hits 1.32
+
+/// Build [`Diagnostic`](struct.Diagnostic.html) instance from provided arguments.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! diagnostic {
+ // from alias
+ ($err:expr) => { $crate::Diagnostic::from($err) };
+
+ // span, message, help
+ ($span:expr, $level:expr, $fmt:expr, $($args:expr),+ ; $($rest:tt)+) => {{
+ let diag = $crate::Diagnostic::spanned(
+ $span.into(),
+ $level,
+ format!($fmt, $($args),*)
+ );
+ $crate::__pme__suggestions!(diag $($rest)*);
+ diag
+ }};
+
+ ($span:expr, $level:expr, $msg:expr ; $($rest:tt)+) => {{
+ let diag = $crate::Diagnostic::spanned($span.into(), $level, $msg.to_string());
+ $crate::__pme__suggestions!(diag $($rest)*);
+ diag
+ }};
+
+ // span, message, no help
+ ($span:expr, $level:expr, $fmt:expr, $($args:expr),+) => {{
+ $crate::Diagnostic::spanned(
+ $span.into(),
+ $level,
+ format!($fmt, $($args),*)
+ )
+ }};
+
+ ($span:expr, $level:expr, $msg:expr) => {{
+ $crate::Diagnostic::spanned($span.into(), $level, $msg.to_string())
+ }};
+
+
+ // trailing commas
+
+ ($span:expr, $level:expr, $fmt:expr, $($args:expr),+, ; $($rest:tt)+) => {
+ $crate::diagnostic!($span, $level, $fmt, $($args),* ; $($rest)*)
+ };
+ ($span:expr, $level:expr, $msg:expr, ; $($rest:tt)+) => {
+ $crate::diagnostic!($span, $level, $msg ; $($rest)*)
+ };
+ ($span:expr, $level:expr, $fmt:expr, $($args:expr),+,) => {
+ $crate::diagnostic!($span, $level, $fmt, $($args),*)
+ };
+ ($span:expr, $level:expr, $msg:expr,) => {
+ $crate::diagnostic!($span, $level, $msg)
+ };
+ // ($err:expr,) => { $crate::diagnostic!($err) };
+}
+
+/// Abort proc-macro execution right now and display the error.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+#[macro_export]
+macro_rules! abort {
+ ($err:expr) => {
+ $crate::diagnostic!($err).abort()
+ };
+
+ ($span:expr, $($tts:tt)*) => {
+ $crate::diagnostic!($span, $crate::Level::Error, $($tts)*).abort()
+ };
+}
+
+/// Shortcut for `abort!(Span::call_site(), msg...)`. This macro
+/// is still preferable over plain panic, panics are not for error reporting.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! abort_call_site {
+ ($($tts:tt)*) => {
+ $crate::diagnostic!(
+ $crate::proc_macro2::Span::call_site(),
+ $crate::Level::Error,
+ $($tts)*
+ ).abort()
+ };
+}
+
+/// Emit an error while not aborting the proc-macro right away.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_error {
+ ($err:expr) => {
+ $crate::diagnostic!($err).emit()
+ };
+
+ ($span:expr, $($tts:tt)*) => {{
+ let level = $crate::Level::Error;
+ $crate::diagnostic!($span, level, $($tts)*).emit()
+ }};
+}
+
+/// Shortcut for `emit_error!(Span::call_site(), ...)`. This macro
+/// is still preferable over plain panic, panics are not for error reporting..
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_call_site_error {
+ ($($tts:tt)*) => {
+ $crate::diagnostic!(
+ $crate::proc_macro2::Span()::call_site(),
+ $crate::Level::Error,
+ $($tts)*
+ ).emit()
+ };
+}
+
+/// Emit a warning. Warnings are not errors and compilation won't fail because of them.
+///
+/// **Does nothing on stable**
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_warning {
+ ($span:expr, $($tts:tt)*) => {
+ $crate::diagnostic!($span, $crate::Level::Warning, $($tts)*).emit()
+ };
+}
+
+/// Shortcut for `emit_warning!(Span::call_site(), ...)`.
+///
+/// **Does nothing on stable**
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_call_site_warning {
+ ($($tts:tt)*) => {{
+ let span = $crate::proc_macro2::Span()::call_site();
+ $crate::diagnostic!(span, $crate::Level::Warning, $($tts)*).emit()
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __pme__suggestions {
+ ($var:ident) => ();
+
+ ($var:ident $help:ident =? $msg:expr) => {
+ let $var = if let Some(msg) = $msg {
+ $var.suggestion(stringify!($help), msg.to_string())
+ } else {
+ $var
+ };
+ };
+ ($var:ident $help:ident =? $span:expr => $msg:expr) => {
+ let $var = if let Some(msg) = $msg {
+ $var.span_suggestion($span.into(), stringify!($help), msg.to_string())
+ } else {
+ $var
+ };
+ };
+
+ ($var:ident $help:ident =? $msg:expr ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help =? $msg);
+ $crate::__pme__suggestions!($var $($rest)*);
+ };
+ ($var:ident $help:ident =? $span:expr => $msg:expr ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help =? $span => $msg);
+ $crate::__pme__suggestions!($var $($rest)*);
+ };
+
+
+ ($var:ident $help:ident = $msg:expr) => {
+ let $var = $var.suggestion(stringify!($help), $msg.to_string());
+ };
+ ($var:ident $help:ident = $fmt:expr, $($args:expr),+) => {
+ let $var = $var.suggestion(
+ stringify!($help),
+ format!($fmt, $($args),*)
+ );
+ };
+ ($var:ident $help:ident = $span:expr => $msg:expr) => {
+ let $var = $var.span_suggestion($span.into(), stringify!($help), $msg.to_string());
+ };
+ ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),+) => {
+ let $var = $var.span_suggestion(
+ $span.into(),
+ stringify!($help),
+ format!($fmt, $($args),*)
+ );
+ };
+
+ ($var:ident $help:ident = $msg:expr ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $msg);
+ $crate::__pme__suggestions!($var $($rest)*);
+ };
+ ($var:ident $help:ident = $fmt:expr, $($args:expr),+ ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $fmt, $($args),*);
+ $crate::__pme__suggestions!($var $($rest)*);
+ };
+ ($var:ident $help:ident = $span:expr => $msg:expr ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $span => $msg);
+ $crate::__pme__suggestions!($var $($rest)*);
+ };
+ ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),+ ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $span => $fmt, $($args),*);
+ $crate::__pme__suggestions!($var $($rest)*);
+ };
+
+ // trailing commas
+
+ ($var:ident $help:ident = $msg:expr,) => {
+ $crate::__pme__suggestions!($var $help = $msg)
+ };
+ ($var:ident $help:ident = $fmt:expr, $($args:expr),+,) => {
+ $crate::__pme__suggestions!($var $help = $fmt, $($args)*)
+ };
+ ($var:ident $help:ident = $span:expr => $msg:expr,) => {
+ $crate::__pme__suggestions!($var $help = $span => $msg)
+ };
+ ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),*,) => {
+ $crate::__pme__suggestions!($var $help = $span => $fmt, $($args)*)
+ };
+ ($var:ident $help:ident = $msg:expr, ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $msg; $($rest)*)
+ };
+ ($var:ident $help:ident = $fmt:expr, $($args:expr),+, ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $fmt, $($args),*; $($rest)*)
+ };
+ ($var:ident $help:ident = $span:expr => $msg:expr, ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $span => $msg; $($rest)*)
+ };
+ ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),+, ; $($rest:tt)*) => {
+ $crate::__pme__suggestions!($var $help = $span => $fmt, $($args),*; $($rest)*)
+ };
+}
diff --git a/proc-macro-error/proc-macro-error/src/nightly.rs b/proc-macro-error/proc-macro-error/src/nightly.rs
new file mode 100644
index 0000000..d053c2f
--- /dev/null
+++ b/proc-macro-error/proc-macro-error/src/nightly.rs
@@ -0,0 +1,49 @@
+use std::sync::atomic::{AtomicBool, Ordering};
+
+use proc_macro::{Diagnostic as PDiag, Level as PLevel};
+
+use crate::{abort_now, check_correctness, Diagnostic, Level, SuggestionKind};
+
+pub fn abort_if_dirty() {
+ check_correctness();
+ if IS_DIRTY.load(Ordering::SeqCst) {
+ abort_now()
+ }
+}
+
+pub(crate) fn cleanup() -> Vec<Diagnostic> {
+ vec![]
+}
+
+pub(crate) fn emit_diagnostic(diag: Diagnostic) {
+ let Diagnostic {
+ level,
+ span,
+ msg,
+ suggestions,
+ } = diag;
+
+ let level = match level {
+ Level::Warning => PLevel::Warning,
+ Level::Error => {
+ IS_DIRTY.store(true, Ordering::SeqCst);
+ PLevel::Error
+ }
+ _ => unreachable!(),
+ };
+
+ let mut res = PDiag::spanned(span.unwrap(), level, msg);
+
+ for (kind, msg, span) in suggestions {
+ res = match (kind, span) {
+ (SuggestionKind::Note, Some(span)) => res.span_note(span.unwrap(), msg),
+ (SuggestionKind::Help, Some(span)) => res.span_help(span.unwrap(), msg),
+ (SuggestionKind::Note, None) => res.note(msg),
+ (SuggestionKind::Help, None) => res.help(msg),
+ }
+ }
+
+ res.emit()
+}
+
+static IS_DIRTY: AtomicBool = AtomicBool::new(false);
diff --git a/proc-macro-error/proc-macro-error/src/stable.rs b/proc-macro-error/proc-macro-error/src/stable.rs
new file mode 100644
index 0000000..07042d3
--- /dev/null
+++ b/proc-macro-error/proc-macro-error/src/stable.rs
@@ -0,0 +1,26 @@
+use std::cell::RefCell;
+
+use crate::{abort_now, check_correctness, Diagnostic, Level};
+
+pub fn abort_if_dirty() {
+ check_correctness();
+ ERR_STORAGE.with(|storage| {
+ if !storage.borrow().is_empty() {
+ abort_now()
+ }
+ });
+}
+
+pub(crate) fn cleanup() -> Vec<Diagnostic> {
+ ERR_STORAGE.with(|storage| storage.replace(Vec::new()))
+}
+
+pub(crate) fn emit_diagnostic(diag: Diagnostic) {
+ if diag.level == Level::Error {
+ ERR_STORAGE.with(|storage| storage.borrow_mut().push(diag));
+ }
+}
+
+thread_local! {
+ static ERR_STORAGE: RefCell<Vec<Diagnostic>> = RefCell::new(Vec::new());
+}
diff --git a/proc-macro-error/test-crate/Cargo.toml b/proc-macro-error/test-crate/Cargo.toml
new file mode 100644
index 0000000..96dadc3
--- /dev/null
+++ b/proc-macro-error/test-crate/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "test-crate"
+version = "0.0.0"
+authors = ["CreepySkeleton <creepy-skeleton@yandex.ru>"]
+edition = "2018"
+publish = false
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro-error = { path = "../proc-macro-error"}
+quote = "1"
+proc-macro2 = "1"
+syn = {version = "1", default-features = false }
+
+[dev-dependencies]
+trybuild = "1.0"
+rustversion = "1.0"
+toml = "=0.5.2" # DO NOT BUMP
diff --git a/proc-macro-error/test-crate/src/lib.rs b/proc-macro-error/test-crate/src/lib.rs
new file mode 100644
index 0000000..2ea9175
--- /dev/null
+++ b/proc-macro-error/test-crate/src/lib.rs
@@ -0,0 +1,123 @@
+#[macro_use]
+extern crate proc_macro_error;
+#[macro_use]
+extern crate syn;
+extern crate proc_macro;
+
+use proc_macro2::Span;
+use proc_macro_error::{set_dummy, Level, OptionExt, ResultExt};
+use syn::{
+ parse::{Parse, ParseStream},
+ punctuated::Punctuated,
+ spanned::Spanned,
+ Ident,
+};
+
+struct IdentOrUnderscore {
+ span: Span,
+ part: String,
+}
+
+impl IdentOrUnderscore {
+ fn new(span: Span, part: String) -> Self {
+ IdentOrUnderscore { span, part }
+ }
+}
+
+impl Parse for IdentOrUnderscore {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let la = input.lookahead1();
+
+ if la.peek(Ident) {
+ let t = input.parse::<Ident>().unwrap();
+ Ok(IdentOrUnderscore::new(t.span(), t.to_string()))
+ } else if la.peek(Token![_]) {
+ let t = input.parse::<Token![_]>().unwrap();
+ Ok(IdentOrUnderscore::new(t.span(), "_".to_string()))
+ } else {
+ Err(la.error())
+ }
+ }
+}
+
+struct Args(Vec<IdentOrUnderscore>);
+
+impl Parse for Args {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let args = Punctuated::<_, Token![,]>::parse_terminated(input)?;
+ Ok(Args(args.into_iter().collect()))
+ }
+}
+
+#[proc_macro]
+#[proc_macro_error]
+pub fn make_fn(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let mut name = String::new();
+ let input = parse_macro_input!(input as Args);
+
+ for arg in input.0 {
+ match &*arg.part {
+ "abort" => abort!(
+ arg.span,
+ "abort! 3{} args {}", "+", "test";
+ hint = "help {} test", "message"
+ ),
+
+ "abort_call_site" => abort_call_site!(
+ "abort_call_site! 2{} args {}", "+", "test";
+ help = "help {} test", "message"
+ ),
+
+ "direct_abort" => {
+ diagnostic!(arg.span, Level::Error, "direct MacroError::abort() test").abort()
+ }
+
+ "result_expect" => {
+ let e = syn::Error::new(arg.span, "error");
+ Err(e).expect_or_abort("Result::expect_or_abort() test")
+ }
+
+ "result_unwrap" => {
+ let e = syn::Error::new(arg.span, "Result::unwrap_or_abort() test");
+ Err(e).unwrap_or_abort()
+ }
+
+ "option_expect" => None.expect_or_abort("Option::expect_or_abort() test"),
+
+ "need_default" => {
+ set_dummy(quote! {
+ impl Default for NeedDefault {
+ fn default() -> Self {
+ NeedDefault::A
+ }
+ }
+ });
+
+ abort!(arg.span, "set_dummy test")
+ }
+
+ part if part.starts_with("multi") => {
+ let no_help: Option<String> = Option::None;
+ let help = Some("Option help test");
+ emit_error!(
+ arg.span,
+ "multiple error part: {}", part;
+ note = "help {} test", "message";
+ hint =? help;
+ wow = "I see what you did here...";
+ help =? no_help
+ )
+ }
+
+ _ => name.push_str(&arg.part),
+ }
+ }
+
+ // test that unrelated panics are not affected
+ if name.is_empty() {
+ panic!("unrelated panic test")
+ }
+
+ let name = Ident::new(&name, Span::call_site());
+ quote!( fn #name() {} ).into()
+}
diff --git a/proc-macro-error/test-crate/tests/macro-errors.rs b/proc-macro-error/test-crate/tests/macro-errors.rs
new file mode 100644
index 0000000..8c672eb
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/macro-errors.rs
@@ -0,0 +1,6 @@
+#[rustversion::attr(any(not(stable), before(1.39)), ignore)]
+#[test]
+fn ui() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/ui/*.rs");
+}
diff --git a/proc-macro-error/test-crate/tests/ok.rs b/proc-macro-error/test-crate/tests/ok.rs
new file mode 100644
index 0000000..9b6a3d1
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ok.rs
@@ -0,0 +1,9 @@
+extern crate test_crate;
+
+use test_crate::make_fn;
+
+make_fn!(it, _, works);
+
+fn main() {
+ it_works();
+}
diff --git a/proc-macro-error/test-crate/tests/ui/abort.rs b/proc-macro-error/test-crate/tests/ui/abort.rs
new file mode 100644
index 0000000..717d772
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/abort.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!(abort);
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/abort.stderr b/proc-macro-error/test-crate/tests/ui/abort.stderr
new file mode 100644
index 0000000..7c4e6a0
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/abort.stderr
@@ -0,0 +1,8 @@
+error: abort! 3+ args test
+
+ = help: help message test
+
+ --> $DIR/abort.rs:4:10
+ |
+4 | make_fn!(abort);
+ | ^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/call_site.rs b/proc-macro-error/test-crate/tests/ui/call_site.rs
new file mode 100644
index 0000000..7184cc4
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/call_site.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!(abort_call_site);
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/call_site.stderr b/proc-macro-error/test-crate/tests/ui/call_site.stderr
new file mode 100644
index 0000000..d630a3a
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/call_site.stderr
@@ -0,0 +1,8 @@
+error: abort_call_site! 2+ args test
+
+ = help: help message test
+
+ --> $DIR/call_site.rs:4:1
+ |
+4 | make_fn!(abort_call_site);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
diff --git a/proc-macro-error/test-crate/tests/ui/direct_abort.rs b/proc-macro-error/test-crate/tests/ui/direct_abort.rs
new file mode 100644
index 0000000..b5a4c97
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/direct_abort.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!(direct_abort);
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/direct_abort.stderr b/proc-macro-error/test-crate/tests/ui/direct_abort.stderr
new file mode 100644
index 0000000..7cfbae8
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/direct_abort.stderr
@@ -0,0 +1,5 @@
+error: direct MacroError::abort() test
+ --> $DIR/direct_abort.rs:4:10
+ |
+4 | make_fn!(direct_abort);
+ | ^^^^^^^^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/dummy.rs b/proc-macro-error/test-crate/tests/ui/dummy.rs
new file mode 100644
index 0000000..7514fe0
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/dummy.rs
@@ -0,0 +1,16 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+enum NeedDefault {
+ A,
+ B
+}
+
+make_fn!(need_default);
+
+fn main() {
+ let _ = NeedDefault::default();
+}
+
+
+
diff --git a/proc-macro-error/test-crate/tests/ui/dummy.stderr b/proc-macro-error/test-crate/tests/ui/dummy.stderr
new file mode 100644
index 0000000..fd531be
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/dummy.stderr
@@ -0,0 +1,5 @@
+error: set_dummy test
+ --> $DIR/dummy.rs:9:10
+ |
+9 | make_fn!(need_default);
+ | ^^^^^^^^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/multi-error.rs b/proc-macro-error/test-crate/tests/ui/multi-error.rs
new file mode 100644
index 0000000..07fbb03
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/multi-error.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!(multi1, multi2, _, multi3);
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/multi-error.stderr b/proc-macro-error/test-crate/tests/ui/multi-error.stderr
new file mode 100644
index 0000000..25174d5
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/multi-error.stderr
@@ -0,0 +1,32 @@
+error: multiple error part: multi1
+
+ = note: help message test
+ = help: Option help test
+ = note: I see what you did here...
+
+ --> $DIR/multi-error.rs:4:10
+ |
+4 | make_fn!(multi1, multi2, _, multi3);
+ | ^^^^^^
+
+error: multiple error part: multi2
+
+ = note: help message test
+ = help: Option help test
+ = note: I see what you did here...
+
+ --> $DIR/multi-error.rs:4:18
+ |
+4 | make_fn!(multi1, multi2, _, multi3);
+ | ^^^^^^
+
+error: multiple error part: multi3
+
+ = note: help message test
+ = help: Option help test
+ = note: I see what you did here...
+
+ --> $DIR/multi-error.rs:4:29
+ |
+4 | make_fn!(multi1, multi2, _, multi3);
+ | ^^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/not_proc_macro.rs b/proc-macro-error/test-crate/tests/ui/not_proc_macro.rs
new file mode 100644
index 0000000..e241c5c
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/not_proc_macro.rs
@@ -0,0 +1,4 @@
+use proc_macro_error::proc_macro_error;
+
+#[proc_macro_error]
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/not_proc_macro.stderr b/proc-macro-error/test-crate/tests/ui/not_proc_macro.stderr
new file mode 100644
index 0000000..52d6a09
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/not_proc_macro.stderr
@@ -0,0 +1,8 @@
+error: #[proc_macro_error] attribute can be used only with a proc-macro
+
+ hint: if you are really sure that #[proc_macro_error] should be applied to this exact function use #[proc_macro_error(allow_not_macro)]
+
+ --> $DIR/not_proc_macro.rs:3:1
+ |
+3 | #[proc_macro_error]
+ | ^^^^^^^^^^^^^^^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/option_expect.rs b/proc-macro-error/test-crate/tests/ui/option_expect.rs
new file mode 100644
index 0000000..20288ca
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/option_expect.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!(option_expect);
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/option_expect.stderr b/proc-macro-error/test-crate/tests/ui/option_expect.stderr
new file mode 100644
index 0000000..dd9ecd8
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/option_expect.stderr
@@ -0,0 +1,5 @@
+error: Option::expect_or_abort() test
+ --> $DIR/option_expect.rs:4:1
+ |
+4 | make_fn!(option_expect);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
diff --git a/proc-macro-error/test-crate/tests/ui/result_expect.rs b/proc-macro-error/test-crate/tests/ui/result_expect.rs
new file mode 100644
index 0000000..a42740b
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/result_expect.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!(result_expect);
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/result_expect.stderr b/proc-macro-error/test-crate/tests/ui/result_expect.stderr
new file mode 100644
index 0000000..c2dd81c
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/result_expect.stderr
@@ -0,0 +1,5 @@
+error: Result::expect_or_abort() test: error
+ --> $DIR/result_expect.rs:4:10
+ |
+4 | make_fn!(result_expect);
+ | ^^^^^^^^^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/result_unwrap.rs b/proc-macro-error/test-crate/tests/ui/result_unwrap.rs
new file mode 100644
index 0000000..9b7fb1c
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/result_unwrap.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!(result_unwrap);
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/result_unwrap.stderr b/proc-macro-error/test-crate/tests/ui/result_unwrap.stderr
new file mode 100644
index 0000000..2e614bd
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/result_unwrap.stderr
@@ -0,0 +1,5 @@
+error: Result::unwrap_or_abort() test
+ --> $DIR/result_unwrap.rs:4:10
+ |
+4 | make_fn!(result_unwrap);
+ | ^^^^^^^^^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/unknown_setting.rs b/proc-macro-error/test-crate/tests/ui/unknown_setting.rs
new file mode 100644
index 0000000..d8e58ea
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/unknown_setting.rs
@@ -0,0 +1,4 @@
+use proc_macro_error::proc_macro_error;
+
+#[proc_macro_error(allow_not_macro, assert_unwind_safe, trololo)]
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/unknown_setting.stderr b/proc-macro-error/test-crate/tests/ui/unknown_setting.stderr
new file mode 100644
index 0000000..a55de0b
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/unknown_setting.stderr
@@ -0,0 +1,5 @@
+error: unknown setting `trololo`, expected one of `assert_unwind_safe`, `allow_not_macro`, `proc_macro_hack`
+ --> $DIR/unknown_setting.rs:3:57
+ |
+3 | #[proc_macro_error(allow_not_macro, assert_unwind_safe, trololo)]
+ | ^^^^^^^
diff --git a/proc-macro-error/test-crate/tests/ui/unrelated_panic.rs b/proc-macro-error/test-crate/tests/ui/unrelated_panic.rs
new file mode 100644
index 0000000..4863e5b
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/unrelated_panic.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::make_fn;
+
+make_fn!();
+
+fn main() {}
diff --git a/proc-macro-error/test-crate/tests/ui/unrelated_panic.stderr b/proc-macro-error/test-crate/tests/ui/unrelated_panic.stderr
new file mode 100644
index 0000000..b852cfd
--- /dev/null
+++ b/proc-macro-error/test-crate/tests/ui/unrelated_panic.stderr
@@ -0,0 +1,7 @@
+error: proc macro panicked
+ --> $DIR/unrelated_panic.rs:4:1
+ |
+4 | make_fn!();
+ | ^^^^^^^^^^^
+ |
+ = help: message: unrelated panic test