Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Diagnostics

The #[zyn::element] attribute generates error!, warn!, note!, help!, and bail! macros that push diagnostics to an auto-injected diagnostics accumulator.

error! — Compile Error

#[zyn::element]
fn validated(#[zyn(input)] ident: syn::Ident) -> zyn::TokenStream {
    if ident.to_string() == "forbidden" {
        error!("reserved identifier");
    }
    bail!();

    zyn::zyn! { fn {{ ident }}() {} }
}
error: reserved identifier
  --> src/lib.rs:8:24

warn! — Compiler Warning

Does not halt compilation:

#[zyn::element]
fn legacy(#[zyn(input)] ident: syn::Ident) -> zyn::TokenStream {
    warn!("this usage is deprecated, use `new_api` instead");

    zyn::zyn! { fn {{ ident }}() {} }
}

note! — Informational Note

note!("only named fields are supported");
note!("expected `{}`", expected; span = ident.span());

help! — Help Suggestion

help!("consider using `Builder::new()` instead");
help!("try `{}` instead", suggestion; span = ident.span());

bail! — Early Return on Errors

Returns early if any errors have accumulated, or pushes an error and returns immediately:

bail!();                             // return if any errors
bail!("struct must have fields");    // push error + return

Errors with Notes and Help

Accumulate multiple diagnostics before returning:

#[zyn::element]
fn validated(name: syn::Ident) -> zyn::TokenStream {
    if name.to_string() == "forbidden" {
        error!("reserved identifier"; span = name.span());
        note!("this name is reserved by the compiler");
        help!("try a different name like `my_handler`");
    }
    bail!();

    zyn::zyn! { fn {{ name }}() {} }
}
error: reserved identifier
note: this name is reserved by the compiler
help: try a different name like `my_handler`
  --> src/lib.rs:8:24
   |
 8 |     @validated(name = forbidden)
   |                       ^^^^^^^^^

Format String Interpolation

All macros accept format!-style arguments:

error!("field `{}` is required", name);
warn!("type `{}` is deprecated", ty);

Custom Spans

Override the default span with ; span = expr:

error!("invalid field"; span = field.span());
bail!("missing `{}`", name; span = ident.span());

Accessing the Accumulator Directly

The diagnostics variable is a zyn::Diagnostics and can be used directly:

#[zyn::element]
fn my_element(#[zyn(input)] fields: zyn::Fields<syn::Field>) -> zyn::TokenStream {
    for field in fields.iter() {
        if field.ident.is_none() {
            error!("all fields must be named"; span = field.span());
        }
    }

    if diagnostics.has_errors() {
        return diagnostics.emit();
    }

    zyn::zyn! { struct Validated; }
}