Pedantic Clippy

One of the reasons developers love Rust is its well-developed ecosystem. Clippy, a linter for the Rust code, is one of the main components in this ecosystem. It performs additional checks of the developed code reporting found issues and explaining how to fix them (and sometimes it even can fix them automatically). Its usage may be beneficial for Rust beginners and even professionals. In this article, I describe this tool and explain how to start using it.

Table of Contents

Introduction

Theory and practice should go hand in hand when you learn new things. That is why, when you learn new programming language, e.g., Rust, you should try the examples you read about. At the same time, you should not only repeat them but also change some things and see, how this influences the behavior of the compiler and your program. It is highly beneficial if during these experiments someone points on the issues in your code and explains how to fix them. This is where clippy shines. The usage of this tool may be very advantageous if you try to learn Rust on your own, without an external mentor, because it may teach you how to avoid common issues and write more idiomatic code. To my bad, I have discovered this tool just recently, and I decided to spend some time to understand how it works and how to use it.

First of all, in order to start using this tool you need to check if it is installed in your system. I have just checked on my Linux system, the instructions explaining how to install Rust also install clippy on your system by default. If you have already installed Rust and you use Linux, go to your $HOME/.cargo/bin directory (default directory where cargo binaries are installed) and check if it contains cargo-clippy (the path on Windows-based systems is different). If it is there, then clippy is installed on your system and ready to go. Also, you can try to execute cargo clippy -V in console and check the output. If the command exits correctly and reports clippy version, then your system is ready:

$ cargo clippy -V
clippy 0.1.54 (a178d03 2021-07-26)

If clippy is not present in your system, then try to install it using rustup:

$ rustup component add clippy

Example

In order to show that clippy improves your code, let’s consider the following completely artificial example:

use std::env;

fn equal_to_approx_pi(x: f32) -> bool {
    const PI: f32 = 3.14;
    if x == PI {
        return true;
    }
    return false;
}

fn main() {
    const PI: f32 = 3.14;
    println!("The approximate value of PI is {}", PI);

    let submitted_value = env::args()
        .nth(1).unwrap()
        .parse().unwrap();
    let equal = equal_to_approx_pi(submitted_value);
    if equal == true {
        println!("Equal!");
    } else {
        println!("Not equal!");
    }
}

This binary parses the first argument into a 32-bit float number, and compares it to approximate $\pi$ value in the equal_to_approx_pi function. If the value is equal to the approximate $\pi$ value, the binary prints Equal! and Not equal! otherwise. Note, in this example we simply use the unwrap method to get wrapped values without processing possible errors.

If we try to compile this program, the compiler will build it successfully without any error or warning:

$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.31s

This means that from the compiler point of view this is a correct program. But is it from a developer point of view? Obviously not, and clippy can help you to identify some “stinky” parts in your code (I put outputs under the spoiler tag, just click on the following line to see the output):

Clippy Output

