From ba2f117125690e7d8ce202443178073c10830647 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 22 May 2018 13:40:37 -0500 Subject: [PATCH 1/6] Add info about emitting lints and errors --- src/SUMMARY.md | 1 + src/appendix-code-index.md | 3 + src/diag.md | 176 +++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 src/diag.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 4923d4972..2db3e62c3 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -54,6 +54,7 @@ - [miri const evaluator](./miri.md) - [Parameter Environments](./param_env.md) - [Generating LLVM IR](./trans.md) +- [Emitting Diagnostics](./diag.md) --- diff --git a/src/appendix-code-index.md b/src/appendix-code-index.md index 62edd0f5b..915d00828 100644 --- a/src/appendix-code-index.md +++ b/src/appendix-code-index.md @@ -8,11 +8,13 @@ Item | Kind | Short description | Chapter | ----------------|----------|-----------------------------|--------------------|------------------- `CodeMap` | struct | The CodeMap maps the AST nodes to their source code | [The parser] | [src/libsyntax/codemap.rs](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/codemap/struct.CodeMap.html) `CompileState` | struct | State that is passed to a callback at each compiler pass | [The Rustc Driver] | [src/librustc_driver/driver.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/driver/struct.CompileState.html) +`DiagnosticBuilder` | struct | A struct for building up compiler diagnostics, such as errors or lints | [Emitting Diagnostics] | [src/librustc_errors/diagnostic_builder.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagnosticBuilder.html) `DocContext` | struct | A state container used by rustdoc when crawling through a crate to gather its documentation | [Rustdoc] | [src/librustdoc/core.rs](https://github.com/rust-lang/rust/blob/master/src/librustdoc/core.rs) `ast::Crate` | struct | Syntax-level representation of a parsed crate | [The parser] | [src/librustc/hir/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/struct.Crate.html) `hir::Crate` | struct | More abstract, compiler-friendly form of a crate's AST | [The Hir] | [src/librustc/hir/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc/hir/struct.Crate.html) `ParseSess` | struct | This struct contains information about a parsing session | [the Parser] | [src/libsyntax/parse/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/struct.ParseSess.html) `Session` | struct | The data associated with a compilation session | [the Parser], [The Rustc Driver] | [src/librustc/session/mod.html](https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html) +`Span` | struct | A location in the user's source code, used for error reporting primarily | [Emitting Diagnostics] | [src/libsyntax_pos/span_encoding.rs](https://doc.rust-lang.org/nightly/nightly-rustc/syntax_pos/struct.Span.html) `StringReader` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [src/libsyntax/parse/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/lexer/struct.StringReader.html) `TraitDef` | struct | This struct contains a trait's definition with type information | [The `ty` modules] | [src/librustc/ty/trait_def.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/trait_def/struct.TraitDef.html) `Ty<'tcx>` | struct | This is the internal representation of a type used for type checking | [Type checking] | [src/librustc/ty/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/type.Ty.html) @@ -24,3 +26,4 @@ Item | Kind | Short description | Chapter | [Type checking]: type-checking.html [The `ty` modules]: ty.html [Rustdoc]: rustdoc.html +[Emitting Diagnostics]: diag.html diff --git a/src/diag.md b/src/diag.md new file mode 100644 index 000000000..25f45e002 --- /dev/null +++ b/src/diag.md @@ -0,0 +1,176 @@ +# Emitting Diagnostics + +A lot of effort has been put into making `rustc` have great error messages. +This chapter is about how to emit compile errors and lints from the compiler. + +## `Span` + +`Span` 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 easier error reporting whenever an error comes up. + +A `Span` can be looked up in a `CodeMap` to get a "snippet" useful for +displaying errors with [`span_to_snippet` and other similar methods][sptosnip] +on the `CodeMap`. + +[sptosnip]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/codemap/struct.CodeMap.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 + +Most "session"-like types in the compiler (e.g. [`Session`][session]) have +methods (or fields with methods) that allow reporting errors. These methods +usually have names like `span_err` or `struct_span_err` or `span_warn`, etc... +There are lots of them; they emit different types of "errors", such as +warnings, errors, fatal errors, suggestions, etc. + +[session]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html + +In general, there are two class of such methods: ones that emit an error +directly and ones that allow finer control over what to emit. For example, +[`span_err`][spanerr] emits the given error message at the given `Span`, but +[`struct_span_err`][strspanerr] instead returns a [`DiagnosticBuilder`][diagbuild]. + +`DiagnosticBuilder` allows you to add related notes and suggestions to an error +before emitting it by calling the [`emit`][emit] method. See the +[docs][diagbuild] for more info on what you can do. + +[spanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html#method.span_err +[strspanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html#method.struct_span_err +[diagbuild]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilder.html +[emit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilder.html#method.emit + +For example, to add a help message to an error, one might do: + +```rust,ignore +let snip = sess.codemap().span_to_snippet(sp); + +sess.struct_span_err(sp, "oh no! this is an error!") + .span_suggestion(other_sp, "try using a qux here", format!("qux {}", snip)) + .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`. +``` + +## Lints + +The compiler linting infrastructure is defined in the [`rustc::lint`][rlint] +module. + +[rlint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/index.html + +### Declaring a lint + +The built-in compiler lints are defined in the [`rustc_lint`][builtin] +crate. + +[builtin]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/index.html + +Each lint is defined as a `struct` that implements the `LintPass` `trait`. The +trait implementation allows you to check certain syntactic constructs the +linter walks the source code. You can then choose to emit lints in a very +similar way to compile errors. Finally, you register the lint to actually get +it to be run by the compiler by using the `declare_lint!` macro. + +For example, the following lint checks for uses +of `while true { ... }` and suggests using `loop { ... }` instead. + +```rust,ignore +// Declare a lint called `WHILE_TRUE` +declare_lint! { + WHILE_TRUE, + + // warn-by-default + Warn, + + // This string is the lint description + "suggest using `loop { }` instead of `while true { }`" +} + +// Define a struct and `impl LintPass` for it. +#[derive(Copy, Clone)] +pub struct WhileTrue; + +impl LintPass for WhileTrue { + fn get_lints(&self) -> LintArray { + lint_array!(WHILE_TRUE) + } +} + +// LateLintPass has lots of methods. We only override the definition of +// `check_expr` for this lint because that's all we need, but you could +// override other methods for your own lint. See the rustc docs for a full +// list of methods. +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue { + fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { + if let hir::ExprWhile(ref cond, ..) = e.node { + if let hir::ExprLit(ref lit) = cond.node { + if let ast::LitKind::Bool(true) = lit.node { + if lit.span.ctxt() == SyntaxContext::empty() { + let msg = "denote infinite loops with `loop { ... }`"; + let condition_span = cx.tcx.sess.codemap().def_span(e.span); + let mut err = cx.struct_span_lint(WHILE_TRUE, condition_span, msg); + err.span_suggestion_short(condition_span, "use `loop`", "loop".to_owned()); + err.emit(); + } + } + } + } + } +} +``` + +### Edition 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. + +### 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 by using `!#[warn(nonstandard_style)]`. From a81eb8a7a5abc7d161d3068d511696cf10e7b71d Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 22 May 2018 13:46:33 -0500 Subject: [PATCH 2/6] 80 chars --- src/diag.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/diag.md b/src/diag.md index 25f45e002..6679e4249 100644 --- a/src/diag.md +++ b/src/diag.md @@ -33,7 +33,8 @@ warnings, errors, fatal errors, suggestions, etc. In general, there are two class of such methods: ones that emit an error directly and ones that allow finer control over what to emit. For example, [`span_err`][spanerr] emits the given error message at the given `Span`, but -[`struct_span_err`][strspanerr] instead returns a [`DiagnosticBuilder`][diagbuild]. +[`struct_span_err`][strspanerr] instead returns a +[`DiagnosticBuilder`][diagbuild]. `DiagnosticBuilder` allows you to add related notes and suggestions to an error before emitting it by calling the [`emit`][emit] method. See the @@ -172,5 +173,5 @@ For example, NON_UPPER_CASE_GLOBALS); ``` -This defines the `nonstandard_style` group which turns on the listed lints. A user -can turn on these lints by using `!#[warn(nonstandard_style)]`. +This defines the `nonstandard_style` group which turns on the listed lints. A +user can turn on these lints by using `!#[warn(nonstandard_style)]`. From 367a159aeb4d5f619572984250e78376ba514869 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 22 May 2018 14:55:55 -0500 Subject: [PATCH 3/6] Address reviewers' comments --- src/diag.md | 86 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/src/diag.md b/src/diag.md index 6679e4249..a2ec3bb7e 100644 --- a/src/diag.md +++ b/src/diag.md @@ -22,12 +22,13 @@ reporting errors. [errors]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html -Most "session"-like types in the compiler (e.g. [`Session`][session]) have +[`Session`][session] and [`ParseSess`][parsesses] have methods (or fields with methods) that allow reporting errors. These methods usually have names like `span_err` or `struct_span_err` or `span_warn`, etc... There are lots of them; they emit different types of "errors", such as warnings, errors, fatal errors, suggestions, etc. +[parsesses]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/struct.ParseSess.html [session]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html In general, there are two class of such methods: ones that emit an error @@ -45,20 +46,64 @@ before emitting it by calling the [`emit`][emit] method. See the [diagbuild]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilder.html [emit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilder.html#method.emit -For example, to add a help message to an error, one might do: +```rust,ignore +// Get a DiagnosticBuilder. This does _not_ emit an error yet. +let mut err = sess.struct_span_err(sp, "oh no! this is an error!"); + +// In some cases, you might need to check if `sp` is generated by a macro to +// avoid printing weird errors about macro-generated code. + +if let Some(snippet) = sess.codemap().span_to_snippet(sp) { + // Use the snippet to generate a suggested fix + err.span_suggestion(suggestion_sp, "try using a qux here", format!("qux {}", snip)); +} else { + // If we weren't able to generate a snippet, then emit a "help" message + // instead of a concrete "suggestion". In practice this is unlikely to be + // reached. + err.span_help(suggestion_sp, "you could use a qux here instead"); +} + +// emit the error +err.emit(); +``` + +## Suggestions + +We would like to make edition transitions as smooth as possible. To that end, +`rustfix` can use compiler suggestions to automatically fix code. For example, +we could use `rustfix` to mechanically apply the `qux` suggestion from the +previous example. However, not all suggestions are mechanically applicable. We +use the [`span_suggestion_with_applicability`][sswa] method of +`DiagnosticBuilder` to inform the emitter of whether a suggestion is +mechanically applicable or not. This information, in turn, is outputed by +rustc when the error format is `json`, which is used by `rustfix`. + +[sswa]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagnosticBuilder.html#method.span_suggestion_with_applicability + +For example, to make our `qux` suggestion machine-applicable, we would do: ```rust,ignore -let snip = sess.codemap().span_to_snippet(sp); +let mut err = sess.struct_span_err(sp, "oh no! this is an error!"); + +if let Some(snippet) = sess.codemap().span_to_snippet(sp) { + // Add applicability info! + err.span_suggestion_with_applicability( + suggestion_sp, + "try using a qux here", + format!("qux {}", snip), + Applicability::MachineApplicable, + ); +} else { + err.span_help(suggestion_sp, "you could use a qux here instead"); +} -sess.struct_span_err(sp, "oh no! this is an error!") - .span_suggestion(other_sp, "try using a qux here", format!("qux {}", snip)) - .emit(); +err.emit(); ``` This might emit an error like ```console -$ rustc mycode.rs +$ rustc mycode.rs error[E0999]: oh no! this is an error! --> mycode.rs:3:5 | @@ -70,10 +115,29 @@ 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`. +``` + ## Lints The compiler linting infrastructure is defined in the [`rustc::lint`][rlint] -module. +module. [rlint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/index.html @@ -138,7 +202,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue { } ``` -### Edition Lints +### 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!`: @@ -155,6 +219,10 @@ declare_lint! { This makes the `ANONYMOUS_PARAMETERS` lint allow-by-default in the 2015 edition but warn-by-default in the 2018 edition. +Lints that represent an incompatibility (i.e. error) in the upcoming edition should +also be registered as `FutureIncompatibilityLint`s in +[`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. + ### Lint Groups Lints can be turned on in groups. These groups are declared in the From 9918aa83cf0212aefcb320717731e8cec55646e7 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 22 May 2018 15:00:27 -0500 Subject: [PATCH 4/6] add more on applicabilities --- src/diag.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/diag.md b/src/diag.md index a2ec3bb7e..8b067fde7 100644 --- a/src/diag.md +++ b/src/diag.md @@ -134,6 +134,18 @@ error: aborting due to previous error For more information about this error, try `rustc --explain E0999`. ``` +There are a few other [`Applicability`][appl] possibilities: + +- `MachineApplicable`: Can be applied mechanically. +- `HasPlaceholders`: Cannot be applied mechanically and has placeholder text in + the suggestions. For example, "Try adding a type: \`let x: \`". +- `MaybeIncorrect`: Cannot be applied mechanically because the suggestion may + or may not be a good one. +- `Unspecified`: Cannot be applied mechanically because we don't know which + of the above cases it falls into. + +[appl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html + ## Lints The compiler linting infrastructure is defined in the [`rustc::lint`][rlint] From 1485c0d851648e688bdae7a7005cbcc9b39b5a2a Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 22 May 2018 15:01:04 -0500 Subject: [PATCH 5/6] line length --- src/diag.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/diag.md b/src/diag.md index 8b067fde7..af619ed83 100644 --- a/src/diag.md +++ b/src/diag.md @@ -231,8 +231,8 @@ declare_lint! { This makes the `ANONYMOUS_PARAMETERS` lint allow-by-default in the 2015 edition but warn-by-default in the 2018 edition. -Lints that represent an incompatibility (i.e. error) in the upcoming edition should -also be registered as `FutureIncompatibilityLint`s in +Lints that represent an incompatibility (i.e. error) in the upcoming edition +should also be registered as `FutureIncompatibilityLint`s in [`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. ### Lint Groups From 7075a69c9848c305f0e3d513871f167a4b3ba244 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 22 May 2018 15:05:57 -0500 Subject: [PATCH 6/6] fix typo --- src/diag.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diag.md b/src/diag.md index af619ed83..6043a79b7 100644 --- a/src/diag.md +++ b/src/diag.md @@ -138,7 +138,7 @@ There are a few other [`Applicability`][appl] possibilities: - `MachineApplicable`: Can be applied mechanically. - `HasPlaceholders`: Cannot be applied mechanically and has placeholder text in - the suggestions. For example, "Try adding a type: \`let x: \`". + the suggestions. For example, "Try adding a type: \`let x: \\`". - `MaybeIncorrect`: Cannot be applied mechanically because the suggestion may or may not be a good one. - `Unspecified`: Cannot be applied mechanically because we don't know which