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

Debugging

Inspect generated code by adding the debug argument to any zyn attribute macro. Debug output is emitted as a compiler note diagnostic, visible in both terminal and IDE.

Setup

Two conditions must be met for debug output:

  1. Add debug (or debug = "pretty") to the macro attribute
  2. Set the ZYN_DEBUG environment variable to match the generated type name
#[zyn::element(debug)]
fn greeting(name: syn::Ident) -> zyn::TokenStream {
    zyn::zyn!(fn {{ name }}() {})
}
ZYN_DEBUG="*" cargo build

Without ZYN_DEBUG set, the debug argument is inert — no output, no overhead. This makes it safe to leave in source code during development.

Note

An element annotated with debug — the argument is inert until ZYN_DEBUG is set.

Element with debug arg

Supported macros

MacroSyntax
#[zyn::element]#[zyn::element(debug)], #[zyn::element("name", debug)]
#[zyn::pipe]#[zyn::pipe(debug)], #[zyn::pipe("name", debug)]
#[zyn::derive]#[zyn::derive("Name", debug)], #[zyn::derive("Name", attributes(skip), debug)]
#[zyn::attribute]#[zyn::attribute(debug)]

All macros also accept debug = "pretty" in place of debug:

MacroPretty syntax
#[zyn::element]#[zyn::element(debug = "pretty")], #[zyn::element("name", debug = "pretty")]
#[zyn::pipe]#[zyn::pipe(debug = "pretty")], #[zyn::pipe("name", debug = "pretty")]
#[zyn::derive]#[zyn::derive("Name", debug = "pretty")]
#[zyn::attribute]#[zyn::attribute(debug = "pretty")]

Output formats

Raw (default)

The default format emits the raw TokenStream::to_string() output. This is a flat, single-line string with fully-qualified paths and spaces between all tokens. No extra dependencies are required.

#[zyn::element(debug)]
fn greeting(name: syn::Ident) -> zyn::TokenStream {
    zyn::zyn!(fn {{ name }}() {})
}
ZYN_DEBUG="Greeting" cargo build
note: zyn::element ─── Greeting

      struct Greeting { pub name : zyn :: syn :: Ident , } impl :: zyn :: Render
      for Greeting { fn render (& self , input : & :: zyn :: Input) -> :: zyn ::
      proc_macro2 :: TokenStream { ... } }
  --> src/lib.rs:1:1

The raw format is useful for quick checks and when you want to see the exact tokens being generated.

Note

Raw debug output shown as an inline compiler diagnostic in the editor.

Raw debug output — inline diagnostic

Note

The same raw output surfaced in the Problems panel for easy navigation.

Raw debug output — Problems panel

Note

Pretty-printed debug output in the console for a pipe.

Pretty debug console output

Pretty (feature-gated)

The pretty format uses prettyplease to produce properly formatted Rust code with indentation and line breaks.

Enable the pretty feature in your Cargo.toml:

[dependencies]
zyn = { version = "0.4", features = ["pretty"] }

Then use debug = "pretty":

#[zyn::element(debug = "pretty")]
fn greeting(name: syn::Ident) -> zyn::TokenStream {
    zyn::zyn!(fn {{ name }}() {})
}
ZYN_DEBUG="Greeting" cargo build
note: zyn::element ─── Greeting

      struct Greeting {
          pub name: zyn::syn::Ident,
      }
      impl ::zyn::Render for Greeting {
          fn render(&self, input: &::zyn::Input) -> ::zyn::Output {
              let mut diagnostics = ::zyn::mark::new();
              let name = &self.name;
              let __body = { zyn::zyn!(fn {{ name }}() {}) };
              ::zyn::Output::new()
                  .tokens(__body)
                  .diagnostic(diagnostics)
                  .build()
          }
      }
  --> src/lib.rs:1:1

Note

Pretty-printed debug output — formatted with prettyplease for readable, indented Rust code.

Pretty debug output

If debug = "pretty" is used without the pretty feature enabled, you’ll get a helpful compile error:

error: enable the `pretty` feature to use `debug = "pretty"`
 --> src/lib.rs:1:24
  |
1 | #[zyn::element(debug = "pretty")]
  |                        ^^^^^^^^

ZYN_DEBUG environment variable

The ZYN_DEBUG environment variable controls which items produce debug output. It accepts comma-separated patterns with * wildcards, matched against the generated type name (the PascalCase struct name, not the function name).

For an element defined as fn greeting(...), the generated type is Greeting. For a pipe fn shout(...), the type is Shout.

PatternMatches
*Everything
GreetingExact match only
Greet*Greeting, GreetingElement, etc.
*ElementFieldElement, GreetingElement, etc.
Greeting,ShoutGreeting and Shout
Greet*,Shout,*PipeMix wildcards and exact
ZYN_DEBUG="*" cargo build
ZYN_DEBUG="Greeting" cargo build
ZYN_DEBUG="Greet*" cargo build
ZYN_DEBUG="Greeting,Shout" cargo build

Noise stripping

Before formatting (in both raw and pretty modes), zyn strips internal boilerplate from the generated code:

  • #[doc = "..."] attributes — removes the doc comment blocks on generated diagnostic macros
  • #[allow(...)] attributes — removes #[allow(unused)] and similar
  • macro_rules! definitions — removes the internal error!, warn!, note!, help!, and bail! macro definitions

This keeps the debug output focused on the code you care about: the generated struct and its Render / Pipe implementation.

Full example

Given this element:

#[zyn::element(debug = "pretty")]
fn field_getter(
    name: syn::Ident,
    ty: syn::Type,
) -> zyn::TokenStream {
    zyn::zyn!(
        pub fn {{ name | ident:"get_{}" }}(&self) -> &{{ ty }} {
            &self.{{ name }}
        }
    )
}

Running with ZYN_DEBUG="FieldGetter" cargo build produces:

note: zyn::element ─── FieldGetter

      struct FieldGetter {
          pub name: syn::Ident,
          pub ty: syn::Type,
      }
      impl ::zyn::Render for FieldGetter {
          fn render(&self, input: &::zyn::Input) -> ::zyn::Output {
              let mut diagnostics = ::zyn::mark::new();
              let name = &self.name;
              let ty = &self.ty;
              let __body = {
                  zyn::zyn!(
                      pub fn {{ name | ident:"get_{}" }}(&self) -> &{{ ty }} {
                          &self.{{ name }}
                      }
                  )
              };
              ::zyn::Output::new()
                  .tokens(__body)
                  .diagnostic(diagnostics)
                  .build()
          }
      }

Pipeline API

For library authors building on top of zyn, the debug module exposes a pipeline API via the DebugExt trait:

use zyn::debug::DebugExt;

// Raw format — always available
let raw: String = tokens.debug().raw();

// Pretty format — requires `pretty` feature
#[cfg(feature = "pretty")]
let pretty: String = tokens.debug().pretty();