$ cargo clippy
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
warning: unneeded `return` statement
--> src/main.rs:8:5
|
8 |     return false;
|     ^^^^^^^^^^^^^ help: remove `return`: `false`
|
= note: `#[warn(clippy::needless_return)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:4:21
|
4 |     const PI: f32 = 3.14;
|                     ^^^^
|
= note: `#[deny(clippy::approx_constant)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:12:21
|
12 |     const PI: f32 = 3.14;
|                     ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: equality checks against true are unnecessary
--> src/main.rs:19:8
|
19 |     if equal == true {
|        ^^^^^^^^^^^^^ help: try simplifying it as shown: `equal`
|
= note: `#[warn(clippy::bool_comparison)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison

error: aborting due to 2 previous errors; 2 warnings emitted

error: could not compile `clippy_test`

To learn more, run the command again with --verbose.

As you can see, even in this short listing clippy was able to spot three parts that “smell” – imagine how many errors it may find in a mid-size project.

Clippy Lints

Currently, clippy contains more than 450 different lints (according to my calculations, 491 as of September, 2021). One can say that this is not a large number comparing to the amount of checks in static analyzers for other programming languages, however due to the active community, this list constantly grows, and the tool becomes better with every iteration.

Every lint is assigned with a level that controls how clippy reacts if an issue, described by the lint, is found. There are three levels:

  • allow - clippy does not complain;
  • warning - clippy issues and displays a warning, the clippy process exits with zero success code;
  • deny - clippy issues and displays an error, the clippy process exits with non-zero error code.
Knowing exit code is very important if you develop CI pipelines. If a CI task generates an error, then the pipeline usually fails.

All the lints are divided into several categories, e.g., clippy::correctness or clippy::style. These categories are usually used to change the lints level in bulk instead of listing each of them. Semantically, they unite lints related to some set of similar issues. Table 1 contains the list of lints categories with the description, amount of lints (as of September, 2021) and their level (taken partially from the README file):

Table 1: Clippy Categories List
Category Level Amount Description
clippy::correctness deny 68 Code that is outright wrong or useless
clippy::suspicious warn 14 Code that is most likely wrong or useless
clippy::style warn 108 Code that should be written in a more idiomatic way
clippy::complexity warn 88 Code that does something simple but in a complex way
clippy::perf warn 21 Code that can be written to run faster
clippy::pedantic allow 92 Lints which are rather strict or might have false positives
clippy::restriction allow 57 Lints that apply additional restrictions on the code
clippy::nursery allow 22 New lints that are still under development
clippy::cargo allow 5 Lints for the cargo manifest
clippy::deprecated none 16 Lints that already deprecated and should not be used

There is one special category, clippy::all, that unites all the lints enabled by default. Clippy checks a crate against clippy::all lints if you run it without additional configurations. The example, considered in the previous section, exemplifies exactly this.

Lints Configuration

Some of the lints can be additionally configured. For instance, using the configuration you can set the list of blacklisted names, or change the maximum cognitive complexity level if your code is quite complex. Table 2 contain the list of all configuration values with their descriptions, types and in what lints they are used (as of September, 2021):

Table 2: Clippy Lint Configuration Values
Value / Description Type Lints
array-size-threshold

The maximum allowed size for arrays on the stack (defaults to `512000`)
u64
large_stack_arrays
large_const_arrays
avoid-breaking-exported-api

Suppress lints whenever the suggested change would cause breakage for other crates. (defaults to `true`)
bool
upper_case_acronyms
unnecessary_wraps
box_vec
trivially_copy_pass_by_ref
large_types_passed_by_value
option_option
enum_variant_names
rc_buffer
redundant_allocation
wrong_self_convention
vec_box
linkedlist
rc_mutex
blacklisted-names

The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (defaults to `["foo", "baz", "quux"]`)
Vec<String>
blacklisted_name
cognitive-complexity-threshold

The maximum cognitive complexity a function can have (defaults to `25`)
u64
cognitive_complexity
disallowed-methods

The list of disallowed methods, written as fully qualified paths. (defaults to `[]`)
Vec<String>
disallowed_method
disallowed-types

The list of disallowed types, written as fully qualified paths. (defaults to `[]`)
Vec<String>
disallowed_type
doc-valid-idents

The list of words this lint should not consider as identifiers needing ticks (defaults to `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`)
Vec<String>
doc_markdown
enforced-import-renames

:utils::conf::Rename>`: The list of imports to always rename, a fully qualified path followed by the rename. (defaults to `[]`)
Vec<crat
missing_enforced_import_renames
enum-variant-name-threshold

The minimum number of enum variants for the lints about variant names to trigger (defaults to `3`)
u64
enum_variant_names
enum-variant-size-threshold

The maximum size of an enum's variant to avoid box suggestion (defaults to `200`)
u64
large_enum_variant
literal-representation-threshold

The lower bound for linting decimal literals (defaults to `16384`)
u64
decimal_literal_representation
max-fn-params-bools

The maximum number of bool parameters a function can have (defaults to `3`)
u64
fn_params_excessive_bools
max-struct-bools

The maximum number of bool fields a struct can have (defaults to `3`)
u64
struct_excessive_bools
max-trait-bounds

The maximum number of bounds a trait can have to be linted (defaults to `3`)
u64
type_repetition_in_bounds
msrv

The minimum rust version that the project supports (defaults to `None`)
Option<String>
missing_const_for_fn
redundant_field_names
approx_constant
filter_map_next
checked_conversions
manual_strip
use_self
unnested_or_patterns
from_over_into
map_unwrap_or
manual_range_contains
mem_replace_with_default
option_as_ref_deref
manual_non_exhaustive
if_then_some_else_none
redundant_static_lifetimes
match_like_matches_macro
cloned_instead_of_copied
ptr_as_ptr
manual_split_once
manual_str_repeat
single-char-binding-names-threshold

The maximum number of single char bindings a scope may have (defaults to `4`)
u64
many_single_char_names
standard-macro-braces

:nonstandard_macro_braces::MacroMatcher>`: Enforce the named macros always use the braces specified.
Vec<crat
nonstandard_macro_braces
too-large-for-stack

The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap (defaults to `200`)
u64
useless_vec
boxed_local
too-many-arguments-threshold

The maximum number of argument a function or method can have (defaults to `7`)
u64
too_many_arguments
too-many-lines-threshold

The maximum number of lines a function or method can have (defaults to `100`)
u64
too_many_lines
trivial-copy-size-limit

The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (defaults to `None`)
Option<u64>
trivially_copy_pass_by_ref
type-complexity-threshold

The maximum complexity a type can have (defaults to `250`)
u64
type_complexity
unreadable-literal-lint-fractions

Should the fraction of a decimal be linted to include separators. (defaults to `true`)
bool
unreadable_literal
upper-case-acronyms-aggressive

Enables verbose mode. Triggers if there is more than one uppercase char next to each other (defaults to `false`)
bool
upper_case_acronyms
vec-box-size-threshold

The size of the boxed type in bytes, where boxing in a `Vec` is allowed (defaults to `4096`)
u64
vec_box
verbose-bit-mask-threshold

The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' (defaults to `1`)
u64
verbose_bit_mask
warn-on-all-wildcard-imports

Whether to allow certain wildcard imports (prelude, super in tests). (defaults to `false`)
bool
wildcard_imports

This additional configuration is specified in the clippy.toml or .clippy.toml files located in the root of your project in the form variable = value:

blacklisted-names = ["toto", "tata", "titi"]
cognitive-complexity-threshold = 30

Changing Clippy Behavior

You can influence on clippy behavior by adjusting the level of clippy lints. For instance, you may want to enable lints that are currently in development, or you may want to disable some particular checks. There are two ways of doing this:

  1. By modifying crate’s root source code file. Rust provides a set of attributes (allow, warn and deny) that you can use in the crate’s root.
  2. Through command line arguments. If you do not want to change source code, you can run clippy with special command line arguments that would enable (or disable) some lints.

Let’s consider these two options.

Source Code Based Approach

You can modify the level of a category or a particular lint on the crate level. In order to do this, in the crate’s main file (main.rs or lib.rs) add the attributes (allow, warn and deny) in the preamble (note the exclamation mark ! in the attribute). For instance, the following attribute, added in the beginning of the main.rs file, sets the level of cargo manifest lints (clippy::cargo category) to warn and allows the usage of approximate constants (clippy::approx_constant) withing the crate:

#![warn(clippy::cargo)]
#![allow(clippy::approx_constant)]

use std::env;

fn equal_to_approx_pi(x: f32) -> bool {
...

Now, if you run the cargo clippy command you should see clippy complaining on the issues in the cargo manifest file Cargo.toml, while “forgetting” the lint checking the usage of approximate constants:

Clippy Output

$ cargo clippy
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
warning: package `clippy_test` is missing `package.description` metadata
|
note: the lint level is defined here
--> src/main.rs:1:9
|
1 | #![warn(clippy::cargo)]
|         ^^^^^^^^^^^^^
= note: `#[warn(clippy::cargo_common_metadata)]` implied by `#[warn(clippy::cargo)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `either package.license or package.license_file` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.repository` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.readme` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.keywords` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.categories` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: unneeded `return` statement
--> src/main.rs:11:5
|
11 |     return false;
|     ^^^^^^^^^^^^^ help: remove `return`: `false`
|
= note: `#[warn(clippy::needless_return)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return

warning: equality checks against true are unnecessary
--> src/main.rs:22:8
|
22 |     if equal == true {
|        ^^^^^^^^^^^^^ help: try simplifying it as shown: `equal`
|
= note: `#[warn(clippy::bool_comparison)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison

warning: 8 warnings emitted

Finished dev [unoptimized + debuginfo] target(s) in 0.34s

Command Line Based Approach

Sometimes, changing the source code is not an option. For instance, you may just need to check an external project. In this case, the alternative of adjusting lints' levels through clippy’s command line arguments may be preferable. In order to enable/disable some categories or individual lints checks for your crate, run clippy with -A (means allow), -W (warn) and -D (deny) command line argument providing the corresponding category/lint after it. For instance, the same effect as in the previous example can be achieved with the following command:

$ cargo clippy -- -W clippy::cargo -A clippy::approx_constant

So as we run clippy using cargo, we pass the command line arguments to clippy after double dash --. The arguments before double dash will be passed to cargo and thus, most probably, result in an error.

Note that the source code based configuration has a precedence over the command line based. For instance, if you set in the source code of our example the level of the lint checking the usage of approximate constants to deny, while allowing it through the command line arguments, clippy will report an error. This is pretty odd and should be taken into the account because to my knowledge, command line arguments usually have a precedence over the source code default configuration (if you can explain this behavior, please drop a comment).

Paranoid Clippy

With this knowledge, it should be clear now how to make clippy completely paranoic:

#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![warn(clippy::restriction)]
#![warn(clippy::nursery)]
#![warn(clippy::cargo)]

use std::env;
...

For clippy::all I set the deny level, other lint categories are warn, however, you can adjust those levels according to your needs. The resulting output is getting pretty long with 5 errors and 16 warnings emitted:

Clippy Output

$ cargo clippy
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
warning: use of `println!`
--> src/main.rs:19:5
|
19 |     println!("The approximate value of PI is {}", PI);
|     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> src/main.rs:3:9
|
3  | #![warn(clippy::restriction)]
|         ^^^^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::print_stdout)]` implied by `#[warn(clippy::restriction)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout

