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
impl Diagnostic
Sourcepub fn new(
severity: Severity,
message: impl Into<Box<str>>,
primary: Label,
) -> Diagnostic
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());Sourcepub fn with_secondary(self, label: Label) -> Diagnostic
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);Sourcepub fn with_note(self, note: impl Into<Box<str>>) -> Diagnostic
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"));Sourcepub fn with_help(self, help: impl Into<Box<str>>) -> Diagnostic
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"));Sourcepub fn with_code(self, code: Code) -> Diagnostic
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));Sourcepub fn with_suggestion(self, suggestion: Suggestion) -> Diagnostic
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);Sourcepub const fn code(&self) -> Option<Code>
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));Sourcepub fn suggestions(&self) -> &[Suggestion]
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");Sourcepub const fn severity(&self) -> Severity
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);Sourcepub fn message(&self) -> &str
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");Sourcepub const fn primary(&self) -> &Label
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");Sourcepub fn secondary(&self) -> &[Label]
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");Sourcepub fn notes(&self) -> impl ExactSizeIterator
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"]);Sourcepub fn help(&self) -> impl ExactSizeIterator
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
impl Diagnostic
Sourcepub fn to_json(&self, map: &SourceMap) -> String
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":[]}"#,
),
);Sourcepub fn write_json(
&self,
out: &mut impl Write,
map: &SourceMap,
) -> Result<(), Error>
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
impl Clone for Diagnostic
Source§fn clone(&self) -> Diagnostic
fn clone(&self) -> Diagnostic
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for Diagnostic
impl Debug for Diagnostic
impl Eq for Diagnostic
Source§impl PartialEq for Diagnostic
impl PartialEq for Diagnostic
Source§fn eq(&self, other: &Diagnostic) -> bool
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.