We have several methods indicating the presence of errors, lint errors,
and delayed bugs. I find it frustrating that it's very unclear which one
you should use in any particular spot. This commit attempts to instill a
basic principle of "use the least general one possible", because that
reflects reality in practice -- `has_errors` is the least general one
and has by far the most uses (esp. via `abort_if_errors`).
Specifics:
- Add some comments giving some usage guidelines.
- Prefer `has_errors` to comparing `err_count` to zero.
- Remove `has_errors_or_span_delayed_bugs` because it's a weird one: in
the cases where we need to count delayed bugs, we should really be
counting lint errors as well.
- Rename `is_compilation_going_to_fail` as
`has_errors_or_lint_errors_or_span_delayed_bugs`, for consistency with
`has_errors` and `has_errors_or_lint_errors`.
- Change a few other `has_errors_or_lint_errors` calls to `has_errors`,
as per the "least general" principle.
This didn't turn out to be as neat as I hoped when I started, but I
think it's still an improvement.
`Diagnostic::code` has the type `DiagnosticId`, which has `Error` and
`Lint` variants. Plus `Diagnostic::is_lint` is a bool, which should be
redundant w.r.t. `Diagnostic::code`.
Seems simple. Except it's possible for a lint to have an error code, in
which case its `code` field is recorded as `Error`, and `is_lint` is
required to indicate that it's a lint. This is what happens with
`derive(LintDiagnostic)` lints. Which means those lints don't have a
lint name or a `has_future_breakage` field because those are stored in
the `DiagnosticId::Lint`.
It's all a bit messy and confused and seems unintentional.
This commit:
- removes `DiagnosticId`;
- changes `Diagnostic::code` to `Option<String>`, which means both
errors and lints can straightforwardly have an error code;
- changes `Diagnostic::is_lint` to `Option<IsLint>`, where `IsLint` is a
new type containing a lint name and a `has_future_breakage` bool, so
all lints can have those, error code or not.
`-Ztreat-err-as-bug` treats normal errors and delayed bugs equally,
which can lead to some really surprising results.
This commit changes `-Ztreat-err-as-bug` so it ignores delayed bugs,
unless they get promoted to proper bugs and are printed.
This feels to me much simpler and more logical. And it simplifies the
implementation:
- The `-Ztreat-err-as-bug` check is removed from in
`DiagCtxt::{delayed_bug,span_delayed_bug}`.
- `treat_err_as_bug` doesn't need to count delayed bugs.
- The `-Ztreat-err-as-bug` panic message is simpler, because it doesn't
have to mention delayed bugs.
Output of delayed bugs is now more consistent. They're always printed
the same way. Previously when they triggered `-Ztreat-err-as-bug` they
would be printed slightly differently, via `span_bug` in
`span_delayed_bug` or `delayed_bug`.
A minor behaviour change: the "no errors encountered even though
`span_delayed_bug` issued" printed before delayed bugs is now a note
rather than a bug. This is done so it doesn't get counted as an error
that might trigger `-Ztreat-err-as-bug`, which would be silly.
This means that if you use `-Ztreat-err-as-bug=1` and there are no
normal errors but there are delayed bugs, the first delayed bug will be
shown (and the panic will happen after it's printed).
Also, I have added a second note saying "those delayed bugs will now be
shown as internal compiler errors". I think this makes it clearer what
is happening, because the whole concept of delayed bugs is non-obvious.
There are some test changes.
- equality-in-canonical-query.rs: Minor output changes, and the error
count reduces by one because the "no errors encountered even though
`span_delayed_bug` issued" message is no longer counted as an error.
- rpit_tait_equality_in_canonical_query.rs: Ditto.
- storage-live.rs: The query stack disappears because these delayed bugs
are now printed at the end, rather than when they are created.
- storage-return.rs, span_delayed_bug.rs: now need
`-Zeagerly-emit-delayed-bugs` because they need the delayed bugs
emitted immediately to preserve behaviour.
There are two places that handle normal delayed bugs. This commit
factors out some repeated code.
Also, we can use `std::mem::take` instead of `std::mem::replace`.
Errors in `DiagCtxtInner::emit_diagnostic` are never set to
`Level::Bug`, because the condition never succeeds, because
`self.treat_err_as_bug()` is called *before* the error counts are
incremented.
This commit switches to `self.treat_next_err_as_bug()`, fixing the
problem. This changes the error message output to actually say "internal
compiler error".
Of the error levels satisfying `is_error`, `Level::Error` is the only
one that can be a lint, so there's no need to check for it.
(And even if it wasn't, it would make more sense to include
non-`Error`-but-`is_error` lints under `lint_err_count` than under
`err_count`.)
There are four functions that adjust error and warning counts:
- `stash_diagnostic` (increment)
- `steal_diagnostic` (decrement)
- `emit_stashed_diagnostics) (decrement)
- `emit_diagnostic` (increment)
The first three all behave similarly, and only update `warn_count` for
forced warnings. But the last one updates `warn_count` for both forced
and non-forced warnings.
Seems like a bug. How should it be fixed? Well, `warn_count` is only
used in one place: `DiagCtxtInner::drop`, where it's part of the
condition relating to the printing of `good_path_delayed_bugs`. The
intention of that condition seems to be "have any errors been printed?"
so this commit replaces `warn_count` with `has_printed`, which is set
when printing occurs. This is simpler than all the ahead-of-time
incrementing and decrementing.
`is_force_warn` is only possible for diagnostics with `Level::Warning`,
but it is currently stored in `Diagnostic::code`, which every diagnostic
has.
This commit:
- removes the boolean `DiagnosticId::Lint::is_force_warn` field;
- adds a `ForceWarning` variant to `Level`.
Benefits:
- The common `Level::Warning` case now has no arguments, replacing
lots of `Warning(None)` occurrences.
- `rustc_session::lint::Level` and `rustc_errors::Level` are more
similar, both having `ForceWarning` and `Warning`.
In #119606 I added them and used a `_mv` suffix, but that wasn't great.
A `with_` prefix has three different existing uses.
- Constructors, e.g. `Vec::with_capacity`.
- Wrappers that provide an environment to execute some code, e.g.
`with_session_globals`.
- Consuming chaining methods, e.g. `Span::with_{lo,hi,ctxt}`.
The third case is exactly what we want, so this commit changes
`DiagnosticBuilder::foo_mv` to `DiagnosticBuilder::with_foo`.
Thanks to @compiler-errors for the suggestion.
We have `span_delayed_bug` and often pass it a `DUMMY_SP`. This commit
adds `delayed_bug`, which matches pairs like `err`/`span_err` and
`warn`/`span_warn`.
It was added in #54232. It seems like it was aimed at NLL development,
which is well in the past. Also, it looks like `-Ztreat-err-as-bug` can
be used to achieve the same effect. So it doesn't seem necessary.
They are no longer used, because
`{DiagCtxt,DiagCtxtInner}::emit_diagnostic` are used everywhere instead.
This also means `track_diagnostic` can become consuming.
Currently it's used for two dynamic checks:
- When a diagnostic is emitted, has it been emitted before?
- When a diagnostic is dropped, has it been emitted/cancelled?
The first check is no longer need, because `emit` is consuming, so it's
impossible to emit a `DiagnosticBuilder` twice. The second check is
still needed.
This commit replaces `DiagnosticBuilderState` with a simpler
`Option<Box<Diagnostic>>`, which is enough for the second check:
functions like `emit` and `cancel` can take the `Diagnostic` and then
`drop` can check that the `Diagnostic` was taken.
The `DiagCtxt` reference from `DiagnosticBuilderState` is now stored as
its own field, removing the need for the `dcx` method.
As well as making the code shorter and simpler, the commit removes:
- One (deprecated) `ErrorGuaranteed::unchecked_claim_error_was_emitted`
call.
- Two `FIXME(eddyb)` comments that are no longer relevant.
- The use of a dummy `Diagnostic` in `into_diagnostic`.
Nice!
Remove `-Zreport-delayed-bugs`.
It's not used within the repository in any way (e.g. in tests), and doesn't seem useful.
It was added in #52568.
r? ````@oli-obk````
Because it's redundant w.r.t. `Diagnostic::is_lint`, which is present
for every diagnostic level.
`struct_lint_level_impl` was the only place that set the `Error` field
to `true`, and it's also the only place that calls
`Diagnostic::is_lint()` to set the `is_lint` field.
`Diagnostic` has 40 methods that return `&mut Self` and could be
considered setters. Four of them have a `set_` prefix. This doesn't seem
necessary for a type that implements the builder pattern. This commit
removes the `set_` prefixes on those four methods.
Lots of vectors of messages called `message` or `msg`. This commit
pluralizes them.
Note that `emit_message_default` and `emit_messages_default` both
already existed, and both process a vector, so I renamed the former
`emit_messages_default_inner` because it's called by the latter.
`DiagCtxt::span_bug` is different to the other `DiagCtxt::span_*`
methods. This commit makes it the same, which requires changing
`DiagCtxt::span_delayed_bug` to not do everything within the
`inner.borrow_mut()`.
- Take a `Vec` instead of an iterator, because that's all that is
needed.
- Do an early return for the "no bugs" case.
- Use `enumerate` and an `i == 0` test to identify the first bug.
Those changes mean the `no_bug` variable can be removed, which I found
hard to read.
`IntoDiagnostic` defaults to `ErrorGuaranteed`, because errors are the
most common diagnostic level. It makes sense to do likewise for the
closely-related (and much more widely used) `DiagnosticBuilder` type,
letting us write `DiagnosticBuilder<'a, ErrorGuaranteed>` as just
`DiagnosticBuilder<'a>`. This cuts over 200 lines of code due to many
multi-line things becoming single line things.