Skip to main content

Diagnostic

Struct Diagnostic 

Source
pub struct Diagnostic { /* private fields */ }
Expand description

One thing a compiler stage has to say about the source: a Severity, a headline message, a primary Label pointing at the span it concerns, and optionally secondary labels for related locations and trailing note/help lines. Since 1.1.0 it can also carry a Code naming its kind and fix-it Suggestions.

A Diagnostic is the unit a lexer, parser, or type checker emits and a Renderer turns into caret-annotated output. Constructing one is cheap — it allocates only what the message, labels, and notes require — because it is on the front-end’s hot path. The primary label is supplied at construction, so a diagnostic always knows where it points; secondary labels and notes are added with the chainable with_* builders.

§Examples

use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(
    Severity::Error,
    "mismatched types",
    Label::new(Span::new(20, 27), "expected `i32`, found `&str`"),
)
.with_secondary(Label::new(Span::new(8, 11), "expected due to this"))
.with_note("string literals have type `&str`")
.with_help("convert with `.parse()`");

assert_eq!(diag.secondary().len(), 1);
assert_eq!(diag.notes().count(), 1);
assert_eq!(diag.help().count(), 1);

Implementations§

Source§

impl Diagnostic

Source

pub fn new( severity: Severity, message: impl Into<Box<str>>, primary: Label, ) -> Diagnostic

Builds a diagnostic at severity, headed by message, pointing at primary.

The message is taken by value (a &str or an owned String), so the diagnostic owns its text. The primary label is required: a diagnostic always has a span to render a caret under, which keeps the type total and the renderer free of a “no location” branch. Secondary labels and notes start empty; add them with with_secondary, with_note, and with_help.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(
    Severity::Warning,
    "unused variable `x`",
    Label::new(Span::new(4, 5), "first defined here"),
);
assert_eq!(diag.severity(), Severity::Warning);
assert!(diag.secondary().is_empty());
Source

pub fn with_secondary(self, label: Label) -> Diagnostic

Adds a secondary label, returning the diagnostic for chaining.

Secondary labels mark locations related to the primary one — “expected because of this”, “first defined here”. A renderer draws them with a - underline to distinguish them from the primary ^. Any number may be added; they render in a stable order driven by where they point, not by the order they were added.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(
    Severity::Error,
    "duplicate definition",
    Label::new(Span::new(30, 33), "redefined here"),
)
.with_secondary(Label::new(Span::new(4, 7), "first defined here"));
assert_eq!(diag.secondary().len(), 1);
Source

pub fn with_note(self, note: impl Into<Box<str>>) -> Diagnostic

Adds a trailing note, returning the diagnostic for chaining.

A note is neutral context with no span of its own; a renderer prints it on its own line below the frame, marked note:. Notes render in the order they are added, after any help lines’ siblings — see with_help.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)))
    .with_note("the value was moved on the previous line");
assert_eq!(diag.notes().next(), Some("the value was moved on the previous line"));
Source

pub fn with_help(self, help: impl Into<Box<str>>) -> Diagnostic

Adds a trailing help line, returning the diagnostic for chaining.

A help line is a suggestion toward a fix; a renderer prints it below the notes, marked help:. Help lines render in the order they are added.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)))
    .with_help("add a semicolon");
assert_eq!(diag.help().next(), Some("add a semicolon"));
Source

pub fn with_code(self, code: Code) -> Diagnostic

Sets the diagnostic’s Code, returning the diagnostic for chaining.

The code is the stable, machine-readable name of this kind of diagnostic (see Code for the format and the recommended ranges). The renderer shows it in the header, error[E0001]: …, and the JSON output carries it. Setting a code again replaces the previous one.

Since 1.1.0.

§Examples
use diag_lang::{Code, Diagnostic, Label, Severity, Span};

const UNKNOWN_NAME: Code = Code::new("E", 2001).unwrap();

let diag = Diagnostic::new(Severity::Error, "unknown name", Label::unlabelled(Span::empty(0)))
    .with_code(UNKNOWN_NAME);
assert_eq!(diag.code(), Some(UNKNOWN_NAME));
Source

pub fn with_suggestion(self, suggestion: Suggestion) -> Diagnostic

Adds a fix-it Suggestion, returning the diagnostic for chaining.

Suggestions render after the notes and help lines, each as a help: block showing the source as it would read with the fix applied; they appear in the JSON output with every edit; and apply_fixes applies the machine-applicable ones. Any number may be added; they keep the order they were added in.

Since 1.1.0.

§Examples
use diag_lang::{Applicability, Diagnostic, Edit, Label, Severity, Span, Suggestion};

let diag = Diagnostic::new(Severity::Error, "expected `;`", Label::unlabelled(Span::empty(9)))
    .with_suggestion(
        Suggestion::new("add `;`", Applicability::MachineApplicable)
            .with_edit(Edit::insert(9, ";")),
    );
assert_eq!(diag.suggestions().len(), 1);
Source

pub const fn code(&self) -> Option<Code>

Returns the diagnostic’s code, if one was set with with_code.

Since 1.1.0.

§Examples
use diag_lang::{Code, Diagnostic, Label, Severity, Span};

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)));
assert_eq!(diag.code(), None);
let diag = diag.with_code(Code::new("E", 1).unwrap());
assert_eq!(diag.code().map(|c| c.number()), Some(1));
Source

pub fn suggestions(&self) -> &[Suggestion]

Returns the fix-it suggestions, in the order they were added.

