-
Notifications
You must be signed in to change notification settings - Fork 543
Add info about emitting lints and errors #139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ba2f117
Add info about emitting lints and errors
mark-i-m a81eb8a
80 chars
mark-i-m 367a159
Address reviewers' comments
mark-i-m 9918aa8
add more on applicabilities
mark-i-m 1485c0d
line length
mark-i-m 7075a69
fix typo
mark-i-m File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
# 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 | ||
|
||
[`Session`][session] and [`ParseSess`][parsesses] have | ||
methods (or fields with methods) that allow reporting errors. These methods | ||
usually have names like `span_err` or `struct_span_err` or `span_warn`, etc... | ||
There are lots of them; they emit different types of "errors", such as | ||
warnings, errors, fatal errors, suggestions, etc. | ||
|
||
[parsesses]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/struct.ParseSess.html | ||
[session]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/session/struct.Session.html | ||
|
||
In general, there are two class of such methods: ones that emit an error | ||
directly and ones that allow finer control over what to emit. For example, | ||
[`span_err`][spanerr] emits the given error message at the given `Span`, but | ||
[`struct_span_err`][strspanerr] instead returns a | ||
[`DiagnosticBuilder`][diagbuild]. | ||
|
||
`DiagnosticBuilder` allows you to add related notes and suggestions to an error | ||
before emitting it by calling the [`emit`][emit] method. 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 | ||
|
||
```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 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"); | ||
} | ||
|
||
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`. | ||
``` | ||
|
||
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: \<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 | ||
|
||
## 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-gated Lints | ||
|
||
Sometimes we want to change the behavior of a lint in a new edition. To do this, | ||
we just add the transition to our invocation of `declare_lint!`: | ||
|
||
```rust,ignore | ||
declare_lint! { | ||
pub ANONYMOUS_PARAMETERS, | ||
Allow, | ||
"detects anonymous parameters", | ||
Edition::Edition2018 => Warn, | ||
} | ||
``` | ||
|
||
This makes the `ANONYMOUS_PARAMETERS` lint allow-by-default in the 2015 edition | ||
but warn-by-default in the 2018 edition. | ||
|
||
Lints that represent an incompatibility (i.e. error) in the upcoming edition | ||
should also be registered as `FutureIncompatibilityLint`s in | ||
[`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. | ||
|
||
### Lint Groups | ||
|
||
Lints can be turned on in groups. These groups are declared in the | ||
[`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. The | ||
`add_lint_group!` macro is used to declare a new group. | ||
|
||
[rbuiltins]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html | ||
|
||
For example, | ||
|
||
```rust,ignore | ||
add_lint_group!(sess, | ||
"nonstandard_style", | ||
NON_CAMEL_CASE_TYPES, | ||
NON_SNAKE_CASE, | ||
NON_UPPER_CASE_GLOBALS); | ||
``` | ||
|
||
This defines the `nonstandard_style` group which turns on the listed lints. A | ||
user can turn on these lints by using `!#[warn(nonstandard_style)]`. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add explanation:
In some cases, like when the suggestion spans multiple lines or when there are multiple suggestions, the suggestions are displayed on its own: