Crate proc_macro_error
source · [−]Expand description
proc-macro-error
This crate aims to make error reporting in proc-macros simple and easy to use.
Migrate from panic!
-based errors for as little effort as possible!
(Also, you can explicitly append a dummy token stream 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
[dependencies]
proc-macro-error = { version = "1", 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
(abort-like usage)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.
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:#[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
andassert_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
orthread_local
withCell/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!
andabort_call_site!
. - Emit the error but do not abort right away, looking for other errors to report.
Served by
emit_error!
andemit_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 insyn
andproc-macro2
do). This source is the preferable one since it doesn’t lose span information on multi-token spans, see this issue for details. proc_macro::Span
proc-macro2::Span
The rest is your message in format-like style.
See the next section for detailed syntax.
-
Very much panic-like usage - abort right away and show the error. Expands to
!
(never type). -
Shortcut for
abort!(Span::call_site(), ...)
. Expands to!
(never type). -
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). -
Shortcut for
emit_error!(Span::call_site(), ...)
. Expands to()
(unit type). -
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.
-
Shortcut for
emit_warning!(Span::call_site(), ...)
. Expands to()
(unit type). -
Build an instance of
Diagnostic
in format-like style.
Syntax
All the macros have pretty much the same syntax:
-
ⓘ
abort!(single_expr)
Shortcut for
Diagnostic::from(expr).abort()
. -
ⓘ
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
. -
ⓘ
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 ofToString
.
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
orsyn
to point to, do not call.span()
on it but rather use it directly: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
- Every macro can have “note” attachments (only 2 and 3 form).
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.
Re-exports
pub use crate::dummy::append_dummy;
pub use crate::dummy::set_dummy;
Modules
Facility to emit dummy implementations (or whatever) in case an error happen.
Macros
Abort proc-macro execution right now and display the error.
Shortcut for abort!(Span::call_site(), msg...)
. This macro
is still preferable over plain panic, panics are not for error reporting.
Build Diagnostic
instance from provided arguments.
Shortcut for emit_error!(Span::call_site(), ...)
. This macro
is still preferable over plain panic, panics are not for error reporting..
Shortcut for emit_warning!(Span::call_site(), ...)
.
Emit an error while not aborting the proc-macro right away.
Emit a warning. Warnings are not errors and compilation won’t fail because of them.
Structs
Represents a single diagnostic message
Enums
Represents a diagnostic level
Traits
A collection of methods that do not exist in proc_macro::Diagnostic
but still useful to have around.
This traits expands Option
with some handy shortcuts.
This traits expands Result<T, Into<Diagnostic>>
with some handy shortcuts.
Functions
Abort macro execution and display all the emitted errors, if any.