Skip to content

Commit

Permalink
Some updates for recent diagnostics changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
nnethercote committed Feb 9, 2024
1 parent af8e2fe commit 1c09ec6
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 69 deletions.
2 changes: 1 addition & 1 deletion src/bug-fix-procedure.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ Let's say that we've adopted `E0592` as our code. Then we can change the
`add_lint()` call above to something like:

```rust
struct_span_err!(self.tcx.sess, self.tcx.span_of_impl(item1).unwrap(), msg)
struct_span_code_err!(self.dcx(), self.tcx.span_of_impl(item1).unwrap(), E0592, msg)
.emit();
```

Expand Down
15 changes: 9 additions & 6 deletions src/compiler-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,8 @@ stack backtrace:

If you want to get a backtrace to the point where the compiler emits an
error message, you can pass the `-Z treat-err-as-bug=n`, which will make
the compiler panic on the `nth` error on `span_delayed_bug`. If you leave
off `=n`, the compiler will assume `1` for `n` and thus panic on the
first error it encounters.

This can also help when debugging `span_delayed_bug` calls - it will make
the first `span_delayed_bug` call panic, which will give you a useful backtrace.
the compiler panic on the `nth` error. If you leave off `=n`, the compiler will
assume `1` for `n` and thus panic on the first error it encounters.

For example:

Expand Down Expand Up @@ -184,6 +180,13 @@ stack backtrace:

Cool, now I have a backtrace for the error!

## Debugging delayed bugs

The `-Z eagerly-emit-delayed-bugs` option makes it easy to debug delayed bugs.
It turns them into normal errors, i.e. makes them visible. This can be used in
combination with `-Z treat-err-as-bug` to stop at a particular delayed bug and
get a backtrace.

## Getting the error creation location

