# Custom error diagnostics with procedural macros on (almost) stable Rust

**URL:** <https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113>\
**Category:** Uncategorized\
**Created:** [August 1, 2018, 11:40pm UTC](https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113 "2018-08-01T23:40:36Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![alexcrichton](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alexcrichton/32/4501_2.png) [@alexcrichton](https://internals.rust-lang.org/u/alexcrichton)\
**Post date:** [August 1, 2018, 11:40pm UTC](https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113/1 "2018-08-01T23:40:36Z")

</div>

I realized a capability of the compiler today that I don’t think I’ve seen used much, and I wanted to share! Currently the `proc_macro` API, the foundation of procedural macros, is quite slim on its implementation. The upcoming stabilization for 1.30 will bring a long-awaited feature, working with `Span` information!

Currently today if you’re parsing something, say in a custom derive, when you hit an error your only recourse is to `panic!`. This is a pretty bad UI experience for your users because the panic message isn’t attached to the right span of code that the error originated from, it just points to an attribute. While today `proc_macro` has some [unstable APIs](https://doc.rust-lang.org/nightly/proc_macro/struct.Diagnostic.html) for diagnostics, they’re unlikely to be stable for the 2018 edition’s initial release. As it turns out, though, we’ll be able to have **stable custom errors with custom spans** as part of the 2018 edition release!

It turns out the compiler already has a feature for custom error messages, the `compile_error!` macro. Furthermore, as a procedural macro, you can manipulate the spans of each of the tokens of the `compile_error!("foo")` invocation. This means, subsequently, that you can target any error at any span!

For example given this demo library:

> ****
>
> ```rust
> extern crate proc_macro;
> 
> use proc_macro::*;
> 
> #[proc_macro]
> pub fn span_one_token(x: TokenStream) -> TokenStream {
> let span = x.into_iter().next().unwrap().span();
> 
> error("this is a single token error", span, span)
> }
> 
> #[proc_macro]
> pub fn span_two_tokens(x: TokenStream) -> TokenStream {
> let mut iter = x.into_iter();
> let start = iter.next().unwrap().span();
> let end = iter.next().unwrap().span();
> 
> error("this error spans two tokens, maybe more!", start, end)
> }
> 
> #[proc_macro]
> pub fn generate_two_errors(x: TokenStream) -> TokenStream {
> let mut iter = x.into_iter();
> let err1 = iter.next().unwrap().span();
> let err2 = iter.next().unwrap().span();
> 
> let a = error("this error is for one token ...", err1, err1);
> let b = error("... and this error is for another", err2, err2);
> 
> a.into_iter().chain(b.into_iter()).collect()
> }
> 
> fn error(s: &str, start: Span, end: Span) -> TokenStream {
> let mut v = Vec::new();
> v.push(respan(Literal::string(&s), Span::call_site()));
> let group = v.into_iter().collect();
> 
> let mut r = Vec::<TokenTree>::new();
> r.push(respan(Ident::new("compile_error", start), start));
> r.push(respan(Punct::new('!', Spacing::Alone), Span::call_site()));
> r.push(respan(Group::new(Delimiter::Brace, group), end));
> 
> r.into_iter().collect()
> }
> 
> fn respan<T: Into<TokenTree>>(t: T, span: Span) -> TokenTree {
> let mut t = t.into();
> t.set_span(span);
> t
> }
> 
> ```

and this invocation:

```rust
#![feature(use_extern_macros)]

extern crate foo;

foo::span_one_token! { a }
foo::span_two_tokens! { c d }
foo::generate_two_errors! { e f }

fn main() {
    println!("Hello, world!");
}

```

you get these errors:

```rust
   Compiling foo v0.1.0 (file:///home/alex/code/foo)
error: this is a single token error
 --> src/main.rs:5:24
  |
5 | foo::span_one_token! { a }
  | ^

error: this error spans two tokens, maybe more!
 --> src/main.rs:6:25
  |
6 | foo::span_two_tokens! { c d }
  | ^^^

error: this error is for one token ...
 --> src/main.rs:7:29
  |
7 | foo::generate_two_errors! { e f }
  | ^

error: ... and this error is for another
 --> src/main.rs:7:31
  |
7 | foo::generate_two_errors! { e f }
  | ^

error: aborting due to 4 previous errors

```

and voila! Custom errors on stable Rust [as soon as macros 1.2 is stabilized](https://internals.rust-lang.org/t/macros-1-2-stabilization-status-update/7917) in the 2018 edition release.

If you’re curious about seeing this in action, I’ve [opened an issue on `wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen/issues/601) to make use of this trick and have started to capitalize on this ability in [rustwasm/wasm-bindgen#608](https://github.com/rustwasm/wasm-bindgen/pull/608)

---

<div class="post-metadata">

**Author:** ![akiselev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/akiselev/32/4517_2.png) [@akiselev](https://internals.rust-lang.org/u/akiselev)\
**Post date:** [August 2, 2018, 1:24am UTC](https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113/2 "2018-08-02T01:24:45Z")

</div>

Awesome! Is it possible to emit those errors conditionally on type checking results, maybe by wrapping it with another macro? Even just gating them on an associated `const` or type comparison would open up a lot of possibilities, especially when working with recursive types where just the `crate::mod::` path eats up 95% of the error and makes it nearly impossible to identify where in the chain of trait bounds the problem is, let alone what the types are.

---

<div class="post-metadata">

**Author:** ![Centril](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/centril/32/3334_2.png) [@Centril](https://internals.rust-lang.org/u/Centril)\
**Post date:** [August 2, 2018, 1:59am UTC](https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113/3 "2018-08-02T01:59:56Z")

</div>

An idea with is slightly off-topic to this thread but which I’d like to record for future reference is…

… It would be cool if you could control the **color and boldness of certain segments of your custom error message;** for example, I might have custom error codes (and an index of the errors elsewhere) and I want to color the error codes with `red` and make them bold…

… I have tried ANSI escape codes in `compile_error!(..)` but that did not work.

---

<div class="post-metadata">

**Author:** ![alexcrichton](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alexcrichton/32/4501_2.png) [@alexcrichton](https://internals.rust-lang.org/u/alexcrichton)\
**Post date:** [August 2, 2018, 4:43am UTC](https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113/4 "2018-08-02T04:43:29Z")

</div>

Ah unfortunately I don’t know how to do that sort of conditional error handling in the compiler. This idea though could perhaps be extended to different strategies in the future though! We can also just spend time stabilizing the `Diagnostic` API in the `proc_macro` crate too 🙂

---

<div class="post-metadata">

**Author:** ![alexcrichton](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alexcrichton/32/4501_2.png) [@alexcrichton](https://internals.rust-lang.org/u/alexcrichton)\
**Post date:** [March 25, 2019, 8:30am UTC](https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113/5 "2019-03-25T08:30:38Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
