mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-05-15 12:12:53 +00:00
Documentation: rust: discuss #[expect(...)]
in the guidelines
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 <gary@garyguo.net> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://lore.kernel.org/r/20240904204347.168520-19-ojeda@kernel.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
parent
1f9ed17254
commit
04866494e9
1 changed files with 110 additions and 0 deletions
|
@ -262,6 +262,116 @@ default (i.e. outside ``W=`` levels). In particular, those that may have some
|
||||||
false positives but that are otherwise quite useful to keep enabled to catch
|
false positives but that are otherwise quite useful to keep enabled to catch
|
||||||
potential mistakes.
|
potential mistakes.
|
||||||
|
|
||||||
|
On top of that, Rust provides the ``expect`` attribute which takes this further.
|
||||||
|
It makes the compiler warn if the warning was not produced. For instance, the
|
||||||
|
following will ensure that, when ``f()`` is called somewhere, we will have to
|
||||||
|
remove the attribute:
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn f() {}
|
||||||
|
|
||||||
|
If we do not, we get a warning from the compiler::
|
||||||
|
|
||||||
|
warning: this lint expectation is unfulfilled
|
||||||
|
--> x.rs:3:10
|
||||||
|
|
|
||||||
|
3 | #[expect(dead_code)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
|
||||||
|
|
||||||
|
This means that ``expect``\ s do not get forgotten when they are not needed, which
|
||||||
|
may happen in several situations, e.g.:
|
||||||
|
|
||||||
|
- Temporary attributes added while developing.
|
||||||
|
|
||||||
|
- Improvements in lints in the compiler, Clippy or custom tools which may
|
||||||
|
remove a false positive.
|
||||||
|
|
||||||
|
- When the lint is not needed anymore because it was expected that it would be
|
||||||
|
removed at some point, such as the ``dead_code`` example above.
|
||||||
|
|
||||||
|
It also increases the visibility of the remaining ``allow``\ s and reduces the
|
||||||
|
chance of misapplying one.
|
||||||
|
|
||||||
|
Thus prefer ``except`` over ``allow`` unless:
|
||||||
|
|
||||||
|
- The lint attribute is intended to be temporary, e.g. while developing.
|
||||||
|
|
||||||
|
- Conditional compilation triggers the warning in some cases but not others.
|
||||||
|
|
||||||
|
If there are only a few cases where the warning triggers (or does not
|
||||||
|
trigger) compared to the total number of cases, then one may consider using
|
||||||
|
a conditional ``expect`` (i.e. ``cfg_attr(..., expect(...))``). Otherwise,
|
||||||
|
it is likely simpler to just use ``allow``.
|
||||||
|
|
||||||
|
- Inside macros, when the different invocations may create expanded code that
|
||||||
|
triggers the warning in some cases but not in others.
|
||||||
|
|
||||||
|
- When code may trigger a warning for some architectures but not others, such
|
||||||
|
as an ``as`` cast to a C FFI type.
|
||||||
|
|
||||||
|
As a more developed example, consider for instance this program:
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
fn g() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(CONFIG_X)]
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
|
||||||
|
Here, function ``g()`` is dead code if ``CONFIG_X`` is not set. Can we use
|
||||||
|
``expect`` here?
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn g() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(CONFIG_X)]
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
|
||||||
|
This would emit a lint if ``CONFIG_X`` is set, since it is not dead code in that
|
||||||
|
configuration. Therefore, in cases like this, we cannot use ``expect`` as-is.
|
||||||
|
|
||||||
|
A simple possibility is using ``allow``:
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn g() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(CONFIG_X)]
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
|
||||||
|
An alternative would be using a conditional ``expect``:
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
#[cfg_attr(not(CONFIG_X), expect(dead_code))]
|
||||||
|
fn g() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(CONFIG_X)]
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
|
||||||
|
This would ensure that, if someone introduces another call to ``g()`` somewhere
|
||||||
|
(e.g. unconditionally), then it would be spotted that it is not dead code
|
||||||
|
anymore. However, the ``cfg_attr`` is more complex than a simple ``allow``.
|
||||||
|
|
||||||
|
Therefore, it is likely that it is not worth using conditional ``expect``\ s when
|
||||||
|
more than one or two configurations are involved or when the lint may be
|
||||||
|
triggered due to non-local changes (such as ``dead_code``).
|
||||||
|
|
||||||
For more information about diagnostics in Rust, please see:
|
For more information about diagnostics in Rust, please see:
|
||||||
|
|
||||||
https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html
|
https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html
|
||||||
|
|
Loading…
Add table
Reference in a new issue