]> git.lizzy.rs Git - rust.git/blobdiff - doc/adding_lints.md
Merge commit 'b40ea209e7f14c8193ddfc98143967b6a2f4f5c9' into clippyup
[rust.git] / doc / adding_lints.md
index 1a7a30c61be5b8fb9fc8987fc4fdc32667765165..99b86953d51a6287ceccb8bcb9207d881b0f368e 100644 (file)
@@ -18,11 +18,13 @@ because that's clearly a non-descriptive name.
   - [Lint passes](#lint-passes)
   - [Emitting a lint](#emitting-a-lint)
   - [Adding the lint logic](#adding-the-lint-logic)
+  - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
   - [Author lint](#author-lint)
   - [Documentation](#documentation)
   - [Running rustfmt](#running-rustfmt)
   - [Debugging](#debugging)
   - [PR Checklist](#pr-checklist)
+  - [Adding configuration to a lint](#adding-configuration-to-a-lint)
   - [Cheatsheet](#cheatsheet)
 
 ## Setup
@@ -107,6 +109,9 @@ should only commit files changed by `cargo dev bless` for the
 specific lint you are creating/editing. Note that if the generated files are
 empty, they should be removed.
 
+Note that you can run multiple test files by specifying a comma separated list:
+`TESTNAME=foo_functions,test2,test3`.
+
 ### Cargo lints
 
 For cargo lints, the process of testing differs in that we are interested in
@@ -288,7 +293,7 @@ the next section. Let's worry about the details later and emit our lint for
 
 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].
+[`clippy_utils/src/diagnostics.rs`][diagnostics].
 
 `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
@@ -317,7 +322,7 @@ When code or an identifier must appear in a message or label, it should be
 surrounded with single grave accents \`.
 
 [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
+[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
 [the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
 
 ## Adding the lint logic
@@ -383,18 +388,19 @@ pass.
 [`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
 
-## Specifying the lint's minimum supported Rust version (msrv)
+## Specifying the lint's minimum supported Rust version (MSRV)
 
-Projects supporting older versions of Rust would need to disable a lint if it targets features
-present in later versions. Support for this can be added by specifying an msrv in your lint like so,
+Projects supporting older versions of Rust would need to disable a lint if it
+targets features present in later versions. Support for this can be added by
+specifying an MSRV in your lint like so,
 
 ```rust
 const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
 ```
 
-The project's msrv will also have to be an attribute in the lint so you'll have to add a struct
-and constructor for your lint. The project's msrv needs to be passed when the lint is registered
-in `lib.rs`
+The project's MSRV will also have to be an attribute in the lint so you'll have
+to add a struct and constructor for your lint. The project's MSRV needs to be
+passed when the lint is registered in `lib.rs`
 
 ```rust
 pub struct ManualStrip {
@@ -409,8 +415,8 @@ impl ManualStrip {
 }
 ```
 
-The project's msrv can then be matched against the lint's msrv in the LintPass using the `meets_msrv` utility
-function.
+The project's MSRV can then be matched against the lint's `msrv` in the LintPass
+using the `meets_msrv` utility function.
 
 ``` rust
 if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
@@ -418,9 +424,10 @@ if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
 }
 ```
 
-The project's msrv can also be specified as an inner attribute, which overrides the value from
-`clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing
-LateContext/EarlyContext.
+The project's MSRV can also be specified as an inner attribute, which overrides
+the value from `clippy.toml`. This can be accounted for using the
+`extract_msrv_attr!(LintContext)` macro and passing
+`LateContext`/`EarlyContext`.
 
 ```rust
 impl<'tcx> LateLintPass<'tcx> for ManualStrip {
@@ -431,8 +438,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
 }
 ```
 
-Once the msrv is added to the lint, a relevant test case should be added to `tests/ui/min_rust_version_attr.rs`
-which verifies that the lint isn't emitted if the project's msrv is lower.
+Once the `msrv` is added to the lint, a relevant test case should be added to
+`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted
+if the project's MSRV is lower.
+
+As a last step, the lint should be added to the lint documentation. This is done
+in `clippy_lints/src/utils/conf.rs`:
+
+```rust
+define_Conf! {
+    /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
+    (msrv, "msrv": Option<String>, None),
+    ...
+}
+```
 
 ## Author lint
 
@@ -526,6 +545,81 @@ Before submitting your PR make sure you followed all of the basic requirements:
 - \[ ] Added lint documentation
 - \[ ] Run `cargo dev fmt`
 
+## Adding configuration to a lint
+
+Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace
+directory. Adding a configuration to a lint can be useful for thresholds or to constrain some
+behavior that can be seen as a false positive for some users. Adding a configuration is done
+in the following steps:
+
+1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs)
+    like this:
+    ```rust
+    /// Lint: LINT_NAME. <The configuration field doc comment>
+    (configuration_ident, "configuration_value": Type, DefaultValue),
+    ```
+    The configuration value and identifier should usually be the same. The doc comment will be
+    automatically added to the lint documentation.
+2. Adding the configuration value to the lint impl struct:
+    1. This first requires the definition of a lint impl struct. Lint impl structs are usually
+        generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
+        to add some kind of metadata to it:
+        ```rust
+        // Generated struct definition
+        declare_lint_pass!(StructName => [
+            LINT_NAME
+        ]);
+
+        // New manual definition struct
+        #[derive(Copy, Clone)]
+        pub struct StructName {}
+
+        impl_lint_pass!(StructName => [
+            LINT_NAME
+        ]);
+        ```
+
+    2. Next add the configuration value and a corresponding creation method like this:
+        ```rust
+        #[derive(Copy, Clone)]
+        pub struct StructName {
+            configuration_ident: Type,
+        }
+
+        // ...
+
+        impl StructName {
+            pub fn new(configuration_ident: Type) -> Self {
+                Self {
+                    configuration_ident,
+                }
+            }
+        }
+        ```
+3. Passing the configuration value to the lint impl struct:
+
+    First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs).
+    The configuration value is now cloned or copied into a local value that is then passed to the
+    impl struct like this:
+    ```rust
+    // Default generated registration:
+    store.register_*_pass(|| box module::StructName);
+
+    // New registration with configuration value
+    let configuration_ident = conf.configuration_ident.clone();
+    store.register_*_pass(move || box module::StructName::new(configuration_ident));
+    ```
+
+    Congratulations the work is almost done. The configuration value can now be accessed
+    in the linting code via `self.configuration_ident`.
+
+4. Adding tests:
+    1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui).
+    2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml).
+        Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file
+        with the configuration value and a rust file that should be linted by Clippy. The test can
+        otherwise be written as usual.
+
 ## Cheatsheet
 
 Here are some pointers to things you are likely going to need for every lint:
@@ -557,7 +651,7 @@ 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 [Zulip] or in the issue/PR.
 
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs
+[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
 [if_chain]: https://docs.rs/if_chain/*/if_chain/
 [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_middle/lint/fn.in_external_macro.html