Hide irrelevant lines in suggestions to allow for suggestions that are far from each other to be shown
This is an attempt to fix suggestions one part of which is 6 lines or more far from the first. I've noticed "the problem" (of not showing some parts of the suggestion) here: https://github.com/rust-lang/rust/pull/97759#discussion_r889689230.
I'm not sure about the implementation (this big closure is just bad and makes already complicated code even more so), but I want to at least discuss the result.
Here is an example of how this changes the output:
Before:
```text
help: consider enclosing expression in a block
|
3 ~ 'l: { match () { () => break 'l,
4 |
5 |
6 |
7 |
8 |
...
```
After:
```text
help: consider enclosing expression in a block
|
3 ~ 'l: { match () { () => break 'l,
4 |
...
31|
32~ } };
|
```
r? `@estebank`
`@rustbot` label +A-diagnostics +A-suggestion-diagnostics
Support lint expectations for `--force-warn` lints (RFC 2383)
Rustc has a `--force-warn` flag, which overrides lint level attributes and forces the diagnostics to always be warn. This means, that for lint expectations, the diagnostic can't be suppressed as usual. This also means that the expectation would not be fulfilled, even if a lint had been triggered in the expected scope.
This PR now also tracks the expectation ID in the `ForceWarn` level. I've also made some minor adjustments, to possibly catch more bugs and make the whole implementation more robust.
This will probably conflict with https://github.com/rust-lang/rust/pull/97718. That PR should ideally be reviewed and merged first. The conflict itself will be trivial to fix.
---
r? `@wesleywiser`
cc: `@flip1995` since you've helped with the initial review and also discussed this topic with me. 🙃
Follow-up of: https://github.com/rust-lang/rust/pull/87835
Issue: https://github.com/rust-lang/rust/issues/85549
Yeah, and that's it.
lint: add diagnostic translation migration lints
Introduce allow-by-default lints for checking whether diagnostics are written in
`SessionDiagnostic` or `AddSubdiagnostic` impls and whether diagnostics are translatable. These lints can be denied for modules once they are fully migrated to impls and translation.
These lints are intended to be temporary - once all diagnostics have been changed then we can just change the APIs we have and that will enforce these constraints thereafter.
r? `````@oli-obk`````
Introduce allow-by-default lints for checking whether diagnostics are
written in `SessionDiagnostic`/`AddSubdiagnostic` impls and whether
diagnostics are translatable. These lints can be denied for modules once
they are fully migrated to impls and translation.
Signed-off-by: David Wood <david.wood@huawei.com>
Fix `delayed_good_path_bug` ice for expected diagnostics (RFC 2383)
Fixes a small ICE with the `delayed_good_path_bug` check.
---
r? ``@wesleywiser``
cc: ``@eddyb`` this might be interesting, since you've added a `FIXME` comment above the modified check which kind of discusses a case like this
closes: https://github.com/rust-lang/rust/issues/95540
cc: https://github.com/rust-lang/rust/issues/85549
To render the message of a Fluent attribute, the identifier of the
Fluent message must be known. `DiagnosticMessage::FluentIdentifier`
contains both the message's identifier and optionally the identifier of
an attribute. Generated constants for each attribute would therefore
need to be named uniquely (amongst all error messages) or be able to
refer to only the attribute identifier which will be combined with a
message identifier later. In this commit, the latter strategy is
implemented as part of the `Diagnostic` type's functions for adding
subdiagnostics of various kinds.
Signed-off-by: David Wood <david.wood@huawei.com>
Adds a new `fluent_messages` macro which performs compile-time
validation of the compiler's Fluent resources (i.e. that the resources
parse and don't multiply define the same messages) and generates
constants that make using those messages in diagnostics more ergonomic.
For example, given the following invocation of the macro..
```ignore (rust)
fluent_messages! {
typeck => "./typeck.ftl",
}
```
..where `typeck.ftl` has the following contents..
```fluent
typeck-field-multiply-specified-in-initializer =
field `{$ident}` specified more than once
.label = used more than once
.label-previous-use = first use of `{$ident}`
```
...then the macro parse the Fluent resource, emitting a diagnostic if it
fails to do so, and will generate the following code:
```ignore (rust)
pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] = &[
include_str!("./typeck.ftl"),
];
mod fluent_generated {
mod typeck {
pub const field_multiply_specified_in_initializer: DiagnosticMessage =
DiagnosticMessage::fluent("typeck-field-multiply-specified-in-initializer");
pub const field_multiply_specified_in_initializer_label_previous_use: DiagnosticMessage =
DiagnosticMessage::fluent_attr(
"typeck-field-multiply-specified-in-initializer",
"previous-use-label"
);
}
}
```
When emitting a diagnostic, the generated constants can be used as
follows:
```ignore (rust)
let mut err = sess.struct_span_err(
span,
fluent::typeck::field_multiply_specified_in_initializer
);
err.span_default_label(span);
err.span_label(
previous_use_span,
fluent::typeck::field_multiply_specified_in_initializer_label_previous_use
);
err.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
Add a new derive, `#[derive(SessionSubdiagnostic)]`, which enables
deriving structs for labels, notes, helps and suggestions.
Signed-off-by: David Wood <david.wood@huawei.com>
Since Cargo wants to do its own fatal error handling for unused
dependencies, add the option `--json unused-externs-silent` which
has the original behaviour of not indicating non-zero exit status for
`deny`/`forbid`-level unused dependencies.
Remove `--extern-location` and all associated code
`--extern-location` was an experiment to investigate the best way to
generate useful diagnostics for unused dependency warnings by enabling a
build system to identify the corresponding build config.
While I did successfully use this, I've since been convinced the
alternative `--json unused-externs` mechanism is the way to go, and
there's no point in having two mechanisms with basically the same
functionality.
This effectively reverts https://github.com/rust-lang/rust/pull/72603
`--extern-location` was an experiment to investigate the best way to
generate useful diagnostics for unused dependency warnings by enabling a
build system to identify the corresponding build config.
While I did successfully use this, I've since been convinced the
alternative `--json unused-externs` mechanism is the way to go, and
there's no point in having two mechanisms with basically the same
functionality.
This effectively reverts https://github.com/rust-lang/rust/pull/72603
Loading the fallback bundle in compilation sessions that won't go on to
emit any errors unnecessarily degrades compile time performance, so
lazily create the Fluent bundle when it is first required.
Signed-off-by: David Wood <david.wood@huawei.com>
Non-subdiagnostic fields (i.e. those that don't have `#[label]`
attributes or similar and are just additional context) have to be added
as arguments for Fluent messages to refer them. This commit extends the
`SessionDiagnostic` derive to do this for all fields that do not have
attributes and introduces an `IntoDiagnosticArg` trait that is
implemented on all types that can be converted to a argument for Fluent.
Signed-off-by: David Wood <david.wood@huawei.com>
Extend loading of Fluent bundles so that bundles can be loaded from the
sysroot based on the language requested by the user, or using a nightly
flag.
Sysroot bundles are loaded from `$sysroot/share/locale/$locale/*.ftl`.
Signed-off-by: David Wood <david.wood@huawei.com>
This commit updates the signatures of all diagnostic functions to accept
types that can be converted into a `DiagnosticMessage`. This enables
existing diagnostic calls to continue to work as before and Fluent
identifiers to be provided. The `SessionDiagnostic` derive just
generates normal diagnostic calls, so these APIs had to be modified to
accept Fluent identifiers.
In addition, loading of the "fallback" Fluent bundle, which contains the
built-in English messages, has been implemented.
Each diagnostic now has "arguments" which correspond to variables in the
Fluent messages (necessary to render a Fluent message) but no API for
adding arguments has been added yet. Therefore, diagnostics (that do not
require interpolation) can be converted to use Fluent identifiers and
will be output as before.
`MultiSpan` contains labels, which are more complicated with the
introduction of diagnostic translation and will use types from
`rustc_errors` - however, `rustc_errors` depends on `rustc_span` so
`rustc_span` cannot use types like `DiagnosticMessage` without
dependency cycles. Introduce a new `rustc_error_messages` crate that can
contain `DiagnosticMessage` and `MultiSpan`.
Signed-off-by: David Wood <david.wood@huawei.com>
Introduce a `DiagnosticMessage` type that will enable diagnostic
messages to be simple strings or Fluent identifiers.
`DiagnosticMessage` is now used in the implementation of the standard
`DiagnosticBuilder` APIs.
Signed-off-by: David Wood <david.wood@huawei.com>
There are a few places were we have to construct it, though, and a few
places that are more invasive to change. To do this, we create a
constructor with a long obvious name.
Improve `expect` impl and handle `#[expect(unfulfilled_lint_expectations)]` (RFC 2383)
This PR updates unstable `ExpectationIds` in stashed diagnostics and adds some asserts to ensure that the stored expectations are really empty in the end. Additionally, it handles the `#[expect(unfulfilled_lint_expectations)]` case.
According to the [Errors and lints docs](https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels) the `error` level should only be used _"when the compiler detects a problem that makes it unable to compile the program"_. As this isn't the case with `#[expect(unfulfilled_lint_expectations)]` I decided to only create a warning. To avoid adding a new lint only for this case, I simply emit a `unfulfilled_lint_expectations` diagnostic with an additional note.
---
r? `@wesleywiser` I'm requesting a review from you since you reviewed the previous PR https://github.com/rust-lang/rust/pull/87835. You are welcome to reassign it if you're busy 🙃
rfc: [RFC-2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)
tracking issue: https://github.com/rust-lang/rust/issues/85549
cc: `@flip1995` In case you're also interested in this :)
This updates the standard library's documentation to use the new syntax. The
documentation is worthwhile to update as it should be more idiomatic
(particularly for features like this, which are nice for users to get acquainted
with). The general codebase is likely more hassle than benefit to update: it'll
hurt git blame, and generally updates can be done by folks updating the code if
(and when) that makes things more readable with the new format.
A few places in the compiler and library code are updated (mostly just due to
already having been done when this commit was first authored).
Implementation of the `expect` attribute (RFC 2383)
This is an implementation of the `expect` attribute as described in [RFC-2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html). The attribute allows the suppression of lint message by expecting them. Unfulfilled lint expectations (meaning no expected lint was caught) will emit the `unfulfilled_lint_expectations` lint at the `expect` attribute.
### Example
#### input
```rs
// required feature flag
#![feature(lint_reasons)]
#[expect(unused_mut)] // Will warn about an unfulfilled expectation
#[expect(unused_variables)] // Will be fulfilled by x
fn main() {
let x = 0;
}
```
#### output
```txt
warning: this lint expectation is unfulfilled
--> $DIR/trigger_lint.rs:3:1
|
LL | #[expect(unused_mut)] // Will warn about an unfulfilled expectation
| ^^^^^^^^^^
|
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
```
### Implementation
This implementation introduces `Expect` as a new lint level for diagnostics, which have been expected. All lint expectations marked via the `expect` attribute are collected in the [`LintLevelsBuilder`] and assigned an ID that is stored in the new lint level. The `LintLevelsBuilder` stores all found expectations and the data needed to emit the `unfulfilled_lint_expectations` in the [`LintLevelsMap`] which is the result of the [`lint_levels()`] query.
The [`rustc_errors::HandlerInner`] is the central error handler in rustc and handles the emission of all diagnostics. Lint message with the level `Expect` are suppressed during this emission, while the expectation ID is stored in a set which marks them as fulfilled. The last step is then so simply check if all expectations collected by the [`LintLevelsBuilder`] in the [`LintLevelsMap`] have been marked as fulfilled in the [`rustc_errors::HandlerInner`]. Otherwise, a new lint message will be emitted.
The implementation of the `LintExpectationId` required some special handling to make it stable between sessions. Lints can be emitted during [`EarlyLintPass`]es. At this stage, it's not possible to create a stable identifier. The level instead stores an unstable identifier, which is later converted to a stable `LintExpectationId`.
### Followup TO-DOs
All open TO-DOs have been marked with `FIXME` comments in the code. This is the combined list of them:
* [ ] The current implementation doesn't cover cases where the `unfulfilled_lint_expectations` lint is actually expected by another `expect` attribute.
* This should be easily possible, but I wanted to get some feedback before putting more work into this.
* This could also be done in a new PR to not add to much more code to this one
* [ ] Update unstable documentation to reflect this change.
* [ ] Update unstable expectation ids in [`HandlerInner::stashed_diagnostics`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.HandlerInner.html#structfield.stashed_diagnostics)
### Open questions
I also have a few open questions where I would like to get feedback on:
1. The RFC discussion included a suggestion to change the `expect` attribute to something else. (Initiated by `@Ixrec` [here](https://github.com/rust-lang/rfcs/pull/2383#issuecomment-378424091), suggestion from `@scottmcm` to use `#[should_lint(...)]` [here](https://github.com/rust-lang/rfcs/pull/2383#issuecomment-378648877)). No real conclusion was drawn on that point from my understanding. Is this still open for discussion, or was this discarded with the merge of the RFC?
2. How should the expect attribute deal with the new `force-warn` lint level?
---
This approach was inspired by a discussion with `@LeSeulArtichaut.`
RFC tracking issue: #54503
Mentoring/Implementation issue: #85549
[`LintLevelsBuilder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/levels/struct.LintLevelsBuilder.html
[`LintLevelsMap`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/struct.LintLevelMap.html
[`lint_levels()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.lint_levels
[`rustc_errors::HandlerInner`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.HandlerInner.html
[`EarlyLintPass`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
`Decoder` has two impls:
- opaque: this impl is already partly infallible, i.e. in some places it
currently panics on failure (e.g. if the input is too short, or on a
bad `Result` discriminant), and in some places it returns an error
(e.g. on a bad `Option` discriminant). The number of places where
either happens is surprisingly small, just because the binary
representation has very little redundancy and a lot of input reading
can occur even on malformed data.
- json: this impl is fully fallible, but it's only used (a) for the
`.rlink` file production, and there's a `FIXME` comment suggesting it
should change to a binary format, and (b) in a few tests in
non-fundamental ways. Indeed #85993 is open to remove it entirely.
And the top-level places in the compiler that call into decoding just
abort on error anyway. So the fallibility is providing little value, and
getting rid of it leads to some non-trivial performance improvements.
Much of this commit is pretty boring and mechanical. Some notes about
a few interesting parts:
- The commit removes `Decoder::{Error,error}`.
- `InternIteratorElement::intern_with`: the impl for `T` now has the same
optimization for small counts that the impl for `Result<T, E>` has,
because it's now much hotter.
- Decodable impls for SmallVec, LinkedList, VecDeque now all use
`collect`, which is nice; the one for `Vec` uses unsafe code, because
that gave better perf on some benchmarks.
This was a regression from https://github.com/rust-lang/rust/pull/87337;
the `panic_if_treat_err_as_bug` function only checked the number of hard
errors, not the number of lint errors.
The only reason to use `abort_if_errors` is when the program is so broken that either:
1. later passes get confused and ICE
2. any diagnostics from later passes would be noise
This is never the case for lints, because the compiler has to be able to deal with `allow`-ed lints.
So it can continue to lint and compile even if there are lint errors.
Adopt let_else across the compiler
This performs a substitution of code following the pattern:
```
let <id> = if let <pat> = ... { identity } else { ... : ! };
```
To simplify it to:
```
let <pat> = ... { identity } else { ... : ! };
```
By adopting the `let_else` feature (cc #87335).
The PR also updates the syn crate because the currently used version of the crate doesn't support `let_else` syntax yet.
Note: Generally I'm the person who *removes* usages of unstable features from the compiler, not adds more usages of them, but in this instance I think it hopefully helps the feature get stabilized sooner and in a better state. I have written a [comment](https://github.com/rust-lang/rust/issues/87335#issuecomment-944846205) on the tracking issue about my experience and what I feel could be improved before stabilization of `let_else`.
This performs a substitution of code following the pattern:
let <id> = if let <pat> = ... { identity } else { ... : ! };
To simplify it to:
let <pat> = ... { identity } else { ... : ! };
By adopting the let_else feature.
In `splice_lines`, there is some arithmetic to compute the required
alignment such that future substitutions in a suggestion are aligned
correctly. However, this assumed that the current substitution's span
was only on a single line. In circumstances where this was not true, it
could result in a arithmetic overflow when the substitution's end
column was less than the substitution's start column.
Signed-off-by: David Wood <david.wood@huawei.com>
"Fix" an overflow in byte position math
r? `@estebank`
help! I fixed the ICE only to brick the diagnostic.
I mean, it was wrong previously (using an already expanded macro span), but it is really bad now XD
* On suggestions that include deletions, use a diff inspired output format
* When suggesting addition, use `+` as underline
* Color highlight modified span
# Stabilization report
## Summary
This stabilizes using macro expansion in key-value attributes, like so:
```rust
#[doc = include_str!("my_doc.md")]
struct S;
#[path = concat!(env!("OUT_DIR"), "/generated.rs")]
mod m;
```
See the changes to the reference for details on what macros are allowed;
see Petrochenkov's excellent blog post [on internals](https://internals.rust-lang.org/t/macro-expansion-points-in-attributes/11455)
for alternatives that were considered and rejected ("why accept no more
and no less?")
This has been available on nightly since 1.50 with no major issues.
## Notes
### Accepted syntax
The parser accepts arbitrary Rust expressions in this position, but any expression other than a macro invocation will ultimately lead to an error because it is not expected by the built-in expression forms (e.g., `#[doc]`). Note that decorators and the like may be able to observe other expression forms.
### Expansion ordering
Expansion of macro expressions in "inert" attributes occurs after decorators have executed, analogously to macro expressions appearing in the function body or other parts of decorator input.
There is currently no way for decorators to accept macros in key-value position if macro expansion must be performed before the decorator executes (if the macro can simply be copied into the output for later expansion, that can work).
## Test cases
- https://github.com/rust-lang/rust/blob/master/src/test/ui/attributes/key-value-expansion-on-mac.rs
- https://github.com/rust-lang/rust/blob/master/src/test/rustdoc/external-doc.rs
The feature has also been dogfooded extensively in the compiler and
standard library:
- https://github.com/rust-lang/rust/pull/83329
- https://github.com/rust-lang/rust/pull/83230
- https://github.com/rust-lang/rust/pull/82641
- https://github.com/rust-lang/rust/pull/80534
## Implementation history
- Initial proposal: https://github.com/rust-lang/rust/issues/55414#issuecomment-554005412
- Experiment to see how much code it would break: https://github.com/rust-lang/rust/pull/67121
- Preliminary work to restrict expansion that would conflict with this
feature: https://github.com/rust-lang/rust/pull/77271
- Initial implementation: https://github.com/rust-lang/rust/pull/78837
- Fix for an ICE: https://github.com/rust-lang/rust/pull/80563
## Unresolved Questions
~~https://github.com/rust-lang/rust/pull/83366#issuecomment-805180738 listed some concerns, but they have been resolved as of this final report.~~
## Additional Information
There are two workarounds that have a similar effect for `#[doc]`
attributes on nightly. One is to emulate this behavior by using a limited version of this feature that was stabilized for historical reasons:
```rust
macro_rules! forward_inner_docs {
($e:expr => $i:item) => {
#[doc = $e]
$i
};
}
forward_inner_docs!(include_str!("lib.rs") => struct S {});
```
This also works for other attributes (like `#[path = concat!(...)]`).
The other is to use `doc(include)`:
```rust
#![feature(external_doc)]
#[doc(include = "lib.rs")]
struct S {}
```
The first works, but is non-trivial for people to discover, and
difficult to read and maintain. The second is a strange special-case for
a particular use of the macro. This generalizes it to work for any use
case, not just including files.
I plan to remove `doc(include)` when this is stabilized. The
`forward_inner_docs` workaround will still compile without warnings, but
I expect it to be used less once it's no longer necessary.
This defers backtrace formatting to the point where we
actually want to flush delayed diagnostics. If they are discarded
before that point then we can avoid invoking the backtrace formatting
machinery which will parse debug info and symbol tables.
for debuginfo=2 this leads to a 20% walltime reduction of the UI testsuite