`-Z track-diagnostics` can help figure out where errors are emitted. It uses `#[track_caller]`
Expand Down
58 changes: 27 additions & 31 deletions src/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ LL | more code
(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. Diagnostics created
by lints don't have a code in the emitted message.
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:
Expand Down Expand Up @@ -116,7 +116,7 @@ Here are a few examples:
so warnings are instead emitted,
and will eventually be turned into fixed (hard) errors.

Hard-coded warnings (those using the `span_warn` methods) should be avoided
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.

Expand All @@ -139,7 +139,7 @@ use an error-level lint instead of a fixed error.
- 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::diagnostic_builder::DiagnosticBuilder`][diagbuild]'s
[`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.
Expand Down Expand Up @@ -324,14 +324,12 @@ described below can be used as normal.

[diagnostic-structs]: ./diagnostics/diagnostic-structs.md

[`Session`][session] and [`ParseSess`][parsesses] have
methods (or fields with methods) that allow reporting errors. These methods
[`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.

[parsesses]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html
[session]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html
[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,
Expand All @@ -350,15 +348,15 @@ before emitting it by calling the [`emit`][emit] method. (Failing to either
emit or [cancel][cancel] a `DiagnosticBuilder` will result in an ICE.) 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
[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
[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
[cancel]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diagnostic.html#method.cancel
[cancel]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilder.html#method.cancel

```rust,ignore
// Get a DiagnosticBuilder. This does _not_ emit an error yet.
let mut err = sess.struct_span_err(sp, fluent::example::example_error);
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.
Expand Down Expand Up @@ -421,7 +419,7 @@ apply them)
For example, to make our `qux` suggestion machine-applicable, we would do:

```rust,ignore
let mut err = sess.struct_span_err(sp, fluent::example::message);
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(
Expand Down Expand Up @@ -644,24 +642,22 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
// 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 {
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 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();
})
}
}
}
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();
})
}
}
}
Expand Down
27 changes: 14 additions & 13 deletions src/diagnostics/diagnostic-structs.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# Diagnostic and subdiagnostic structs
rustc has two diagnostic derives that can be used to create simple diagnostics,
rustc has three diagnostic derives that can be used to create simple diagnostics,
which are recommended to be used when they are applicable:
`#[derive(Diagnostic)]` and `#[derive(Subdiagnostic)]`.
`#[derive(Diagnostic)]`, #[derive(LintDiagnostic)], and `#[derive(Subdiagnostic)]`.

Diagnostics created with the derive macros can be translated into different
languages and each has a slug that uniquely identifies the diagnostic.

## `#[derive(Diagnostic)]`
## `#[derive(Diagnostic)]` and `#[derive(LintDiagnostic)]`
Instead of using the `DiagnosticBuilder` API to create and emit diagnostics,
the `Diagnostic` derive can be used. `#[derive(Diagnostic)]` is
only applicable for simple diagnostics that don't require much logic in
deciding whether or not to add additional subdiagnostics.
these derives can be used. They are only applicable for simple diagnostics that
don't require much logic in deciding whether or not to add additional
subdiagnostics.

Consider the [definition][defn] of the "field already declared" diagnostic
shown below:

```rust,ignore
#[derive(Diagnostic)]
#[diag(hir_analysis_field_already_declared, code = "E0124")]
#[diag(hir_analysis_field_already_declared, code = E0124)]
pub struct FieldAlreadyDeclared {
pub field_name: Ident,
#[primary_span]
Expand Down Expand Up @@ -113,17 +113,18 @@ In the end, the `Diagnostic` derive will generate an implementation of
`IntoDiagnostic` that looks like the following:

```rust,ignore
impl IntoDiagnostic<'_> for FieldAlreadyDeclared {
fn into_diagnostic(self, handler: &'_ rustc_errors::Handler) -> DiagnosticBuilder<'_> {
let mut diag = handler.struct_err(rustc_errors::fluent::hir_analysis_field_already_declared);
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a> for FieldAlreadyDeclared {
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
let mut diag =
DiagnosticBuilder::new(dcx, level, fluent::hir_analysis_field_already_declared);
diag.set_span(self.span);
diag.span_label(
self.span,
rustc_errors::fluent::hir_analysis_label
fluent::hir_analysis_label
);
diag.span_label(
self.prev_span,
rustc_errors::fluent::hir_analysis_previous_decl_label
fluent::hir_analysis_previous_decl_label
);
diag
}
Expand All @@ -135,7 +136,7 @@ straightforward, just create an instance of the struct and pass it to
`emit_err` (or `emit_warning`):

```rust,ignore
tcx.sess.emit_err(FieldAlreadyDeclared {
tcx.dcx().emit_err(FieldAlreadyDeclared {
field_name: f.ident,
span: f.span,
prev_span,
Expand Down
6 changes: 3 additions & 3 deletions src/diagnostics/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ register_diagnostics! {
}
```

To actually issue the error, you can use the `struct_span_err!` macro:
To actually issue the error, you can use the `struct_span_code_err!` macro:

```rust
struct_span_err!(self.tcx.sess, // some path to the session here
struct_span_code_err!(self.dcx(), // some path to the `DiagCtxt` here
span, // whatever span in the source you want
E0592, // your new error code
fluent::example::an_error_message)
Expand All @@ -84,7 +84,7 @@ If you want to add notes or other snippets, you can invoke methods before you
call `.emit()`:

```rust
struct_span_err!(...)
struct_span_code_err!(...)
.span_label(another_span, fluent::example::example_label)
.span_note(another_span, fluent::example::separate_note)
.emit()
Expand Down
4 changes: 2 additions & 2 deletions src/diagnostics/error-guaranteed.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ error code path leads to a failure.
There are some important considerations about the usage of `ErrorGuaranteed`:

* It does _not_ convey information about the _kind_ of error. For example, the
error may be due (indirectly) to a `span_delayed_bug` or other compiler error.
error may be due (indirectly) to a delayed bug or other compiler error.
Thus, you should not rely on
`ErrorGuaranteed` when deciding whether to emit an error, or what kind of error
to emit.
Expand All @@ -23,7 +23,7 @@ There are some important considerations about the usage of `ErrorGuaranteed`:
_has already been_ emitted -- that is, the [`emit()`][emit] function has
already been called. For example, if we detect that a future part of the
compiler will error, we _cannot_ use `ErrorGuaranteed` unless we first emit
an error ourselves.
an error or delayed bug ourselves.

Thankfully, in most cases, it should be statically impossible to abuse
`ErrorGuaranteed`.
Expand Down
2 changes: 1 addition & 1 deletion src/diagnostics/lintstore.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Registration of these lints happens in the [`rustc_lint::register_internals`]
function which is called when constructing a new lint store inside
[`rustc_lint::new_lint_store`].

### Builtin Lints
#### Builtin Lints

These are primarily described in two places,
`rustc_lint_defs::builtin` and `rustc_lint::builtin`.
Expand Down
6 changes: 3 additions & 3 deletions src/diagnostics/translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ rustc's diagnostic infrastructure supports translatable diagnostics using

There are two ways of writing translatable diagnostics:

1. For simple diagnostics, using a diagnostic (or subdiagnostic) derive
("simple" diagnostics being those that don't require a lot of logic in
1. For simple diagnostics, using a diagnostic (or subdiagnostic) derive.
("Simple" diagnostics being those that don't require a lot of logic in
deciding to emit subdiagnostics and can therefore be represented as
diagnostic structs). See [the diagnostic and subdiagnostic structs
documentation](./diagnostic-structs.md).
2. Using typed identifiers with `DiagnosticBuilder` APIs (in
`Diagnostic` implementations).
`IntoDiagnostic` or `AddToDiagnostic` or `DecorateLint` implementations).

When adding or changing a translatable diagnostic,
you don't need to worry about the translations.
Expand Down
20 changes: 11 additions & 9 deletions src/ty.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,24 +342,26 @@ emitting an error to the user, then this could cause later errors to be suppress
compilation might inadvertently succeed!

Sometimes there is a third case. You believe that an error has been reported, but you believe it
would've been reported earlier in the compilation, not locally. In that case, you can invoke
[`span_delayed_bug`] This will make a note that you expect compilation to yield an error -- if
however compilation should succeed, then it will trigger a compiler bug report.
would've been reported earlier in the compilation, not locally. In that case, you can create a
"delayed bug" with [`delayed_bug`] or [`span_delayed_bug`]. This will make a note that you expect
compilation to yield an error -- if however compilation should succeed, then it will trigger a
compiler bug report.

[`delayed_bug`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.delayed_bug
[`span_delayed_bug`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.span_delayed_bug

For added safety, it's not actually possible to produce a `TyKind::Error` value
outside of [`rustc_middle::ty`][ty]; there is a private member of
`TyKind::Error` that prevents it from being constructable elsewhere. Instead,
one should use the [`TyCtxt::ty_error`][terr] or
[`TyCtxt::ty_error_with_message`][terrmsg] methods. These methods automatically
call `span_delayed_bug` before returning an interned `Ty` of kind `Error`. If you
one should use the [`Ty::new_error`][terr] or
[`Ty::new_error_with_message`][terrmsg] methods. These methods either take an `ErrorGuaranteed`
or call `span_delayed_bug` before returning an interned `Ty` of kind `Error`. If you
were already planning to use [`span_delayed_bug`], then you can just pass the
span and message to [`ty_error_with_message`][terrmsg] instead to avoid
delaying a redundant span bug.
a redundant delayed bug.

[terr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.ty_error
[terrmsg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.ty_error_with_message
[terr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_error
[terrmsg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_error_with_message

## Question: Why not substitute “inside” the `AdtDef`?

Expand Down

0 comments on commit 1c09ec6

Please sign in to comment.