warning: use of `println!`
--> src/main.rs:26:9
|
26 |         println!("Equal!");
|         ^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout

warning: use of `println!`
--> src/main.rs:28:9
|
28 |         println!("Not equal!");
|         ^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout

warning: missing documentation for the crate
--> src/main.rs:1:1
|
1  | / #![deny(clippy::all)]
2  | | #![warn(clippy::pedantic)]
3  | | #![warn(clippy::restriction)]
4  | | #![warn(clippy::nursery)]
...  |
29 | |     }
30 | | }
| |_^
|
note: the lint level is defined here
--> src/main.rs:3:9
|
3  | #![warn(clippy::restriction)]
|         ^^^^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::missing_docs_in_private_items)]` implied by `#[warn(clippy::restriction)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items

warning: package `clippy_test` is missing `package.description` metadata
|
note: the lint level is defined here
--> src/main.rs:5:9
|
5 | #![warn(clippy::cargo)]
|         ^^^^^^^^^^^^^
= note: `#[warn(clippy::cargo_common_metadata)]` implied by `#[warn(clippy::cargo)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `either package.license or package.license_file` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.repository` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.readme` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.keywords` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: package `clippy_test` is missing `package.categories` metadata
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

warning: missing documentation for a function
--> src/main.rs:9:1
|
9  | / fn equal_to_approx_pi(x: f32) -> bool {
10 | |     const PI: f32 = 3.14;
11 | |     if x == PI {
12 | |         return true;
13 | |     }
14 | |     return false;
15 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items

error: unneeded `return` statement
--> src/main.rs:14:5
|
14 |     return false;
|     ^^^^^^^^^^^^^ help: remove `return`: `false`
|
note: the lint level is defined here
--> src/main.rs:1:9
|
1  | #![deny(clippy::all)]
|         ^^^^^^^^^^^
= note: `#[deny(clippy::needless_return)]` implied by `#[deny(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return

warning: missing documentation for a constant
--> src/main.rs:10:5
|
10 |     const PI: f32 = 3.14;
|     ^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:10:21
|
10 |     const PI: f32 = 3.14;
|                     ^^^^
|
note: the lint level is defined here
--> src/main.rs:1:9
|
1  | #![deny(clippy::all)]
|         ^^^^^^^^^^^
= note: `#[deny(clippy::approx_constant)]` implied by `#[deny(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: strict comparison of `f32` or `f64` constant
--> src/main.rs:11:8
|
11 |     if x == PI {
|        ^^^^^^^ help: consider comparing them within some margin of error: `(x - PI).abs() < error_margin`
|
note: the lint level is defined here
--> src/main.rs:3:9
|
3  | #![warn(clippy::restriction)]
|         ^^^^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::float_cmp_const)]` implied by `#[warn(clippy::restriction)]`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const

