feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"b5cd28bcd700a00eede3eefc22c7c7f47489df158f3c8ac16748e7238107bf00",".github/workflows/ci.yml":"ff5648ca7deb2361614fd1ef4004c7cb5b88fcf91254d60213e2814a0e8677a8","CHANGELOG.md":"be4c548354e0713a7ce362a6d668d5304c940e02c9e7c95c0f58763691088e8d","Cargo.toml":"9419aea8db386d6d06fabe4b2b808b853e233e84df0484d598b81a1836d64ae8","Cargo.toml.orig":"cc1ef48b17690d7c657b874c358ebca1e22ae641faaf5f2c7b2cbf521de6d83d","LICENSE-APACHE":"4665f973ccb9393807a7fb1264add6b3513d19abc6b357e4cb52c6fe59cc6a3b","LICENSE-MIT":"544b3aed1fd723d0cadea567affdcfe0431e43e18d997a718f9d67256b814fde","README.md":"186fa2911fda58f18b9c012f844e6110cc497791643dd01854988b2bf4f36b45","src/diagnostic.rs":"638f2c7113f71694d4249e1c61b814d60e65cabca771eb197b531a9e33fd5926","src/dummy.rs":"ae20aeaa2262067b281492db68cbc8669ae65bfcbac91f6d471d0f8f89047148","src/imp/delegate.rs":"ba738cf74ae42fbe058bf3251a412c6131f332d74eed0c1644877a35565cf8dd","src/imp/fallback.rs":"125aa1d68c96c471f39af2c441891a7fc61ca8366635bccb6eea94027d9aa084","src/lib.rs":"eeada6be6ecd1172526ffd680372e9a1d5667a68820a85d91b8719f39ab879d4","src/macros.rs":"3be6feccd343cd9dc4bf03780f3107909bf70e02c6c7c72912e4b160dc6a68fc","src/sealed.rs":"dcf569c4c7ce1d372ff51b0fa73fa67f995bdca8e520cb225cde789c71276439","tests/macro-errors.rs":"ff16f408bd53576995522af1bd6919ef5791d571c03dabd19aed44d7f08e7058","tests/ok.rs":"8379b6066c0b8bd939fee7d92bf6306f19f8300b0b03c201cc78a45f4a58f831","tests/runtime-errors.rs":"fdab67272feaa97fb853e5f015325cd411dca59e4d2f6fe00d0aeaf8546e3760","tests/ui/abort.rs":"25629a3319c8dec464b8bd37d243379348a4c974b22c0df01b55bca499b66ecf","tests/ui/abort.stderr":"41dfc53c80681a3fedc334663e45a3d5237212458800fa4ff5914e24b1e15080","tests/ui/append_dummy.rs":"42942587c0720f3cfca2de7a2a1a38b1c14b35bcfd737c0f95a445884332df95","tests/ui/append_dummy.stderr":"9ea462a1fcc5cc10571c86920c24cc8f223e0fe60d33be55c02005d57faeb6ee","tests/ui/children_messages.rs":"aac44ffcfc73726d9e347735a8a7dea32c7cc0decb2360e3cbdbdcfe46d15f09","tests/ui/children_messages.stderr":"0b09c7eeefa62c31ecd07670980fd8345c243dac89cb1402415ff7f800798bb8","tests/ui/dummy.rs":"e7134b23dc19ef9a58d4b253483e45bcfa882c5c373ce1811cba5acdc5e6287c","tests/ui/dummy.stderr":"3d9394b1a4ce864276ee24be2e3c828f79f76f137bfe0ed9dca6a9b6b7e6ef2f","tests/ui/emit.rs":"ae9dfcc4dbc3e4571db627b8940ae5f448db87c1bf7bc6927c85ec9b9c58b53d","tests/ui/emit.stderr":"03be9ee199a6819d30ed340f64c558fe05214739060ada29c09e48cf55ee0d3a","tests/ui/explicit_span_range.rs":"64e7a187856475d1ad501124355db46ee73cfc3250a0da9b5112338100334b0d","tests/ui/explicit_span_range.stderr":"d8a1288274101ee06baad91280d023732acac0f2a5843c65ea107720698549ad","tests/ui/misuse.rs":"e504820bf8c52854073a6ecfd3486c2759deee7514079ec80ebee5a614a7ccea","tests/ui/misuse.stderr":"d6ec6d638f0f107e8faa97741cd5354583a1ffa4bee07eccc8a4069dd838f9f3","tests/ui/multiple_tokens.rs":"c212bcd27d8551a42bbf2e83a254bc13adb02fbb09cb7df82777b1b426f07451","tests/ui/multiple_tokens.stderr":"c7026c7931c42e204f2c67623cc2755137720182bfc5e4b79e61ae0d7d647f72","tests/ui/not_proc_macro.rs":"23034d95ba47cda5a349b672c3ce5c93fe5ed0d51b4cdb3bf17f9cdad434de32","tests/ui/not_proc_macro.stderr":"12dbf434f8f6f2c4517cba2b07e1f28e0d87f4e87f27527c0c4341373fd6563c","tests/ui/option_ext.rs":"38a7377e4ff1858a5b95116e285c744daf6a010e1deb8fdd9d53532ac745ee70","tests/ui/option_ext.stderr":"986b9d212ad26e7e4854991fa2107fb0d52274b8a92ead4aadab2bb41ac2281b","tests/ui/result_ext.rs":"84f6dfe83fa619d2eac4a6df1f285b0e36755d15ecff75445b1d0dc96ebd2b28","tests/ui/result_ext.stderr":"006105f86d2533309191dbce61f6f388e5d03c94474f1bcd1481db314e4a66ec","tests/ui/to_tokens_span.rs":"4b89ddd4a727422da558c02887ffd712da9534d230593eb7b8e367a0f62eb2cf","tests/ui/to_tokens_span.stderr":"41cc86b25d20bf568b8f14c8572f991e4cf2a0f7c4e35978d35191974a0fa2b4","tests/ui/unknown_setting.rs":"ac88d5775207ed57b921a3139586e055ddcf5f683227fad5723a2a8ddfb7392e","tests/ui/unknown_setting.stderr":"746ef8d8cf2a60acf84b4ba81ae034682aaa8b5f5275803a34fce63378a0f0df","tests/ui/unrelated_panic.rs":"444e74b52373e260ec150de951b27b72f93b0acc3763e064cefbf320189d4bf1","tests/ui/unrelated_panic.stderr":"a21b02875cfc58271c2d8cbdde40a77f41f82c4b829a985f42f579c6e85c4524"},"package":"11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "9b129234ed8f50d3ff8da09041dcbd980b63fc8c"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust-version: ["stable", "beta", "nightly"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup toolchain install ${{ matrix.rust-version }}
|
||||
- run: cargo +${{ matrix.rust-version }} test --all
|
||||
|
||||
msrv-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup toolchain install 1.61
|
||||
- run: cargo +1.61 check --all
|
||||
|
||||
ui-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: RUSTFLAGS='--cfg run_ui_tests' cargo +stable test ui
|
||||
|
||||
test-fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cargo fmt --check --all
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
# v2.0.1 (2024-09-06)
|
||||
|
||||
* Fixed a span location issue due to mistake in refactoring (#2)
|
||||
|
||||
# v2.0.0 (2024-09-05)
|
||||
|
||||
No changes, simply releasing pre-release as full release.
|
||||
|
||||
# v2.0.0-pre.1 (2024-09-01)
|
||||
|
||||
* __Crate has been renamed to `proc-macro-error2`, due to the old maintainer's inactivity.__
|
||||
|
||||
* `syn` has been upgraded to `2`
|
||||
* MSRV has been bumped to `1.61`
|
||||
* Warnings have been fixed, including `clippy::pedantic` lints
|
||||
* CI has been converted to GitHub actions, and testing infrastructure significantly simplified.
|
||||
* Automatic nightly detection has been removed, use the `nightly` feature for improved diagnostics at the cost of stablity.
|
||||
|
||||
# v1.0.4 (2020-7-31)
|
||||
|
||||
* `SpanRange` facility is now public.
|
||||
* Docs have been improved.
|
||||
* Introduced the `syn-error` feature so you can opt-out from the `syn` dependency.
|
||||
|
||||
# v1.0.3 (2020-6-26)
|
||||
|
||||
* Corrected a few typos.
|
||||
* Fixed the `emit_call_site_warning` macro.
|
||||
|
||||
# v1.0.2 (2020-4-9)
|
||||
|
||||
* An obsolete note was removed from documentation.
|
||||
|
||||
# v1.0.1 (2020-4-9)
|
||||
|
||||
* `proc-macro-hack` is now well tested and supported. Not sure about `proc-macro-nested`,
|
||||
please fill a request if you need it.
|
||||
* Fixed `emit_call_site_error`.
|
||||
* Documentation improvements.
|
||||
|
||||
# v1.0.0 (2020-3-25)
|
||||
|
||||
I believe the API can be considered stable because it's been a few months without
|
||||
breaking changes, and I also don't think this crate will receive much further evolution.
|
||||
It's perfect, admit it.
|
||||
|
||||
Hence, meet the new, stable release!
|
||||
|
||||
### Improvements
|
||||
|
||||
* Supported nested `#[proc_macro_error]` attributes. Well, you aren't supposed to do that,
|
||||
but I caught myself doing it by accident on one occasion and the behavior was... surprising.
|
||||
Better to handle this smooth.
|
||||
|
||||
# v0.4.12 (2020-3-23)
|
||||
|
||||
* Error message on macros' misuse is now a bit more understandable.
|
||||
|
||||
# v0.4.11 (2020-3-02)
|
||||
|
||||
* `build.rs` no longer fails when `rustc` date could not be determined,
|
||||
(thanks to [`Fabian Möller`](https://gitlab.com/CreepySkeleton/proc-macro-error/issues/8)
|
||||
for noticing and to [`Igor Gnatenko`](https://gitlab.com/CreepySkeleton/proc-macro-error/-/merge_requests/25)
|
||||
for fixing).
|
||||
|
||||
# v0.4.10 (2020-2-29)
|
||||
|
||||
* `proc-macro-error` doesn't depend on syn\[full\] anymore, the compilation
|
||||
is \~30secs faster.
|
||||
|
||||
# v0.4.9 (2020-2-13)
|
||||
|
||||
* New function: `append_dummy`.
|
||||
|
||||
# v0.4.8 (2020-2-01)
|
||||
|
||||
* Support for children messages
|
||||
|
||||
# v0.4.7 (2020-1-31)
|
||||
|
||||
* Now any type that implements `quote::ToTokens` can be used instead of spans.
|
||||
This allows for high quality error messages.
|
||||
|
||||
# v0.4.6 (2020-1-31)
|
||||
|
||||
* `From<syn::Error>` implementation doesn't lose span info anymore, see
|
||||
[#6](https://gitlab.com/CreepySkeleton/proc-macro-error/issues/6).
|
||||
|
||||
# v0.4.5 (2020-1-20)
|
||||
Just a small intermediate release.
|
||||
|
||||
* Fix some bugs.
|
||||
* Populate license files into subfolders.
|
||||
|
||||
# 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`.
|
||||
Vendored
+91
@@ -0,0 +1,91 @@
|
||||
# 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"
|
||||
rust-version = "1.61"
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
authors = [
|
||||
"CreepySkeleton <creepy-skeleton@yandex.ru>",
|
||||
"GnomedDev <david2005thomas@gmail.com>",
|
||||
]
|
||||
build = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "Almost drop-in replacement to panics in proc-macros"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"proc-macro",
|
||||
"error",
|
||||
"errors",
|
||||
]
|
||||
categories = ["development-tools::procedural-macro-helpers"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/GnomedDev/proc-macro-error-2"
|
||||
|
||||
[lib]
|
||||
name = "proc_macro_error2"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[test]]
|
||||
name = "macro-errors"
|
||||
path = "tests/macro-errors.rs"
|
||||
|
||||
[[test]]
|
||||
name = "ok"
|
||||
path = "tests/ok.rs"
|
||||
|
||||
[[test]]
|
||||
name = "runtime-errors"
|
||||
path = "tests/runtime-errors.rs"
|
||||
|
||||
[dependencies.proc-macro-error-attr2]
|
||||
version = "=2.0.0"
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "1"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "2"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.syn]
|
||||
version = "2"
|
||||
features = ["full"]
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1.0.99"
|
||||
features = ["diff"]
|
||||
|
||||
[features]
|
||||
default = ["syn-error"]
|
||||
nightly = []
|
||||
syn-error = ["dep:syn"]
|
||||
|
||||
[lints.clippy.module_name_repetitions]
|
||||
level = "allow"
|
||||
priority = 0
|
||||
|
||||
[lints.clippy.pedantic]
|
||||
level = "warn"
|
||||
priority = -1
|
||||
|
||||
[lints.rust.unexpected_cfgs]
|
||||
level = "warn"
|
||||
priority = 0
|
||||
check-cfg = ["cfg(run_ui_tests)"]
|
||||
+201
@@ -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-2020 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.
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019-2020 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.
|
||||
Vendored
+250
@@ -0,0 +1,250 @@
|
||||
# Makes error reporting in procedural macros nice and easy
|
||||
|
||||
[](https://docs.rs/proc-macro-error2)
|
||||
[](https://github.com/rust-secure-code/safety-dance/)
|
||||
|
||||
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, you can explicitly [append a dummy token stream][crate::dummy] to your errors.
|
||||
|
||||
To achieve this, this crate serves as a tiny shim around `proc_macro::Diagnostic` and
|
||||
`compile_error!`. It detects the most preferable way to emit errors based on compiler's version.
|
||||
When the underlying diagnostic type is finally stabilized, this crate will be simply
|
||||
delegating to it, requiring no changes in your code!
|
||||
|
||||
So you can just use this crate and have *both* some of `proc_macro::Diagnostic` functionality
|
||||
available on stable ahead of time and your error-reporting code future-proof.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
proc-macro-error2 = "2.0"
|
||||
```
|
||||
|
||||
*Supports rustc 1.61 and up*
|
||||
|
||||
[Documentation and guide][guide]
|
||||
|
||||
## Quick example
|
||||
|
||||
Code:
|
||||
|
||||
```rust
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
pub fn make_fn(input: TokenStream) -> TokenStream {
|
||||
let mut input = TokenStream2::from(input).into_iter();
|
||||
let name = input.next().unwrap();
|
||||
if let Some(second) = input.next() {
|
||||
abort! { second,
|
||||
"I don't like this part!";
|
||||
note = "I see what you did there...";
|
||||
help = "I need only one part, you know?";
|
||||
}
|
||||
}
|
||||
|
||||
quote!( fn #name() {} ).into()
|
||||
}
|
||||
```
|
||||
|
||||
This is how the error is rendered in a terminal:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/50968528/78830016-d3b46a80-79d6-11ea-9de2-972e8d7904ef.png" width="600">
|
||||
</p>
|
||||
|
||||
And this is what your users will see in their IDE:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/50968528/78830547-a9af7800-79d7-11ea-822e-59e29bda335c.png" width="600">
|
||||
</p>
|
||||
|
||||
## Examples
|
||||
|
||||
### Panic-like usage
|
||||
|
||||
```rust
|
||||
use proc_macro_error2::{
|
||||
proc_macro_error,
|
||||
abort,
|
||||
abort_call_site,
|
||||
ResultExt,
|
||||
OptionExt,
|
||||
};
|
||||
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
|
||||
//
|
||||
// You can use `proc_macro::Span`, `proc_macro2::Span`, and
|
||||
// anything that implements `quote::ToTokens` (almost every type from
|
||||
// `syn` and `proc_macro2`)
|
||||
abort!(err, "You made an error, go fix it: {}", err.msg);
|
||||
}
|
||||
|
||||
// `Result` has some handy shortcuts if your error type implements
|
||||
// `Into<Diagnostic>`. `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_error2::*;
|
||||
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, "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()
|
||||
}
|
||||
```
|
||||
|
||||
## Real world examples
|
||||
|
||||
* [`structopt-derive`](https://github.com/TeXitoi/structopt/tree/master/structopt-derive)
|
||||
(abort-like usage)
|
||||
* [`auto-impl`](https://github.com/auto-impl-rs/auto_impl/) (emit-like usage)
|
||||
|
||||
## Limitations
|
||||
|
||||
- Warnings are emitted only on nightly, they are ignored on stable.
|
||||
- "help" suggestions can't have their own span info on stable,
|
||||
(essentially inheriting the parent span).
|
||||
- If your macro happens to trigger a panic, no errors will be displayed. This is not a
|
||||
technical limitation but rather intentional design. `panic` is not for error reporting.
|
||||
|
||||
## MSRV policy
|
||||
|
||||
The MSRV is currently `1.61`, and this is considered a breaking change to increase.
|
||||
|
||||
However, if an existing dependency requires a higher MSRV without a semver breaking update, this may be raised.
|
||||
|
||||
## 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 the 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. 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 the span info via `panic!`.
|
||||
`rustc` will highlight the 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 so 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 the span information
|
||||
over to user.
|
||||
- It must take as little effort as possible to migrate from `panic!`. Ideally, a new
|
||||
macro with similar semantics plus ability to carry out span info.
|
||||
- It must maintain compatibility with [`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_error]` 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 error reporting in procedural macros**, use `Result` and `?` (possibly along with one of the
|
||||
many helpers out there) 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-error2/1/proc_macro_error/dummy/index.html
|
||||
[crate::multi]: https://docs.rs/proc-macro-error2/1/proc_macro_error/multi/index.html
|
||||
|
||||
[`abort_call_site!`]: https://docs.rs/proc-macro-error2/1/proc_macro_error/macro.abort_call_site.html
|
||||
[`abort!`]: https://docs.rs/proc-macro-error2/1/proc_macro_error/macro.abort.html
|
||||
[guide]: https://docs.rs/proc-macro-error2
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
use crate::{abort_now, check_correctness, sealed::Sealed, SpanRange};
|
||||
use proc_macro2::Span;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
use quote::{quote_spanned, ToTokens};
|
||||
|
||||
/// Represents a diagnostic level
|
||||
///
|
||||
/// # Warnings
|
||||
///
|
||||
/// Warnings are ignored on stable/beta
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Level {
|
||||
Error,
|
||||
Warning,
|
||||
}
|
||||
|
||||
/// Represents a single diagnostic message
|
||||
#[derive(Debug)]
|
||||
#[must_use = "A diagnostic does nothing unless emitted"]
|
||||
pub struct Diagnostic {
|
||||
pub(crate) level: Level,
|
||||
pub(crate) span_range: SpanRange,
|
||||
pub(crate) msg: String,
|
||||
pub(crate) suggestions: Vec<(SuggestionKind, String, Option<SpanRange>)>,
|
||||
pub(crate) children: Vec<(SpanRange, String)>,
|
||||
}
|
||||
|
||||
/// A collection of methods that do not exist in `proc_macro::Diagnostic`
|
||||
/// but still useful to have around.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented outside of `proc_macro_error`.
|
||||
pub trait DiagnosticExt: Sealed {
|
||||
/// Create a new diagnostic message that points to the `span_range`.
|
||||
///
|
||||
/// This function is the same as `Diagnostic::spanned` but produces considerably
|
||||
/// better error messages for multi-token spans on stable.
|
||||
fn spanned_range(span_range: SpanRange, level: Level, message: String) -> Self;
|
||||
|
||||
/// Add another error message to self such that it will be emitted right after
|
||||
/// the main message.
|
||||
///
|
||||
/// This function is the same as `Diagnostic::span_error` but produces considerably
|
||||
/// better error messages for multi-token spans on stable.
|
||||
#[must_use]
|
||||
fn span_range_error(self, span_range: SpanRange, msg: String) -> Self;
|
||||
|
||||
/// Attach a "help" note to your main message, the note will have it's own span on nightly.
|
||||
///
|
||||
/// This function is the same as `Diagnostic::span_help` but produces considerably
|
||||
/// better error messages for multi-token spans on stable.
|
||||
///
|
||||
/// # Span
|
||||
///
|
||||
/// The span is ignored on stable, the note effectively inherits its parent's (main message) span
|
||||
#[must_use]
|
||||
fn span_range_help(self, span_range: SpanRange, msg: String) -> Self;
|
||||
|
||||
/// Attach a note to your main message, the note will have it's own span on nightly.
|
||||
///
|
||||
/// This function is the same as `Diagnostic::span_note` but produces considerably
|
||||
/// better error messages for multi-token spans on stable.
|
||||
///
|
||||
/// # Span
|
||||
///
|
||||
/// The span is ignored on stable, the note effectively inherits its parent's (main message) span
|
||||
#[must_use]
|
||||
fn span_range_note(self, span_range: SpanRange, msg: String) -> Self;
|
||||
}
|
||||
|
||||
impl DiagnosticExt for Diagnostic {
|
||||
fn spanned_range(span_range: SpanRange, level: Level, message: String) -> Self {
|
||||
Diagnostic {
|
||||
level,
|
||||
span_range,
|
||||
msg: message,
|
||||
suggestions: vec![],
|
||||
children: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn span_range_error(mut self, span_range: SpanRange, msg: String) -> Self {
|
||||
self.children.push((span_range, msg));
|
||||
self
|
||||
}
|
||||
|
||||
fn span_range_help(mut self, span_range: SpanRange, msg: String) -> Self {
|
||||
self.suggestions
|
||||
.push((SuggestionKind::Help, msg, Some(span_range)));
|
||||
self
|
||||
}
|
||||
|
||||
fn span_range_note(mut self, span_range: SpanRange, msg: String) -> Self {
|
||||
self.suggestions
|
||||
.push((SuggestionKind::Note, msg, Some(span_range)));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
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::spanned_range(
|
||||
SpanRange {
|
||||
first: span,
|
||||
last: span,
|
||||
},
|
||||
level,
|
||||
message,
|
||||
)
|
||||
}
|
||||
|
||||
/// Add another error message to self such that it will be emitted right after
|
||||
/// the main message.
|
||||
pub fn span_error(self, span: Span, msg: String) -> Self {
|
||||
self.span_range_error(
|
||||
SpanRange {
|
||||
first: span,
|
||||
last: span,
|
||||
},
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
/// Attach a "help" note to your main message, the 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(self, span: Span, msg: String) -> Self {
|
||||
self.span_range_help(
|
||||
SpanRange {
|
||||
first: span,
|
||||
last: span,
|
||||
},
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
/// 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, the 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(self, span: Span, msg: String) -> Self {
|
||||
self.span_range_note(
|
||||
SpanRange {
|
||||
first: span,
|
||||
last: span,
|
||||
},
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
#[must_use]
|
||||
pub fn message(&self) -> &str {
|
||||
&self.msg
|
||||
}
|
||||
|
||||
/// Abort the proc-macro's execution and display the diagnostic.
|
||||
///
|
||||
/// # Warnings
|
||||
///
|
||||
/// Warnings are not emitted on stable and 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) {
|
||||
check_correctness();
|
||||
crate::imp::emit_diagnostic(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// **NOT PUBLIC API! NOTHING TO SEE HERE!!!**
|
||||
#[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');
|
||||
}
|
||||
}
|
||||
|
||||
fn diag_to_tokens(
|
||||
span_range: SpanRange,
|
||||
level: &Level,
|
||||
msg: &str,
|
||||
suggestions: &[(SuggestionKind, String, Option<SpanRange>)],
|
||||
) -> TokenStream {
|
||||
if *level == Level::Warning {
|
||||
return TokenStream::new();
|
||||
}
|
||||
|
||||
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 mut msg = proc_macro2::Literal::string(&message);
|
||||
msg.set_span(span_range.last);
|
||||
let group = quote_spanned!(span_range.last=> { #msg } );
|
||||
quote_spanned!(span_range.first=> compile_error!#group)
|
||||
}
|
||||
|
||||
ts.extend(diag_to_tokens(
|
||||
self.span_range,
|
||||
&self.level,
|
||||
&self.msg,
|
||||
&self.suggestions,
|
||||
));
|
||||
ts.extend(
|
||||
self.children
|
||||
.iter()
|
||||
.map(|(span_range, msg)| diag_to_tokens(*span_range, &Level::Error, msg, &[])),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SuggestionKind {
|
||||
Help,
|
||||
Note,
|
||||
}
|
||||
|
||||
impl SuggestionKind {
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
SuggestionKind::Note => "note",
|
||||
SuggestionKind::Help => "help",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "syn-error")]
|
||||
impl From<syn::Error> for Diagnostic {
|
||||
fn from(err: syn::Error) -> Self {
|
||||
use proc_macro2::{Delimiter, TokenTree};
|
||||
|
||||
fn gut_error(ts: &mut impl Iterator<Item = TokenTree>) -> Option<(SpanRange, String)> {
|
||||
let start_span = ts.next()?.span();
|
||||
ts.next().expect(":1");
|
||||
ts.next().expect("core");
|
||||
ts.next().expect(":2");
|
||||
ts.next().expect(":3");
|
||||
ts.next().expect("compile_error");
|
||||
ts.next().expect("!");
|
||||
|
||||
let lit = match ts.next().unwrap() {
|
||||
TokenTree::Group(group) => {
|
||||
// Currently `syn` builds `compile_error!` invocations
|
||||
// exclusively in `ident{"..."}` (braced) form which is not
|
||||
// followed by `;` (semicolon).
|
||||
//
|
||||
// But if it changes to `ident("...");` (parenthesized)
|
||||
// or `ident["..."];` (bracketed) form,
|
||||
// we will need to skip the `;` as well.
|
||||
// Highly unlikely, but better safe than sorry.
|
||||
|
||||
if group.delimiter() == Delimiter::Parenthesis
|
||||
|| group.delimiter() == Delimiter::Bracket
|
||||
{
|
||||
ts.next().unwrap(); // ;
|
||||
}
|
||||
|
||||
match group.stream().into_iter().next().unwrap() {
|
||||
TokenTree::Literal(lit) => lit,
|
||||
_ => unreachable!(""),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(""),
|
||||
};
|
||||
|
||||
let last = lit.span();
|
||||
let mut msg = lit.to_string();
|
||||
|
||||
// "abc" => abc
|
||||
msg.pop();
|
||||
msg.remove(0);
|
||||
|
||||
Some((
|
||||
SpanRange {
|
||||
first: start_span,
|
||||
last,
|
||||
},
|
||||
msg,
|
||||
))
|
||||
}
|
||||
|
||||
let mut ts = err.to_compile_error().into_iter();
|
||||
|
||||
let (span_range, msg) = gut_error(&mut ts).unwrap();
|
||||
let mut res = Diagnostic::spanned_range(span_range, Level::Error, msg);
|
||||
|
||||
while let Some((span_range, msg)) = gut_error(&mut ts) {
|
||||
res = res.span_range_error(span_range, msg);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
//! 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_error2::*;
|
||||
//!
|
||||
//! 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_error2::*;
|
||||
//!
|
||||
//! 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::RefCell;
|
||||
|
||||
use crate::check_correctness;
|
||||
|
||||
thread_local! {
|
||||
static DUMMY_IMPL: RefCell<Option<TokenStream>> = const { RefCell::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).
|
||||
#[allow(clippy::must_use_candidate)] // Mutates thread local state
|
||||
pub fn set_dummy(dummy: TokenStream) -> Option<TokenStream> {
|
||||
check_correctness();
|
||||
DUMMY_IMPL.with(|old_dummy| old_dummy.replace(Some(dummy)))
|
||||
}
|
||||
|
||||
/// Same as [`set_dummy`] but, instead of resetting, appends tokens to the
|
||||
/// existing dummy (if any). Behaves as `set_dummy` if no dummy is present.
|
||||
pub fn append_dummy(dummy: TokenStream) {
|
||||
check_correctness();
|
||||
DUMMY_IMPL.with(|old_dummy| {
|
||||
let mut cell = old_dummy.borrow_mut();
|
||||
if let Some(ts) = cell.as_mut() {
|
||||
ts.extend(dummy);
|
||||
} else {
|
||||
*cell = Some(dummy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn cleanup() -> Option<TokenStream> {
|
||||
DUMMY_IMPL.with(|old_dummy| old_dummy.replace(None))
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
//! This implementation uses [`proc_macro::Diagnostic`], nightly only.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use proc_macro::{Diagnostic as PDiag, Level as PLevel};
|
||||
|
||||
use crate::{
|
||||
abort_now, check_correctness,
|
||||
diagnostic::{Diagnostic, Level, SuggestionKind},
|
||||
};
|
||||
|
||||
pub fn abort_if_dirty() {
|
||||
check_correctness();
|
||||
if IS_DIRTY.with(|c| c.get()) {
|
||||
abort_now()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cleanup() -> Vec<Diagnostic> {
|
||||
IS_DIRTY.with(|c| c.set(false));
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub(crate) fn emit_diagnostic(diag: Diagnostic) {
|
||||
let Diagnostic {
|
||||
level,
|
||||
span_range,
|
||||
msg,
|
||||
suggestions,
|
||||
children,
|
||||
} = diag;
|
||||
|
||||
let span = span_range.collapse().unwrap();
|
||||
|
||||
let level = match level {
|
||||
Level::Warning => PLevel::Warning,
|
||||
Level::Error => {
|
||||
IS_DIRTY.with(|c| c.set(true));
|
||||
PLevel::Error
|
||||
}
|
||||
};
|
||||
|
||||
let mut res = PDiag::spanned(span, level, msg);
|
||||
|
||||
for (kind, msg, span) in suggestions {
|
||||
res = match (kind, span) {
|
||||
(SuggestionKind::Note, Some(span_range)) => {
|
||||
res.span_note(span_range.collapse().unwrap(), msg)
|
||||
}
|
||||
(SuggestionKind::Help, Some(span_range)) => {
|
||||
res.span_help(span_range.collapse().unwrap(), msg)
|
||||
}
|
||||
(SuggestionKind::Note, None) => res.note(msg),
|
||||
(SuggestionKind::Help, None) => res.help(msg),
|
||||
}
|
||||
}
|
||||
|
||||
for (span_range, msg) in children {
|
||||
let span = span_range.collapse().unwrap();
|
||||
res = res.span_error(span, msg);
|
||||
}
|
||||
|
||||
res.emit()
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static IS_DIRTY: Cell<bool> = Cell::new(false);
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//! This implementation uses self-written stable facilities.
|
||||
|
||||
use crate::{
|
||||
abort_now, check_correctness,
|
||||
diagnostic::{Diagnostic, Level},
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
|
||||
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>> = const { RefCell::new(Vec::new()) };
|
||||
}
|
||||
Vendored
+565
@@ -0,0 +1,565 @@
|
||||
//! # proc-macro-error2
|
||||
//!
|
||||
//! 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, you can explicitly [append a dummy token stream](dummy/index.html) to your errors).
|
||||
//!
|
||||
//! To achieve his, this crate serves as a tiny shim around `proc_macro::Diagnostic` and
|
||||
//! `compile_error!`. It detects the best way of emitting available based on compiler's version.
|
||||
//! When the underlying diagnostic type is finally stabilized, this crate will simply be
|
||||
//! delegating to it requiring no changes in your code!
|
||||
//!
|
||||
//! So you can just use this crate and have *both* some of `proc_macro::Diagnostic` functionality
|
||||
//! available on stable ahead of time *and* your error-reporting code future-proof.
|
||||
//!
|
||||
//! ## Cargo features
|
||||
//!
|
||||
//! This crate provides *enabled by default* `syn-error` feature that gates
|
||||
//! `impl From<syn::Error> for Diagnostic` conversion. If you don't use `syn` and want
|
||||
//! to cut off some of compilation time, you can disable it via
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! proc-macro-error2 = { version = "2.0.0", default-features = false }
|
||||
//! ```
|
||||
//!
|
||||
//! ***Please note that disabling this feature makes sense only if you don't depend on `syn`
|
||||
//! directly or indirectly, and you very likely do.**
|
||||
//!
|
||||
//! ## Real world examples
|
||||
//!
|
||||
//! * [`structopt-derive`](https://github.com/TeXitoi/structopt/tree/master/structopt-derive)
|
||||
//! (abort-like usage)
|
||||
//! * [`auto-impl`](https://github.com/auto-impl-rs/auto_impl/) (emit-like usage)
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! - Warnings are emitted only on nightly, they are ignored on stable.
|
||||
//! - "help" suggestions can't have their own span info on stable,
|
||||
//! (essentially inheriting the parent span).
|
||||
//! - If a panic occurs somewhere in your macro no errors will be displayed. This is not a
|
||||
//! technical limitation but rather intentional design. `panic` is not for error reporting.
|
||||
//!
|
||||
//! ### `#[proc_macro_error]` attribute
|
||||
//!
|
||||
//! **This attribute MUST be present on the top level of your macro** (the function
|
||||
//! annotated with any of `#[proc_macro]`, `#[proc_macro_derive]`, `#[proc_macro_attribute]`).
|
||||
//!
|
||||
//! This attribute performs the setup and cleanup necessary to make things work.
|
||||
//!
|
||||
//! In most cases you'll need the simple `#[proc_macro_error]` form without any
|
||||
//! additional settings. Feel free to [skip the "Syntax" section](#macros).
|
||||
//!
|
||||
//! #### Syntax
|
||||
//!
|
||||
//! `#[proc_macro_error]` or `#[proc_macro_error(settings...)]`, where `settings...`
|
||||
//! is a comma-separated list of:
|
||||
//!
|
||||
//! - `proc_macro_hack`:
|
||||
//!
|
||||
//! In order to correctly cooperate with `#[proc_macro_hack]`, `#[proc_macro_error]`
|
||||
//! attribute must be placed *before* (above) it, like this:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use proc_macro2::TokenStream;
|
||||
//! # const IGNORE: &str = "
|
||||
//! #[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.
|
||||
//!
|
||||
//! # Note
|
||||
//!
|
||||
//! If `proc-macro-hack` was detected (by any means) `allow_not_macro`
|
||||
//! and `assert_unwind_safe` will be applied automatically.
|
||||
//!
|
||||
//! - `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.
|
||||
//!
|
||||
//! Pay attention: the function this attribute is applied to must return
|
||||
//! `proc_macro::TokenStream`.
|
||||
//!
|
||||
//! This setting is implied if `proc-macro-hack` was detected.
|
||||
//!
|
||||
//! - `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.
|
||||
//! You would need this for code that uses `lazy_static` or `thread_local` with
|
||||
//! `Cell/RefCell` inside (and the like).
|
||||
//!
|
||||
//! This setting is implied if `#[proc_macro_error]` is applied to a function
|
||||
//! marked as `#[proc_macro]`, `#[proc_macro_derive]` or `#[proc_macro_attribute]`.
|
||||
//!
|
||||
//! This setting is also implied if `proc-macro-hack` was detected.
|
||||
//!
|
||||
//! ## Macros
|
||||
//!
|
||||
//! Most of the time you want to use the macros. Syntax is described in the next section below.
|
||||
//!
|
||||
//! You'll need to decide how you want to emit errors:
|
||||
//!
|
||||
//! * Emit the error and abort. Very much panic-like usage. Served by [`abort!`] and
|
||||
//! [`abort_call_site!`].
|
||||
//! * Emit the error but do not abort right away, looking for other errors to report.
|
||||
//! Served by [`emit_error!`] and [`emit_call_site_error!`].
|
||||
//!
|
||||
//! You **can** mix these usages.
|
||||
//!
|
||||
//! `abort` and `emit_error` take a "source span" as the first argument. This source
|
||||
//! will be used to highlight the place the error originates from. It must be one of:
|
||||
//!
|
||||
//! * *Something* that implements [`ToTokens`] (most types in `syn` and `proc-macro2` do).
|
||||
//! This source is the preferable one since it doesn't lose span information on multi-token
|
||||
//! spans, see [this issue](https://gitlab.com/CreepySkeleton/proc-macro-error/-/issues/6)
|
||||
//! for details.
|
||||
//! * [`proc_macro::Span`]
|
||||
//! * [`proc-macro2::Span`]
|
||||
//!
|
||||
//! The rest is your message in format-like style.
|
||||
//!
|
||||
//! See [the next section](#syntax-1) for detailed syntax.
|
||||
//!
|
||||
//! - [`abort!`]:
|
||||
//!
|
||||
//! Very much panic-like usage - abort right away 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 keep going,
|
||||
//! looking for other errors to report.
|
||||
//! 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 an 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(expr).abort()`.
|
||||
//!
|
||||
//! 2. ```ignore
|
||||
//! abort!(span, message)
|
||||
//! ```
|
||||
//! The first argument is an expression the span info should be taken from.
|
||||
//!
|
||||
//! The second argument is the error message, it must implement [`ToString`].
|
||||
//!
|
||||
//! 3. ```ignore
|
||||
//! abort!(span, format_literal, format_args...)
|
||||
//! ```
|
||||
//!
|
||||
//! This form is pretty much the same as 2, except `format!(format_literal, format_args...)`
|
||||
//! will be used to for the message instead of [`ToString`].
|
||||
//!
|
||||
//! 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'th and 3'th forms. Those are essentially shortcuts for
|
||||
//! `macro!(Span::call_site(), args...)`.
|
||||
//!
|
||||
//! `diagnostic!` requires a [`Level`] instance between `span` and second argument
|
||||
//! (1'th form is the same).
|
||||
//!
|
||||
//! > **Important!**
|
||||
//! >
|
||||
//! > If you have some type from `proc_macro` or `syn` to point to, do not call `.span()`
|
||||
//! > on it but rather use it directly:
|
||||
//! > ```no_run
|
||||
//! > # use proc_macro_error2::abort;
|
||||
//! > # let input = proc_macro2::TokenStream::new();
|
||||
//! > let ty: syn::Type = syn::parse2(input).unwrap();
|
||||
//! > abort!(ty, "BOOM");
|
||||
//! > // ^^ <-- avoid .span()
|
||||
//! > ```
|
||||
//! >
|
||||
//! > `.span()` calls work too, but you may experience regressions in message quality.
|
||||
//!
|
||||
//! #### 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
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
|
||||
//! ### 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
|
||||
//! [`abort_call_site!`]: macro.abort_call_site.html
|
||||
//! [`emit_warning!`]: macro.emit_warning.html
|
||||
//! [`emit_error!`]: macro.emit_error.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
|
||||
//! [`Diagnostic`]: struct.Diagnostic.html
|
||||
//!
|
||||
//! [`proc_macro::Span`]: https://doc.rust-lang.org/proc_macro/struct.Span.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
|
||||
//! [`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html
|
||||
//!
|
||||
//! [`proc-macro2::Span`]: https://docs.rs/proc-macro2/1.0.10/proc_macro2/struct.Span.html
|
||||
//! [`ToTokens`]: https://docs.rs/quote/1.0.3/quote/trait.ToTokens.html
|
||||
//!
|
||||
|
||||
#![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic))]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
pub use crate::{
|
||||
diagnostic::{Diagnostic, DiagnosticExt, Level},
|
||||
dummy::{append_dummy, set_dummy},
|
||||
};
|
||||
pub use proc_macro_error_attr2::proc_macro_error;
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
|
||||
|
||||
pub mod dummy;
|
||||
|
||||
mod diagnostic;
|
||||
mod macros;
|
||||
mod sealed;
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
#[path = "imp/fallback.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[path = "imp/delegate.rs"]
|
||||
mod imp;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[must_use = "A SpanRange does nothing unless used"]
|
||||
pub struct SpanRange {
|
||||
pub first: Span,
|
||||
pub last: Span,
|
||||
}
|
||||
|
||||
impl SpanRange {
|
||||
/// Create a range with the `first` and `last` spans being the same.
|
||||
pub fn single_span(span: Span) -> Self {
|
||||
SpanRange {
|
||||
first: span,
|
||||
last: span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `SpanRange` resolving at call site.
|
||||
pub fn call_site() -> Self {
|
||||
SpanRange::single_span(Span::call_site())
|
||||
}
|
||||
|
||||
/// Construct span range from a `TokenStream`. This method always preserves all the
|
||||
/// range.
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// If the stream is empty, the result is `SpanRange::call_site()`. If the stream
|
||||
/// consists of only one `TokenTree`, the result is `SpanRange::single_span(tt.span())`
|
||||
/// that doesn't lose anything.
|
||||
pub fn from_tokens(ts: &dyn ToTokens) -> Self {
|
||||
let mut spans = ts.to_token_stream().into_iter().map(|tt| tt.span());
|
||||
let first = spans.next().unwrap_or_else(Span::call_site);
|
||||
let last = spans.last().unwrap_or(first);
|
||||
|
||||
SpanRange { first, last }
|
||||
}
|
||||
|
||||
/// Join two span ranges. The resulting range will start at `self.first` and end at
|
||||
/// `other.last`.
|
||||
pub fn join_range(self, other: SpanRange) -> Self {
|
||||
SpanRange {
|
||||
first: self.first,
|
||||
last: other.last,
|
||||
}
|
||||
}
|
||||
|
||||
/// Collapse the range into single span, preserving as much information as possible.
|
||||
#[must_use]
|
||||
pub fn collapse(self) -> Span {
|
||||
self.first.join(self.last).unwrap_or(self.first)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(flag.get() + 1));
|
||||
let caught = catch_unwind(f);
|
||||
let dummy = dummy::cleanup();
|
||||
let err_storage = imp::cleanup();
|
||||
ENTERED_ENTRY_POINT.with(|flag| flag.set(flag.get() - 1));
|
||||
|
||||
let gen_error = || {
|
||||
if proc_macro_hack {
|
||||
quote! {{
|
||||
macro_rules! proc_macro_call {
|
||||
() => ( unimplemented!() )
|
||||
}
|
||||
|
||||
#(#err_storage)*
|
||||
#dummy
|
||||
|
||||
unimplemented!()
|
||||
}}
|
||||
} else {
|
||||
quote!( #(#err_storage)* #dummy )
|
||||
}
|
||||
};
|
||||
|
||||
match caught {
|
||||
Ok(ts) => {
|
||||
if err_storage.is_empty() {
|
||||
ts
|
||||
} else {
|
||||
gen_error().into()
|
||||
}
|
||||
}
|
||||
|
||||
Err(boxed) => match boxed.downcast::<AbortNow>() {
|
||||
Ok(_) => gen_error().into(),
|
||||
Err(boxed) => resume_unwind(boxed),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn abort_now() -> ! {
|
||||
check_correctness();
|
||||
std::panic::panic_any(AbortNow)
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static ENTERED_ENTRY_POINT: Cell<usize> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
struct AbortNow;
|
||||
|
||||
fn check_correctness() {
|
||||
assert!(
|
||||
ENTERED_ENTRY_POINT.with(Cell::get) != 0,
|
||||
"proc-macro-error2 API cannot be used outside of `entry_point` invocation, \
|
||||
perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]"
|
||||
);
|
||||
}
|
||||
|
||||
/// **ALL THE STUFF INSIDE IS NOT PUBLIC API!!!**
|
||||
#[doc(hidden)]
|
||||
pub mod __export {
|
||||
// reexports for use in macros
|
||||
pub use proc_macro;
|
||||
pub use proc_macro2;
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::ToTokens;
|
||||
|
||||
use crate::SpanRange;
|
||||
|
||||
// inspired by
|
||||
// https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md#simple-application
|
||||
|
||||
pub trait SpanAsSpanRange {
|
||||
#[allow(non_snake_case)]
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
|
||||
}
|
||||
|
||||
pub trait Span2AsSpanRange {
|
||||
#[allow(non_snake_case)]
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
|
||||
}
|
||||
|
||||
pub trait ToTokensAsSpanRange {
|
||||
#[allow(non_snake_case)]
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
|
||||
}
|
||||
|
||||
pub trait SpanRangeAsSpanRange {
|
||||
#[allow(non_snake_case)]
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
|
||||
}
|
||||
|
||||
impl<T: ToTokens> ToTokensAsSpanRange for &T {
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
|
||||
let mut ts = self.to_token_stream().into_iter();
|
||||
let first = match ts.next() {
|
||||
Some(t) => t.span(),
|
||||
None => Span::call_site(),
|
||||
};
|
||||
|
||||
let last = match ts.last() {
|
||||
Some(t) => t.span(),
|
||||
None => first,
|
||||
};
|
||||
|
||||
SpanRange { first, last }
|
||||
}
|
||||
}
|
||||
|
||||
impl Span2AsSpanRange for Span {
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
|
||||
SpanRange {
|
||||
first: *self,
|
||||
last: *self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpanAsSpanRange for proc_macro::Span {
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
|
||||
SpanRange {
|
||||
first: (*self).into(),
|
||||
last: (*self).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpanRangeAsSpanRange for SpanRange {
|
||||
fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
|
||||
*self
|
||||
}
|
||||
}
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
// 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)+) => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::__export::{
|
||||
ToTokensAsSpanRange,
|
||||
Span2AsSpanRange,
|
||||
SpanAsSpanRange,
|
||||
SpanRangeAsSpanRange
|
||||
};
|
||||
use $crate::DiagnosticExt;
|
||||
let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
|
||||
|
||||
let diag = $crate::Diagnostic::spanned_range(
|
||||
span_range,
|
||||
$level,
|
||||
format!($fmt, $($args),*)
|
||||
);
|
||||
$crate::__pme__suggestions!(diag $($rest)*);
|
||||
diag
|
||||
}};
|
||||
|
||||
($span:expr, $level:expr, $msg:expr ; $($rest:tt)+) => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::__export::{
|
||||
ToTokensAsSpanRange,
|
||||
Span2AsSpanRange,
|
||||
SpanAsSpanRange,
|
||||
SpanRangeAsSpanRange
|
||||
};
|
||||
use $crate::DiagnosticExt;
|
||||
let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
|
||||
|
||||
let diag = $crate::Diagnostic::spanned_range(span_range, $level, $msg.to_string());
|
||||
$crate::__pme__suggestions!(diag $($rest)*);
|
||||
diag
|
||||
}};
|
||||
|
||||
// span, message, no help
|
||||
($span:expr, $level:expr, $fmt:expr, $($args:expr),+) => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::__export::{
|
||||
ToTokensAsSpanRange,
|
||||
Span2AsSpanRange,
|
||||
SpanAsSpanRange,
|
||||
SpanRangeAsSpanRange
|
||||
};
|
||||
use $crate::DiagnosticExt;
|
||||
let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
|
||||
|
||||
$crate::Diagnostic::spanned_range(
|
||||
span_range,
|
||||
$level,
|
||||
format!($fmt, $($args),*)
|
||||
)
|
||||
}};
|
||||
|
||||
($span:expr, $level:expr, $msg:expr) => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::__export::{
|
||||
ToTokensAsSpanRange,
|
||||
Span2AsSpanRange,
|
||||
SpanAsSpanRange,
|
||||
SpanRangeAsSpanRange
|
||||
};
|
||||
use $crate::DiagnosticExt;
|
||||
let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
|
||||
|
||||
$crate::Diagnostic::spanned_range(span_range, $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::abort!($crate::__export::proc_macro2::Span::call_site(), $($tts)*)
|
||||
};
|
||||
}
|
||||
|
||||
/// 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::emit_error!($crate::__export::proc_macro2::Span::call_site(), $($tts)*)
|
||||
};
|
||||
}
|
||||
|
||||
/// 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)*) => {{
|
||||
$crate::emit_warning!($crate::__export::proc_macro2::Span::call_site(), $($tts)*)
|
||||
}};
|
||||
}
|
||||
|
||||
#[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)*)
|
||||
};
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for crate::Diagnostic {}
|
||||
@@ -0,0 +1,6 @@
|
||||
#[test]
|
||||
#[cfg(run_ui_tests)]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
use test_crate::*;
|
||||
|
||||
ok!(it_works);
|
||||
|
||||
#[test]
|
||||
fn check_it_works() {
|
||||
it_works();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use proc_macro_error2::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic = "proc-macro-error2 API cannot be used outside of"]
|
||||
fn missing_attr_emit() {
|
||||
emit_call_site_error!("You won't see me");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "proc-macro-error2 API cannot be used outside of"]
|
||||
fn missing_attr_abort() {
|
||||
abort_call_site!("You won't see me");
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
use test_crate::*;
|
||||
|
||||
abort_from!(one, two);
|
||||
abort_to_string!(one, two);
|
||||
abort_format!(one, two);
|
||||
direct_abort!(one, two);
|
||||
abort_notes!(one, two);
|
||||
abort_call_site_test!(one, two);
|
||||
|
||||
fn main() {}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
error: abort!(span, from) test
|
||||
--> tests/ui/abort.rs:3:13
|
||||
|
|
||||
3 | abort_from!(one, two);
|
||||
| ^^^
|
||||
|
||||
error: abort!(span, single_expr) test
|
||||
--> tests/ui/abort.rs:4:18
|
||||
|
|
||||
4 | abort_to_string!(one, two);
|
||||
| ^^^
|
||||
|
||||
error: abort!(span, expr1, expr2) test
|
||||
--> tests/ui/abort.rs:5:15
|
||||
|
|
||||
5 | abort_format!(one, two);
|
||||
| ^^^
|
||||
|
||||
error: Diagnostic::abort() test
|
||||
--> tests/ui/abort.rs:6:15
|
||||
|
|
||||
6 | direct_abort!(one, two);
|
||||
| ^^^
|
||||
|
||||
error: This is an error
|
||||
|
||||
= note: simple note
|
||||
= help: simple help
|
||||
= help: simple hint
|
||||
= note: simple yay
|
||||
= note: format note
|
||||
= note: Some note
|
||||
= note: spanned simple note
|
||||
= note: spanned format note
|
||||
= note: Some note
|
||||
|
||||
--> tests/ui/abort.rs:7:14
|
||||
|
|
||||
7 | abort_notes!(one, two);
|
||||
| ^^^
|
||||
|
||||
error: abort_call_site! test
|
||||
--> tests/ui/abort.rs:8:1
|
||||
|
|
||||
8 | abort_call_site_test!(one, two);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `abort_call_site_test` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,12 @@
|
||||
use test_crate::*;
|
||||
|
||||
enum NeedDefault {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
append_dummy!(need_default);
|
||||
|
||||
fn main() {
|
||||
let _ = NeedDefault::default();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: append_dummy test
|
||||
--> tests/ui/append_dummy.rs:8:15
|
||||
|
|
||||
8 | append_dummy!(need_default);
|
||||
| ^^^^^^^^^^^^
|
||||
@@ -0,0 +1,5 @@
|
||||
use test_crate::*;
|
||||
|
||||
children_messages!(one, two, three, four);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,23 @@
|
||||
error: main macro message
|
||||
--> tests/ui/children_messages.rs:3:20
|
||||
|
|
||||
3 | children_messages!(one, two, three, four);
|
||||
| ^^^
|
||||
|
||||
error: child message
|
||||
--> tests/ui/children_messages.rs:3:25
|
||||
|
|
||||
3 | children_messages!(one, two, three, four);
|
||||
| ^^^
|
||||
|
||||
error: main syn::Error
|
||||
--> tests/ui/children_messages.rs:3:30
|
||||
|
|
||||
3 | children_messages!(one, two, three, four);
|
||||
| ^^^^^
|
||||
|
||||
error: child syn::Error
|
||||
--> tests/ui/children_messages.rs:3:37
|
||||
|
|
||||
3 | children_messages!(one, two, three, four);
|
||||
| ^^^^
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
use test_crate::*;
|
||||
|
||||
enum NeedDefault {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
dummy!(need_default);
|
||||
|
||||
fn main() {
|
||||
let _ = NeedDefault::default();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: set_dummy test
|
||||
--> tests/ui/dummy.rs:8:8
|
||||
|
|
||||
8 | dummy!(need_default);
|
||||
| ^^^^^^^^^^^^
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
use test_crate::*;
|
||||
|
||||
emit!(one, two, three, four, five);
|
||||
emit_notes!(one, two);
|
||||
|
||||
fn main() {}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
error: emit!(span, from) test
|
||||
--> tests/ui/emit.rs:3:7
|
||||
|
|
||||
3 | emit!(one, two, three, four, five);
|
||||
| ^^^
|
||||
|
||||
error: emit!(span, expr1, expr2) test
|
||||
--> tests/ui/emit.rs:3:12
|
||||
|
|
||||
3 | emit!(one, two, three, four, five);
|
||||
| ^^^
|
||||
|
||||
error: emit!(span, single_expr) test
|
||||
--> tests/ui/emit.rs:3:17
|
||||
|
|
||||
3 | emit!(one, two, three, four, five);
|
||||
| ^^^^^
|
||||
|
||||
error: Diagnostic::emit() test
|
||||
--> tests/ui/emit.rs:3:24
|
||||
|
|
||||
3 | emit!(one, two, three, four, five);
|
||||
| ^^^^
|
||||
|
||||
error: emit_call_site_error!(expr) test
|
||||
--> tests/ui/emit.rs:3:1
|
||||
|
|
||||
3 | emit!(one, two, three, four, five);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `emit` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: This is an error
|
||||
|
||||
= note: simple note
|
||||
= help: simple help
|
||||
= help: simple hint
|
||||
= note: simple yay
|
||||
= note: format note
|
||||
= note: Some note
|
||||
= note: spanned simple note
|
||||
= note: spanned format note
|
||||
= note: Some note
|
||||
|
||||
--> tests/ui/emit.rs:4:13
|
||||
|
|
||||
4 | emit_notes!(one, two);
|
||||
| ^^^
|
||||
@@ -0,0 +1,5 @@
|
||||
use test_crate::*;
|
||||
|
||||
explicit_span_range!(one, two, three, four);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: explicit SpanRange
|
||||
--> tests/ui/explicit_span_range.rs:3:22
|
||||
|
|
||||
3 | explicit_span_range!(one, two, three, four);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
use proc_macro_error2::abort;
|
||||
|
||||
struct Foo;
|
||||
|
||||
#[allow(unused)]
|
||||
fn foo() {
|
||||
abort!(Foo, "BOOM");
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
error[E0599]: the method `FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange` exists for reference `&Foo`, but its trait bounds were not satisfied
|
||||
--> tests/ui/misuse.rs:7:5
|
||||
|
|
||||
3 | struct Foo;
|
||||
| ---------- doesn't satisfy `Foo: quote::to_tokens::ToTokens`
|
||||
...
|
||||
7 | abort!(Foo, "BOOM");
|
||||
| ^^^^^^^^^^^^^^^^^^^ method cannot be called on `&Foo` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`Foo: quote::to_tokens::ToTokens`
|
||||
which is required by `&Foo: ToTokensAsSpanRange`
|
||||
note: the trait `quote::to_tokens::ToTokens` must be implemented
|
||||
--> $CARGO/quote-1.0.37/src/to_tokens.rs
|
||||
|
|
||||
| pub trait ToTokens {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following traits define an item `FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange`, perhaps you need to implement one of them:
|
||||
candidate #1: `Span2AsSpanRange`
|
||||
candidate #2: `SpanAsSpanRange`
|
||||
candidate #3: `SpanRangeAsSpanRange`
|
||||
candidate #4: `ToTokensAsSpanRange`
|
||||
= note: this error originates in the macro `$crate::diagnostic` which comes from the expansion of the macro `abort` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,4 @@
|
||||
#[test_crate::multiple_tokens]
|
||||
type T = ();
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: ...
|
||||
--> tests/ui/multiple_tokens.rs:2:1
|
||||
|
|
||||
2 | type T = ();
|
||||
| ^^^^^^^^^^^^
|
||||
@@ -0,0 +1,4 @@
|
||||
use proc_macro_error2::proc_macro_error;
|
||||
|
||||
#[proc_macro_error]
|
||||
fn main() {}
|
||||
@@ -0,0 +1,9 @@
|
||||
error: #[proc_macro_error] attribute can be used only with procedural macros
|
||||
|
||||
= hint: if you are really sure that #[proc_macro_error] should be applied to this exact function, use #[proc_macro_error(allow_not_macro)]
|
||||
--> tests/ui/not_proc_macro.rs:3:1
|
||||
|
|
||||
3 | #[proc_macro_error]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the attribute macro `proc_macro_error` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,5 @@
|
||||
use test_crate::*;
|
||||
|
||||
option_ext!(one, two);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
error: Option::expect_or_abort() test
|
||||
--> tests/ui/option_ext.rs:3:1
|
||||
|
|
||||
3 | option_ext!(one, two);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `option_ext` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,6 @@
|
||||
use test_crate::*;
|
||||
|
||||
result_unwrap_or_abort!(one, two);
|
||||
result_expect_or_abort!(one, two);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Result::unwrap_or_abort() test
|
||||
--> tests/ui/result_ext.rs:3:25
|
||||
|
|
||||
3 | result_unwrap_or_abort!(one, two);
|
||||
| ^^^
|
||||
|
||||
error: BOOM: Result::expect_or_abort() test
|
||||
--> tests/ui/result_ext.rs:4:25
|
||||
|
|
||||
4 | result_expect_or_abort!(one, two);
|
||||
| ^^^
|
||||
@@ -0,0 +1,5 @@
|
||||
use test_crate::*;
|
||||
|
||||
to_tokens_span!(std::option::Option);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: whole type
|
||||
--> tests/ui/to_tokens_span.rs:3:17
|
||||
|
|
||||
3 | to_tokens_span!(std::option::Option);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit .span()
|
||||
--> tests/ui/to_tokens_span.rs:3:17
|
||||
|
|
||||
3 | to_tokens_span!(std::option::Option);
|
||||
| ^^^
|
||||
@@ -0,0 +1,4 @@
|
||||
use proc_macro_error2::proc_macro_error;
|
||||
|
||||
#[proc_macro_error(allow_not_macro, assert_unwind_safe, trololo)]
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: unknown setting `trololo`, expected one of `assert_unwind_safe`, `allow_not_macro`, `proc_macro_hack`
|
||||
--> tests/ui/unknown_setting.rs:3:57
|
||||
|
|
||||
3 | #[proc_macro_error(allow_not_macro, assert_unwind_safe, trololo)]
|
||||
| ^^^^^^^
|
||||
@@ -0,0 +1,5 @@
|
||||
use test_crate::*;
|
||||
|
||||
unrelated_panic!();
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
error: proc macro panicked
|
||||
--> tests/ui/unrelated_panic.rs:3:1
|
||||
|
|
||||
3 | unrelated_panic!();
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: message: unrelated panic test
|
||||
Reference in new issue
Block a user