Since 1.1.0.

§Examples
use diag_lang::{Applicability, Diagnostic, Label, Severity, Span, Suggestion};

let diag = Diagnostic::new(Severity::Warning, "w", Label::unlabelled(Span::empty(0)))
    .with_suggestion(Suggestion::new("try this", Applicability::MaybeIncorrect));
assert_eq!(diag.suggestions()[0].message(), "try this");
Source

pub const fn severity(&self) -> Severity

Returns the level this diagnostic reports at.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)));
assert_eq!(diag.severity(), Severity::Error);
Source

pub fn message(&self) -> &str

Returns the headline message.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)));
assert_eq!(diag.message(), "boom");
Source

pub const fn primary(&self) -> &Label

Returns the primary label — the span the caret underlines.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(
    Severity::Error,
    "boom",
    Label::new(Span::new(2, 5), "right here"),
);
assert_eq!(diag.primary().message(), "right here");
Source

pub fn secondary(&self) -> &[Label]

Returns the secondary labels, in the order they were added.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)))
    .with_secondary(Label::new(Span::new(1, 2), "related"));
assert_eq!(diag.secondary()[0].message(), "related");
Source

pub fn notes(&self) -> impl ExactSizeIterator

Iterates over the note lines, in the order they were added.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)))
    .with_note("one")
    .with_note("two");
assert_eq!(diag.notes().collect::<Vec<_>>(), ["one", "two"]);
Source

pub fn help(&self) -> impl ExactSizeIterator

Iterates over the help lines, in the order they were added.

§Examples
use diag_lang::{Diagnostic, Label, Severity};
use span_lang::Span;

let diag = Diagnostic::new(Severity::Error, "boom", Label::unlabelled(Span::empty(0)))
    .with_help("try this");
assert_eq!(diag.help().collect::<Vec<_>>(), ["try this"]);
Source§

impl Diagnostic

Source

pub fn to_json(&self, map: &SourceMap) -> String

Returns the diagnostic as one line of JSON, its spans resolved against map.

The output is a single object with no whitespace and no trailing newline, so diagnostics can be streamed one per line (JSON Lines). Key order is fixed and the same diagnostic and map always produce the same bytes. Every string is escaped per RFC 8259, with U+2028 and U+2029 escaped as well so the output is also a valid JavaScript literal.

{"version":1,"severity":"error","code":"E0425","message":"…",
 "labels":[{"primary":true,"message":"…","span":{"start":24,"end":27},
            "location":{"file":"main.rs","start":{"offset":24,"line":2,"column":13},
                                         "end":{"offset":27,"line":2,"column":16}}}],
 "notes":["…"],"help":["…"],
 "suggestions":[{"message":"…","applicability":"machine_applicable",
                 "edits":[{"span":{…},"location":{…},"replacement":"…"}]}]}

code is null when no code is set. labels lists the primary label first, then the secondary labels in the order they were added. span is the label’s global byte span as given; location resolves it: the file name, and for each end the byte offset into that file, the 1-based line, and the 1-based column counted in Unicode scalar values (the numbers the text renderer prints after -->). An end past the file is clamped to it and an offset inside a character is moved to that character’s start. location is null for a span outside every loaded source. The full schema is in docs/API.md.

Since 1.1.0.

§Examples
use diag_lang::{Code, Diagnostic, Label, Severity, SourceMap, Span};

let mut map = SourceMap::new();
map.add("a.txt", "let x = y;").expect("fits");
let diag = Diagnostic::new(Severity::Error, "unknown `y`", Label::new(Span::new(8, 9), "here"))
    .with_code(Code::new("E", 2001).unwrap());

assert_eq!(
    diag.to_json(&map),
    concat!(
        r#"{"version":1,"severity":"error","code":"E2001","message":"unknown `y`","#,
        r#""labels":[{"primary":true,"message":"here","span":{"start":8,"end":9},"#,
        r#""location":{"file":"a.txt","start":{"offset":8,"line":1,"column":9},"#,
        r#""end":{"offset":9,"line":1,"column":10}}}],"#,
        r#""notes":[],"help":[],"suggestions":[]}"#,
    ),
);
Source

pub fn write_json( &self, out: &mut impl Write, map: &SourceMap, ) -> Result<(), Error>

Writes the diagnostic as JSON into any fmt::Write sink; see to_json for the format.

Since 1.1.0.

§Errors

Returns the first fmt::Error the sink reports; writing into a String never fails.

§Examples
use diag_lang::{Diagnostic, Label, Severity, SourceMap, Span};

let diag = Diagnostic::new(Severity::Warning, "w", Label::unlabelled(Span::empty(0)));
let mut line = String::new();
diag.write_json(&mut line, &SourceMap::new()).expect("string write");
line.push('\n'); // one diagnostic per line
assert!(line.contains(r#""location":null"#));

Trait Implementations§

Source§

impl Clone for Diagnostic

Source§

fn clone(&self) -> Diagnostic

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Diagnostic

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Lists every part by name, in the 1.0 field order with the 1.1 parts (code, suggestions) after them.

Source§

impl Eq for Diagnostic

Source§

impl PartialEq for Diagnostic

Source§

fn eq(&self, other: &Diagnostic) -> bool

Two diagnostics are equal when every part is: severity, code, message, primary label, and the secondary labels, notes, help lines, and suggestions in order. Compared through the accessors, so an empty suggestion list equals an absent one however it is stored.

1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.