Skip to content

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 6 commits into from
May 28, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
- [miri const evaluator](./miri.md)
- [Parameter Environments](./param_env.md)
- [Generating LLVM IR](./trans.md)
- [Emitting Diagnostics](./diag.md)

---

Expand Down
3 changes: 3 additions & 0 deletions src/appendix-code-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
177 changes: 177 additions & 0 deletions src/diag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to just say Session and ParseSess here.

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs an unwrap_or, span_to_snippet returns an Option

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to show the more common code of:

let mut err = sess.struct_span_err(sp, "oh no! this is an error!");
// add code to verify that `sp` is not a macro if this is a consideration <...expand explanation of this later...>
if let Some(snippet) = sess.codemap().span_to_snippet(sp) {
    err.span_suggestion(suggestion_sp, "try using a qux here", format!("qux {}", snip));
} else { // in practice this is unlikely to be reached <...add explanation of this later...>
    err.span_help(suggestion_sp, "you could use a qux here instead");
}
err.emit();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// add code to verify that sp is not a macro if this is a consideration <...expand explanation of this later...>

@estebank I didn't quite understand what you meant by this.


I think will just create a new section for suggestions since they will play a big role in the edition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, you can forego that comment. It is a caveat that we'll need to add at some point, as suggestions that do not verify wether the current context is mixing macros can lead to very incorrect suggestions (like in #39450). This is not a problem under normal circumstances, but adding a check for this in the documentation could go a long way. Don't need to add it now though.


sess.struct_span_err(sp, "oh no! this is an error!")
.span_suggestion(other_sp, "try using a qux here", format!("qux {}", snip))
.emit();
```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also include some stuff on suggestion applicabilities here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to block too much on this, once this PR is merged it can be expanded more iteratively.

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`.
```
Copy link
Contributor

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:

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.

[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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Edition-gated lints", there are more kinds of 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)]`.