]> git.lizzy.rs Git - rust.git/blobdiff - doc/adding_lints.md
Auto merge of #5864 - rust-lang:ci_debug, r=Manishearth
[rust.git] / doc / adding_lints.md
index 54034f664834dece95ead6cd714ca3c89fa9b4c4..168092f7329cc0caed65ea30f0edfe20ac91644c 100644 (file)
@@ -27,10 +27,7 @@ because that's clearly a non-descriptive name.
 
 ## Setup
 
-When working on Clippy, you will need the current git master version of rustc,
-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.
+See the [Basics](basics.md#get-the-code) documentation.
 
 ## Getting Started
 
@@ -38,12 +35,16 @@ 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!
+(more on this later on). If you're not sure if the name you chose fits the lint,
+take a look at our [lint naming guidelines][lint_naming]. 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. For cargo lints, two project hierarchies (fail/pass) will
+be created by default under `tests/ui-cargo`.
+
+Next, we'll open up these files and add our lint!
 
 ## Testing
 
@@ -101,19 +102,41 @@ Once we are satisfied with the output, we need to run
 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.
+our lint, we need to commit the generated `.stderr` files, too. In general, you
+should only commit files changed by `tests/ui/update-all-references.sh` for the
+specific lint you are creating/editing.
+
+### Cargo lints
+
+For cargo lints, the process of testing differs in that we are interested in
+the `Cargo.toml` manifest file. We also need a minimal crate associated
+with that manifest.
+
+If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
+we will find by default two new crates, each with its manifest file:
+
+* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
+* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint.
+
+If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
+
+The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
+variable to `cargo uitest` works too, but the script to update the references
+is in another path: `tests/ui-cargo/update-all-references.sh`.
 
 ## Rustfix tests
 
 If the lint you are working on is making use of structured suggestions, the
 test file should include a `// run-rustfix` comment at the top. This will
-additionally run [rustfix](https://github.com/rust-lang-nursery/rustfix) for
-that test. Rustfix will apply the suggestions from the lint to the code of the
-test file and compare that to the contents of a `.fixed` file.
+additionally run [rustfix] for that test. Rustfix will apply the suggestions
+from the lint to the code of the 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.
 
+[rustfix]: https://github.com/rust-lang/rustfix
+
 ## Edition 2018 tests
 
 Some features require the 2018 edition to work (e.g. `async_await`), but
@@ -167,10 +190,10 @@ declare_clippy_lint! {
 * The section of lines prefixed with `///` constitutes the lint documentation
   section. This is the default documentation style and will be displayed
   [like this][example_lint_page].
-* `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
-  `allow`/`warn`/`deny`.
+* `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 `allow`/`warn`/`deny`.
 * `pedantic` sets the lint level to `Allow`.
   The exact mapping can be found [here][category_level_mapping]
 * The last part should be a text that explains what exactly is wrong with the
@@ -200,7 +223,10 @@ automate everything. We will have to register our lint pass manually in the
 store.register_early_pass(|| box foo_functions::FooFunctions);
 ```
 
-[example lint page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
+[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
+[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
+[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
+[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
 
 ## Lint passes
 
@@ -220,6 +246,9 @@ 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.
 
+[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
+[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+
 ## Emitting a lint
 
 With UI tests and the lint declaration in place, we can start working on the
@@ -257,6 +286,7 @@ impl EarlyLintPass for FooFunctions {
             FOO_FUNCTIONS,
             span,
             "function named `foo`",
+            None,
             "consider using a more meaningful name"
         );
     }
@@ -265,13 +295,16 @@ impl EarlyLintPass for FooFunctions {
 
 Running our UI test should now produce output that contains the lint message.
 
+[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
+[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
+
 ## Adding the lint logic
 
 Writing the logic for your lint will most likely be different from our example,
 so this section is kept rather short.
 
 Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
-that has the `FnKind::Fn` variant. It provides access to the name of the
+that has the [`FnKind::Fn`] variant. It provides access to the name of the
 function/method via an [`Ident`][ident].
 
 With that we can expand our `check_fn` method to:
@@ -285,6 +318,7 @@ impl EarlyLintPass for FooFunctions {
                 FOO_FUNCTIONS,
                 span,
                 "function named `foo`",
+                None,
                 "consider using a more meaningful name"
             );
         }
@@ -323,6 +357,10 @@ implementation is not violating any Clippy lints itself.
 That should be it for the lint implementation. Running `cargo test` should now
 pass.
 
+[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
+[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
+[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
+
 ## Author lint
 
 If you have trouble implementing your lint, there is also the internal `author`
@@ -340,6 +378,8 @@ see the generated code in the output below.
 If the command was executed successfully, you can copy the code over to where
 you are implementing your lint.
 
+[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
+
 ## Documentation
 
 The final thing before submitting our PR is to add some documentation to our
@@ -373,11 +413,13 @@ declare_clippy_lint! {
 Once your lint is merged, this documentation will show up in the [lint
 list][lint_list].
 
+[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
+
 ## Running rustfmt
 
-[Rustfmt](https://github.com/rust-lang/rustfmt) is a tool for formatting Rust
-code according to style guidelines. Your code has to be formatted by `rustfmt`
-before a PR can be merged. Clippy uses nightly `rustfmt` in the CI.
+[Rustfmt] is a tool for formatting Rust code according to style guidelines.
+Your code has to be formatted by `rustfmt` before a PR can be merged.
+Clippy uses nightly `rustfmt` in the CI.
 
 It can be installed via `rustup`:
 
@@ -388,12 +430,16 @@ rustup component add rustfmt --toolchain=nightly
 Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
 installed for the nightly toolchain.
 
+[Rustfmt]: https://github.com/rust-lang/rustfmt
+
 ## Debugging
 
 If you want to debug parts of your lint implementation, you can use the [`dbg!`]
 macro anywhere in your code. Running the tests should then include the debug
 output in the `stdout` part.
 
+[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
+
 ## PR Checklist
 
 Before submitting your PR make sure you followed all of the basic requirements:
@@ -418,6 +464,7 @@ Here are some pointers to things you are likely going to need for every lint:
 * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
 * [`Span`][span]
 * [`Applicability`][applicability]
+* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
 * [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
 * [The nightly rustc docs][nightly_docs] which has been linked to throughout
   this guide
@@ -437,26 +484,14 @@ documentation currently. This is unfortunate, but in most cases you can probably
 get away with copying things from existing similar lints. If you are stuck,
 don't hesitate to ask on [Discord] or in the issue/PR.
 
-[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
-[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
-[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/bd23cb89ec0ea63403a17d3fc5e50c88e38dd54f/clippy_lints/src/lib.rs#L43
-[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/a71acac1da7eaf667ab90a1d65d10e5cc4b80191/clippy_lints/src/lib.rs#L39
-[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
-[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
-[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
-[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
-[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/rustc_span/symbol/struct.Ident.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/*/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/rustc_ast/ast/index.html
 [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
-[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
+[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.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
 [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
-[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/
+[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
+[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
+[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
 [Discord]: https://discord.gg/rust-lang
-[`dbg`!]: https://doc.rust-lang.org/std/macro.dbg.html