-# Emitting Errors and other Diagnostics
+# Errors and Lints
+
+<!-- toc -->
A lot of effort has been put into making `rustc` have great error messages.
This chapter is about how to emit compile errors and lints from the compiler.
+## Diagnostic structure
+
+The main parts of a diagnostic error are the following:
+
+```
+error[E0000]: main error message
+ --> file.rs:LL:CC
+ |
+LL | <code>
+ | -^^^^- secondary label
+ | |
+ | primary label
+ |
+ = note: note without a `Span`, created with `.note`
+note: sub-diagnostic message for `.span_note`
+ --> file.rs:LL:CC
+ |
+LL | more code
+ | ^^^^
+```
+
+- Description (`error`, `warning`, etc.).
+- Code (for example, for "mismatched types", it is `E0308`). It helps
+ users get more information about the current error through an extended
+ description of the problem in the error code index.
+- Message. It is the main description of the problem. It should be general and
+ able to stand on its own, so that it can make sense even in isolation.
+- Diagnostic window. This contains several things:
+ - The path, line number and column of the beginning of the primary span.
+ - The users' affected code and its surroundings.
+ - Primary and secondary spans underlying the users' code. These spans can
+ optionally contain one or more labels.
+ - Primary spans should have enough text to describe the problem in such a
+ way that if it where the only thing being displayed (for example, in an
+ IDE) it would still make sense. Because it is "spatially aware" (it
+ points at the code), it can generally be more succinct than the error
+ message.
+ - If cluttered output can be foreseen in cases when multiple span labels
+ overlap, it is a good idea to tweak the output appropriately. For
+ example, the `if/else arms have incompatible types` error uses different
+ spans depending on whether the arms are all in the same line, if one of
+ the arms is empty and if none of those cases applies.
+- Sub-diagnostics. Any error can have multiple sub-diagnostics that look
+ similar to the main part of the error. These are used for cases where the
+ order of the explanation might not correspond with the order of the code. If
+ the order of the explanation can be "order free", leveraging secondary labels
+ in the main diagnostic is preferred, as it is typically less verbose.
+
+The text should be matter of fact and avoid capitalization and periods, unless
+multiple sentences are _needed_:
+
+```txt
+error: the fobrulator needs to be krontrificated
+```
+
+When code or an identifier must appear in a message or label, it should be
+surrounded with single acute accents \`.
+
+### Error explanations
+
+Some errors include long form descriptions. They may be viewed with the
+`--explain` flag, or via the [error index]. Each explanation comes with an
+example of how to trigger it and advice on how to fix it.
+
+Please read [RFC 1567] for details on how to format and write long error
+codes.
+
+The descriptions are written in Markdown, and all of them are linked in the
+[`rustc_error_codes`] crate.
+
+As a general rule, give an error a code (with an associated explanation) if the
+explanation would give more information than the error itself. A lot of the time
+it's better to put all the information in the emitted error itself. However,
+sometimes that would make the error verbose or there are too many possible
+triggers to include useful information for all cases in the error, in which case
+it's a good idea to add an explanation.[^estebank]
+As always, if you are not sure, just ask your reviewer!
+
+[^estebank]: This rule of thumb was suggested by **@estebank** [here][estebank-comment].
+
+[`rustc_error_codes`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_error_codes/error_codes/index.html
+[error index]: https://doc.rust-lang.org/error-index.html
+[RFC 1567]: https://github.com/rust-lang/rfcs/blob/master/text/1567-long-error-codes-explanation-normalization.md
+[estebank-comment]: https://github.com/rust-lang/rustc-dev-guide/pull/967#issuecomment-733218283
+
+### Lints versus fixed diagnostics
+
+Some messages are emitted via [lints](#lints), where the user can control the
+level. Most diagnostics are hard-coded such that the user cannot control the
+level.
+
+Usually it is obvious whether a diagnostic should be "fixed" or a lint, but
+there are some grey areas.
+
+Here are a few examples:
+
+- Borrow checker errors: these are fixed errors. The user cannot adjust the
+ level of these diagnostics to silence the borrow checker.
+- Dead code: this is a lint. While the user probably doesn't want dead code in
+ their crate, making this a hard error would make refactoring and development
+ very painful.
+- [safe_packed_borrows future compatibility warning][safe_packed_borrows]:
+ this is a silencable lint related to safety. It was judged that the making
+ this a hard (fixed) error would cause too much breakage, so instead a
+ warning is emitted that eventually will be turned into a hard error.
+
+Hard-coded warnings (those using the `span_warn` methods) should be avoided
+for normal code, preferring to use lints instead. Some cases, such as warnings
+with CLI flags, will require the use of hard-coded warnings.
+
+See the `deny` [lint level](#diagnostic-levels) below for guidelines when to
+use an error-level lint instead of a fixed error.
+
+[safe_packed_borrows]: https://github.com/rust-lang/rust/issues/46043
+
+## Diagnostic output style guide
+
+- Write in plain simple English. If your message, when shown on a – possibly
+ small – screen (which hasn't been cleaned for a while), cannot be understood
+ by a normal programmer, who just came out of bed after a night partying,
+ it's too complex.
+- `Error`, `Warning`, `Note`, and `Help` messages start with a lowercase
+ letter and do not end with punctuation.
+- Error messages should be succinct. Users will see these error messages many
+ times, and more verbose descriptions can be viewed with the `--explain`
+ flag. That said, don't make it so terse that it's hard to understand.
+- The word "illegal" is illegal. Prefer "invalid" or a more specific word
+ instead.
+- Errors should document the span of code where they occur – the
+ [`rustc_errors::diagnostic_builder::DiagnosticBuilder`][diagbuild] `span_*`
+ methods allow to easily do this. Also `note` other spans that have
+ contributed to the error if the span isn't too large.
+- When emitting a message with span, try to reduce the span to the smallest
+ amount possible that still signifies the issue
+- Try not to emit multiple error messages for the same error. This may require
+ detecting duplicates.
+- When the compiler has too little information for a specific error message,
+ consult with the compiler team to add new attributes for library code that
+ allow adding more information. For example see
+ [`#[rustc_on_unimplemented]`](#rustc_on_unimplemented). Use these
+ annotations when available!
+- Keep in mind that Rust's learning curve is rather steep, and that the
+ compiler messages are an important learning tool.
+- When talking about the compiler, call it `the compiler`, not `Rust` or
+ `rustc`.
+
+### Lint naming
+
+From [RFC 0344], lint names should be consistent, with the following
+guidelines:
+
+The basic rule is: the lint name should make sense when read as "allow
+*lint-name*" or "allow *lint-name* items". For example, "allow
+`deprecated` items" and "allow `dead_code`" makes sense, while "allow
+`unsafe_block`" is ungrammatical (should be plural).
+
+- Lint names should state the bad thing being checked for, e.g. `deprecated`,
+ so that `#[allow(deprecated)]` (items) reads correctly. Thus `ctypes` is not
+ an appropriate name; `improper_ctypes` is.
+
+- Lints that apply to arbitrary items (like the stability lints) should just
+ mention what they check for: use `deprecated` rather than
+ `deprecated_items`. This keeps lint names short. (Again, think "allow
+ *lint-name* items".)
+
+- If a lint applies to a specific grammatical class, mention that class and
+ use the plural form: use `unused_variables` rather than `unused_variable`.
+ This makes `#[allow(unused_variables)]` read correctly.
+
+- Lints that catch unnecessary, unused, or useless aspects of code should use
+ the term `unused`, e.g. `unused_imports`, `unused_typecasts`.
+
+- Use snake case in the same way you would for function names.
+
+[RFC 0344]: https://github.com/rust-lang/rfcs/blob/master/text/0344-conventions-galore.md#lints
+
+### Diagnostic levels
+
+Guidelines for different diagnostic levels:
+
+- `error`: emitted when the compiler detects a problem that makes it unable to
+ compile the program, either because the program is invalid or the programmer
+ has decided to make a specific `warning` into an error.
+
+- `warning`: emitted when the compiler detects something odd about a program.
+ Care should be taken when adding warnings to avoid warning fatigue, and
+ avoid false-positives where there really isn't a problem with the code. Some
+ examples of when it is appropriate to issue a warning:
+
+ - A situation where the user *should* take action, such as swap out a
+ deprecated item, or use a `Result`, but otherwise doesn't prevent
+ compilation.
+ - Unnecessary syntax that can be removed without affecting the semantics of
+ the code. For example, unused code, or unnecessary `unsafe`.
+ - Code that is very likely to be incorrect, dangerous, or confusing, but the
+ language technically allows, and is not ready or confident enough to make
+ an error. For example `unused_comparisons` (out of bounds comparisons) or
+ `bindings_with_variant_name` (the user likely did not intend to create a
+ binding in a pattern).
+ - [Future-incompatible lints](#future-incompatible), where something was
+ accidentally or erroneously accepted in the past, but rejecting would
+ cause excessive breakage in the ecosystem.
+ - Stylistic choices. For example, camel or snake case, or the `dyn` trait
+ warning in the 2018 edition. These have a high bar to be added, and should
+ only be used in exceptional circumstances. Other stylistic choices should
+ either be allow-by-default lints, or part of other tools like Clippy or
+ rustfmt.
+
+- `help`: emitted following an `error` or `warning` to give additional
+ information to the user about how to solve their problem. These messages
+ often include a suggestion string and [`rustc_errors::Applicability`]
+ confidence level to guide automated source fixes by tools. See the
+ [Suggestions](#suggestions) section for more details.
+
+ The error or warning portion should *not* suggest how to fix the problem,
+ only the "help" sub-diagnostic should.
+
+- `note`: emitted to identify additional circumstances and parts of the code
+ that caused the warning or error. For example, the borrow checker will note
+ any previous conflicting borrows.
+
+Not to be confused with *lint levels*, whose guidelines are:
+
+- `forbid`: Lints should never default to `forbid`.
+- `deny`: Equivalent to `error` diagnostic level. Some examples:
+
+ - A future-incompatible or edition-based lint that has graduated from the
+ warning level.
+ - Something that has an extremely high confidence that is incorrect, but
+ still want an escape hatch to allow it to pass.
+
+- `warn`: Equivalent to the `warning` diagnostic level. See `warning` above
+ for guidelines.
+- `allow`: Examples of the kinds of lints that should default to `allow`:
+
+ - The lint has a too high false positive rate.
+ - The lint is too opinionated.
+ - The lint is experimental.
+ - The lint is used for enforcing something that is not normally enforced.
+ For example, the `unsafe_code` lint can be used to prevent usage of unsafe
+ code.
+
+More information about lint levels can be found in the [rustc
+book][rustc-lint-levels] and the [reference][reference-diagnostics].
+
+[`rustc_errors::Applicability`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
+[reference-diagnostics]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#lint-check-attributes
+[rustc-lint-levels]: https://doc.rust-lang.org/nightly/rustc/lints/levels.html
+
+## Helpful tips and options
+
+### Finding the source of errors
+
+There are two main ways to find where a given error is emitted:
+
+- `grep` for either a sub-part of the error message/label or error code. This
+ usually works well and is straightforward, but there are some cases where
+ the error emitting code is removed from the code where the error is
+ constructed behind a relatively deep call-stack. Even then, it is a good way
+ to get your bearings.
+- Invoking `rustc` with the nightly-only flag `-Z treat-err-as-bug=1`, which
+ will treat the first error being emitted as an Internal Compiler Error, which
+ allows you to use the environment variable `RUST_BACKTRACE=full` to get a
+ stack trace at the point the error has been emitted. Change the `1` to
+ something else if you whish to trigger on a later error. Some limitations
+ with this approach is that some calls get elided from the stack trace because
+ they get inlined in the compiled `rustc`, and the same problem we faced with
+ the prior approach, where the _construction_ of the error is far away from
+ where it is _emitted_. In some cases we buffer multiple errors in order to
+ emit them in order.
+
+The regular development practices apply: judicious use of `debug!()` statements
+and use of a debugger to trigger break points in order to figure out in what
+order things are happening.
+
## `Span`
[`Span`][span] is the primary data structure in `rustc` used to represent a
err.emit();
```
+Alternatively, for less-complex diagnostics, the `SessionDiagnostic` derive
+macro can be used -- see [Creating Errors With SessionDiagnostic][sessiondiagnostic].
+
+[sessiondiagnostic]: ./diagnostics/sessiondiagnostic.md
+
+
## Suggestions
In addition to telling the user exactly _why_ their code is wrong, it's
[rls]: https://github.com/rust-lang/rls
[rustfix]: https://github.com/rust-lang/rustfix
-Not all suggestions should be applied mechanically. Use the
+Not all suggestions should be applied mechanically, they have a degree of
+confidence in the suggested code, from high
+(`Applicability::MachineApplicable`) to low (`Applicability::MaybeIncorrect`).
+Be conservative when choosing the level. Use the
[`span_suggestion`][span_suggestion] method of `DiagnosticBuilder` to
make a suggestion. The last argument provides a hint to tools whether
the suggestion is mechanically applicable or not.
+Suggestions point to one or more spans with corresponding code that will
+replace their current content.
+
+The message that accompanies them should be understandable in the following
+contexts:
+
+- shown as an independent sub-diagnostic (this is the default output)
+- shown as a label pointing at the affected span (this is done automatically if
+some heuristics for verbosity are met)
+- shown as a `help` sub-diagnostic with no content (used for cases where the
+suggestion is obvious from the text, but we still want to let tools to apply
+them))
+- not shown (used for _very_ obvious cases, but we still want to allow tools to
+apply them)
+
[span_suggestion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagnosticBuilder.html#method.span_suggestion
For example, to make our `qux` suggestion machine-applicable, we would do:
[appl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
+### Suggestion Style Guide
+
+- Suggestions should not be a question. In particular, language like "did you
+ mean" should be avoided. Sometimes, it's unclear why a particular suggestion
+ is being made. In these cases, it's better to be upfront about what the
+ suggestion is.
+
+ Compare "did you mean: `Foo`" vs. "there is a struct with a similar name: `Foo`".
+
+- The message should not contain any phrases like "the following", "as shown",
+ etc. Use the span to convey what is being talked about.
+- The message may contain further instruction such as "to do xyz, use" or "to do
+ xyz, use abc".
+- The message may contain a name of a function, variable, or type, but avoid
+ whole expressions.
+
## Lints
-The compiler linting infrastructure is defined in the [`rustc::lint`][rlint]
+The compiler linting infrastructure is defined in the [`rustc_middle::lint`][rlint]
module.
[rlint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/index.html
### Declaring a lint
The built-in compiler lints are defined in the [`rustc_lint`][builtin]
-crate.
+crate. Lints that need to be implemented in other crates are defined in
+[`rustc_lint_defs`]. You should prefer to place lints in `rustc_lint` if
+possible. One benefit is that it is close to the dependency root, so it can be
+much faster to work on.
[builtin]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/index.html
+[`rustc_lint_defs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/index.html
Every lint is implemented via a `struct` that implements the `LintPass` `trait`
-(you also implement one of the more specific lint pass traits, either
-`EarlyLintPass` or `LateLintPass`). The trait implementation allows you to
-check certain syntactic constructs as the linter walks the source code. You can
-then choose to emit lints in a very similar way to compile errors.
+(you can also implement one of the more specific lint pass traits, either
+`EarlyLintPass` or `LateLintPass` depending on when is best for your lint to run).
+The trait implementation allows you to check certain syntactic constructs
+as the linter walks the AST. You can then choose to emit lints in a
+very similar way to compile errors.
You also declare the metadata of a particular lint via the `declare_lint!`
macro. This includes the name, the default level, a short description, and some
"suggest using `loop { }` instead of `while true { }`"
}
-// Define a struct and `impl LintPass` for it.
-#[derive(Copy, Clone)]
-pub struct WhileTrue;
-
-// This declares a lint pass, providing a list of associated lints. The
+// This declares a struct and a lint pass, providing a list of associated lints. The
// compiler currently doesn't use the associated lints directly (e.g., to not
// run the pass or otherwise check that the pass emits the appropriate set of
// lints). However, it's good to be accurate here as it's possible that we're
// going to register the lints via the get_lints method on our lint pass (that
// this macro generates).
-impl_lint_pass!(
- WhileTrue => [WHILE_TRUE],
-);
+declare_lint_pass!(WhileTrue => [WHILE_TRUE]);
-// LateLintPass has lots of methods. We only override the definition of
+// Helper function for `WhileTrue` lint.
+// Traverse through any amount of parenthesis and return the first non-parens expression.
+fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
+ while let ast::ExprKind::Paren(sub) = &expr.kind {
+ expr = sub;
+ }
+ expr
+}
+
+// `EarlyLintPass` has lots of methods. We only override the definition of
// `check_expr` for this lint because that's all we need, but you could
// override other methods for your own lint. See the rustc docs for a full
// list of methods.
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue {
- fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
- if let hir::ExprWhile(ref cond, ..) = e.node {
- if let hir::ExprLit(ref lit) = cond.node {
- if let ast::LitKind::Bool(true) = lit.node {
- if lit.span.ctxt() == SyntaxContext::empty() {
+impl EarlyLintPass for WhileTrue {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
+ if let ast::ExprKind::While(cond, ..) = &e.kind {
+ if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind {
+ if let ast::LitKind::Bool(true) = lit.kind {
+ if !lit.span.from_expansion() {
let msg = "denote infinite loops with `loop { ... }`";
- let condition_span = cx.tcx.sess.source_map().def_span(e.span);
- let mut err = cx.struct_span_lint(WHILE_TRUE, condition_span, msg);
- err.span_suggestion_short(condition_span, "use `loop`", "loop".to_owned());
- err.emit();
+ let condition_span = cx.sess.source_map().guess_head_span(e.span);
+ cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
+ lint.build(msg)
+ .span_suggestion_short(
+ condition_span,
+ "use `loop`",
+ "loop".to_owned(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ })
}
}
}
}
```
-### Edition-gated Lints
+### Edition-gated lints
Sometimes we want to change the behavior of a lint in a new edition. To do this,
we just add the transition to our invocation of `declare_lint!`:
This makes the `ANONYMOUS_PARAMETERS` lint allow-by-default in the 2015 edition
but warn-by-default in the 2018 edition.
+### Future-incompatible lints
+
+The use of the term `future-incompatible` within the compiler has a slightly
+broader meaning than what rustc exposes to users of the compiler.
+
+Inside rustc, future-incompatible lints are for signalling to the user that code they have
+written may not compile in the future. In general, future-incompatible code
+exists for two reasons:
+* the user has written unsound code that the compiler mistakenly accepted. While
+it is within Rust's backwards compatibility guarantees to fix the soundness hole
+(breaking the user's code), the lint is there to warn the user that this will happen
+in some upcoming version of rustc *regardless of which edition the code uses*. This is the
+meaning that rustc exclusively exposes to users as "future incompatible".
+* the user has written code that will either no longer compiler *or* will change
+meaning in an upcoming *edition*. These are often called "edition lints" and can be
+typically seen in the various "edition compatibility" lint groups (e.g., `rust_2021_compatibility`)
+that are used to lint against code that will break if the user updates the crate's edition.
+
A future-incompatible lint should be declared with the `@future_incompatible`
additional "field":
"detects anonymous parameters",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>",
- edition: Some(Edition::Edition2018),
+ reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018),
};
}
```
+Notice the `reason` field which describes why the future incompatible change is happening.
+This will change the diagnostic message the user receives as well as determine which
+lint groups the lint is added to. In the example above, the lint is an "edition lint"
+(since it's "reason" is `EditionError`) signifying to the user that the use of anonymous
+parameters will no longer compile in Rust 2018 and beyond.
+
+Inside [LintStore::register_lints][fi-lint-groupings], lints with `future_incompatible`
+fields get placed into either edition-based lint groups (if their `reason` is tied to
+an edition) or into the `future_incompatibility` lint group.
+
+[fi-lint-groupings]: https://github.com/rust-lang/rust/blob/51fd129ac12d5bfeca7d216c47b0e337bf13e0c2/compiler/rustc_lint/src/context.rs#L212-L237
+
If you need a combination of options that's not supported by the
-`declare_lint!` macro, you can always define your own static with a type of
-`&Lint` but this is currently linted against in the compiler tree.
+`declare_lint!` macro, you can always change the `declare_lint!` macro
+to support this.
-#### Guidelines for creating a future incompatibility lint
+### Renaming or removing a lint
-- Create a lint defaulting to warn as normal, with ideally the same error
- message you would normally give.
-- Add a suitable reference, typically an RFC or tracking issue. Go ahead
- and include the full URL, sort items in ascending order of issue numbers.
-- Later, change lint to error.
-- Eventually, remove lint.
+If it is determined that a lint is either improperly named or no longer needed,
+the lint must be registered for renaming or removal, which will trigger a warning if a user tries
+to use the old lint name. To declare a rename/remove, add a line with
+[`store.register_renamed`] or [`store.register_removed`] to the code of the
+[`rustc_lint::register_builtins`] function.
+
+```rust,ignore
+store.register_renamed("single_use_lifetime", "single_use_lifetimes");
+```
+
+[`store.register_renamed`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_renamed
+[`store.register_removed`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_removed
+[`rustc_lint::register_builtins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html
### Lint Groups
user can turn on these lints with a `!#[warn(nonstandard_style)]` attribute in
the source code, or by passing `-W nonstandard-style` on the command line.
+Some lint groups are created automatically in `LintStore::register_lints`. For instance,
+any lint declared with `FutureIncompatibleInfo` where the reason is
+`FutureIncompatibilityReason::FutureReleaseError` (the default when
+`@future_incompatible` is used in `declare_lint!`), will be added to
+the `future_incompatible` lint group. Editions also have their own lint groups
+(e.g., `rust_2021_compatibility`) automatically generated for any lints signaling
+future-incompatible code that will break in the specified edition.
+
### Linting early in the compiler
On occasion, you may need to define a lint that runs before the linting system
#### Linting even earlier in the compiler
-The parser (`librustc_ast`) is interesting in that it cannot have dependencies on
-any of the other `librustc*` crates. In particular, it cannot depend on
-`librustc_middle::lint` or `librustc_lint`, where all of the compiler linting
+The parser (`rustc_ast`) is interesting in that it cannot have dependencies on
+any of the other `rustc*` crates. In particular, it cannot depend on
+`rustc_middle::lint` or `rustc_lint`, where all of the compiler linting
infrastructure is defined. That's troublesome!
-To solve this, `librustc_ast` defines its own buffered lint type, which
+To solve this, `rustc_ast` defines its own buffered lint type, which
`ParseSess::buffer_lint` uses. After macro expansion, these buffered lints are
then dumped into the `Session::buffered_lints` used by the rest of the compiler.
The compiler accepts an `--error-format json` flag to output
diagnostics as JSON objects (for the benefit of tools such as `cargo
-fix` or the RLS). It looks like this—
+fix` or the RLS). It looks like this:
```console
$ rustc json_error_demo.rs --error-format json
without having to compile everything twice.
The "human" readable and the json format emitter can be found under
-librustc_errors, both were moved from the `librustc_ast` crate to the
-[librustc_errors crate](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html).
+`rustc_errors`, both were moved from the `rustc_ast` crate to the
+[rustc_errors crate](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html).
The JSON emitter defines [its own `Diagnostic`
struct](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/json/struct.Diagnostic.html)