diff --git a/src/attributes.md b/src/attributes.md index 9ce8166db..16a72a098 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -222,7 +222,7 @@ The following is an index of all built-in attributes. - [`proc_macro_derive`] — Defines a derive macro. - [`proc_macro_attribute`] — Defines an attribute macro. - Diagnostics - - [`allow`], [`warn`], [`deny`], [`forbid`] — Alters the default lint level. + - [`allow`], [`expect`], [`warn`], [`deny`], [`forbid`] — Alters the default lint level. - [`deprecated`] — Generates deprecation notices. - [`must_use`] — Generates a lint for unused values. - [`diagnostic::on_unimplemented`] — Hints the compiler to emit a certain error @@ -303,6 +303,7 @@ The following is an index of all built-in attributes. [`deprecated`]: attributes/diagnostics.md#the-deprecated-attribute [`derive`]: attributes/derive.md [`export_name`]: abi.md#the-export_name-attribute +[`expect`]: attributes/diagnostics.md#lint-check-attributes [`forbid`]: attributes/diagnostics.md#lint-check-attributes [`global_allocator`]: runtime.md#the-global_allocator-attribute [`ignore`]: attributes/testing.md#the-ignore-attribute diff --git a/src/attributes/diagnostics.md b/src/attributes/diagnostics.md index c636a96cc..e9b67267a 100644 --- a/src/attributes/diagnostics.md +++ b/src/attributes/diagnostics.md @@ -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")] + let mut file_name = PathBuf::from("git"); + + #[cfg(target_os = "windows")] + file_name.set_extension("exe"); + + file_name +} +``` + +### 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