Skip to content

Commit 0e3f0e5

Browse files
committed
Documentation: rust: discuss #[expect(...)] in the guidelines
commit 0486649 upstream. Discuss `#[expect(...)]` in the Lints sections of the coding guidelines document, which is an upcoming feature in Rust 1.81.0, and explain that it is generally to be preferred over `allow` unless there is a reason not to use it (e.g. conditional compilation being involved). Tested-by: Gary Guo <[email protected]> Reviewed-by: Gary Guo <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent 0a16e50 commit 0e3f0e5

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

Documentation/rust/coding-guidelines.rst

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,116 @@ default (i.e. outside ``W=`` levels). In particular, those that may have some
262262
false positives but that are otherwise quite useful to keep enabled to catch
263263
potential mistakes.
264264

265+
On top of that, Rust provides the ``expect`` attribute which takes this further.
266+
It makes the compiler warn if the warning was not produced. For instance, the
267+
following will ensure that, when ``f()`` is called somewhere, we will have to
268+
remove the attribute:
269+
270+
.. code-block:: rust
271+
272+
#[expect(dead_code)]
273+
fn f() {}
274+
275+
If we do not, we get a warning from the compiler::
276+
277+
warning: this lint expectation is unfulfilled
278+
--> x.rs:3:10
279+
|
280+
3 | #[expect(dead_code)]
281+
| ^^^^^^^^^
282+
|
283+
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
284+
285+
This means that ``expect``\ s do not get forgotten when they are not needed, which
286+
may happen in several situations, e.g.:
287+
288+
- Temporary attributes added while developing.
289+
290+
- Improvements in lints in the compiler, Clippy or custom tools which may
291+
remove a false positive.
292+
293+
- When the lint is not needed anymore because it was expected that it would be
294+
removed at some point, such as the ``dead_code`` example above.
295+
296+
It also increases the visibility of the remaining ``allow``\ s and reduces the
297+
chance of misapplying one.
298+
299+
Thus prefer ``except`` over ``allow`` unless:
300+
301+
- The lint attribute is intended to be temporary, e.g. while developing.
302+
303+
- Conditional compilation triggers the warning in some cases but not others.
304+
305+
If there are only a few cases where the warning triggers (or does not
306+
trigger) compared to the total number of cases, then one may consider using
307+
a conditional ``expect`` (i.e. ``cfg_attr(..., expect(...))``). Otherwise,
308+
it is likely simpler to just use ``allow``.
309+
310+
- Inside macros, when the different invocations may create expanded code that
311+
triggers the warning in some cases but not in others.
312+
313+
- When code may trigger a warning for some architectures but not others, such
314+
as an ``as`` cast to a C FFI type.
315+
316+
As a more developed example, consider for instance this program:
317+
318+
.. code-block:: rust
319+
320+
fn g() {}
321+
322+
fn main() {
323+
#[cfg(CONFIG_X)]
324+
g();
325+
}
326+
327+
Here, function ``g()`` is dead code if ``CONFIG_X`` is not set. Can we use
328+
``expect`` here?
329+
330+
.. code-block:: rust
331+
332+
#[expect(dead_code)]
333+
fn g() {}
334+
335+
fn main() {
336+
#[cfg(CONFIG_X)]
337+
g();
338+
}
339+
340+
This would emit a lint if ``CONFIG_X`` is set, since it is not dead code in that
341+
configuration. Therefore, in cases like this, we cannot use ``expect`` as-is.
342+
343+
A simple possibility is using ``allow``:
344+
345+
.. code-block:: rust
346+
347+
#[allow(dead_code)]
348+
fn g() {}
349+
350+
fn main() {
351+
#[cfg(CONFIG_X)]
352+
g();
353+
}
354+
355+
An alternative would be using a conditional ``expect``:
356+
357+
.. code-block:: rust
358+
359+
#[cfg_attr(not(CONFIG_X), expect(dead_code))]
360+
fn g() {}
361+
362+
fn main() {
363+
#[cfg(CONFIG_X)]
364+
g();
365+
}
366+
367+
This would ensure that, if someone introduces another call to ``g()`` somewhere
368+
(e.g. unconditionally), then it would be spotted that it is not dead code
369+
anymore. However, the ``cfg_attr`` is more complex than a simple ``allow``.
370+
371+
Therefore, it is likely that it is not worth using conditional ``expect``\ s when
372+
more than one or two configurations are involved or when the lint may be
373+
triggered due to non-local changes (such as ``dead_code``).
374+
265375
For more information about diagnostics in Rust, please see:
266376

267377
https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html

0 commit comments

Comments
 (0)