Reading a diagnostic
Every error and warning has the same five parts.
── tests/diag_call_notes.mettle:6:20 ──────────────────────────────
error[E0003]: Function 'add' expects 2 arguments, got 3
────┬─────────────────────────────────────────────────────────────
5 │ fn main() -> int64 {
6 │ var r: int64 = add(1, 2, 3);
· ^^^ expected 2 arguments, got 3
7 │ return r;
────┼─────────────────────────────────────────────────────────────
│ note function 'add' defined here
1 │ fn add(a: int64, b: int64) -> int64 {
· ^^^
────┴─────────────────────────────────────────────────────────────
help: pass 2 arguments, or change 'add' to take 3
The rule
Carries the location and nothing else, as file:line:column, so a
terminal or an editor can turn it into a jump. Every diagnostic opens with one,
which is also what separates one from the next.
The headline
Severity, code, message. The bracketed code is stable across compiler versions, which is what makes it worth grepping for and what the reference is indexed by. This line is never wrapped, even on a narrow terminal.
The frame
The line the diagnostic is about, with one line of context either side. Tabs expand to four columns so the caret lands under the character you see rather than the byte offset.
The caret
^^^ marks the exact span, and the text after it says what is
wrong there. The headline says what happened; the label says which part
of the line caused it.
Notes and help
A note points at a second place that matters: the declaration a call
disagrees with, the previous declaration a duplicate collides with, or the
comptime iteration a generated declaration came from. Notes share
the parent's frame, so one error reads as one object. help: is the
suggested fix.
Several errors at once
The compiler reports every error it can reach, then summarizes:
──────────────────────────────────────────────────────────────────
error: could not compile `bad.mettle` due to 2 previous errors
help: for more about this error, run `mettle explain M0118`
The parser resynchronizes at block boundaries, so one missing brace reports once instead of cascading. Diagnostics that are only consequences of an earlier one are suppressed.
Severity
error fails the build, warning does not. Most
memory diagnostics are warnings, because the code they describe is legal and
wrong. The ones that cannot possibly be intended, such as returning the address
of a local, are errors.
The codes
E0001 to E0007 are the compiler's categories: lexical,
syntax, semantic, type, scope, I/O, internal. M0101 to M0119
come from the memory, borrow and range analyses, which stamp a finer code over
the category they would otherwise carry.
mettle explain E0003
mettle explain list
The same text is on this site: every code the compiler can print. Both come from one table, so they cannot disagree.
JSON
--error-format=json writes one object per diagnostic to stderr,
newline-delimited:
{"severity":"error","code":"M0118","message":"Integer 300 is out of range for 'int8'",
"file":"bad.mettle","line":2,"column":17,"length":3,
"label":"does not fit in 'int8' (-128..127)",
"help":"'int8' holds -128..127. Widen the type, or cast to say the wrap is meant: (int8)value",
"notes":[]}
length is how many columns the caret spans; notes
carries the secondary locations.
Presentation
NO_COLOR,CLICOLOR=0andTERM=dumbturn colour off;CLICOLOR_FORCEturns it on even when stderr is not a terminal.METTLE_DIAG_COLUMNSpins the width.METTLE_DIAG_UNICODE=0forces the ASCII frame.
Redirected output gets no colour, no box characters, and no line wrapping, so a captured build log is stable regardless of anyone's terminal.