6.9 KiB
Define New Lints
The first step in the journey of a new lint is the definition and registration of the lint in Clippy's codebase. We can use the Clippy dev tools to handle this step since setting up the lint involves some boilerplate code.
In our example, we're going to create a lint to detect functions named foo because it is a highly non-descriptive
name for a function, so we want to trigger this and fix it early in the development process.
Lint name
A good lint name is important, make sure to check the lint naming guidelines. Don't worry, if the lint name doesn't fit, a Clippy team member will alert you in the PR process.
We'll name our example lint that detects functions named "foo" foo_functions. Check the
lint naming guidelines to see why this name makes sense.
Add and Register the Lint
Now that a name is chosen, we shall register foo_functions as a lint to the codebase.
There are two ways to register a lint.
Standalone
If you believe that this new lint is a standalone lint (that doesn't belong to any specific type like functions or loops), you can run the following
command in your Clippy project:
$ cargo dev new_lint --name=lint_name --pass=late --category=pedantic
There are two things to note here:
- We set
--pass=latein this command to do a late lint pass. The alternative is anearlylint pass. We will discuss this difference in a later chapter.
- If not provided, the
categoryof this new lint will default tonursery.
The cargo dev new_lint command will create a new file: clippy_lints/src/foo_functions.rs
as well as register the lint.
Overall, you should notice that the following files are modified or created:
$ git status
On branch foo_functions
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: CHANGELOG.md
modified: clippy_lints/src/lib.register_lints.rs
modified: clippy_lints/src/lib.register_pedantic.rs
modified: clippy_lints/src/lib.rs
Untracked files:
(use "git add <file>..." to include in what will be committed)
clippy_lints/src/foo_functions.rs
tests/ui/foo_functions.rs
Specific Type
Note
: Lint types are listed in the "Lint types" section
If you believe that this new lint belongs to a specific type of lints,
you can run cargo dev new_lint with a --type option.
Since our foo_functions lint is related to function calls, one could
argue that we should put it into a group of lints that detect some behaviors
of functions, we can put it in the functions group.
Let's run the following command in your Clippy project:
$ cargo dev new_lint --name=foo_functions --type=functions --category=pedantic
This command will create, among other things, a new file:
clippy_lints/src/{type}/foo_functions.rs.
In our case, the path will be clippy_lints/src/functions/foo_functions.rs.
Notice how this command has a --type flag instead of --pass. Unlike a standalone
definition, this lint won't be registered in the traditional sense. Instead, you will
call your lint from within the type's lint pass, found in clippy_lints/src/{type}/mod.rs.
A type is just the name of a directory in clippy_lints/src, like functions in
the example command. Clippy groups together some lints that share common behaviors,
so if your lint falls into one, it would be best to add it to that type.
Read more about lint types below.
Overall, you should notice that the following files are modified or created:
$ git status
On branch foo_functions
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: CHANGELOG.md
modified: clippy_lints/src/declared_lints.rs
modified: clippy_lints/src/functions/mod.rs
Untracked files:
(use "git add <file>..." to include in what will be committed)
clippy_lints/src/functions/foo_functions.rs
tests/ui/foo_functions.rs
Lint types
As of the writing of this documentation update, there are 12 groups (a.k.a. types)
of lints besides the numerous standalone lints living under clippy_lints/src/:
cargocastsfunctionsloopsmatchesmethodsmisc_earlyoperatorstransmutetypesunit_typesutils / internal(Clippy internal lints)
These categories group together lints that share some common behaviors.
For instance, as we have mentioned earlier, functions groups together lints
that deal with some aspects of function calls in Rust.
For more information, feel free to compare the lint files under any category with All Clippy lints or ask one of the maintainers.
The define_clippy_lints macro
After cargo dev new_lint, you should see a macro with the name define_clippy_lints. It will be in the same file if you defined a standalone lint, and it will be in mod.rs if you defined a type-specific lint.
The macro looks something like this:
declare_clippy_lint! {
/// ### What it does
///
/// // Describe here what does the lint do.
///
/// Triggers when detects...
///
/// ### Why is this bad?
///
/// // Describe why this pattern would be bad
///
/// It can lead to...
///
/// ### Example
/// ```rust
/// // example code where clippy issues a warning
/// ```
/// Use instead:
/// ```rust
/// // example code which does not raise clippy warning
/// ```
#[clippy::version = "1.70.0"] // <- In which version was this implemented, keep it up to date!
pub LINT_NAME, // <- The lint name IN_ALL_CAPS
pedantic, // <- The lint group
"default lint description" // <- A lint description, e.g. "A function has an unit return type."
}
Lint registration
If we run the cargo dev new_lint command for a new lint,
the lint will be automatically registered and there is nothing more to do.
However, sometimes we might want to declare a new lint by hand.
In this case, we'd use cargo dev update_lints command afterwards.
When a lint is manually declared, we might need to register the lint pass
manually in the register_plugins function in clippy_lints/src/lib.rs:
store.register_late_pass(|| Box::new(foo_functions::FooFunctions));
As you might have guessed, where there's something late, there is something early:
in Clippy there is a register_early_pass method as well.
More on early vs. late passes in a later chapter.
Without a call to one of register_early_pass or register_late_pass,
the lint pass in question will not be run.