| # 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 |
| | ^^^^ |
| ``` |
| |
| - 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. |
| |
| ### 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 given 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]). |
| |
| [translation]: ./diagnostics/translation.md |
| |
| `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. |
| |
| [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 |
| [diag]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html |
| [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]. |
| |
| [diag]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html |
| [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]: ast-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 { |
| reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>", |
| 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 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(...)]` |
| |
| The `#[rustc_on_unimplemented]` attribute allows trait definitions to add specialized |
| notes to error messages when an implementation was expected but not found. |
| You can refer to the trait's generic arguments by name and to the resolved type using `Self`. |
| |
| For example: |
| |
| ```rust,ignore |
| #![feature(rustc_attrs)] |
| |
| #[rustc_on_unimplemented="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]: the trait bound `&[{integer}]: MyIterator<char>` is not satisfied |
| --> <anon>:14:5 |
| | |
| 14 | iterate_chars(&[1, 2, 3][..]); |
| | ^^^^^^^^^^^^^ an iterator over elements of type `char` cannot be built from a collection of type `&[{integer}]` |
| | |
| = help: the trait `MyIterator<char>` is not implemented for `&[{integer}]` |
| = note: required by `iterate_chars` |
| ``` |
| |
| `rustc_on_unimplemented` also supports advanced filtering for better targeting |
| of messages, as well as modifying specific parts of the error message. You |
| target the text of: |
| |
| - the main error message (`message`) |
| - the label (`label`) |
| - an extra note (`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 |
| --> <anon>:14:5 |
| | |
| 14 | iterate_chars(&[1, 2, 3][..]); |
| | ^^^^^^^^^^^^^ label |
| | |
| = note: note |
| = help: the trait `MyIterator<char>` is not implemented for `&[{integer}]` |
| = note: required by `iterate_chars` |
| ``` |
| |
| To allow more targeted error messages, it is possible to filter the |
| application of these fields based on a variety of attributes when using |
| `on`: |
| |
| - `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. |
| - Any of the generic arguments that can be substituted in the text can be |
| referred by name as well for filtering, like `Rhs="i32"`, except for |
| `Self`. |
| - `_Self`: to filter only on a particular calculated trait resolution, like |
| `Self="std::iter::Iterator<char>"`. This is needed because `Self` is a |
| keyword which cannot appear in attributes. |
| - `direct`: user-specified rather than derived obligation. |
| - `from_desugaring`: usable both as boolean (whether the flag is present) |
| or matching against a particular desugaring. The desugaring is identified |
| with its variant name in the `DesugaringKind` enum. |
| |
| For example, the `Iterator` trait can be annotated 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` |
| ``` |
| |
| If you need to filter on multiple attributes, you can use `all`, `any` or |
| `not` in the following way: |
| |
| ```rust,ignore |
| #[rustc_on_unimplemented( |
| on( |
| all(_Self="&str", T="std::string::String"), |
| note="you can coerce a `{T}` into a `{Self}` by writing `&*variable`" |
| ) |
| )] |
| pub trait From<T>: Sized { /* ... */ } |
| ``` |