@@ -256,3 +256,113 @@ By that virtue, it makes it possible to comfortably enable more diagnostics by
256
256
default (i.e. outside ``W= `` levels). In particular, those that may have some
257
257
false positives but that are otherwise quite useful to keep enabled to catch
258
258
potential 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