-
Notifications
You must be signed in to change notification settings - Fork 533
Document new #[expect]
attribute and reasons
parameter (RFC 2383)
#1237
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
Changes from all commits
1cc29b6
c76dce5
f454c0f
a1b095a
70e746d
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 |
---|---|---|
|
@@ -7,17 +7,20 @@ messages during compilation. | |
|
||
A lint check names a potentially undesirable coding pattern, such as | ||
unreachable code or omitted documentation. The lint attributes `allow`, | ||
`warn`, `deny`, and `forbid` use the [_MetaListPaths_] syntax to specify a | ||
list of lint names to change the lint level for the entity to which the | ||
attribute applies. | ||
`expect`, `warn`, `deny`, and `forbid` use the [_MetaListPaths_] syntax | ||
to specify a list of lint names to change the lint level for the entity | ||
to which the attribute applies. | ||
|
||
For any lint check `C`: | ||
|
||
* `allow(C)` overrides the check for `C` so that violations will go | ||
unreported, | ||
* `warn(C)` warns about violations of `C` but continues compilation. | ||
* `deny(C)` signals an error after encountering a violation of `C`, | ||
* `forbid(C)` is the same as `deny(C)`, but also forbids changing the lint | ||
* `#[allow(C)]` overrides the check for `C` so that violations will go | ||
unreported. | ||
* `#[expect(C)]` indicates that lint `C` is expected to be emitted. The | ||
attribute will suppress the emission of `C` or issue a warning, if the | ||
expectation is unfulfilled. | ||
* `#[warn(C)]` warns about violations of `C` but continues compilation. | ||
* `#[deny(C)]` signals an error after encountering a violation of `C`, | ||
* `#[forbid(C)]` is the same as `deny(C)`, but also forbids changing the lint | ||
level afterwards, | ||
|
||
> Note: The lint checks supported by `rustc` can be found via `rustc -W help`, | ||
|
@@ -66,8 +69,8 @@ pub mod m2 { | |
} | ||
``` | ||
|
||
This example shows how one can use `forbid` to disallow uses of `allow` for | ||
that lint check: | ||
This example shows how one can use `forbid` to disallow uses of `allow` or | ||
`expect` for that lint check: | ||
|
||
```rust,compile_fail | ||
#[forbid(missing_docs)] | ||
|
@@ -83,6 +86,124 @@ pub mod m3 { | |
> [command-line][rustc-lint-cli], and also supports [setting | ||
> caps][rustc-lint-caps] on the lints that are reported. | ||
|
||
### Lint Reasons | ||
|
||
All lint attributes support an additional `reason` parameter, to give context why | ||
a certain attribute was added. This reason will be displayed as part of the lint | ||
message if the lint is emitted at the defined level. | ||
|
||
```rust,edition2015,compile_fail | ||
// `keyword_idents` is allowed by default. Here we deny it to | ||
// avoid migration of identifiers when we update the edition. | ||
#![deny( | ||
keyword_idents, | ||
reason = "we want to avoid these idents to be future compatible" | ||
)] | ||
|
||
// This name was allowed in Rust's 2015 edition. We still aim to avoid | ||
// this to be future compatible and not confuse end users. | ||
fn dyn() {} | ||
``` | ||
|
||
Here is another example, where the lint is allowed with a reason: | ||
|
||
```rust | ||
use std::path::PathBuf; | ||
|
||
pub fn get_path() -> PathBuf { | ||
// The `reason` parameter on `allow` attributes acts as documentation for the reader. | ||
#[allow(unused_mut, reason = "this is only modified on some platforms")] | ||
xFrednet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let mut file_name = PathBuf::from("git"); | ||
|
||
#[cfg(target_os = "windows")] | ||
file_name.set_extension("exe"); | ||
|
||
file_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. I think it might help to include a second example where the #![deny(
unsafe_op_in_unsafe_fn,
reason = "using explicit unsafe blocks can help us better review where unsafe operations are being used"
)]
# use std::num::FpCategory;
pub unsafe fn partial_classify(value: f32) -> FpCategory {
const EXP_MASK: u32 = 0x7f800000;
const MAN_MASK: u32 = 0x007fffff;
// ERROR: This function call should be wrapped in an unsafe block.
// The error message will include the `reason` message so that the author
// knows why it is not allowed.
let b = std::mem::transmute::<f32, u32>(value);
match (b & MAN_MASK, b & EXP_MASK) {
(0, 0) => FpCategory::Zero,
(_, 0) => FpCategory::Subnormal,
_ => FpCategory::Normal,
}
} It's tough to come up with a realistic example that isn't too large. I stole this one from the standard library, but if you have a better example, that would be good. 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 add another example in the next commit :) |
||
### The `#[expect]` attribute | ||
|
||
The `#[expect(C)]` attribute creates a lint expectation for lint `C`. The | ||
expectation will be fulfilled, if a `#[warn(C)]` attribute at the same location | ||
would result in a lint emission. If the expectation is unfulfilled, because | ||
lint `C` would not be emitted, the `unfulfilled_lint_expectations` lint will | ||
be emitted at the attribute. | ||
|
||
```rust | ||
fn main() { | ||
// This `#[expect]` attribute creates a lint expectation, that the `unused_variables` | ||
// lint would be emitted by the following statement. This expectation is | ||
// unfulfilled, since the `question` variable is used by the `println!` macro. | ||
// Therefore, the `unfulfilled_lint_expectations` lint will be emitted at the | ||
// attribute. | ||
#[expect(unused_variables)] | ||
let question = "who lives in a pineapple under the sea?"; | ||
println!("{question}"); | ||
|
||
// This `#[expect]` attribute creates a lint expectation that will be fulfilled, since | ||
// the `answer` variable is never used. The `unused_variables` lint, that would usually | ||
// be emitted, is suppressed. No warning will be issued for the statement or attribute. | ||
#[expect(unused_variables)] | ||
let answer = "SpongeBob SquarePants!"; | ||
} | ||
``` | ||
|
||
The lint expectation is only fulfilled by lint emissions which have been suppressed by | ||
the `expect` attribute. If the lint level is modified in the scope with other level | ||
attributes like `allow` or `warn`, the lint emission will be handled accordingly and the | ||
expectation will remain unfulfilled. | ||
|
||
```rust | ||
#[expect(unused_variables)] | ||
fn select_song() { | ||
// This will emit the `unused_variables` lint at the warn level | ||
// as defined by the `warn` attribute. This will not fulfill the | ||
// expectation above the function. | ||
#[warn(unused_variables)] | ||
let song_name = "Crab Rave"; | ||
|
||
// The `allow` attribute suppresses the lint emission. This will not | ||
// fulfill the expectation as it has been suppressed by the `allow` | ||
// attribute and not the `expect` attribute above the function. | ||
#[allow(unused_variables)] | ||
let song_creator = "Noisestorm"; | ||
|
||
// This `expect` attribute will suppress the `unused_variables` lint emission | ||
// at the variable. The `expect` attribute above the function will still not | ||
// be fulfilled, since this lint emission has been suppressed by the local | ||
// expect attribute. | ||
#[expect(unused_variables)] | ||
let song_version = "Monstercat Release"; | ||
} | ||
``` | ||
|
||
If the `expect` attribute contains several lints, each one is expected separately. For a | ||
lint group it's enough if one lint inside the group has been emitted: | ||
|
||
```rust | ||
// This expectation will be fulfilled by the unused value inside the function | ||
// since the emitted `unused_variables` lint is inside the `unused` lint group. | ||
#[expect(unused)] | ||
pub fn thoughts() { | ||
let unused = "I'm running out of examples"; | ||
} | ||
|
||
pub fn another_example() { | ||
// This attribute creates two lint expectations. The `unused_mut` lint will be | ||
// suppressed and with that fulfill the first expectation. The `unused_variables` | ||
// wouldn't be emitted, since the variable is used. That expectation will therefore | ||
// be unsatisfied, and a warning will be emitted. | ||
#[expect(unused_mut, unused_variables)] | ||
let mut link = "https://www.rust-lang.org/"; | ||
|
||
println!("Welcome to our community: {link}"); | ||
} | ||
``` | ||
|
||
> Note: The behavior of `#[expect(unfulfilled_lint_expectations)]` is currently | ||
> defined to always generate the `unfulfilled_lint_expectations` lint. | ||
|
||
### Lint groups | ||
|
||
Lints may be organized into named groups so that the level of related lints | ||
|
Uh oh!
There was an error while loading. Please reload this page.