proc-macro-error-0.2.6/Cargo.toml.orig000064400000000000000000000012241353310756500157740ustar0000000000000000[package] name = "proc-macro-error" version = "0.2.6" authors = ["CreepySkeleton "] description = "Drop-in replacement to panics in proc-macros" repository = "https://github.com/CreepySkeleton/proc-macro-error" readme = "../README.md" keywords = ["proc-macro", "error", "errors"] categories = ["development-tools::procedural-macro-helpers"] license = "MIT" edition = "2018" [package.metadata.release] tag-name = "v{{version}}" pre-release-commit-message = "v{{version}}" no-dev-version = true [badges] maintenance = { status = "actively-developed" } [features] dummy = [] [dependencies] quote = "1" proc-macro2 = "1" syn = "1" proc-macro-error-0.2.6/Cargo.toml0000644000000023160000000000000122710ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "proc-macro-error" version = "0.2.6" authors = ["CreepySkeleton "] description = "Drop-in replacement to panics in proc-macros" readme = "../README.md" keywords = ["proc-macro", "error", "errors"] categories = ["development-tools::procedural-macro-helpers"] license = "MIT" repository = "https://github.com/CreepySkeleton/proc-macro-error" [package.metadata.release] no-dev-version = true pre-release-commit-message = "v{{version}}" tag-name = "v{{version}}" [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "1" [features] dummy = [] [badges.maintenance] status = "actively-developed" proc-macro-error-0.2.6/src/dummy.rs000064400000000000000000000076341353310733700154050ustar0000000000000000//! `compile_error!` does not interrupt 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 //! trait MyTrait { //! fn do_thing(); //! } //! //! // this proc macro is supposed to generate MyTrait impl //! #[proc_macro_derive(MyTrait)] //! fn example(input: TokenStream) -> TokenStream { //! // somewhere deep inside //! span_error!(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: set_dummy test //! --> $DIR/probe.rs:9:10 //! | //! 9 |#[proc_macro_derive(MyTrait)] //! | ^^^^^^^ //! //! error[E0277]: the trait bound `Foo: Default` is not satisfied //! //! --> $DIR/probe.rs:14:10 //! //! | //! 98 | #[derive(MyTrait)] //! | ^^^^^^^ the trait `Default` is not implemented for `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 //! trait MyTrait { //! fn do_thing(); //! } //! //! // this proc macro is supposed to generate MyTrait impl //! #[proc_macro_derive(MyTrait)] //! 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 //! proc_macro_error::set_dummy(Some(quote! { //! impl MyTrait for #name { //! fn do_thing() { unimplemented!() } //! } //! })); //! //! // somewhere deep inside //! span_error!(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; thread_local! { pub(crate) static DUMMY_IMPL: RefCell> = RefCell::new(None); } pub(crate) fn take_dummy() -> Option { DUMMY_IMPL.with(|dummy| dummy.replace(None)) } /// Sets dummy token stream which will be appended to `compile_error!(msg);...` /// invocations, should a trigger happen. Returns an old dummy, if set. /// /// # Warning: /// If you do `set_dummy(Some(ts))` you **must** do `set_dummy(None)` /// before macro execution completes (`filer_macro_errors!` will do that for you)! /// Otherwise `rustc` will fail with creepy /// ```text /// thread 'rustc' panicked at 'use-after-free in `proc_macro` handle', src\libcore\option.rs:1166:5 /// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. /// error: proc-macro derive panicked /// ``` pub fn set_dummy(dummy: Option) -> Option { DUMMY_IMPL.with(|old_dummy| old_dummy.replace(dummy)) } proc-macro-error-0.2.6/src/lib.rs000064400000000000000000000260711353310635500150130ustar0000000000000000//! # proc-macro-error //! //! This crate aims to provide an error reporting mechanism that is usable inside //! `proc-macros`, can highlight a specific span, and can be migrated from //! `panic!`-based errors with minimal efforts. //! //! Also, there's [ability to append a dummy token stream][dummy] to your errors. //! //! ## Usage //! //! In your `Cargo.toml`: //! //! ```toml //! proc-macro-error = "0.2" //! ``` //! //! In `lib.rs`: //! //! ```rust,ignore //! extern crate proc_macro_error; //! use proc_macro_error::{ //! filter_macro_errors, //! span_error, //! call_site_error, //! ResultExt, //! OptionExt //! }; //! //! // This is your main entry point //! #[proc_macro] //! pub fn make_answer(input: TokenStream) -> TokenStream { //! // This macro **must** be placed at the top level. //! // No need to touch the code inside though. //! filter_macro_errors! { //! // `parse_macro_input!` and friends work just fine inside this macro //! let input = parse_macro_input!(input as MyParser); //! //! if let Err(err) = some_logic(&input) { //! // we've got a span to blame, let's use it //! let span = err.span_should_be_highlighted(); //! let msg = err.message(); //! // This call jumps directly to the end of `filter_macro_errors!` invocation //! span_error!(span, "You made an error, go fix it: {}", msg); //! } //! //! // `Result` gets some handy shortcuts if your error type implements //! // `Into<``MacroError``>`. `Option` has one unconditionally //! use proc_macro_error::ResultExt; //! more_logic(&input).expect_or_exit("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 //! call_site_error!( //! "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() //! } //! //! // At this point we have a new shining `proc_macro::TokenStream`! //! } //! ``` //! //! //! ## Motivation and Getting started //! //! Error handling in proc-macros sucks. It's not much of a choice today: //! you either "bubble up" the error up to the top-level of your macro and convert it to //! a [`compile_error!`][compl_err] invocation or just use a good old panic. Both these ways suck: //! //! - Former sucks because it's quite redundant to unroll a proper error handling //! just for critical errors that will crash the macro anyway so people mostly //! choose not to bother with it at all and use panic. Almost nobody does it, //! simple `.expect` is too tempting. //! - Later sucks because there's no way to carry out span info via `panic!`. `rustc` will highlight //! the whole invocation itself but not some specific token inside it. //! Furthermore, panics aren't for error-reporting at all; panics are for bug-detecting //! (like unwrapping on `None` or out-of range indexing) or for early development stages //! when you need a prototype ASAP and error handling can wait. Mixing these usages only //! messes things up. //! - There is [`proc_macro::Diagnostics`](https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html) //! but it's experimental. (This crate will be deprecated once `Diagnostics` is stable.) //! //! That said, we need a solution, but this solution must meet these conditions: //! //! - It must be better than `panic!`. The main point: it must offer a way to carry span information //! over to user. //! - It must require as little effort as possible to migrate from `panic!`. Ideally, a new //! macro with the same semantics plus ability to carry out span info. //! - It must be usable on stable. //! //! This crate aims to provide such a mechanism. All you have to do is enclose all //! the code inside your top-level `#[proc_macro]` function in [`filter_macro_errors!`] //! invocation and change panics to [`span_error!`]/[`call_site_error!`] where appropriate, //! see [Usage](#usage) //! //! # How it works //! Effectively, it emulates try-catch mechanism on the top of panics. //! //! Essentially, the [`filter_macro_errors!`] macro is (C++ like pseudo-code) //! //! ```C++ //! try { //! /* your code */ //! } catch (MacroError) { //! /* conversion to compile_error! */ //! } catch (MultiMacroErrors) { //! /* conversion to multiple compile_error! invocations */ //! } //! ``` //! //! [`span_error!`] and co are //! //! ```C++ //! throw MacroError::new(span, format!(msg...)); //! ``` //! //! By calling [`span_error!`] you trigger panic that will be caught by [`filter_macro_errors!`] //! and converted to [`compile_error!`][compl_err] invocation. //! All the panics that weren't triggered by [`span_error!`] and co will be resumed as is. //! //! Panic catching is indeed *slow* but the macro is about to abort anyway so speed is not //! a concern here. Please note that **this crate is not intended to be used in any other way //! than a proc-macro error reporting**, use `Result` and `?` instead. //! //! [compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html //! [`proc_macro::Diagnostics`](https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html) // reexports for use in macros pub extern crate proc_macro; pub extern crate proc_macro2; pub mod dummy; pub mod multi; pub mod single; pub use dummy::set_dummy; pub use multi::MultiMacroErrors; pub use single::MacroError; /// Makes a [`MacroError`] instance from provided arguments (`panic!`-like) /// and triggers panic in hope it will be caught by [`filter_macro_errors!`]. /// /// # Syntax /// /// This macro is meant to be a `panic!` drop-in replacement so its syntax is very similar to `panic!`, /// but it has three forms instead of two: /// /// 1. "panic-format-like" form: span, formatting [`str`] literal, comma-separated list of args. /// First argument is a span, all the rest gets passed to [`format!`] to build the error message. /// 2. "panic-single-arg-like" form: span, expr, no comma at the end. /// First argument is a span, the second is our error message, it must implement [`ToString`]. /// 3. "MacroError::trigger-like" form: single expr. /// Literally `MacroError::from(arg).trigger()`. It's here just for convenience so [`span_error!`] /// can be used with instances of [`syn::Error`], [`MacroError`], [`&str`], [`String`] and so on... /// #[macro_export] macro_rules! span_error { ($span:expr, $fmt:literal, $($args:expr),*) => {{ let msg = format!($fmt, $($args),*); // we use $span.into() so it would work with proc_macro::Span and // proc_macro2::Span all the same $crate::MacroError::new($span.into(), msg).trigger() }}; ($span:expr, $msg:expr) => {{ // we use $span.into() so it would work with proc_macro::Span and // proc_macro2::Span all the same $crate::MacroError::new($span.into(), $msg.to_string()).trigger() }}; ($err:expr) => { $crate::MacroError::from($err).trigger() }; } /// Shortcut for `span_error!(Span::call_site(), msg...)`. This macro /// is still preferable over plain panic, see [Motivation](#motivation-and-getting-started) #[macro_export] macro_rules! call_site_error { ($fmt:literal, $($args:expr),*) => {{ use $crate::span_error; let span = $crate::proc_macro2::Span::call_site(); span_error!(span, $fmt, $($args),*) }}; ($fmt:expr) => {{ use $crate::span_error; let span = $crate::proc_macro2::Span::call_site(); span_error!(span, $fmt) }}; } /// This macro is supposed to be used at the top level of your `proc-macro`, /// the function marked with a `#[proc_macro*]` attribute. It catches all the /// errors triggered by [`span_error!`], [`call_site_error!`], [`MacroError::trigger`] /// and [`MultiMacroErrors`]. /// Once caught, it converts it to a [`proc_macro::TokenStream`] /// containing a [`compile_error!`][compl_err] invocation. /// /// See the [module-level documentation](self) for usage example /// /// [compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html #[macro_export] macro_rules! filter_macro_errors { ($($code:tt)*) => { let f = move || -> $crate::proc_macro::TokenStream { $($code)* }; $crate::filter_macro_error_panics(f) }; } /// This traits expands [`Result>`](std::result::Result) 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 [`span_error!`]. fn unwrap_or_exit(self) -> Self::Ok; /// Behaves like [`Result::expect`]: if self is `Ok` yield the contained value, /// otherwise abort macro execution via [`span_error!`]. /// If it aborts then resulting message will be preceded with `message`. fn expect_or_exit(self, msg: &str) -> Self::Ok; } /// This traits expands [`Option`][std::option::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 [`call_site_error!`]. /// 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_exit(self, msg: &str) -> Self::Some; } impl OptionExt for Option { type Some = T; fn expect_or_exit(self, message: &str) -> T { match self { Some(res) => res, None => call_site_error!(message), } } } /// Execute the closure and catch all the panics triggered by /// [`single::MacroError::trigger`] and [`multi::MultiMacroErrors::trigger`], /// converting them to [`proc_macro::TokenStream`] instance. /// Any panic that is unrelated to this crate will be passed through as is. /// /// You're not supposed to use this function directly, use [`filter_macro_errors!`] /// instead. #[doc(hidden)] pub fn filter_macro_error_panics(f: F) -> proc_macro::TokenStream where F: FnOnce() -> proc_macro::TokenStream, { use proc_macro2::TokenStream; use quote::ToTokens; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; let caught = catch_unwind(AssertUnwindSafe(f)); let dummy = dummy::take_dummy(); macro_rules! probe_error { ($t:ty) => { |boxed: Box| { let payload = boxed.downcast::>()?; let mut ts: TokenStream = (*payload).0.into(); dummy.to_tokens(&mut ts); Ok(ts.into()) } }; } caught .or_else(probe_error!(MacroError)) .or_else(probe_error!(MultiMacroErrors)) .unwrap_or_else(|boxed| resume_unwind(boxed)) } struct Payload(T); // SAFE: Payload is private, a user can't use it to make any harm. unsafe impl Send for Payload {} unsafe impl Sync for Payload {} proc-macro-error-0.2.6/src/multi.rs000064400000000000000000000064671353307772100154120ustar0000000000000000//! **This module is deprecated! Come back in v0.3.** #![doc(hidden)] use crate::ResultExt; use crate::{MacroError, Payload}; use proc_macro2::{Span, TokenStream}; use std::iter::FromIterator; use std::ops::{Deref, DerefMut}; /// This type represents a container for multiple errors. Each error has it's own span /// location. pub struct MultiMacroErrors(Vec); impl MultiMacroErrors { /// Create an empty errors storage. pub fn new() -> Self { MultiMacroErrors(Vec::new()) } /// Test if the storage contains no errors. pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Add an error to the list of errors. pub fn add(&mut self, err: MacroError) { self.0.push(err) } /// Shortcut for `.add(MacroError::new(span, msg))` pub fn add_span_msg(&mut self, span: Span, msg: String) { self.add(MacroError::new(span, msg)) } /// Convert this storage into [`TokenStream`] consisting of `compile_error!(msg1); compile_error!(msg2);`... /// sequence. Each `compile_error!` invocation gets the span attached to the particular message. pub fn into_compile_errors(self) -> TokenStream { let errs = self.0.into_iter().flat_map(MacroError::into_compile_error); TokenStream::from_iter(errs) } /// Abort the proc-macro's execution and display the errors contained. /// /// ### Note: /// /// This function aborts even no errors are present! It's very much possibly that /// [`trigger_on_error`][MultiMacroErrors::trigger_on_error] is what you really want. pub fn trigger(self) -> ! { panic!(Payload(self)) } /// If this storage is not empty abort the proc-macro's execution and display the errors contained. pub fn trigger_on_error(self) { if !self.is_empty() { self.trigger() } } } impl ResultExt for Result { type Ok = T; fn unwrap_or_exit(self) -> T { match self { Ok(res) => res, Err(e) => e.trigger(), } } fn expect_or_exit(self, message: &str) -> T { match self { Ok(res) => res, Err(mut e) => { for MacroError { msg, .. } in e.0.iter_mut() { *msg = format!("{}: {}", message, msg); } e.trigger() } } } } impl From for TokenStream { fn from(errors: MultiMacroErrors) -> Self { errors.into_compile_errors() } } impl From for proc_macro::TokenStream { fn from(errors: MultiMacroErrors) -> Self { errors.into_compile_errors().into() } } impl FromIterator for MultiMacroErrors { fn from_iter(iter: I) -> Self where I: IntoIterator, { MultiMacroErrors(Vec::from_iter(iter)) } } impl IntoIterator for MultiMacroErrors { type Item = MacroError; type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Deref for MultiMacroErrors { type Target = [MacroError]; fn deref(&self) -> &[MacroError] { &self.0 } } impl DerefMut for MultiMacroErrors { fn deref_mut(&mut self) -> &mut [MacroError] { &mut self.0 } } proc-macro-error-0.2.6/src/single.rs000064400000000000000000000071621353310233600155210ustar0000000000000000//! This module contains data types and functions to be used for single-error reporting. //! //! These are supposed to be used through [`span_error!`] and [`call_site_error!`], //! see [crate level documentation](crate). use crate::Payload; use crate::ResultExt; use proc_macro2::{Span, TokenStream}; use quote::quote_spanned; use std::convert::{AsMut, AsRef}; use std::fmt::{Display, Formatter}; /// An single error in a proc-macro. This struct preserves /// the given span so `rustc` can highlight the exact place in user code /// responsible for the error. /// /// You're not supposed to use this type directly, use [`span_error!`] and [`call_site_error!`]. #[derive(Debug)] pub struct MacroError { pub(crate) span: Span, pub(crate) msg: String, } impl MacroError { /// Create an error with the span and message provided. pub fn new(span: Span, msg: String) -> Self { MacroError { span, msg } } /// A shortcut for `MacroError::new(Span::call_site(), message)` pub fn call_site(msg: String) -> Self { MacroError::new(Span::call_site(), msg) } /// Convert this error into a [`TokenStream`] containing these tokens: `compiler_error!();`. /// All these tokens carry the span this error contains attached. /// /// There are `From<[MacroError]> for proc_macro/proc_macro2::TokenStream` implementations /// so you can use `error.into()` instead of this method. /// /// [compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html pub fn into_compile_error(self) -> TokenStream { let MacroError { span, msg } = self; quote_spanned! { span=> compile_error!(#msg); } } /// Abandon the old span and replace it with the given one. pub fn set_span(&mut self, span: Span) { self.span = span; } /// Get the span contained. pub fn span(&self) -> Span { self.span.clone() } /// Trigger single error, aborting the proc-macro's execution. /// /// You're not supposed to use this function directly. /// Use [`span_error!`] or [`call_site_error!`] instead. pub fn trigger(self) -> ! { panic!(Payload(self)) } } impl From for MacroError { fn from(e: syn::Error) -> Self { MacroError::new(e.span(), e.to_string()) } } impl From for MacroError { fn from(msg: String) -> Self { MacroError::call_site(msg) } } impl From<&str> for MacroError { fn from(msg: &str) -> Self { MacroError::call_site(msg.into()) } } impl Display for MacroError { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { Display::fmt(&self.msg, f) } } impl> ResultExt for Result { type Ok = T; fn unwrap_or_exit(self) -> T { match self { Ok(res) => res, Err(e) => e.into().trigger(), } } fn expect_or_exit(self, message: &str) -> T { match self { Ok(res) => res, Err(e) => { let MacroError { msg, span } = e.into(); let msg = format!("{}: {}", message, msg); MacroError::new(span, msg).trigger() } } } } impl From for TokenStream { fn from(err: MacroError) -> Self { err.into_compile_error() } } impl From for proc_macro::TokenStream { fn from(err: MacroError) -> Self { err.into_compile_error().into() } } impl AsRef for MacroError { fn as_ref(&self) -> &String { &self.msg } } impl AsMut for MacroError { fn as_mut(&mut self) -> &mut String { &mut self.msg } } proc-macro-error-0.2.6/.cargo_vcs_info.json0000644000000001120000000000000142630ustar00{ "git": { "sha1": "33717754c467a9d808ad136b6cbdfaf6452dfb52" } }