Skip to content

Commit 5062e74

Browse files
authored
Merge pull request #1237 from xFrednet/rfc-2383-document-lint-reason-changes
Document new `#[expect]` attribute and `reasons` parameter (RFC 2383)
2 parents 3b68105 + 70e746d commit 5062e74

File tree

2 files changed

+133
-11
lines changed

2 files changed

+133
-11
lines changed

src/attributes.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ The following is an index of all built-in attributes.
222222
- [`proc_macro_derive`] — Defines a derive macro.
223223
- [`proc_macro_attribute`] — Defines an attribute macro.
224224
- Diagnostics
225-
- [`allow`], [`warn`], [`deny`], [`forbid`] — Alters the default lint level.
225+
- [`allow`], [`expect`], [`warn`], [`deny`], [`forbid`] — Alters the default lint level.
226226
- [`deprecated`] — Generates deprecation notices.
227227
- [`must_use`] — Generates a lint for unused values.
228228
- [`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.
303303
[`deprecated`]: attributes/diagnostics.md#the-deprecated-attribute
304304
[`derive`]: attributes/derive.md
305305
[`export_name`]: abi.md#the-export_name-attribute
306+
[`expect`]: attributes/diagnostics.md#lint-check-attributes
306307
[`forbid`]: attributes/diagnostics.md#lint-check-attributes
307308
[`global_allocator`]: runtime.md#the-global_allocator-attribute
308309
[`ignore`]: attributes/testing.md#the-ignore-attribute

src/attributes/diagnostics.md

+131-10
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@ messages during compilation.
77

88
A lint check names a potentially undesirable coding pattern, such as
99
unreachable code or omitted documentation. The lint attributes `allow`,
10-
`warn`, `deny`, and `forbid` use the [_MetaListPaths_] syntax to specify a
11-
list of lint names to change the lint level for the entity to which the
12-
attribute applies.
10+
`expect`, `warn`, `deny`, and `forbid` use the [_MetaListPaths_] syntax
11+
to specify a list of lint names to change the lint level for the entity
12+
to which the attribute applies.
1313

1414
For any lint check `C`:
1515

16-
* `allow(C)` overrides the check for `C` so that violations will go
17-
unreported,
18-
* `warn(C)` warns about violations of `C` but continues compilation.
19-
* `deny(C)` signals an error after encountering a violation of `C`,
20-
* `forbid(C)` is the same as `deny(C)`, but also forbids changing the lint
16+
* `#[allow(C)]` overrides the check for `C` so that violations will go
17+
unreported.
18+
* `#[expect(C)]` indicates that lint `C` is expected to be emitted. The
19+
attribute will suppress the emission of `C` or issue a warning, if the
20+
expectation is unfulfilled.
21+
* `#[warn(C)]` warns about violations of `C` but continues compilation.
22+
* `#[deny(C)]` signals an error after encountering a violation of `C`,
23+
* `#[forbid(C)]` is the same as `deny(C)`, but also forbids changing the lint
2124
level afterwards,
2225

2326
> Note: The lint checks supported by `rustc` can be found via `rustc -W help`,
@@ -66,8 +69,8 @@ pub mod m2 {
6669
}
6770
```
6871

69-
This example shows how one can use `forbid` to disallow uses of `allow` for
70-
that lint check:
72+
This example shows how one can use `forbid` to disallow uses of `allow` or
73+
`expect` for that lint check:
7174

7275
```rust,compile_fail
7376
#[forbid(missing_docs)]
@@ -83,6 +86,124 @@ pub mod m3 {
8386
> [command-line][rustc-lint-cli], and also supports [setting
8487
> caps][rustc-lint-caps] on the lints that are reported.
8588
89+
### Lint Reasons
90+
91+
All lint attributes support an additional `reason` parameter, to give context why
92+
a certain attribute was added. This reason will be displayed as part of the lint
93+
message if the lint is emitted at the defined level.
94+
95+
```rust,edition2015,compile_fail
96+
// `keyword_idents` is allowed by default. Here we deny it to
97+
// avoid migration of identifiers when we update the edition.
98+
#![deny(
99+
keyword_idents,
100+
reason = "we want to avoid these idents to be future compatible"
101+
)]
102+
103+
// This name was allowed in Rust's 2015 edition. We still aim to avoid
104+
// this to be future compatible and not confuse end users.
105+
fn dyn() {}
106+
```
107+
108+
Here is another example, where the lint is allowed with a reason:
109+
110+
```rust
111+
use std::path::PathBuf;
112+
113+
pub fn get_path() -> PathBuf {
114+
// The `reason` parameter on `allow` attributes acts as documentation for the reader.
115+
#[allow(unused_mut, reason = "this is only modified on some platforms")]
116+
let mut file_name = PathBuf::from("git");
117+
118+
#[cfg(target_os = "windows")]
119+
file_name.set_extension("exe");
120+
121+
file_name
122+
}
123+
```
124+
125+
### The `#[expect]` attribute
126+
127+
The `#[expect(C)]` attribute creates a lint expectation for lint `C`. The
128+
expectation will be fulfilled, if a `#[warn(C)]` attribute at the same location
129+
would result in a lint emission. If the expectation is unfulfilled, because
130+
lint `C` would not be emitted, the `unfulfilled_lint_expectations` lint will
131+
be emitted at the attribute.
132+
133+
```rust
134+
fn main() {
135+
// This `#[expect]` attribute creates a lint expectation, that the `unused_variables`
136+
// lint would be emitted by the following statement. This expectation is
137+
// unfulfilled, since the `question` variable is used by the `println!` macro.
138+
// Therefore, the `unfulfilled_lint_expectations` lint will be emitted at the
139+
// attribute.
140+
#[expect(unused_variables)]
141+
let question = "who lives in a pineapple under the sea?";
142+
println!("{question}");
143+
144+
// This `#[expect]` attribute creates a lint expectation that will be fulfilled, since
145+
// the `answer` variable is never used. The `unused_variables` lint, that would usually
146+
// be emitted, is suppressed. No warning will be issued for the statement or attribute.
147+
#[expect(unused_variables)]
148+
let answer = "SpongeBob SquarePants!";
149+
}
150+
```
151+
152+
The lint expectation is only fulfilled by lint emissions which have been suppressed by
153+
the `expect` attribute. If the lint level is modified in the scope with other level
154+
attributes like `allow` or `warn`, the lint emission will be handled accordingly and the
155+
expectation will remain unfulfilled.
156+
157+
```rust
158+
#[expect(unused_variables)]
159+
fn select_song() {
160+
// This will emit the `unused_variables` lint at the warn level
161+
// as defined by the `warn` attribute. This will not fulfill the
162+
// expectation above the function.
163+
#[warn(unused_variables)]
164+
let song_name = "Crab Rave";
165+
166+
// The `allow` attribute suppresses the lint emission. This will not
167+
// fulfill the expectation as it has been suppressed by the `allow`
168+
// attribute and not the `expect` attribute above the function.
169+
#[allow(unused_variables)]
170+
let song_creator = "Noisestorm";
171+
172+
// This `expect` attribute will suppress the `unused_variables` lint emission
173+
// at the variable. The `expect` attribute above the function will still not
174+
// be fulfilled, since this lint emission has been suppressed by the local
175+
// expect attribute.
176+
#[expect(unused_variables)]
177+
let song_version = "Monstercat Release";
178+
}
179+
```
180+
181+
If the `expect` attribute contains several lints, each one is expected separately. For a
182+
lint group it's enough if one lint inside the group has been emitted:
183+
184+
```rust
185+
// This expectation will be fulfilled by the unused value inside the function
186+
// since the emitted `unused_variables` lint is inside the `unused` lint group.
187+
#[expect(unused)]
188+
pub fn thoughts() {
189+
let unused = "I'm running out of examples";
190+
}
191+
192+
pub fn another_example() {
193+
// This attribute creates two lint expectations. The `unused_mut` lint will be
194+
// suppressed and with that fulfill the first expectation. The `unused_variables`
195+
// wouldn't be emitted, since the variable is used. That expectation will therefore
196+
// be unsatisfied, and a warning will be emitted.
197+
#[expect(unused_mut, unused_variables)]
198+
let mut link = "https://www.rust-lang.org/";
199+
200+
println!("Welcome to our community: {link}");
201+
}
202+
```
203+
204+
> Note: The behavior of `#[expect(unfulfilled_lint_expectations)]` is currently
205+
> defined to always generate the `unfulfilled_lint_expectations` lint.
206+
86207
### Lint groups
87208

88209
Lints may be organized into named groups so that the level of related lints

0 commit comments

Comments
 (0)