| # Errors and lints | |
| 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 | |
| | ^^^^ | |
| ``` | |
| - Level (`error`, `warning`, etc.). It indicates the severity of the message. | |
| (See [diagnostic levels](#diagnostic-levels)) | |
| - 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. | |
| Not all diagnostic have a code. | |
| For example, diagnostics created by lints don't have one. | |
| - 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 were 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 backticks: | |
| ```txt | |
| error: the identifier `foo.bar` is invalid | |
| ``` | |
| ### Error codes and explanations | |
| Most errors have an associated error code. | |
| Error codes are linked to long-form | |
| explanations which contains an example of how to trigger the error and in-depth | |
| details about the error. | |
| They may be viewed with the `--explain` flag, or via the [error index]. | |
| 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! | |
| If you decide to add a new error with an associated error code, please read | |
| [this section][error-codes] for a guide and important details about the process. | |
| [^estebank]: This rule of thumb was suggested by **@estebank** [here][estebank-comment]. | |
| [error index]: https://doc.rust-lang.org/error-index.html | |
| [estebank-comment]: https://github.com/rust-lang/rustc-dev-guide/pull/967#issuecomment-733218283 | |
| [error-codes]: ./diagnostics/error-codes.md | |
| ### 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. | |
| - [future-incompatible lints]: these are silenceable lints. | |
| It was decided that making them fixed errors would cause too much breakage, | |
| so warnings are instead emitted, | |
| and will eventually be turned into fixed (hard) errors. | |
| Hard-coded warnings (those using methods like `span_warn`) 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. | |
| [future-incompatible lints]: #future-incompatible-lints | |
| ## 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 (use | |
| [`rustc_errors::DiagCtxt`][DiagCtxt]'s | |
| `span_*` methods or a diagnostic struct's `#[primary_span]` 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`. | |
| - Use the [Oxford comma](https://en.wikipedia.org/wiki/Serial_comma) when writing lists of items. | |
| - When mentioning attributes, use this form whenever possible: "the `inline` attribute". | |
| Don't include `#[`/`#![`/`]` delimiters or attribute arguments in the message or the span unless | |
| they are relevant. | |
| ### 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. | |
| Examples are `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 give more context and 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. | |
| `help` vs `note`: `help` should be used to show changes the user can | |
| possibly make to fix the problem. | |
| `note` should be used for everything else, | |
| such as other context, information and facts, online resources to read, etc. | |
| 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 three 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 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. | |
| [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/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/rustc_span/source_map/struct.SourceMap.html | |
| [sptosnip]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/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 | |
| 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. | |
| [diagnostic-structs]: ./diagnostics/diagnostic-structs.md | |
| [`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. | |
| [DiagCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html | |
| 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]: ./diagnostics/translation.md | |
| `Diag` allows you to add related notes and suggestions to an error | |
| before emitting it by calling the [`emit`] method. | |
| (Failing to either emit or [cancel] a `Diag` will result in an ICE.) See the | |
| [docs][diag] for more info on what you can do. | |
| [spanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.span_err | |
| [strspanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.struct_span_err | |
| [`emit`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.emit | |
| [cancel]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.cancel | |
| ```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 | |
| ``` | |
| ## 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, | |
| [`Diag`][diag] 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 like [`rustfix`][rustfix]. | |
| [rustfix]: https://github.com/rust-lang/rustfix | |
| 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 `Diag` 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.Diag.html#method.span_suggestion | |
| For example, to make our `qux` suggestion machine-applicable, we would do: | |
| ```rust,ignore | |
| let mut err = sess.dcx.struct_span_err(sp, fluent::example::message); | |
| if let Ok(snippet) = sess.source_map().span_to_snippet(sp) { | |
| err.span_suggestion( | |
| suggestion_sp, | |
| fluent::example::try_qux_suggestion, | |
| format!("qux {}", snippet), | |
| Applicability::MachineApplicable, | |
| ); | |
| } else { | |
| err.span_help(suggestion_sp, fluent::example::qux_suggestion); | |
| } | |
| 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: <type>` ```. | |
| - `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 | |
| ### 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_middle::lint`][rlint] module. | |
| [rlint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/index.html | |
| ### When do lints run? | |
| Different lints will run at different times based on what information the lint needs to do its job. | |
| Some lints get grouped into *passes* where the lints | |
| within a pass are processed together via a single visitor. | |
| Some of the passes are: | |
| - Pre-expansion pass: Works on [AST nodes] before [macro expansion]. | |
| This should generally be avoided. | |
| - Example: [`keyword_idents`] checks for identifiers that will become | |
| keywords in future editions, but is sensitive to identifiers used in macros. | |
| - Early lint pass: Works on [AST nodes] after [macro expansion] and name | |
| resolution, just before [AST lowering]. | |
| These lints are for purely syntactical lints. | |
| - Example: The [`unused_parens`] lint checks for parenthesized-expressions | |
| in situations where they are not needed, like an `if` condition. | |
| - Late lint pass: Works on [HIR nodes], towards the end of [analysis] (after | |
| borrow checking, etc.). These lints have full type information available. | |
| Most lints are late. | |
| - Example: The [`invalid_value`] lint (which checks for obviously invalid | |
| uninitialized values) is a late lint because it needs type information to | |
| figure out whether a type allows being left uninitialized. | |
| - MIR pass: Works on [MIR nodes]. | |
| This isn't quite the same as other passes; | |
| lints that work on MIR nodes have their own methods for running. | |
| - Example: The [`arithmetic_overflow`] lint is emitted when it detects a | |
| constant value that may overflow. | |
| Most lints work well via the pass systems, and they have a fairly | |
| straightforward interface and easy way to integrate (mostly just implementing | |
| a specific `check` function). | |
| However, some lints are easier to write when | |
| they live on a specific code path anywhere in the compiler. | |
| For example, the [`unused_mut`] lint is implemented in the borrow checker as it requires some | |
| information and state in the borrow checker. | |
| Some of these inline lints fire before the linting system is ready. | |
| Those lints will be *buffered* where they are held until later phases of the | |
| compiler when the linting system is ready. | |
| See [Linting early in the compiler](#linting-early-in-the-compiler). | |
| [AST nodes]: the-parser.md | |
| [AST lowering]: ./hir/lowering.md | |
| [HIR nodes]: hir.md | |
| [MIR nodes]: mir/index.md | |
| [macro expansion]: macro-expansion.md | |
| [analysis]: part-4-intro.md | |
| [`keyword_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#keyword-idents | |
| [`unused_parens`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unused-parens | |
| [`invalid_value`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#invalid-value | |
| [`arithmetic_overflow`]: https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#arithmetic-overflow | |
| [`unused_mut`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unused-mut | |
| ### Lint definition terms | |
| Lints are managed via the [`LintStore`][LintStore] and get registered in various ways. | |
| The following terms refer to the different classes of lints | |
| generally based on how they are registered. | |
| - *Built-in* lints are defined inside the compiler source. | |
| - *Driver-registered* lints are registered when the compiler driver is created | |
| by an external driver. | |
| This is the mechanism used by Clippy, for example. | |
| - *Tool* lints are lints with a path prefix like `clippy::` or `rustdoc::`. | |
| - *Internal* lints are the `rustc::` scoped tool lints that only run on the | |
| rustc source tree itself and are defined in the compiler source like a regular built-in lint. | |
| More information about lint registration can be found in the [LintStore] chapter. | |
| [LintStore]: diagnostics/lintstore.md | |
| ### Declaring a lint | |
| The built-in compiler lints are defined in the [`rustc_lint`][builtin] 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 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 macro includes the name, the default level, a short description, and some more details. | |
| Note that the lint and the lint pass must be registered with the compiler. | |
| 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 { }`" | |
| } | |
| // 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). | |
| declare_lint_pass!(WhileTrue => [WHILE_TRUE]); | |
| // 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 EarlyLintPass for WhileTrue { | |
| fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { | |
| if let ast::ExprKind::While(cond, ..) = &e.kind | |
| && let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind | |
| && let ast::LitKind::Bool(true) = lit.kind | |
| && !lit.span.from_expansion() | |
| { | |
| let condition_span = cx.sess.source_map().guess_head_span(e.span); | |
| cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { | |
| lint.build(fluent::example::use_loop) | |
| .span_suggestion_short( | |
| condition_span, | |
| fluent::example::suggestion, | |
| "loop".to_owned(), | |
| Applicability::MachineApplicable, | |
| ) | |
| .emit(); | |
| }) | |
| } | |
| } | |
| } | |
| ``` | |
| ```fluent | |
| example-use-loop = denote infinite loops with `loop {"{"} ... {"}"}` | |
| .suggestion = use `loop` | |
| ``` | |
| [`declare_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/macro.declare_lint.html | |
| ### 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. | |
| See [Edition-specific lints](./guides/editions.md#edition-specific-lints) for more information. | |
| ### Feature-gated lints | |
| Lints belonging to a feature should only be usable if the feature is enabled in the crate. | |
| To support this, lint declarations can contain a feature gate like so: | |
| ```rust,ignore | |
| declare_lint! { | |
| pub SOME_LINT_NAME, | |
| Warn, | |
| "a new and useful, but feature gated lint", | |
| @feature_gate = sym::feature_name; | |
| } | |
| ``` | |
| ### 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. | |
| See [migration lints](guides/editions.md#migration-lints) for more details. | |
| A future-incompatible lint should be declared with the `@future_incompatible` additional "field": | |
| ```rust,ignore | |
| declare_lint! { | |
| pub ANONYMOUS_PARAMETERS, | |
| Allow, | |
| "detects anonymous parameters", | |
| @future_incompatible = FutureIncompatibleInfo { | |
| reason: fcw!(EditionError 2018 "slug-of-edition-guide-page") | |
| }; | |
| } | |
| ``` | |
| 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 its "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 change the `declare_lint!` macro to support this. | |
| ### Renaming or removing a 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 | |
| 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. | |
| 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 | |
| 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/rustc_session/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 (`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, `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. | |
| ## JSON diagnostic output | |
| The compiler accepts an `--error-format json` flag to output | |
| diagnostics as JSON objects (for the benefit of tools such as `cargo fix`). | |
| It looks like this: | |
| ```console | |
| $ rustc json_error_demo.rs --error-format json | |
| {"message":"cannot add `&str` to `{integer}`","code":{"code":"E0277","explanation":"\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"},"level":"error","spans":[{"file_name":"json_error_demo.rs","byte_start":50,"byte_end":51,"line_start":4,"line_end":4,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" a + b","highlight_start":7,"highlight_end":8}],"label":"no implementation for `{integer} + &str`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"the trait `std::ops::Add<&str>` is not implemented for `{integer}`","code":null,"level":"help","spans":[],"children":[],"rendered":null}],"rendered":"error[E0277]: cannot add `&str` to `{integer}`\n --> json_error_demo.rs:4:7\n |\n4 | a + b\n | ^ no implementation for `{integer} + &str`\n |\n = help: the trait `std::ops::Add<&str>` is not implemented for `{integer}`\n\n"} | |
| {"message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error\n\n"} | |
| {"message":"For more information about this error, try `rustc --explain E0277`.","code":null,"level":"","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0277`.\n"} | |
| ``` | |
| Note that the output is a series of lines, each of which is a JSON | |
| object, but the series of lines taken together is, unfortunately, not | |
| valid JSON, thwarting tools and tricks (such as [piping to `python3 -m | |
| json.tool`](https://docs.python.org/3/library/json.html#module-json.tool)) that require such. | |
| (One speculates that this was intentional for LSP | |
| performance purposes, so that each line/object can be sent as it is flushed?) | |
| Also note the "rendered" field, which contains the "human" output as a | |
| string; this was introduced so that UI tests could both make use of | |
| the structured JSON and see the "human" output (well, _sans_ colors) | |
| without having to compile everything twice. | |
| The "human" readable and the json format emitter can be found under | |
| `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) | |
| (and sub-structs) for the JSON serialization. | |
| Don't confuse this with | |
| [`errors::Diag`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html)! | |
| ## `#[rustc_on_unimplemented]` | |
| This attribute allows trait definitions to modify error messages when an implementation was | |
| expected but not found. | |
| The string literals in the attribute are format strings and can be formatted with named parameters. | |
| See the Formatting section below for what parameters are permitted. | |
| ```rust,ignore | |
| #[rustc_on_unimplemented(message = "an iterator over \ | |
| elements of type `{A}` cannot be built from a \ | |
| collection of type `{Self}`")] | |
| trait MyIterator<A> { | |
| fn next(&mut self) -> A; | |
| } | |
| fn iterate_chars<I: MyIterator<char>>(i: I) { | |
| // ... | |
| } | |
| fn main() { | |
| iterate_chars(&[1, 2, 3][..]); | |
| } | |
| ``` | |
| When the user compiles this, they will see the following; | |
| ```txt | |
| error[E0277]: an iterator over elements of type `char` cannot be built from a collection of type `&[{integer}]` | |
| --> src/main.rs:13:19 | |
| | | |
| 13 | iterate_chars(&[1, 2, 3][..]); | |
| | ------------- ^^^^^^^^^^^^^^ the trait `MyIterator<char>` is not implemented for `&[{integer}]` | |
| | | | |
| | required by a bound introduced by this call | |
| | | |
| note: required by a bound in `iterate_chars` | |
| ``` | |
| You can modify the contents of: | |
| - the main error message (`message`) | |
| - the label (`label`) | |
| - the note(s) (`note`) | |
| For example, the following attribute | |
| ```rust,ignore | |
| #[rustc_on_unimplemented(message = "message", label = "label", note = "note")] | |
| trait MyIterator<A> { | |
| fn next(&mut self) -> A; | |
| } | |
| ``` | |
| Would generate the following output: | |
| ```text | |
| error[E0277]: message | |
| --> <file>:10:19 | |
| | | |
| 10 | iterate_chars(&[1, 2, 3][..]); | |
| | ------------- ^^^^^^^^^^^^^^ label | |
| | | | |
| | required by a bound introduced by this call | |
| | | |
| = help: the trait `MyIterator<char>` is not implemented for `&[{integer}]` | |
| = note: note | |
| note: required by a bound in `iterate_chars` | |
| ``` | |
| The functionality discussed so far is also available with | |
| [`#[diagnostic::on_unimplemented]`](https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnosticon_unimplemented-attribute). | |
| If you can, you should use that instead. | |
| ### Filtering | |
| To allow more targeted error messages, it is possible to filter the | |
| application of these fields with `on`. | |
| You can filter on the following boolean flags: | |
| - `crate_local`: whether the code causing the trait bound to not be | |
| fulfilled is part of the user's crate. | |
| This is used to avoid suggesting code changes that would require modifying a dependency. | |
| - `direct`: whether this is a user-specified rather than derived obligation. | |
| - `from_desugaring`: whether we are in some kind of desugaring, like `?` | |
| or a `try` block for example. | |
| This flag can also be matched on, see below. | |
| You can match on the following names and values, using `name = "value"`: | |
| - `cause`: Match against one variant of the `ObligationCauseCode` enum. | |
| Only `"MainFunctionType"` is supported. | |
| - `from_desugaring`: Match against a particular variant of the `DesugaringKind` enum. | |
| The desugaring is identified by its variant name, for example | |
| `"QuestionMark"` for `?` desugaring, or `"TryBlock"` for `try` blocks. | |
| - `Self` and any generic arguments of the trait, like `Self = "alloc::string::String"` | |
| or `Rhs="i32"`. | |
| The compiler can provide several values to match on, for example: | |
| - the self_ty, pretty printed with and without type arguments resolved. | |
| - `"{integral}"`, if self_ty is an integral of which the type is known. | |
| - `"[]"`, `"[{ty}]"`, `"[{ty}; _]"`, `"[{ty}; $N]"` when applicable. | |
| - references to said slices and arrays. | |
| - `"fn"`, `"unsafe fn"` or `"#[target_feature] fn"` when self is a function. | |
| - `"{integer}"` and `"{float}"` if the type is a number but we haven't inferred it yet. | |
| - `"{struct}"`, `"{enum}"` and `"{union}"` to match self as an ADT | |
| - combinations of the above, like `"[{integral}; _]"`. | |
| For example, the `Iterator` trait can be filtered in the following way: | |
| ```rust,ignore | |
| #[rustc_on_unimplemented( | |
| on(Self = "&str", note = "call `.chars()` or `.as_bytes()` on `{Self}`"), | |
| message = "`{Self}` is not an iterator", | |
| label = "`{Self}` is not an iterator", | |
| note = "maybe try calling `.iter()` or a similar method" | |
| )] | |
| pub trait Iterator {} | |
| ``` | |
| Which would produce the following outputs: | |
| ```text | |
| error[E0277]: `Foo` is not an iterator | |
| --> src/main.rs:4:16 | |
| | | |
| 4 | for foo in Foo {} | |
| | ^^^ `Foo` is not an iterator | |
| | | |
| = note: maybe try calling `.iter()` or a similar method | |
| = help: the trait `std::iter::Iterator` is not implemented for `Foo` | |
| = note: required by `std::iter::IntoIterator::into_iter` | |
| error[E0277]: `&str` is not an iterator | |
| --> src/main.rs:5:16 | |
| | | |
| 5 | for foo in "" {} | |
| | ^^ `&str` is not an iterator | |
| | | |
| = note: call `.chars()` or `.bytes() on `&str` | |
| = help: the trait `std::iter::Iterator` is not implemented for `&str` | |
| = note: required by `std::iter::IntoIterator::into_iter` | |
| ``` | |
| The `on` filter accepts `all`, `any` and `not` predicates similar to the `cfg` attribute: | |
| ```rust,ignore | |
| #[rustc_on_unimplemented(on( | |
| all(Self = "&str", T = "alloc::string::String"), | |
| note = "you can coerce a `{T}` into a `{Self}` by writing `&*variable`" | |
| ))] | |
| pub trait From<T>: Sized { | |
| /* ... */ | |
| } | |
| ``` | |
| ### Formatting | |
| The string literals are format strings that accept parameters wrapped in braces | |
| but positional and listed parameters are not accepted. | |
| The following parameter names are valid: | |
| - `Self` and all generic parameters of the trait. | |
| - `This`: the name of the trait the attribute is on, without generics. | |
| - `This:path`: the full path of the trait the attribute is on, with unresolved generics. | |
| - `This:resolved`: the full path of the trait the attribute is on, with resolved generics. | |
| Additionally, this will "sugar" the `Fn(...)` traits. | |
| - `ItemContext`: the kind of `hir::Node` we're in, things like `"an async block"`, | |
| `"a function"`, `"an async function"`, etc. | |
| Something like: | |
| ```rust,ignore | |
| #![feature(rustc_attrs)] | |
| #[rustc_on_unimplemented(message = "Self = `{Self}`, \ | |
| T = `{T}`, this = `{This}`, trait = `{Trait}`, \ | |
| context = `{ItemContext}`")] | |
| pub trait From<T>: Sized { | |
| fn from(x: T) -> Self; | |
| } | |
| fn main() { | |
| let x: i8 = From::from(42_i32); | |
| } | |
| ``` | |
| Will format the message into | |
| ```text | |
| "Self = `i8`, T = `i32`, this = `From`, trait = `From<i32>`, context = `a function`" | |
| ``` | |
| [diag]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html |