## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# Emitting a lint

Once we have defined a lint, written UI tests and chosen the lint pass for the lint, we can begin the implementation of the lint logic so that we can emit it and gradually work towards a lint that behaves as expected.

Note that we will not go into concrete implementation of a lint logic in this chapter. We will go into details in later chapters as well as in two examples of real Clippy lints.

To emit a lint, we must implement a pass (see Lint Passes) for the lint that we have declared. In this example we’ll implement a “late” lint, so take a look at the LateLintPass documentation, which provides an abundance of methods that we can implement for our lint.

    #![allow(unused)]
    fn main() {
    pub trait LateLintPass<'tcx>: LintPass {
        // Trait methods
    }
    }

By far the most common method used for Clippy lints is `check_expr` method, this is because Rust is an expression language and, more often than not, the lint we want to work on must examine expressions.

> Note: If you don’t fully understand what expressions are in Rust, take a look at the official documentation on expressions

Other common ones include the `check_fn` method and the `check_item` method.

### Emitting a lint

Inside the trait method that we implement, we can write down the lint logic and emit the lint with suggestions.

Clippy’s diagnostics provides quite a few diagnostic functions that we can use to emit lints. Take a look at the documentation to pick one that suits your lint’s needs the best. Some common ones you will encounter in the Clippy repository includes:

- `span_lint`: Emits a lint without providing any other information
- `span_lint_and_note`: Emits a lint and adds a note
- `span_lint_and_help`: Emits a lint and provides a helpful message
- `span_lint_and_sugg`: Emits a lint and provides a suggestion to fix the code
- `span_lint_and_then`: Like `span_lint`, but allows for a lot of output customization.

    #![allow(unused)]
    fn main() {
    impl<'tcx> LateLintPass<'tcx> for LintName {
        fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)  {
            // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
            if some_lint_expr_logic(expr) {
                span_lint_and_help(
                    cx, // < The context
                    LINT_NAME, // < The name of the lint in ALL CAPS
                    expr.span, // < The span to lint
                    "message on why the lint is emitted",
                    None, // < An optional help span (to highlight something in the lint)
                    "message that provides a helpful suggestion",
                );
            }
        }
    }
    }

> Note: The message should be matter of fact and avoid capitalization and punctuation. If multiple sentences are needed, the messages should probably be split up into an error + a help / note / suggestion message.

## Suggestions: Automatic fixes

Some lints know what to change in order to fix the code. For example, the lint `range_plus_one` warns for ranges where the user wrote `x..y + 1` instead of using an inclusive range (`x..=y`). The fix to this code would be changing the `x..y + 1` expression to `x..=y`. This is where suggestions come in.

A suggestion is a change that the lint provides to fix the issue it is linting. The output looks something like this (from the example earlier):

    error: an inclusive range would be more readable
      --> tests/ui/range_plus_minus_one.rs:37:14
       |
    LL |     for _ in 1..1 + 1 {}
       |              ^^^^^^^^ help: use: `1..=1`

### Applicability

Not all suggestions are always right, some of them require human supervision, that’s why we have Applicability.

Applicability indicates confidence in the correctness of the suggestion, some are always right (`Applicability::MachineApplicable`), but we use `Applicability::MaybeIncorrect` and others when talking about a suggestion that may be incorrect.

### Example

The same lint `LINT_NAME` but that emits a suggestion would look something like this:

    #![allow(unused)]
    fn main() {
    impl<'tcx> LateLintPass<'tcx> for LintName {
        fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)  {
            // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
            if some_lint_expr_logic(expr) {
                span_lint_and_then( // < Note this change
                    cx,
                    LINT_NAME,
                    expr.span,
                    "message on why the lint is emitted",
                    |diag| {
                        // v Build and emit the suggestion
                        let mut app = Applicability::MachineApplicable;
                        let expr_snippet = snippet_with_applicability(cx, expr.span, "_", &mut app);
                        let sugg = format!("foo + {expr_snippet} * bar");
                        diag.span_suggestion(expr.span, "use", sugg, app);
                    }
                );
            }
        }
    }
    }

Suggestions generally use the `format!` macro to interpolate the old values with the new ones. For information on getting code snippets, see Snippets.

## How to choose between notes, help messages and suggestions

Notes are presented separately from the main lint message, they provide useful information that the user needs to understand why the lint was activated. They are the most helpful when attached to a span.

Examples:

### Notes

    error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
      --> tests/ui/drop_forget_ref.rs:10:5
       |
    10 |     forget(&SomeStruct);
       |     ^^^^^^^^^^^^^^^^^^^
       |
       = note: `-D clippy::forget-ref` implied by `-D warnings`
    note: argument has type &SomeStruct
      --> tests/ui/drop_forget_ref.rs:10:12
       |
    10 |     forget(&SomeStruct);
       |            ^^^^^^^^^^^

### Help Messages

Help messages are specifically to help the user. These are used in situation where you can’t provide a specific machine applicable suggestion. They can also be attached to a span.

Example:

    error: constant division of 0.0 with 0.0 will always result in NaN
      --> tests/ui/zero_div_zero.rs:6:25
       |
    6  |     let other_f64_nan = 0.0f64 / 0.0;
       |                         ^^^^^^^^^^^^
       |
       = help: consider using `f64::NAN` if you would like a constant representing NaN

### Suggestions

Suggestions are the most helpful, they are changes to the source code to fix the error. The magic in suggestions is that tools like `rustfix` can detect them and automatically fix your code.

Example:

    error: This `.fold` can be more succinctly expressed as `.any`
    --> tests/ui/methods.rs:390:13
        |
    390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
        |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
        |

## Snippets

Snippets are pieces of the source code (as a string), they are extracted generally using the various `snippet_*` functions from `clippy_utils::source`.

If you’re using the snippets not to build a suggestion, it’s usually enough to use `snippet` – it accepts the span of the item, and also a fallback string (see Fallback string).

For example, if you want to know how an item looks (and you know the item’s span), you could use `snippet(cx, span, "_")`.

If you do use the snippet for a suggestion, it’s recommended to use `snippet_with_applicability` instead. This is so that Clippy can reduce the applicability of the suggestion in case it couldn’t extract the snippet “cleanly” – see the function’s documentation for more information.

It’s often necessary to create multiple snippets to build a suggestion, in which case you’ll have the following pattern:

    #![allow(unused)]
    fn main() {
    // inside `span_lint_and_then`

    // 1. Create initial applicability.
    let mut app = Applicability::MachineApplicable;

    // 2. Use it to create all the snippets
    let foo_snippet = snippet_with_applicability(cx, foo.span, "_", &mut app);
    let bar_snippet = snippet_with_applicability(cx, bar.span, "_", &mut app);
    let sugg = format!("{foo_snippet} + {bar_snippet}"); // or whatever

    // 3. Use it to emit the final suggestion
    diag.span_suggestion(span, msg, sugg, app);
    }

### Fallback string

This is the string that is used for the snippet when the source code that the span points to is unavailable. That mostly only happens when proc-macros mishandle spans – see the section on proc-macros.

Most of the time, the snippets in a suggestion come from a (span of a) single expression, and so `"_"` is an appropriate fallback string. If you instead find yourself suggesting to insert multiple statements, or a block–basically anything “big”–then consider using `".."` instead.

## Final: Run UI Tests to Emit the Lint

Now, if we run our UI test, we should see that Clippy now produces output that contains the lint message we designed.

The next step is to implement the logic properly, which is a detail that we will cover in the next chapters.