Use cases for #[expect]
A collection of use cases for the #[expect]
attribute as proposed in RFC-2383
Use case 1: Allow lints during development
- Description: A user wants to add a new feature to a project, which spans over multiple functions. During development, they wish to suppress lints, which will be resolved in the end. They intend to use
#[expect]
to be notified when the lint is no longer triggered.
- Preconditions:
- The user has a project that compiles without any lint triggers
- Events:
- The user adds a new function, which does some calculations for the feature:
- The compiler issues the
dead_code
lint for the (currently) unused function.
- The user adds the
#[expect(dead_code)]
attribute to the function, to keep the compiler output clean.
- The user continues development.
- The user finally uses the
calc_magic
function.
- The compiler issues a warning, that the lint expectation for
dead_code
is no longer fulfilled.
- The user removes the expectation from the function.
- Other options:
- The user could use
#[allow(dead_code)]
on the function, but they might forget to remove the attribute later
- The user could just keep the
dead_code
warning, but this makes the compiler output noisy during development
- Note: This example is very close to the one used in RFC-2383 as the motivation for the attribute.
Use case 2: Expect a false positive
- Description: A user wants to suppress a false positive(FP) lint trigger until the FP has been fixed.
- Preconditions:
- A lint has a FP that triggers on a specific code snippet.
- The lint is usually helpful and should only be allowed locally until the FP has been fixed.
- Events:
- The FP triggers on a code snippet.
- The user uses
#[expect(lint, reason="FP tool#101010")]
on the specific statement.
- The lint expectation now suppresses the FP.
- The FP gets fixed.
- The user uses the new toolchain, with the new fix.
- The compiler warns the user about the unfulfilled lint expectation.
- The user removes the
#[expect]
attribute.
- Other options
- The user could instead use
#[allow]
. However, this would keep the lint silent, even if the FP has been fixed. Modifications to the statement, that violate the lint, would not be caught as a result.
- Description: A user wants to add a lint expectation for a lint from a tool
- Preconditions:
- The user uses a tool, like Clippy, to check lint their project.
- The tool issues a warning of the
tool::exmaple
lint on one statement
- The user wishes to expect the lint on the statement, until it is fixed
- Events:
- The compiler issues no warnings.
- The tool issues one
tool::example
warning.
- The user adds
#[expect(tool::example)]
to the offending code.
- The tool no longer issues any warnings.
- The compiler still issues no warnings.
- Other options
- The compiler could issue a warning, if
tool::example
is unknown or not issued. However, this would add a lot of noise to the output. Ignoring unregistered lints should ignore the expectation, similar to how #[warn(tool::example)]
has no effect if rustc runs without the tool.
- The user could use
#[allow(tool::example)]
, but they prefer to use #[expect]
here.
Use case 4: Suppress lints from CI
- Description: A user wants to expect the
clippy::todo
lint that is only enabled in the CI.
- Preconditions:
- A project uses Clippy in their CI, to prevent accidental merges of code containing the
todo!()
macro by denying the clippy::todo
lint.
- Locally, the
clippy::todo
lint is allowed, to enable the use of the todo!()
macro during development.
- A user has developed a feature and one small component, is blocked by a different team.
- Events:
- The user developed a feature and adds a
todo!()
macro for the part that is waiting on the other team.
- Clippy runs locally without any warnings.
- The user pushes the code and creates a PR.
- The CI warns about the presence of the
todo!()
macro.
- The user, in agreement with the reviewer, adds a lint expectation, to this
todo!()
macro, to unblock the PR.
- Clippy passes locally and in the CI.
- The reviewer approves the PR and merges the code into master.
- Other options
- The user could use
#[allow]
, but the usage of the expectation emphasizes that the attribute should be removed, once the todo!()
has been resolved
- Notes
- Restricting some lints to only run in the CI, is also useful for performance intensive or platform-specific lints
Use case 5: Incremental removal of #[expect]
- Description: A user wants to remove a
#![expect(unused)]
attribute incrementally, to limit the number of warnings generated at a time.
- Preconditions:
- A user has used
#![expect(unused)]
in a module during development. Removing the attribute stops the suppression of 20+ warnings.
- Events:
- The user adds
#[warn(unused)]
to an item in the module.
- The compiler now emits 5 warnings, which are no longer suppressed by the lint expectation.
- The user resolves the emitted warnings.
- The user removes the
#[warn(unsued)]
again from the item.
- The user continues using
#[warn]
attributes to incrementally resolve the issues.
- The compiler emits a warning, that the lint expectation is no longer fulfilled.
- The user removes the
#![expect(unused)]
attribute.
- Variations
- The user could remove the
#![expect(unused)]
attribute directly and move it to the sub items. This would still be incremental.
Use case 6: --force-warn
for an expected lint
- Description: A user wants to check all
clippy::todo
lint triggers, before a release.
- Preconditions:
- The project has some
todo!()
macros, which are marked with #[expect(clippy::todo)]
.
- The user also wishes to include expected lint triggers in the manual check.
- Events:
- The user runs
c clippy -- --force-warn clippy::todo
.
- The expected lints are included in the report, the expectations are still marked as fulfilled.
- The user manually checks the list of lint triggers.
- Notes
- The expected lint triggers could be excluded from
--force-warn
. However, this would make the #[expect]
attribute work differently from other level attributes and is probably not what the user wishes for.
- The expectations could be marked as unfulfilled, since the lint was issued as a warning. However, in this case it would make the output quite noisy, and the lint was still triggered in the expected scope. Therefore, it makes sense to mark the expectation as fulfilled.