warning: missing documentation for a constant
--> src/main.rs:18:5
|
18 |     const PI: f32 = 3.14;
|     ^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:18:21
|
18 |     const PI: f32 = 3.14;
|                     ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: used `unwrap()` on `a Result` value
--> src/main.rs:21:27
|
21 |       let submitted_value = env::args()
|  ___________________________^
22 | |         .nth(1).unwrap()
23 | |         .parse().unwrap();
| |_________________________^
|
note: the lint level is defined here
--> src/main.rs:3:9
|
3  | #![warn(clippy::restriction)]
|         ^^^^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::unwrap_used)]` implied by `#[warn(clippy::restriction)]`
= help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used

warning: used `unwrap()` on `an Option` value
--> src/main.rs:21:27
|
21 |       let submitted_value = env::args()
|  ___________________________^
22 | |         .nth(1).unwrap()
| |________________________^
|
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used

error: equality checks against true are unnecessary
--> src/main.rs:25:8
|
25 |     if equal == true {
|        ^^^^^^^^^^^^^ help: try simplifying it as shown: `equal`
|
note: the lint level is defined here
--> src/main.rs:1:9
|
1  | #![deny(clippy::all)]
|         ^^^^^^^^^^^
= note: `#[deny(clippy::bool_comparison)]` implied by `#[deny(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison

error: restriction lints are not meant to be all enabled
--> src/main.rs:3:9
|
3 | #![warn(clippy::restriction)]
|         ^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> src/main.rs:1:9
|
1 | #![deny(clippy::all)]
|         ^^^^^^^^^^^
= note: `#[deny(clippy::blanket_clippy_restriction_lints)]` implied by `#[deny(clippy::all)]`
= help: try enabling only the lints you really need
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints

error: aborting due to 5 previous errors; 16 warnings emitted

error: could not compile `clippy_test`

To learn more, run the command again with --verbose.

Combatting False Positives

Clearly, clippy is not perfect, and from time to time it produces false positives. In this case, you have two options: either you refactor your code (if it is possible) to make clippy happy, or you disable some checks. In the previous section, we showed how to disable a particular lint on the crate level. However, this approach is too coarse-grained, and sometimes you are required to disable a lint for a particular module or function. Luckily, clippy provides possibility to achieve this goal. In order to do this, you have to add the attribute (allow to disable the corresponding lint) in front of the corresponding module/function. Note that in this case you should not use the ! sign. Similarly, clippy also provides a possibility to enable a lint for a particular module/function. For instance, in the following example I show allow the usage of approximate constants in the equal_to_approx_pi function, while its usage is still prohibited in the main function.

use std::env;

#[allow(clippy::approx_constant)]
fn equal_to_approx_pi(x: f32) -> bool {
...

Below, you can see the output. Note that we have disabled the check only for equal_to_approx_pi; clippy still complains on the usage of approximate constants in the main function.

Clippy Output

$ cargo clippy
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
warning: unneeded `return` statement
--> src/main.rs:9:5
|
9 |     return false;
|     ^^^^^^^^^^^^^ help: remove `return`: `false`
|
= note: `#[warn(clippy::needless_return)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:13:21
|
13 |     const PI: f32 = 3.14;
|                     ^^^^
|
= note: `#[deny(clippy::approx_constant)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: equality checks against true are unnecessary
--> src/main.rs:20:8
|
20 |     if equal == true {
|        ^^^^^^^^^^^^^ help: try simplifying it as shown: `equal`
|
= note: `#[warn(clippy::bool_comparison)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison

error: aborting due to previous error; 2 warnings emitted

error: could not compile `clippy_test`

To learn more, run the command again with --verbose.

Automatically Fixing Issues

Clippy is able to fix some issues automatically. The special --fix command line flag is responsible for enabling this feature. However, this feature as of now (September, 2021) is available only for the nightly toolchain version. If you run the command on a stable channel version, you will get the following message:

$ cargo clippy --fix
thread 'main' panicked at 'Usage of `--fix` requires `-Z unstable-options`', src/tools/clippy/src/main.rs:92:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Thus, in order to use this feature, you have at first install the toolchain from the nightly channel. You can do this using the following command:

$ rustup toolchain list
stable-x86_64-unknown-linux-gnu (default)
$ rustup toolchain install nightly
info: syncing channel updates for 'nightly-x86_64-unknown-linux-gnu'
info: latest update on 2021-09-06, rust version 1.57.0-nightly (e30b68353 2021-09-05)
info: downloading component 'cargo'
info: downloading component 'clippy'
...

$ rustup toolchain list
stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu

As you can see after executing this command, you have two toolchains installed in your system: stable (default) and nightly. After nightly toolchain is installed in your system, you have two options how to activate it:

  1. You can set the nightly toolchain as default globally in your system.
  2. You can override the toolchain only for the current project.
  3. You can directly run clippy from nightly channel using special flags.

If you would like to switch your system to the nightly channel toolchain, use the following command:

$ rustup default nightly
info: using existing install for 'nightly-x86_64-unknown-linux-gnu'
info: default toolchain set to 'nightly-x86_64-unknown-linux-gnu'

nightly-x86_64-unknown-linux-gnu unchanged - rustc 1.57.0-nightly (e30b68353 2021-09-05)

$ rustup toolchain list
stable-x86_64-unknown-linux-gnu
nightly-x86_64-unknown-linux-gnu (default)

So as in my projects I prefer to rely on stable features, I prefer the second approach – overriding the toolchain only for your current project. In order to do this, change directory to your project and execute the following command:

$ rustup override set nightly
info: using existing install for 'nightly-x86_64-unknown-linux-gnu'
info: override toolchain for '/home/yury/PROJECTS/TESTS/RUST/clippy_test' set to 'nightly-x86_64-unknown-linux-gnu'

nightly-x86_64-unknown-linux-gnu unchanged - rustc 1.57.0-nightly (e30b68353 2021-09-05)

After doing this if you list the toolchains for this project, you should get the following output:

$ rustup toolchain list      
stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu (override)

Now, you can use the --fix clippy feature. I use additional --allow-no-vcs flag because this test project I have created without version control system support: cargo new clippy_test --vcs none. So as during the fixing clippy rewrites the code, its usage is considered as insecure, therefore, you need an additional flag to make clippy working.

$ cargo clippy --fix --allow-no-vcs
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
Fixed src/main.rs (2 fixes)
warning: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:4:21
|
4 |     const PI: f32 = 3.14;
|                     ^^^^
|
= note: `#[warn(clippy::approx_constant)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:12:21
|
12 |     const PI: f32 = 3.14;
|                     ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: `clippy_test` (bin "clippy_test") generated 2 warnings
warning: `clippy_test` (bin "clippy_test" test) generated 2 warnings (2 duplicates)
Finished dev [unoptimized + debuginfo] target(s) in 0.14s

You can see that clippy automatically fixed 2 issues:

  1. return false; - the return is redundant;
  2. if equal == true { - there is not need to compare boolean values.

Note that clippy cannot correct all the issues automatically, some of them you have to correct manually:

$ cargo clippy
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:4:21
|
4 |     const PI: f32 = 3.14;
|                     ^^^^
|
= note: `#[deny(clippy::approx_constant)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:12:21
|
12 |     const PI: f32 = 3.14;
|                     ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: could not compile `clippy_test` due to 2 previous errors

After you have made the modifications, you can change the toolchain back to stable (if you develop using the stable toolchain) by executing the following command in the project’s directory:

$ rustup override unset
info: override toolchain for '/home/yury/PROJECTS/TESTS/RUST/clippy_test' removed
yury@ideap:~/PROJECTS/TESTS/RUST/clippy_test$ rustup toolchain list
stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu

Alternatively, you can once nightly toolchain is installed in your system, you can directly call clippy using special command line options. For instance, if you do not want to change the default toolchain or do not want to override it for particular project, you can remember the following command:

$ cargo +nightly clippy --fix -Z unstable-options --allow-no-vcs

Additional Cargo Clippy CLI Options

Sometimes in order to change what clippy checks, you need to provide additional command line options. For instance, if clippy is run as a task in your CI pipeline you may want to check only the given crate without the dependencies, or you would also like to lint the tests or other conditionally compiled targets.

For instance, let’s augment our example with the following test function:

#[test]
fn test_num_equal_to_approx_pi() {
    const PI: f32 = 3.14;
    let equal = 3.14;
    assert_eq!(equal, PI);
}

If you run clippy on this code, it will not report errors in this test function:

Clippy Output

$ cargo clippy
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
warning: unneeded `return` statement
--> src/main.rs:8:5
|
8 |     return false;
|     ^^^^^^^^^^^^^ help: remove `return`: `false`
|
= note: `#[warn(clippy::needless_return)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:4:21
|
4 |     const PI: f32 = 3.14;
|                     ^^^^
|
= note: `#[deny(clippy::approx_constant)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:12:21
|
12 |     const PI: f32 = 3.14;
|                     ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: equality checks against true are unnecessary
--> src/main.rs:19:8
|
19 |     if equal == true {
|        ^^^^^^^^^^^^^ help: try simplifying it as shown: `equal`
|
= note: `#[warn(clippy::bool_comparison)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison

warning: `clippy_test` (bin "clippy_test") generated 2 warnings
error: could not compile `clippy_test` due to 2 previous errors; 2 warnings emitted

As you can see, the output contains no information about the problems in the test function. In order to make clippy to check tests, run it with the --tests command line flag:

Clippy Output

$ cargo clippy --tests
Checking clippy_test v0.1.0 (/home/yury/PROJECTS/TESTS/RUST/clippy_test)
warning: unneeded `return` statement
--> src/main.rs:8:5
|
8 |     return false;
|     ^^^^^^^^^^^^^ help: remove `return`: `false`
|
= note: `#[warn(clippy::needless_return)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:4:21
|
4 |     const PI: f32 = 3.14;
|                     ^^^^
|
= note: `#[deny(clippy::approx_constant)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:12:21
|
12 |     const PI: f32 = 3.14;
|                     ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

warning: equality checks against true are unnecessary
--> src/main.rs:19:8
|
19 |     if equal == true {
|        ^^^^^^^^^^^^^ help: try simplifying it as shown: `equal`
|
= note: `#[warn(clippy::bool_comparison)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:28:21
|
28 |     const PI: f32 = 3.14;
|                     ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:29:17
|
29 |     let equal = 3.14;
|                 ^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: strict comparison of `f32` or `f64`
--> src/main.rs:30:5
|
30 |     assert_eq!(equal, PI);
|     ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[deny(clippy::float_cmp)]` on by default
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

warning: `clippy_test` (bin "clippy_test" test) generated 2 warnings
error: could not compile `clippy_test` due to 5 previous errors; 2 warnings emitted

Similarly, clippy will check only default features and default targets. To check all targets and all features run clippy with the --all-targets and --all-features flags correspondingly.

The list of available options is the same as for cargo check command. You can see them in the help:

$ cargo check --help
cargo-check 
Check a local package and all of its dependencies for errors

USAGE:
cargo check [OPTIONS]

OPTIONS:
-q, --quiet                      No output printed to stdout
-p, --package <SPEC>...          Package(s) to check
--workspace                  Check all packages in the workspace
--exclude <SPEC>...          Exclude packages from the check
--all                        Alias for --workspace (deprecated)
-j, --jobs <N>                   Number of parallel jobs, defaults to # of CPUs
--lib                        Check only this package's library
--bin <NAME>...              Check only the specified binary
--bins                       Check all binaries
--example <NAME>...          Check only the specified example
--examples                   Check all examples
--test <NAME>...             Check only the specified test target
--tests                      Check all tests
--bench <NAME>...            Check only the specified bench target
--benches                    Check all benches
--all-targets                Check all targets
--release                    Check artifacts in release mode, with optimizations
--profile <PROFILE-NAME>     Check artifacts with the specified profile
--features <FEATURES>...     Space or comma separated list of features to activate
--all-features               Activate all available features
--no-default-features        Do not activate the `default` feature
--target <TRIPLE>...         Check for the target triple
--target-dir <DIRECTORY>     Directory for all generated artifacts
--manifest-path <PATH>       Path to Cargo.toml
--ignore-rust-version        Ignore `rust-version` specification in packages
--message-format <FMT>...    Error format
--unit-graph                 Output build graph in JSON (unstable)
--future-incompat-report     Outputs a future incompatibility report at the end of the build (unstable)
-v, --verbose                    Use verbose output (-vv very verbose/build.rs output)
--color <WHEN>               Coloring: auto, always, never
--frozen                     Require Cargo.lock and cache are up to date
--locked                     Require Cargo.lock is up to date
--offline                    Run without accessing the network
--config <KEY=VALUE>...      Override a configuration value (unstable)
-Z <FLAG>...                     Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
-h, --help                       Prints help information

Run `cargo help check` for more detailed information.

The only difference is the --no-deps option that makes clippy to check only current crate without the dependencies.

Yury Zhauniarovich
Yury Zhauniarovich
Assistant Professor

Related