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