-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: Add a semantically non-blocking lint level #3730
base: master
Are you sure you want to change the base?
Changes from 12 commits
95ca8a9
a8d4932
ee5c90e
066712c
91be5d2
1bc7f95
883fb82
b7093ce
6445281
56876e2
f82b8ca
fb61bb2
9e3c40a
a03f5d2
5e30760
14faa69
2410bb1
21f543a
d581438
eaf0145
a9d8e56
9f27926
bf52f75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,385 @@ | ||||||
- Feature Name: `nit-lint-level` | ||||||
- Start Date: 2024-11-15 | ||||||
- RFC PR: [rust-lang/rfcs#3730](https://github.com/rust-lang/rfcs/pull/3730) | ||||||
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||||||
- [Internals](https://internals.rust-lang.org/t/forbid-deny-warn-allow-and-notice/19986) | ||||||
|
||||||
# Summary | ||||||
[summary]: #summary | ||||||
|
||||||
Add a new visible lint level below `warn` to allow linters to act like a pair-programmer / code-reviewer where feedback is evaluated on a case-by-case basis. | ||||||
- `cargo` does not display these lints by default, requiring an opt-in | ||||||
- This RFC assumes LSPs/IDEs will opt-in | ||||||
- CIs could opt-in and open issues in the code review to show nits introduced in the current PR but not block merge on them | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
There is no expectation that crates will be `nit`-free. | ||||||
|
||||||
*Note:* The name `nit` is being used for illustrative purposes and is assumed to not be the final choice | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Nit: missing period. |
||||||
|
||||||
*Note:* This RFC leaves the determination of what lints will be `nit` by | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
default to the respective teams. Any lints referenced in this document are | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
for illustrating the intent of this feature and how teams could reason about | ||||||
this new lint level. | ||||||
|
||||||
# Motivation | ||||||
[motivation]: #motivation | ||||||
|
||||||
By its name and literal usage, the `warn` level is non-blocking. | ||||||
However, most projects treat `warn` as a soft-error. | ||||||
It doesn't block for local development but CI blocks it from being merged. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
This is an attempt to balance final correctness with rapid prototyping. | ||||||
Requiring "warnings clean" code also avoids warnings fatigue where warnings | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
make it hard to see "relevant" compiler output, and make a codebase feel | ||||||
lower-quality in a way that does not inspire people or invite people to | ||||||
help solve the problem. | ||||||
This convention is not new with the Rust community; many C++ projects have take | ||||||
this approach before Rust came to be with "warnings clean" being a goal for | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
correctness. | ||||||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
Cargo is looking to further cement this meaning by adding | ||||||
[`CARGO_BUILD_WARNINGS=deny`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#warnings) | ||||||
for CIs to set rather than `RUSTFLAGS=-Dwarnings`. | ||||||
|
||||||
This leaves a gap in the developer user experience for truly non-blocking lints. | ||||||
- Have maintainers `allow` a lint means they can't benefit from it | ||||||
- Have contributors `#[allow]`ing the lints to bless code, | ||||||
- The weight of `warn` may encourage contributors to do what it says, rather than `#[allow]` it | ||||||
- Sprinkling `#[allow]` in code can be noisy and the lint may not be worth it | ||||||
- If CI is using `stable`, then new lints can break CI | ||||||
- This is also true for lints changing behavior but that isn't a problem as often | ||||||
- Having maintainers `-W` or `-A` these lints through `RUSTFLAGS` in CI | ||||||
- This makes it more difficult for users to reproduce this locally. | ||||||
- `RUSTFLAGS` also comes with its own set of problems and the Cargo team is interested in finding alternatives to maintainers setting `RUSTFLAGS`, | ||||||
see [cargo#12738](https://github.com/rust-lang/cargo/issues/12738), [cargo#12739](https://github.com/rust-lang/cargo/issues/12739) | ||||||
|
||||||
Another problem is when adopting lints. | ||||||
This experience is more taken from legacy C++ code bases but the assumption is | ||||||
that this can become a problem in our future as the Rust code bases grow over | ||||||
time. | ||||||
A complaint that can happen when adopting a linter or lints is the time it takes to clean up the code base to be lint free | ||||||
because their only choice is to block on a lint or completely ignoring it. | ||||||
If we had a non-blocking lint level, a project could switch interested lints to | ||||||
that level and at least limit the introduction of new lint violations, | ||||||
either viewing that as good enough or while the existing violations are resolved in parallel. | ||||||
|
||||||
A secondary benefit of non-blocking lints is that more lints could move out of `allow`, raising their visibility. | ||||||
It can take effort to sift through all of the | ||||||
[default-`allow`ed lints](https://rust-lang.github.io/rust-clippy/master/index.html?levels=allow) | ||||||
for which ones a maintainer may want to turn into a soft or hard error. | ||||||
|
||||||
## Example: `clap` | ||||||
|
||||||
Each lint below from `clap`s `Cargo.toml` represents a lint that could be useful but not worthwhile enough to `allow`: | ||||||
```toml | ||||||
[workspace.lints.clippy] | ||||||
bool_assert_comparison = "allow" | ||||||
branches_sharing_code = "allow" | ||||||
collapsible_else_if = "allow" | ||||||
if_same_then_else = "allow" | ||||||
let_and_return = "allow" # sometimes good to name what you are returning | ||||||
multiple_bound_locations = "allow" | ||||||
assigning_clones = "allow" | ||||||
blocks_in_conditions = "allow" | ||||||
``` | ||||||
|
||||||
Also, CI runs `clippy` with `-D warnings -A deprecated` | ||||||
- Dependency upgrades should not be blocked on an API becoming deprecated | ||||||
- The maintainers want visibility into what is deprecated though | ||||||
- Combined with other circumstances, this also blocks [`clippy-sarif`](https://crates.io/crates/clippy-sarif) from opening issues in PRs for deprecated APIs. | ||||||
|
||||||
Part of this stems from `deprecated` having two purposes: | ||||||
- This API is broken and you should severely question any use of it | ||||||
- This API will go away with the next major version you should migrate to the new API at some point in time | ||||||
|
||||||
The second case is very useful for compiler-guided upgrades through breaking changes | ||||||
(e.g. [winnow's migration guide](https://github.com/winnow-rs/winnow/blob/v0.6.0/CHANGELOG.md)). | ||||||
|
||||||
Allowing dependency upgrades in the presence of deprecations is not just something that `clap` is interested in for itself but | ||||||
users of clap reported that they want to handle deprecations on their own schedule ([clap#3822](https://github.com/clap-rs/clap/issues/3822)). | ||||||
This led to adding a `deprecated` feature to `clap` so deprecations became opt-in | ||||||
([clap#3830](https://github.com/clap-rs/clap/pull/3830)). | ||||||
However, this can be easy for users to miss and won't catch new uses of the deprecated APIs. | ||||||
|
||||||
## Example: `cargo` | ||||||
|
||||||
In 2021 ([cargo#9356](https://github.com/rust-lang/cargo/pull/9356)), the Cargo team switched their `src/lib.rs` to: | ||||||
```rust | ||||||
#![allow(clippy::all)] | ||||||
#![warn(clippy::needless_borrow)] | ||||||
#![warn(clippy::redundant_clone)] | ||||||
``` | ||||||
due to false positives and the level of subjectivity of the improvements. | ||||||
This approach was first relaxed in 2024 ([cargo#11722](https://github.com/rust-lang/cargo/pull/11722)). | ||||||
|
||||||
Over time and with the addition of the `[lints]` table, this eventually led to ([cargo#12178](https://github.com/rust-lang/cargo/pull/12178)): | ||||||
```toml | ||||||
[workspace.lints.clippy] | ||||||
all = { level = "allow", priority = -1 } | ||||||
dbg_macro = "warn" | ||||||
disallowed_methods = "warn" | ||||||
print_stderr = "warn" | ||||||
print_stdout = "warn" | ||||||
self_named_module_files = "warn" | ||||||
``` | ||||||
|
||||||
In a recent PR, a bug that clippy would have found without our level overrides (`derive_ord_xor_partial_ord`) | ||||||
was only caught in review because one of the reviewers has seen clippy report | ||||||
this lint many times in other projects. | ||||||
After some discussion, the `Cargo.toml` was updated to ([cargo#14796](https://github.com/rust-lang/cargo/pull/14796)): | ||||||
```toml | ||||||
[workspace.lints.clippy] | ||||||
all = { level = "allow", priority = -2 } | ||||||
correctness = { level = "warn", priority = -1 } | ||||||
dbg_macro = "warn" | ||||||
disallowed_methods = "warn" | ||||||
print_stderr = "warn" | ||||||
print_stdout = "warn" | ||||||
self_named_module_files = "warn" | ||||||
``` | ||||||
|
||||||
If more clippy lints were non-blocking, maybe the Cargo team would not have set | ||||||
This comment was marked as outdated.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That statement is made within the context of the start of the motivation section:
This comment was marked as outdated.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do people
|
||||||
`all` to `allow` and been able to catch this more easily. | ||||||
Granted, a non-blocking lint level could still lead to low quality "lint | ||||||
reduction" PRs being posted and avoiding those was one of the original | ||||||
motivations for the initial change. | ||||||
|
||||||
# Guide-level explanation | ||||||
[guide-level-explanation]: #guide-level-explanation | ||||||
|
||||||
## Example workflow | ||||||
|
||||||
*This will be reported from perspective of a Cargo user.* | ||||||
|
||||||
Rust users will have a new `nit` lint level for providing optional coding feedback. | ||||||
|
||||||
If introduced today without any changes to levels for existing lints, | ||||||
a maintainer may decide that they want `clippy::let_and_return` to be optional. | ||||||
In their `Cargo.toml`, they would set: | ||||||
```toml | ||||||
[package.lints.clippy] | ||||||
let_and_return = "nit" | ||||||
``` | ||||||
|
||||||
A contributor may write: | ||||||
```rust | ||||||
fn foo() -> String { | ||||||
if condition() { | ||||||
let semantically_meaningful_name_one = bar(); | ||||||
semantically_meaningful_name_one | ||||||
} else { | ||||||
let semantically_meaningful_name_two = baz(); | ||||||
semantically_meaningful_name_two | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
Rust-analyzer would give them feedback that they wrote extraneous code that could be reduced down, | ||||||
with a suggested fix. | ||||||
However, they feel the variable name is acting as a form of documentation and want to keep it. | ||||||
|
||||||
When the contributor checks for lints, they run | ||||||
```console | ||||||
$ cargo clippy | ||||||
Compiling foo | ||||||
Finished `foo` profile [optimized + debuginfo] target(s) in 15.85s | ||||||
``` | ||||||
No lints reported. | ||||||
|
||||||
A non-rust-analyzer user could run the following to look for feedback: | ||||||
```console | ||||||
$ CARGO_BUILD_NITS=nit cargo clippy | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm surprised that opt-in to show the lints is so verbose. For users who don't use an IDE, this may be tiresome to type. The verbosity of the env var could make users set it always in their default env (bashrc, etc.), and that would defeat the point of them being hidden by default. I'd like something like Even when a CI is configured to display the nits, I still want to run them locally so that I can open the affected files easily (I can click file paths in my terminal), and can check whether I've fixed all instances without having to push code and wait for the CI. |
||||||
Compiling foo | ||||||
note: creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block | ||||||
... | ||||||
Finished `foo` profile [optimized + debuginfo] target(s) in 15.85s | ||||||
``` | ||||||
|
||||||
They then go and post a PR. | ||||||
On the PR, the code above gets the following issue opened: | ||||||
``` | ||||||
X Check failure | ||||||
|
||||||
Code scanning clippy | ||||||
|
||||||
creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block (warning) | ||||||
|
||||||
Show more details | ||||||
``` | ||||||
This gives the contributor an opportunity to know of this potential improvement | ||||||
if they missed it and for the code reviewer to double check if they agreed with | ||||||
the author's determination to ignore it. | ||||||
|
||||||
For future PRs, Github should not report this as it should recognize that the report is for existing code. | ||||||
|
||||||
## Choosing lint levels | ||||||
|
||||||
When creating a lint or overriding a default lint level, | ||||||
its important to consider the semantics, something like: | ||||||
|
||||||
`error` is for hard errors that even prevent reporting of errors further in the build, running of tests, etc. | ||||||
This should be reserved for when there is no point building or testing anything further because of how broken the code is. | ||||||
|
||||||
`warn` generally has the semantics of a soft-error. | ||||||
Its non-blocking locally but will be blocked when the code is merged upstream. | ||||||
This should be used when eventual correctness or consistency is desired. | ||||||
|
||||||
`nit` is for coding suggestions for users that may or may not improve the code or may not need to be done yet. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find this definition of the levels is quite vague. Also, as @Manishearth notes, clippy and rusttc have very different interpretations of lint levels already. I find the example of Similar arguments could be made about many (many most) lints in clippy's Are we proposing to downgrade |
||||||
|
||||||
`allow` is for when there is no right answer (e.g. mutually exclusive lints), there are false positives, the lint is overly noisy, or the lint is of limited value. | ||||||
|
||||||
*Note:* This is for communicating the intent of this RFC and to get people started with this new feature and is not meant to precisely redefine the | ||||||
[lint levels](https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels). | ||||||
|
||||||
# Reference-level explanation | ||||||
[reference-level-explanation]: #reference-level-explanation | ||||||
Comment on lines
+272
to
+273
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to also add a Also opening this thread for @rust-lang/clippy to discuss the implications this will have on Clippy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also think some The thing is, I for the most part already see the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah actually we don't need "less than warn", we've got pedantic. we just need a way to run pedantic lints on new code only and we'd be set. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine calling out that clippy would also need to do any relevant work for the new lint level. However, I would like to treat what new groups are needed, their naming, and any other lint organization related stuff as out of scope like setting lint levels. I think it would be much easier, more productive conversation for you to do that within your team and through your team's decision making process for that level of work rather than pulling in more major decision making into this multi-team RFC. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The differences between this and pedantic are that the user can move things into this level and its a cross-linter convention. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The end-user workflow, at least for a Cargo user, isn't great for moving things in and out of allow/warn for non-blocking lints.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't understand: you use the Cargo lints table for this, yes? That can move between allow and warn pretty easily. Are you looking for a way to also do it over CLI? (As I mention in a different comment near the end I think user-defined lint groupings, or lint profiles, for that would be great)
I've noted this in another comment but I think the unstated assumption muddling things here is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fully agree. I opened this thread so that the Clippy team has an isolate space in this RFC to discuss things. Those shouldn't necessarily end up in this RFC though. Thanks for adding a Clippy section. Could you add a note to that section, that Clippy would make further decisions how to use this new feature in a team-specific decision making process? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I have the general disclaimer in the Summary along with another in the Motivation to remind people not to read too much into my lint level descriptions. What are you hoping to get out of having more, particularly having them per-team? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this is bleeding over into our other discussion at #3730 (comment) which is about workflows |
||||||
|
||||||
## Rustc | ||||||
|
||||||
Rustc focuses on the mechanics of a new, visible lint level `nit`. | ||||||
|
||||||
Add support for the new lint: | ||||||
```rust | ||||||
#![nit(deprecated)] | ||||||
``` | ||||||
```console | ||||||
$ rustc --nit deprecated ... | ||||||
``` | ||||||
```console | ||||||
$ rustc -Ndeprecated ... | ||||||
``` | ||||||
|
||||||
These will be rendered like other lint levels with a diagnostic level and coloring that conveys the non-blocking, helpful nature. | ||||||
For example, this could be rendered like a `note:` or `help:` diagnostic level. | ||||||
|
||||||
Rustc will have a dynamic lint group of all `nit`s, much like `warnings` is all lints at level `warn` at the time of processing, `-Anits`. | ||||||
|
||||||
## Cargo | ||||||
|
||||||
Cargo implements the semantics for this to be a first-class non-blocking lint level. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Lokathor from #3730 (comment)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. epage from #3730 (comment)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Lokathor from #3730 (comment)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the Guide section, the lints are shown to that user in two primary scenarios
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think the easy part of this RFC is saying "show me diagnostics on new code only". I think the hard part is to define a system for what's "new code". If you can't actually say how cargo or rustc or rust-analyzer is picking out new code and only showing diagnostics for that, then the RFC is only half done. At the same time, if there's a system to run a lint against "new code only", as a lesser level than "warn", then any time rustc adds a new lint people can just set the lint to "new_only" if they feel it's too much of a change to their codebase all at once. And all existing lints that got allowed through can be bumped to new and then bumped to warn eventually. So I'd suggest focusing on what "new code" means, and trying to define that more specifically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've summarized the Github side of my comment in 9e3c40a and reached out to r-a people in #3730 (comment) |
||||||
|
||||||
Add support for the new lint: | ||||||
```toml | ||||||
[workspace.lints.rust] | ||||||
deprecated = "nit" | ||||||
``` | ||||||
|
||||||
A new config field will be added to mirror `build.warnings`: | ||||||
|
||||||
### `build.nits` | ||||||
|
||||||
- Type: string | ||||||
- Default: `allow` | ||||||
- Environment: `CARGO_BUILD_NITS` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is |
||||||
|
||||||
Controls how Cargo handles nits. Allowed values are: | ||||||
|
||||||
- `nit`: warnings are emitted as warnings. | ||||||
- `allow`: warnings are hidden (default). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another option that would be interesting to me is to display nits only if there are no other warnings to display. Unnecessary nits/lints are annoying when they reduce signal-to-noise ratio of compiler's output (e.g. displays 100 lints and 1 error, and I have to scroll through all the lints to find the error). But if there's nothing else to report, then nits aren't getting in the way of anything. |
||||||
|
||||||
# Drawbacks | ||||||
[drawbacks]: #drawbacks | ||||||
|
||||||
Rust-analyzer might be limited in what it can do through LSP to avoid overwhelming the user with `nit`s. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Veykril any insight on how LSP clients use Looking to better talk about how this lint level will work in practice, see also #3730 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a lint is limited to new code, then almost by definition it won't show too many of that lint at once. I think normal "information" style display isn't likely to overload the user. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is LSP support for an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like nits being left to IDEs. Rust-Analyzer/LSP are very biased towards MS Visual Studio Code. Where LSP doesn't support something, Rust Analyzer uses VSCode-specific workarounds, but other editors are left out. Other editors usually have sub-par experience. Sublime Text for example loses a lot of fidelity when displaying Rust's errors (in most cases it can't show a connection between a span and a note). The problem is that Rust Analyzer uses VS Code as the reference implementation of LSP, and doesn't want to test and work around issues in other editors. Developers of the other IDEs/editors also just want to support LSP, and don't want to add features/workarounds for Rust Analyzer specifically. This means that any issues with how Rust errors are displayed in an LSP IDE end up being nobody's responsibility: RA tells to file bugs with your editor, and the editor says to file bugs with RA. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I don't use Rust Analyzer, due to a bunch of different reasons: I've historically always worked on larger codebases, often with custom build systems, and IDEs have never been great at those. I also use Sublime Text and Rust IDE support has always lagged there. Both these problems are not as big a deal as they used to be now, but at this point ten years of writing Rust have made my existing workflows comparably efficient to using an IDE and I don't wish to put effort into switching now. |
||||||
|
||||||
This requires a non-standard CI setup to report these messages. | ||||||
flip1995 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
Non-rust-analyzer users won't get feedback until CI runs and then will have to know to run their linter in a special way to reproduce CI's output locally. | ||||||
|
||||||
Users running `CARGO_BUILD_NITS=nit cargo clippy` will have a hard time finding what lints are relevant for their change. | ||||||
If we had a way to filter lints by part of the API or by "whats new since run X", | ||||||
then that could be resolved. | ||||||
flip1995 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
As `nit`s will always be enabled in the linter, just silenced in Cargo, the performance hit of running | ||||||
them will be present unless `RUSTFLAGS=-Anits` is applied by the user. | ||||||
|
||||||
# Rationale and alternatives | ||||||
[rationale-and-alternatives]: #rationale-and-alternatives | ||||||
|
||||||
- `CARGO_BUILD_NITS=allow` is the default to avoid "warning fatigue" | ||||||
- The default level for lints is left unchanged by this RFC to keep the scope of what is designed and reviewed to the minimum | ||||||
- Rustc shows `nit`s by default, rather than hide them and require a separate opt-in mechanism for Cargo to see them | ||||||
- Problem: People directly using rustc or using a third-party build tool may be inundated with these | ||||||
- Lint levels may change without breaking compatibility | ||||||
- If stabilize with no `nit`s or only downgrading `warn`s to `nit`s, then there will be no difference in the amount of output | ||||||
- We already have a mechanism, let's not invent a new one | ||||||
- `nits` lint group is created to give rustc-only or third-party build tools a built-in way to control these besides ignoring them like Cargo | ||||||
- Cargo could print a message that `nit`s are present to help raise awareness of how to show them but they will likely always be present, so this is noise to experienced users. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, in Clippy we thought about adding something similar for lints that weren't emitted because of a (default) clippy config. For example if
This would help us raising awareness for Clippy configuration options. But we didn't really pursue that, because OTOneH it could be noisy, as stated here, and OTOtherH I/we weren't sure if
Long story short: Clippy and this might both benefit from a way to emit additional metadata/information after There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Had some long debates with someone about the right default for In a way, it could be nice to have lints silenced by
This is implementing support directly in Cargo, so it isn't quite the same as trying to get clippy to report something like this. However, there might be a way to do this today, with Cargo's cooperation. We were talking about cargo tracking unused-crate-dependencies and deciding when to report it back based on the if all crates in a package agree (if enough crates were built). I can't remember if we were just going to key off of the lint name in json or if there was some lint-specific information that was being communicated. If the latter, then maybe something similar can be done here and cargo can count and report it. However, there is still the question of "is this worth it". Also, if we do make these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw I went ahead and called out |
||||||
|
||||||
# Prior art | ||||||
[prior-art]: #prior-art | ||||||
|
||||||
IntelliJ IDEA has the following [lint levels](https://www.jetbrains.com/help/idea/configuring-inspection-severities.html): | ||||||
- Error: Syntax errors | ||||||
- Warning: Code fragments that might produce bugs or require enhancement | ||||||
- Weak Warning: Code fragments that can be improved or optimized (redundant code, duplicated code fragments, and so on) | ||||||
- Server Problem | ||||||
- Grammar Error | ||||||
- Typo | ||||||
- Consideration: Code fragments that can be improved. This severity level is not marked on the error stripe and does not have a default highlighting style, but you can select one from the list of existing styles or configure your own. | ||||||
- No highlighting (but fix available) | ||||||
|
||||||
LSP has the following [diagnostic levels](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic): | ||||||
- Error | ||||||
- Warning | ||||||
- Information | ||||||
- Hint | ||||||
|
||||||
with the following diagnostic tags: | ||||||
- Unnecessary | ||||||
- Deprecated | ||||||
|
||||||
Github/Sarif support the following [severities](https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/about-code-scanning-alerts#about-alert-severity-and-security-severity-levels) | ||||||
- Error | ||||||
- Warning | ||||||
- Note | ||||||
|
||||||
# Unresolved questions | ||||||
[unresolved-questions]: #unresolved-questions | ||||||
|
||||||
- `CARGO_BUILD_NITS=nit` is modeled off of `CARGO_BUILD_WARNINGS=warn` which is just saying "treat X level violations as Y level" | ||||||
- `CARGO_BUILD_WARNINGS=warn` is "weird" but shouldn't show up too often as its the default | ||||||
- However, the frequency is swapped with `CARGO_BUILD_NITS=nit` | ||||||
- We may also want the terms to be consistent across the different level-control fields | ||||||
- Reminder: `CARGO_BUILD_WARNINGS` is unstable and we can still change it | ||||||
|
||||||
## Naming | ||||||
|
||||||
The name should clearly communicate that there is no authoriative weight | ||||||
encouraging them to resolve them but that it is left to them and their reviewer | ||||||
to weigh whether to resolve or ignore them. | ||||||
|
||||||
The verb form is used for: | ||||||
- attribute | ||||||
- long flag | ||||||
|
||||||
Lint names are expected to be read with their level for what should be done, e.g. | ||||||
- `deny(let_and_return)` -> "deny the use of let-and-return in this code" | ||||||
- `warn(let_and_return)` -> "warn on the use of let-and-return in this code" | ||||||
|
||||||
The noun form is used for: | ||||||
- Dynamic lint group | ||||||
|
||||||
Options | ||||||
- `#[nit]` / `--nit` / `-N` / `-Wnits` | ||||||
- Unsure how to word `nit(let_and_return)` as a sentence like the other lint levels | ||||||
- `#[note]` / `--note` / `-N` / `-Wnotes` | ||||||
- "note the let-and-return in this code" | ||||||
- `#[notice]` / `--notice` / `-N` / `-Wnotices` | ||||||
- "notice the let-and-return in this code" | ||||||
- `#[mention]` / `--mention` / `-M` / `-Wmentions` | ||||||
- "mention the let-and-return in this code" | ||||||
Comment on lines
+451
to
+452
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would likely be the case for any standard communication word ( In part, it depends on how you frame it as rustc does communucate these by default and there are recommended workflows where these do get communicated.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at synonyms of
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm +1 on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'd also be in favor of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. existing lints/errors have help and note text. we can at least pick a new name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My concern with
This is why in the RFC body I switched the level name to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll point out that nitpick is also a verb:
Pedantic would also seem appropriate, but that is an adjective, not a verb, and the verb form "pedantize" is unusual, and doesn't quite fit. |
||||||
- `#[consider]` / `--consider` / ~~`-C` (taken)~~ / `-Wconsiderations` (IntelliJ) | ||||||
- "consider the let-and-return in this code" | ||||||
- Verb and noun have a larger divergence | ||||||
- `#[inform]` / `--inform` / `-I` / `-Winformation` (LSP) | ||||||
- "inform you of the let-and-return in this code" | ||||||
- Verb and noun have a larger divergence | ||||||
- `#[hint]` / `--hint` / `-H` / `-Whints` (LSP) | ||||||
- "hint of a let-and-return in this code" | ||||||
Comment on lines
+459
to
+460
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned, this has precedence in LSP. My personal concern with it is that it can come across as too prescriptive / |
||||||
- `#[remark]` / `--remark` / `-R` (supposedly LLVM) / `-Wremarks` | ||||||
- "remark on the let-and-return in this code" | ||||||
- ~~`#[suggest]` / `--suggest` / `-S` / `-Wsuggestions`~~ | ||||||
- When read with the lint name, it sounds like its to find where you *should* do it rather than finding where you are doing it | ||||||
- Verb and noun have a larger divergence | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. last option I can think of:
|
||||||
# Future possibilities | ||||||
[future-possibilities]: #future-possibilities | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's a Future Possibility: if we get a good system for running lints against new code only, we could introduce all new rustc lints against new code only for X many versions before bumping it up a level to a general "warn". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I covered this as a secondary benefit in a03f5d2. Maybe I'm overthinking but I'm not used to talking about future possibilities being for team policies which this is more about, so thats part of why I put it there. |
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.
Hm, I think I got a handle on one of my underlying concerns with this RFC: the problem is one faced by a lot of different people regardless of the tooling they use, but the solution proposed is squarely in the realm of third-party IDE/CI support. Which isn't necessarily a non-starter, but I do think this problem can be solved "in universe" in a way that benefits everyone.
IDEs are an optional thing one may use when working with Rust. So is
cargo
, admittedly, butcargo
is much "less" optional and typically people replace cargo with an equivalent tool, unlike IDEs, which can be replaced with plain editors.CI is also optional but since by and large the primary problem being solved here involves "what shows up locally vs what shows up in CI1" it's not really a problem in a non-CI world.
However, choice of CI systems is optional. Not every CI system will end up with this type of integrated tool, and the ones who do may not have one flexible enough to support folks' needs. People do a lot of fancy stuff in their Rust CI and I wouldn't expect a "Run lints but only show nits on the CI" to tool to actually be usable by most users initially. As a datapoint, ICU4X has had the "show lints on PRs" CI task enabled and disabled a bunch of times for various reasons.
Footnotes
n.b: "shows up" here is about what the user actually notices: I'd consider
warn
lints as "showing up" locally, but not "showing up" in CI, since people seldom look at the logs of successful CI runs. ↩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.
This RFC gives people the building blocks to make that policy. If they don't implement it, they still have the ability to show these lints on the command-line. Granted, that would be a firehose as pointed out in the RFC. The community can explore and iterate on tools to help with this.
In the end, if the user does nothing extra,
nit
will be likeallow
and those users will have lost nothing from this.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.
Okay, but I don't think "don't make the experience worse for other people" ought to be the bar for an RFC. This RFC lists problems that I have, that many people have, a its motivation. If the RFC does not solve those problems for us, I think that that's definitely an issue! It means the RFC is not fully solving the problems it talks about; and I don't think that's good for an RFC.
"Build your own CI tooling around this" is an acceptable conclusion for these folks. But "build your own CI tooling around this" was already a solution in the current world; this is just making that a little easier.
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.
If this isn't solving a problem for some users, how is it making the problem worse for them? The only thing I can think of is that linter teams decide to demote
warnings
intonits
and they no longer show up. Except there is already a common expectation of needing to do a lot of customization, so is that much of a change?Not every solution can solve a problem for every user. Sometimes we intentionally don't try to solve problems for every user.
This is somewhat talked about in the other thread, like with #3730 (comment)
This is at least partially answered in the RFC. To do this today, you need to use RUSTFLAGS. If you just set the RUSTFLAGS in CI, you then can't reproduce it locally.
What you'd likely need to do is
config.toml
, separate from your[lints]
, for this--config
CARGO_TARGET_DIR
to avoid cache thrashing until fix(fingerprint): Don't throwaway the cache on RUSTFLAGS changes cargo#14830 (if this third attempt even pans out)--config
--config
Even then to view the lints today you will get cache thrashing. If rust-lang/cargo#14830 ends up working, you won't get cache thrashing but you won't get any cache reuse between these nit runs and regular runs.
CI will either need a separate run without the nits
config.toml
to run with-Dwarnings
(requiring a full rebuild) or it will have to be the first thing your config does before showing turning all of the other allows into warnings. With the latter, the user will then have r-a turning all standard warnings into errors which at minimum will block for anything built after. if you then use--keep-going
, it will only block dependents. Maybe instead you have two separateconfig.toml
files, one that has-Dwarnings
and one that doesn't, requiring you to track two the nits in two places. The r-a user then has the problem that there is no way to distinguish nits from regular warnings.That is a complicated path requiring knowledge of various advanced features and likely not discovering some of the problems i called out until they are hit. The burden for adopting this and maintaining it severely limits the likelihood someone would bother to adopt it.
So no, this is not about making it a little easier. This is about smoothing the path so people are even willing to do it in the first place.
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.
Solving a problem for some but not all users often uses a lot of time and energy, which are usually both stretched quite thin within the Rust project.
A solution that suits some people can make a solution for the others become farther off because people don't always have the energy to do a thing, 'finally get it done', and then turn around and tackle almost the same problem all over again.
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.
You misunderstand, I'm not saying it makes the problem worse for them, I just think that "doesn't make it worse for people" isn't the right bar for an RFC that's trying to solve a problem like this. You're free to disagree with that.
There is one way in which I'm worried this RFC makes things worse: people have wanted a
nit
-style "less thanwarn
" level for different reasons1 and I'm somewhat worried that havingnit
be used for this purpose would hinder the design of such a thing in the future. A design that explicitly declares thatCARGO_BUILD_NITS=deny
is never going to be a thing would definitely be a problem for a feature that lets you tweak lint UX for some lints but not others.Yes, but I'm not convinced this solves the problem for enough of the people who have it. As mentioned before there's a huge diversity of attitudes towards linting and I'm skeptical this is sufficient for them.
And to be clear, I'm not talking about other lint modality problems like "deny only when releasing" or "deny only when testing" as I was elsewhere, I'm talking about the problems listed in the motivation here.
Furthermore as @Lokathor mentioned, partial solutions do often legitimately hinder full solutions in the future.
No, that's not what I'm talking about here. This is in the context of PR-integrated CI systems (like "show errors on PRs", e.g. clippy-action)
This RFC proposes a feature that is useful if IDEs pick it up (and then, that's only useful to people using IDEs!), or if PR-integrated CI tooling systems pick it up. The CI thing isn't simply a use of
RUSTFLAGS
because for this to be surfaced to the user (people don't read non-failing CI logs) it needs to do the thing where the warnings are slurped up and tagged on the PR diff using the GitHub API (or whatever).My current experience with that type of PR-integrated CI tooling is that it tends to work for simple things and often ends up needing much more configurability than is available when a codebase gets more complex. In which case you're kind of required to roll your own. Overall I don't see people doing that since it's a lot of work, instead when this happens folks just don't use the PR-integrated CI tooling, after all it's mostly just a QoL improvement: you can always figure out your lints from failing CI or a local run.
Thing is, if you're rolling your own anyway, then you can also fix the problems motivating this RFC by making the PR-integrated CI tool have a config for lints like this. That's relatively minor in the scheme of things when it comes to the amount of work involved in building one of these.
That's what I'm talking about when I say "it makes it a little easier". This RFC is predicated on PR-integrated CI tooling being built that consumes this, but you can do this today in clippy-action using its flag settings. This RFC doesn't really enable anything new for these systems, just a better convention that nicely integrates with
[lints]
. Which is valuable! It's far better for this to work with[lints]
than someclippy-action.toml
orclippy_flags: -Dfoo -Abar
or whatever since it's great for[lints]
to be a one-stop shop, and I really don't want to undersell the benefit of that!But! If a PR-integrated CI system doesn't work for me, I have to roll my own anyway, at which point this better convention doesn't buy me much in the scheme of things. That's what i mean by "it makes it a little easier", I'm talking about people for whom the existing PR-integrated CI systems don't work well. I don't think that's a small set of people at all, I've seen relatively low uptake of these systems, partially because of permissions issues, partly because of more bespoke build setups, etc.
It makes it a lot easier for people for whom this style of integrated CI tooling works well out of the box. I'm not convinced that's a lot of people.
Footnotes
Specifically, having a level that indicates "different, less noisy UX", for example collapsing all lints down into a small report, or perhaps making them oneliners. Basically changing how they get reported. Which is kind of what's going on with the r-a and CI proposals here, but there are a lot more different ways the UX can be tweaked around. ↩
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.
Specifically about this: my point here is to highlight that users who care about replicable CI, users who don't use IDEs, and users for whom PR-integrated CI doesn't work are not covered by this RFC, and if we are being intentionally excluded from the solutions here, that is fine, but that intention needs to be there, and when the lang team makes a call on this they should be aware, and attempt to understand what percentage of the target audience of this RFC's motivation is intentionally excluded from the solution.
I don't want it to fly under the radar that these groups are not going to benefit from this RFC. You're absolutely right that we sometimes intentionally don't solve problems for every user. However, we actually do have to be intentional about it.
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.
Posted in a different thread on this RFC (please continue discussion on it there), but I sketched up a design for a solution that extends to this https://hackmd.io/@Manishearth/BJi8K8AGJg
I do still want a
nit
level but with the goal ofnit
being a different lint UX, not for solving the blocking/non-blocking problem. My design does not particularly preclude anit
level in the future.