# Emitting Diagnostics 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. ## `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. [span]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/source_map/struct.Span.html 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`. [sourcemap]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/source_map/struct.SourceMap.html [sptosnip]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/source_map/struct.SourceMap.html#method.span_to_snippet ## Error messages The [`rustc_errors`][errors] crate defines most of the utilities used for reporting errors. [errors]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html [`Session`][session] and [`ParseSess`][parsesses] have methods (or fields with methods) that allow reporting 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. [parsesses]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/struct.ParseSess.html [session]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html In general, there are two class 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 [`DiagnosticBuilder`][diagbuild]. `DiagnosticBuilder` 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 `DiagnosticBuilder` will result in an ICE.) See the [docs][diagbuild] for more info on what you can do. [spanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html#method.span_err [strspanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html#method.struct_span_err [diagbuild]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilder.html [emit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilder.html#method.emit [cancel]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diagnostic.html#method.cancel ```rust,ignore // Get a DiagnosticBuilder. This does _not_ emit an error yet. let mut err = sess.struct_span_err(sp, "oh no! this is an 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, "try using a qux here", format!("qux {}", snip)); } 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, "you could use a qux here instead"); } // emit the error err.emit(); ``` ## Suggestions In addition to telling the user exactly _why_ their code is wrong, it's oftentimes furthermore possible to tell them how to fix it. To this end, `DiagnosticBuilder` offers a structured suggestions API, which formats code suggestions pleasingly in the terminal, or (when the `--error-format json` flag is passed) as JSON for consumption by tools, most notably the [Rust Language Server][rls] and [`rustfix`][rustfix]. [rls]: https://github.com/rust-lang/rls [rustfix]: https://github.com/rust-lang-nursery/rustfix Not all suggestions should be applied mechanically. 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. [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: ```rust,ignore let mut err = sess.struct_span_err(sp, "oh no! this is an error!"); if let Ok(snippet) = sess.source_map().span_to_snippet(sp) { err.span_suggestion( suggestion_sp, "try using a qux here", format!("qux {}", snip), Applicability::MachineApplicable, ); } else { err.span_help(suggestion_sp, "you could use a qux here instead"); } err.emit(); ``` This might emit an error like ```console $ rustc mycode.rs error[E0999]: oh no! this is an error! --> mycode.rs:3:5 | 3 | sad() | ^ help: try using a qux here: `qux sad()` error: aborting due to previous error For more information about this error, try `rustc --explain E0999`. ``` In some cases, like when the suggestion spans multiple lines or when there are multiple suggestions, the suggestions are displayed on their own: ```console error[E0999]: oh no! this is an error! --> mycode.rs:3:5 | 3 | sad() | ^ help: try using a qux here: | 3 | qux sad() | ^^^ error: aborting due to previous error For more information about this error, try `rustc --explain E0999`. ``` The possible values of [`Applicability`][appl] are: - `MachineApplicable`: Can be applied mechanically. - `HasPlaceholders`: Cannot be applied mechanically because it has placeholder text in the suggestions. For example, "Try adding a type: \`let x: \\`". - `MaybeIncorrect`: Cannot be applied mechanically because the suggestion may or may not be a good one. - `Unspecified`: Cannot be applied mechanically because we don't know which of the above cases it falls into. [appl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html ## Lints The compiler linting infrastructure is defined in the [`rustc::lint`][rlint] module. [rlint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/index.html ### Declaring a lint The built-in compiler lints are defined in the [`rustc_lint`][builtin] crate. [builtin]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/index.html Each lint is defined as a `struct` that implements the `LintPass` `trait`. The trait implementation allows you to check certain syntactic constructs the linter walks the source code. You can then choose to emit lints in a very similar way to compile errors. Finally, you register the lint to actually get it to be run by the compiler by using the `declare_lint!` macro. For example, the following lint checks for uses of `while true { ... }` and suggests using `loop { ... }` instead. ```rust,ignore // Declare a lint called `WHILE_TRUE` declare_lint! { WHILE_TRUE, // warn-by-default Warn, // This string is the lint description "suggest using `loop { }` instead of `while true { }`" } // Define a struct and `impl LintPass` for it. #[derive(Copy, Clone)] pub struct WhileTrue; impl LintPass for WhileTrue { fn get_lints(&self) -> LintArray { lint_array!(WHILE_TRUE) } } // LateLintPass 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() { 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(); } } } } } } ``` ### 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!`: ```rust,ignore declare_lint! { pub ANONYMOUS_PARAMETERS, Allow, "detects anonymous parameters", Edition::Edition2018 => Warn, } ``` This makes the `ANONYMOUS_PARAMETERS` lint allow-by-default in the 2015 edition but warn-by-default in the 2018 edition. Lints that represent an incompatibility (i.e. error) in the upcoming edition should also be registered as `FutureIncompatibilityLint`s in [`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. ### Lint Groups Lints can be turned on in groups. These groups are declared in the [`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. The `add_lint_group!` macro is used to declare a new group. [rbuiltins]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html For example, ```rust,ignore add_lint_group!(sess, "nonstandard_style", NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPER_CASE_GLOBALS); ``` This defines the `nonstandard_style` group which turns on the listed lints. A 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. ### Linting early in the compiler On occasion, you may need to define a lint that runs before the linting system has been initialized (e.g. during parsing or macro expansion). This is problematic because we need to have computed lint levels to know whether we should emit a warning or an error or nothing at all. To solve this problem, we buffer the lints until the linting system is processed. [`Session`][sessbl] and [`ParseSess`][parsebl] both have `buffer_lint` methods that allow you to buffer a lint for later. The linting system automatically takes care of handling buffered lints later. [sessbl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html#method.buffer_lint [parsebl]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/struct.ParseSess.html#method.buffer_lint Thus, to define a lint that runs early in the compilation, one defines a lint like normal but invokes the lint with `buffer_lint`. #### Linting even earlier in the compiler The parser (`libsyntax`) is interesting in that it cannot have dependencies on any of the other `librustc*` crates. In particular, it cannot depend on `librustc::lint` or `librustc_lint`, where all of the compiler linting infrastructure is defined. That's troublesome! To solve this, `libsyntax` 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. Usage for buffered lints in `libsyntax` is pretty much the same as the rest of the compiler with one exception because we cannot import the `LintId`s for lints we want to emit. Instead, the [`BufferedEarlyLintId`] type is used. If you are defining a new lint, you will want to add an entry to this enum. Then, add an appropriate mapping to the body of [`Lint::from_parser_lint_id`][fplid]. [`BufferedEarlyLintId`]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/early_buffered_lints/enum.BufferedEarlyLintId.html [fplid]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/struct.Lint.html#method.from_parser_lint_id