Skip to main content

Diagnostics

internal/diag. Structured, position-aware error reporting.

Types

type Pos struct {
File string
Line int // 1-based
Col int // 1-based
ByteOff int
}

type Span struct {
Start, End Pos
}

type Label struct {
Span Span
Msg string
}

type Severity int // SevError | SevWarning | SevNote

type Diagnostic struct {
Severity Severity
Code string // e.g. "E0123"; empty if uncoded
Primary Label // primary location and message
Notes []Label // optional secondary spans + messages
Help string // optional follow-up suggestion
}

A Diagnostic is an error; its Error() method returns a single-line summary built from Severity, optional Code, and the primary span and message. The driver's reportErr uses errors.As to detect it and call Render(src) for the caret-style display; non-diagnostic errors fall back to plain %v.

Caret rendering

test.esq:3:5
3 │ fn main() -> i32 = 5000000000
│ ^^^^^^^^^^ integer literal overflows i32
│ help: try _i64

Render(src) is responsible for:

  • Reading the source line.
  • Drawing carets under Span.
  • Indenting the help and note blocks.

When to produce a Diagnostic

Anywhere the user would benefit from a span-anchored error:

  • Parse errors (already structured).
  • Type-check errors (already structured).
  • CEIR/MIR/x86 lowering errors that surface to the user (e.g. "tabulate(N) requires N ≤ 32").

When in doubt, emit a *diag.Diagnostic rather than fmt.Errorf. The driver will render it for free.

Style

  • Primary message: a sentence fragment, no trailing period, present tense. e.g. "integer literal overflows i32".
  • Help: a forward-pointing suggestion, often referencing a planned feature. e.g. "try _i64".
  • Notes: optional secondary Labels for things like "first defined here" / "first used here". Each note carries its own span and message.