Skip to content

Tooling and error messages

Dazzle is designed to be learned from the compiler. No model has Dazzle in its training data, and neither does a new programmer, so the compiler has to teach.

  • Every diagnostic has a stable code, like DZ0510. Codes are never reused, so you can search for one.
  • It points at your code, never at generated C++.
  • It nearly always ends with help:, saying what to write instead and which section of the specification explains why.
  • One mistake, one error. The parser suppresses cascades, so a typo does not produce four restatements of itself.
error[DZ0510]: fallible call not consumed
┌─ src/main.dz:9:17
│
9 │ let level = load("level1.dat")
│ ^^^^^^^^^^^^^^^^^^ a failure cannot be stored, only handled
│
= help: add an `or` clause: `let v = f() or return`, or `let v = f() or <fallback>` (spec §9.7)
Terminal window
dz explain DZ0510

prints the rule behind a diagnostic, why the rule exists, and a wrong and a corrected program.

A diagnostic can carry the exact edit that fixes it. In the editor it is a quick fix: fill a switch’s missing arms, or implement a trait method an impl left out. On the command line, dz fix applies every fix the compiler is certain of (--dry-run lists them), and dz check --format=json gives every diagnostic, with its fixes, as JSON for scripts and AI agents.

dz lsp is a language server with diagnostics as you type, hover, go to definition, find references, rename, completion (including inside macro invocations and generic bodies), signature help, inlay hints and the outline. It keeps answering around syntax errors, because the lexer and parser never give up on half-typed code.

The VS Code extension adds highlighting, run and debug buttons, and debugger visualisers for Dazzle’s types. See Install.

dz fmt is the single canonical format. It only ever changes layout, and it refuses to touch a file with syntax errors rather than silently dropping the broken parts.

#[test]
fn clamp_keeps_values_in_range() {
assert(clamp01(2) == 1)
assert(clamp01(-3) == 0, "negative values clamp to zero")
}

dz test runs each test in its own process, so one crash does not hide the rest, and prints each failing test’s own panic message. #[bench] functions are timed by dz bench, which builds in release mode and reports the fastest of five calibrated batches. The repository compares Dazzle against hand-written C++ this way: bounds-checked indexing, drop against a destructor, and virtual dispatch against C++’s own.

The compiler chooses where each allocation goes: the region of the scope that owns the value, wherever the caller keeps a result, or the heap (Ownership). The editor shows the choice as an inlay hint at each allocation, and dz placements lists every one with its reason, as text or with --format=json.

The generated C++ carries #line directives back to your .dz files, so gdb, LLDB, the Visual Studio debugger and profilers show your source. --track-memory records every allocation and reports leaks, double frees and mismatched frees with the line that caused them. [build] check_trusted = true in the manifest adds a run-time check of the standard library’s hand-written kills clauses, for test runs. dz expand shows exactly what a macro wrote.

--release builds at -O2 with link-time optimisation, so the C++ compiler inlines across modules, and #[inline] on a function makes its body visible to every module that calls it. The standard library marks its small, hot functions this way.

Tracked in the backlog:

  • diagnostics that recognise habits from Rust and C++ (Result<T, E>, x?, nullptr, p->x, Foo::new()) and give the Dazzle spelling;
  • dz api <module>, which prints a module’s public surface, an imported C++ library’s included, as Dazzle signatures;
  • editor queries from the command line, machine-readable test output, and dz run --frames N, so a game can be checked without anyone watching its window.

§17 Compiler architecture and toolchain.