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