]> git.lizzy.rs Git - rust.git/blobdiff - doc/adding_lints.md
Auto merge of #5155 - krishna-veerareddy:remove-unused-imports, r=flip1995
[rust.git] / doc / adding_lints.md
index 452852f1060b3d6ab1dbcd955e29ee8b9d17cdde..e1f4ce016c5c0cd81de6878d9d01a20b95b1fc75 100644 (file)
@@ -7,20 +7,23 @@ creating an example lint from scratch.
 To get started, we will create a lint that detects functions called `foo`,
 because that's clearly a non-descriptive name.
 
-* [Setup](#Setup)
-* [Testing](#Testing)
-* [Rustfix tests](#Rustfix-tests)
-* [Edition 2018 tests](#Edition-2018-tests)
-* [Lint declaration](#Lint-declaration)
-* [Lint passes](#Lint-passes)
-* [Emitting a lint](#Emitting-a-lint)
-* [Adding the lint logic](#Adding-the-lint-logic)
-* [Author lint](#Author-lint)
-* [Documentation](#Documentation)
-* [Running rustfmt](#Running-rustfmt)
-* [Debugging](#Debugging)
-* [PR Checklist](#PR-Checklist)
-* [Cheatsheet](#Cheatsheet)
+- [Adding a new lint](#adding-a-new-lint)
+  - [Setup](#setup)
+  - [Getting Started](#getting-started)
+  - [Testing](#testing)
+  - [Rustfix tests](#rustfix-tests)
+  - [Edition 2018 tests](#edition-2018-tests)
+  - [Testing manually](#testing-manually)
+  - [Lint declaration](#lint-declaration)
+  - [Lint passes](#lint-passes)
+  - [Emitting a lint](#emitting-a-lint)
+  - [Adding the lint logic](#adding-the-lint-logic)
+  - [Author lint](#author-lint)
+  - [Documentation](#documentation)
+  - [Running rustfmt](#running-rustfmt)
+  - [Debugging](#debugging)
+  - [PR Checklist](#pr-checklist)
+  - [Cheatsheet](#cheatsheet)
 
 ### Setup
 
@@ -29,6 +32,19 @@ which can change rapidly. Make sure you're working near rust-clippy's master,
 and use the `setup-toolchain.sh` script to configure the appropriate toolchain
 for the Clippy directory.
 
+### Getting Started
+
+There is a bit of boilerplate code that needs to be set up when creating a new
+lint. Fortunately, you can use the clippy dev tools to handle this for you. We
+are naming our new lint `foo_functions` (lints are generally written in snake
+case), and we don't need type information so it will have an early pass type
+(more on this later on). To get started on this lint you can run
+`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
+(category will default to nursery if not provided). This command will create
+two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
+as well as run `cargo dev update_lints` to register the new lint. Next, we'll
+open up these files and add our lint!
+
 ### Testing
 
 Let's write some tests first that we can execute while we iterate on our lint.
@@ -39,11 +55,9 @@ we want to check. The output of Clippy is compared against a `.stderr` file.
 Note that you don't have to create this file yourself, we'll get to
 generating the `.stderr` files further down.
 
-We start by creating the test file at `tests/ui/foo_functions.rs`. It doesn't
-really matter what the file is called, but it's a good convention to name it
-after the lint it is testing, so `foo_functions.rs` it is.
+We start by opening the test file created at `tests/ui/foo_functions.rs`.
 
-Inside the file we put some examples to get started:
+Update the file with some examples to get started:
 
 ```rust
 #![warn(clippy::foo_functions)]
@@ -77,7 +91,7 @@ fn main() {
 
 ```
 
-Now we can run the test with `TESTNAME=ui/foo_functions cargo uitest`.
+Now we can run the test with `TESTNAME=foo_functions cargo uitest`.
 Currently this test will fail. If you go through the output you will see that we
 are told that `clippy::foo_functions` is an unknown lint, which is expected.
 
@@ -86,8 +100,10 @@ test. That allows us to check if the output is turning into what we want.
 
 Once we are satisfied with the output, we need to run
 `tests/ui/update-all-references.sh` to update the `.stderr` file for our lint.
-Running `TESTNAME=ui/foo_functions cargo uitest` should pass then. When we
-commit our lint, we need to commit the generated `.stderr` files, too.
+Please note that, we should run `TESTNAME=foo_functions cargo uitest`
+every time before running `tests/ui/update-all-references.sh`.
+Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
+our lint, we need to commit the generated `.stderr` files, too.
 
 ### Rustfix tests
 
@@ -100,8 +116,6 @@ test file and compare that to the contents of a `.fixed` file.
 Use `tests/ui/update-all-references.sh` to automatically generate the
 `.fixed` file after running the tests.
 
-With tests in place, let's have a look at implementing our lint now.
-
 ### Edition 2018 tests
 
 Some features require the 2018 edition to work (e.g. `async_await`), but
@@ -115,28 +129,46 @@ Manually testing against an example file can be useful if you have added some
 your local modifications, run `env CLIPPY_TESTS=true cargo run --bin
 clippy-driver -- -L ./target/debug input.rs` from the working copy root.
 
+With tests in place, let's have a look at implementing our lint now.
+
 ### Lint declaration
 
-We start by creating a new file in the `clippy_lints` crate. That's the crate
-where all the lint code is. We are going to call the file
-`clippy_lints/src/foo_functions.rs` and import some initial things we need:
+Let's start by opening the new file created in the `clippy_lints` crate
+at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
+lint code is. This file has already imported some initial things we will need:
 
 ```rust
-use rustc::lint::{LintArray, LintPass, EarlyLintPass};
-use rustc::{declare_lint_pass, declare_tool_lint};
+use rustc_lint::{EarlyLintPass, EarlyContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use syntax::ast::*;
 ```
 
-The next step is to provide a lint declaration. Lints are declared using the
-[`declare_clippy_lint!`][declare_clippy_lint] macro:
+The next step is to update the lint declaration. Lints are declared using the
+[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
+the auto-generated lint declaration to have a real description, something like this:
 
 ```rust
 declare_clippy_lint! {
+    /// **What it does:**
+    ///
+    /// **Why is this bad?**
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// // example code
+    /// ```
     pub FOO_FUNCTIONS,
     pedantic,
     "function named `foo`, which is not a descriptive name"
 }
 ```
 
+* The section of lines prefixed with `///` constitutes the lint documentation
+section. This is the default documentation style and will be displayed at
+https://rust-lang.github.io/rust-clippy/master/index.html.
 * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the [lint naming
 guidelines][lint_naming] here when naming your lint. In short, the name should
 state the thing that is being checked for and read well when used with
@@ -146,8 +178,8 @@ state the thing that is being checked for and read well when used with
 * The last part should be a text that explains what exactly is wrong with the
   code
 
-With our lint declaration done, we will now make sure that it is assigned to a
-lint pass:
+The rest of this file contains an empty implementation for our lint pass,
+which in this case is `EarlyLintPass` and should look like this:
 
 ```rust
 // clippy_lints/src/foo_functions.rs
@@ -162,12 +194,9 @@ impl EarlyLintPass for FooFunctions {}
 Don't worry about the `name` method here. As long as it includes the name of the
 lint pass it should be fine.
 
-Next we need to run `util/dev update_lints` to register the lint in various
-places, mainly in `clippy_lints/src/lib.rs`.
-
-While `update_lints` automates some things, it doesn't automate everything. We
-will have to register our lint pass manually in the `register_plugins` function
-in `clippy_lints/src/lib.rs`:
+The new lint automation runs `update_lints`, which automates some things, but it
+doesn't automate everything. We will have to register our lint pass manually in
+the `register_plugins` function in `clippy_lints/src/lib.rs`:
 
 ```rust
 reg.register_early_lint_pass(box foo_functions::FooFunctions);
@@ -191,14 +220,9 @@ In short, the `LateLintPass` has access to type information while the
 `EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
 hasn't really been a concern with Clippy so far.
 
-Since we don't need type information for checking the function name, we are
-going to use the `EarlyLintPass`. It has to be imported as well, changing our
-imports to:
-
-```rust
-use rustc::lint::{LintArray, LintPass, EarlyLintPass, EarlyContext};
-use rustc::{declare_tool_lint, lint_array};
-```
+Since we don't need type information for checking the function name, we used
+`--pass=early` when running the new lint automation and all the imports were
+added accordingly.
 
 ### Emitting a lint
 
@@ -225,14 +249,14 @@ Depending on how complex we want our lint message to be, we can choose from a
 variety of lint emission functions. They can all be found in
 [`clippy_lints/src/utils/diagnostics.rs`][diagnostics].
 
-`span_help_and_lint` seems most appropriate in this case. It allows us to
+`span_lint_and_help` seems most appropriate in this case. It allows us to
 provide an extra help message and we can't really suggest a better name
 automatically. This is how it looks:
 
 ```rust
 impl EarlyLintPass for FooFunctions {
     fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, _: &FnDecl, span: Span, _: NodeId) {
-        span_help_and_lint(
+        span_lint_and_help(
             cx,
             FOO_FUNCTIONS,
             span,
@@ -260,7 +284,7 @@ With that we can expand our `check_fn` method to:
 impl EarlyLintPass for FooFunctions {
     fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: &FnDecl, span: Span, _: NodeId) {
         if is_foo_fn(fn_kind) {
-            span_help_and_lint(
+            span_lint_and_help(
                 cx,
                 FOO_FUNCTIONS,
                 span,
@@ -307,7 +331,7 @@ If you have trouble implementing your lint, there is also the internal `author`
 lint to generate Clippy code that detects the offending pattern. It does not
 work for all of the Rust syntax, but can give a good starting point.
 
-The quickest way to use it, is the [Rust playground][play].rust-lang.org).
+The quickest way to use it, is the [Rust playground: play.rust-lang.org][Play].
 Put the code you want to lint into the editor and add the `#[clippy::author]`
 attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
 see the generated code in the output below.
@@ -362,7 +386,7 @@ It can be installed via `rustup`:
 rustup component add rustfmt --toolchain=nightly
 ```
 
-Use `./util/dev fmt` to format the whole codebase. Make sure that `rustfmt` is
+Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
 installed for the nightly toolchain.
 
 ### Debugging
@@ -380,9 +404,9 @@ Before submitting your PR make sure you followed all of the basic requirements:
 - [ ] Followed [lint naming conventions][lint_naming]
 - [ ] Added passing UI tests (including committed `.stderr` file)
 - [ ] `cargo test` passes locally
-- [ ] Executed `./util/dev update_lints`
+- [ ] Executed `cargo dev update_lints`
 - [ ] Added lint documentation
-- [ ] Run `./util/dev fmt`
+- [ ] Run `cargo dev fmt`
 
 ### Cheatsheet
 
@@ -427,12 +451,12 @@ don't hesitate to ask on Discord, IRC or in the issue/PR.
 [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
 [utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs
 [ident]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/source_map/symbol/struct.Ident.html
-[span]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax_pos/struct.Span.html
+[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
 [applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
-[if_chain]: https://docs.rs/if_chain/0.1.2/if_chain/
+[if_chain]: https://docs.rs/if_chain/*/if_chain/
 [ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/sty/index.html
 [ast]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/index.html
-[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax_pos/struct.Span.html#method.from_expansion
+[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/fn.in_external_macro.html
 [play]: https://play.rust-lang.org
 [author_example]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f093b986e80ad62f3b67a1f24f5e66e2