the code emitting the error 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`
will treat the first error being emitted as an Internal Compiler Error, which
allows you to get a
stack trace at the point the error has been emitted. Change the `1` to
something else if you wish to trigger on a later error.
There are limitations with this approach:
- Some calls get elided from the stack trace because they get inlined in the compiled `rustc`.
- The _construction_ of the error is far away from where it is _emitted_,
a problem similar to the one we faced with the `grep` approach.
In some cases, we buffer multiple errors in order to emit them in order.
- Invoking `rustc` with `-Z track-diagnostics` will print error creation
locations alongside the error.
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
location in the code being compiled. `Span`s are attached to most constructs in
HIR and MIR, allowing for more informative error reporting.
A `Span` can be looked up in a [`SourceMap`][sourcemap] to get a "snippet"
useful for displaying errors with [`span_to_snippet`][sptosnip] and other
similar methods on the `SourceMap`.
## Error messages
The [`rustc_errors`][errors] crate defines most of the utilities used for
reporting errors.
Diagnostics can be implemented as types which implement the `Diagnostic`
trait. This is preferred for new diagnostics as it enforces a separation
between diagnostic emitting logic and the main code paths. For less-complex
diagnostics, the `Diagnostic` trait can be derived -- see [Diagnostic
structs][diagnostic-structs]. Within the trait implementation, the APIs
described below can be used as normal.
[`DiagCtxt`][DiagCtxt] has methods that create and emit errors. These methods
usually have names like `span_err` or `struct_span_err` or `span_warn`, etc...
There are lots of them; they emit different types of "errors", such as
warnings, errors, fatal errors, suggestions, etc.
In general, there are two classes of such methods: ones that emit an error
directly and ones that allow finer control over what to emit. For example,
[`span_err`][spanerr] emits the given error message at the given `Span`, but
[`struct_span_err`][strspanerr] instead returns a
[`Diag`][diag].
Most of these methods will accept strings, but it is recommended that typed
identifiers for translatable diagnostics be used for new diagnostics (see
[Translation][translation]).
`Diag` allows you to add related notes and suggestions to an error
before emitting it by calling the [`emit`][emit] method. (Failing to either
emit or [cancel][cancel] a `Diag` will result in an ICE.) See the
[docs][diag] for more info on what you can do.
```rust,ignore
// Get a `Diag`. This does _not_ emit an error yet.
let mut err = sess.dcx.struct_span_err(sp, fluent::example::example_error);
// In some cases, you might need to check if `sp` is generated by a macro to
// avoid printing weird errors about macro-generated code.
if let Ok(snippet) = sess.source_map().span_to_snippet(sp) {
// Use the snippet to generate a suggested fix
err.span_suggestion(suggestion_sp, fluent::example::try_qux_suggestion, format!("qux {}", snippet));
} else {
// If we weren't able to generate a snippet, then emit a "help" message
// instead of a concrete "suggestion". In practice this is unlikely to be
// reached.
err.span_help(suggestion_sp, fluent::example::qux_suggestion);
}
// emit the error
err.emit();
```
```fluent
example-example-error = oh no! this is an error!
.try-qux-suggestion = try using a qux here
.qux-suggestion = you could use a qux here instead