diff --git a/Cargo.lock b/Cargo.lock index 74438734b0f1f..02ca28a287bbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5948,15 +5948,15 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "ui_test" -version = "0.30.5" +version = "0.30.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980133b75aa9a95dc94feaf629d92d22c1172186f1fa1266b91f5b91414cf9a5" +checksum = "8c8811281d587a786747c0c49245925016c07767bc996305bdd34d5ce076786a" dependencies = [ "annotate-snippets 0.11.5", "anyhow", "bstr", - "cargo-platform 0.1.9", - "cargo_metadata 0.18.1", + "cargo-platform 0.3.1", + "cargo_metadata 0.23.1", "color-eyre", "colored", "comma", diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 682e0c7af6d0c..337c900aaf0c8 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,7 +6,101 @@ document. ## Unreleased / Beta / In Rust Nightly -[df995e...master](https://github.com/rust-lang/rust-clippy/compare/df995e...master) +[88f787...master](https://github.com/rust-lang/rust-clippy/compare/88f787...master) + +## Rust 1.96 + +Current stable, released 2026-05-28 + +[View all 48 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2026-02-24T12%3A30%3A17Z..2026-04-03T17%3A32%3A48Z+base%3Amaster) + +### New Lints + +* Added [`manual_noop_waker`] to `complexity` + [#16687](https://github.com/rust-lang/rust-clippy/pull/16687) +* Added [`manual_option_zip`] to `complexity` + [#16600](https://github.com/rust-lang/rust-clippy/pull/16600) +* Added [`manual_pop_if`] to `complexity` + [#16582](https://github.com/rust-lang/rust-clippy/pull/16582) + +### Enhancements + +* [`explicit_counter_loop`] suggest `(init..).take(n)` when loop variable is unused and range is + `0..n` + [#16658](https://github.com/rust-lang/rust-clippy/pull/16658) +* [`iter_kv_map`] handle identity map for `map` and `flat_map` + [#16743](https://github.com/rust-lang/rust-clippy/pull/16743) +* [`manual_noop_waker`] add an MSRV check + [#16850](https://github.com/rust-lang/rust-clippy/pull/16850) +* [`manual_pop_if`] in case the popped value is used, just emit the lint with no suggestion + [#16683](https://github.com/rust-lang/rust-clippy/pull/16683) +* [`manual_pop_if`] also cover `.pop().unwrap_unchecked()` + [#16683](https://github.com/rust-lang/rust-clippy/pull/16683) +* [`manual_pop_if`] detect manual implementations of `BinaryHeap::pop_if()` + [#16734](https://github.com/rust-lang/rust-clippy/pull/16734) +* [`unnecessary_option_map_or_else`] function definitions are no longer traversed when checking if + an expression is the identity + [#15889](https://github.com/rust-lang/rust-clippy/pull/15889) +* [`unnecessary_result_map_or_else`] function definitions are no longer traversed when checking if + an expression is the identity + [#15889](https://github.com/rust-lang/rust-clippy/pull/15889) +* [`question_mark`] fix suggestion-caused error caused by semicolon inference relying only on + parent-node shape + [#16656](https://github.com/rust-lang/rust-clippy/pull/16656) +* Format-related lints now handle `core::panic!` + [#16597](https://github.com/rust-lang/rust-clippy/pull/16597) +* [`explicit_counter_loop`] fix FN when the initializer is not integral + [#16647](https://github.com/rust-lang/rust-clippy/pull/16647) +* [`suboptimal_flops`] fix FN on add and sub assign + [#16625](https://github.com/rust-lang/rust-clippy/pull/16625) +* [`infinite_loop`] fix wrong suggestion to add `-> !` when the loop is inside a conditional branch + [#16619](https://github.com/rust-lang/rust-clippy/pull/16619) +* [`unnecessary_cast`] preserve parentheses in presence of cascaded casts + [#16483](https://github.com/rust-lang/rust-clippy/pull/16483) +* [`cmp_owned`] fix wrong suggestions on `PathBuf` + [#16628](https://github.com/rust-lang/rust-clippy/pull/16628) +* [`redundant_closure`] fix wrong suggestions when local is dereferenced to callable + [#16648](https://github.com/rust-lang/rust-clippy/pull/16648) + +### False Positive Fixes + +* [`collapsible_if`] fix FP when the inner if contains cfg + [#16757](https://github.com/rust-lang/rust-clippy/pull/16757) +* [`collapsible_match`] fix FP when the pat binding is moved or mutated + [#16708](https://github.com/rust-lang/rust-clippy/pull/16708) +* [`collapsible_match`] fix a case where a suggested transformation changes runtime behavior + [#16878](https://github.com/rust-lang/rust-clippy/pull/16878) +* [`match_same_arms`] fix FP with associated consts + [#16701](https://github.com/rust-lang/rust-clippy/pull/16701) +* [`semicolon_inside_block`] fix FP in `try` blocks where moving `;` changes the block's return + type and causes type errors + [#16697](https://github.com/rust-lang/rust-clippy/pull/16697) +* [`unnecessary_safety_comment`] fix FP on code blocks inside inner docs + [#16559](https://github.com/rust-lang/rust-clippy/pull/16559) +* [`doc_paragraphs_missing_punctuation`] no longer lints punctuated paragraphs with a trailing + emoji + [#16514](https://github.com/rust-lang/rust-clippy/pull/16514) + +### ICE Fixes + +* [`match_same_arms`] fix ICE in `match_same_arms` + [#16685](https://github.com/rust-lang/rust-clippy/pull/16685) +* [`nonminimal_bool`] fix ICE in `swap_binop()` by using the proper `TypeckResults` + [#16659](https://github.com/rust-lang/rust-clippy/pull/16659) +* Fix ICE when using the `min_generic_const_args` incomplete feature + [#16692](https://github.com/rust-lang/rust-clippy/pull/16692) + +### Documentation Improvements + +* [`similar_names`] changed the lint docs to reflect its actual behavior + [#16300](https://github.com/rust-lang/rust-clippy/pull/16300) + +### Performance Improvements + +* [`repeat_vec_with_capacity`] optimized by 96.876% (784M -> 24M instructions) + [#16756](https://github.com/rust-lang/rust-clippy/pull/16756) +* [`manual_is_ascii_check`] optimized by 97.125% (822M -> 23M instructions) + [#16755](https://github.com/rust-lang/rust-clippy/pull/16755) ## Rust 1.95 @@ -6746,6 +6840,7 @@ Released 2018-09-13 [`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option [`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles +[`for_unbounded_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_unbounded_range [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref @@ -6906,6 +7001,7 @@ Released 2018-09-13 [`manual_is_multiple_of`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_multiple_of [`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and +[`manual_isolate_lowest_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_isolate_lowest_one [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map @@ -7424,6 +7520,7 @@ Released 2018-09-13 [`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm [`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports [`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns +[`with_capacity_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#with_capacity_zero [`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal [`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline [`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string @@ -7511,6 +7608,7 @@ Released 2018-09-13 [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit +[`profiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#profiles [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 8aeba28494cb3..ccfbb0b88387e 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -124,8 +124,11 @@ To have `rust-analyzer` also work in the `clippy_dev` and `lintcheck` crates, ad ## How Clippy works -[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`]. -For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this: +[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers them in the +[`LintStore`]. All early passes are folded into a single `CombinedEarlyLintPass`, and all late passes into a single +`CombinedLateLintPass`, each registered once with the store. A pass is added to one of these by listing it in the +`early_lint_methods!` or `late_lint_methods!` macro invocation. For example, the +[`else_if_without_else`][else_if_without_else] lint is added like this: ```rust // ./clippy_lints/src/lib.rs @@ -134,18 +137,21 @@ For example, the [`else_if_without_else`][else_if_without_else] lint is register pub mod else_if_without_else; // ... -pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - // ... - store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); - // ... -} +rustc_lint::early_lint_methods!( + crate::combined_early_lint_pass, + [CombinedEarlyLintPass, (/* ... */), [ + // ... + ElseIfWithoutElse: else_if_without_else::ElseIfWithoutElse = else_if_without_else::ElseIfWithoutElse, + // ... + ]] +); ``` -The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints: -[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object -that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in -every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev -update_lints`. When you are writing your own lint, you can use that script to save you some time. +Each entry has the form `Field: Type = constructor`, where the constructor builds the pass (passing `conf` when the pass +needs the user configuration). The combined passes implement [`EarlyLintPass`][early_lint_pass] and +[`LateLintPass`][late_lint_pass] respectively, so each listed pass must also implement the matching trait. It's worth +noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev update_lints`. When you are +writing your own lint, you can use that script to save you some time. ```rust // ./clippy_lints/src/else_if_without_else.rs @@ -167,14 +173,12 @@ The difference between `EarlyLintPass` and `LateLintPass` is that the methods of AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information via the `LateContext` parameter. -That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the +That's why the `else_if_without_else` example is listed in `early_lint_methods!`. Because the [actual lint logic][else_if_without_else] does not depend on any type information. [lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs [else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs [`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html -[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass -[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass [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 diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index b93a3f1cbe9c7..06c1840a3da26 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -34,7 +34,7 @@ anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.23" -ui_test = "0.30.5" +ui_test = "0.30.7" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md index aff9222ea6066..cb2ac67361bef 100644 --- a/src/tools/clippy/book/src/configuration.md +++ b/src/tools/clippy/book/src/configuration.md @@ -89,7 +89,7 @@ cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... #### Lints Section in `Cargo.toml` Finally, lints can be allowed/denied using [the lints -section](https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-lints-section)) in the `Cargo.toml` file: +section](https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-lints-section) in the `Cargo.toml` file: To deny `clippy::enum_glob_use`, put the following in the `Cargo.toml`: diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index d9a5f04c3e1c0..474b8c6c474ef 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -282,21 +282,22 @@ When using `cargo dev new_lint`, the lint is automatically registered and nothing more has to be done. When declaring a new lint by hand and `cargo dev update_lints` is used, the lint -pass may have to be registered manually in the `register_lints` function in -`clippy_lints/src/lib.rs`: +pass may have to be registered manually by adding an entry to the +`early_lint_methods!` macro invocation in `clippy_lints/src/lib.rs`, at the +`// add early passes here` marker: ```rust,ignore -store.register_early_pass(|| Box::new(foo_functions::FooFunctions)); +FooFunctions: foo_functions::FooFunctions = foo_functions::FooFunctions, ``` -As one may expect, there is a corresponding `register_late_pass` method -available as well. Without a call to one of `register_early_pass` or -`register_late_pass`, the lint pass in question will not be run. +As one may expect, there is a corresponding `late_lint_methods!` macro available +as well. Without an entry in one of `early_lint_methods!` or `late_lint_methods!`, +the lint pass in question will not be run. One reason that `cargo dev update_lints` does not automate this step is that multiple lints can use the same lint pass, so registering the lint pass may already be done when adding a new lint. Another reason that this step is not -automated is that the order that the passes are registered determines the order +automated is that the order that the passes are listed determines the order the passes actually run, which in turn affects the order that any emitted lints are output in. diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index cb6d7b740dbd1..cc108ce70d3d7 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -184,18 +184,19 @@ However, sometimes we might want to declare a new lint by hand. In this case, we'd use `cargo dev update_lints` command afterwards. When a lint is manually declared, we might need to register the lint pass -manually in the `register_lints` function in `clippy_lints/src/lib.rs`: +manually by adding an entry to the `late_lint_methods!` macro invocation in +`clippy_lints/src/lib.rs`, at the `// add late passes here` marker: ```rust -store.register_late_pass(|_| Box::new(foo_functions::FooFunctions)); +FooFunctions: foo_functions::FooFunctions = foo_functions::FooFunctions, ``` As you might have guessed, where there's something late, there is something -early: in Clippy there is a `register_early_pass` method as well. More on early +early: in Clippy there is an `early_lint_methods!` macro as well. More on early vs. late passes in the [Lint Passes] chapter. -Without a call to one of `register_early_pass` or `register_late_pass`, the lint -pass in question will not be run. +Without an entry in one of `early_lint_methods!` or `late_lint_methods!`, the +lint pass in question will not be run. [all_lints]: https://rust-lang.github.io/rust-clippy/master/ diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 64d0bf9b62f69..e123d7ba5704d 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -609,7 +609,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "SQLite", "MySQL", "PostgreSQL", "MariaDB", "MongoDB", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** @@ -780,7 +780,8 @@ be filtering for common types. ## `max-fn-params-bools` -The maximum number of bool parameters a function can have +The maximum number of bool parameters a function can have. +Use `0` to lint on any function with a bool parameter. **Default Value:** `3` @@ -925,6 +926,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) * [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) * [`manual_is_power_of_two`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two) +* [`manual_isolate_lowest_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_isolate_lowest_one) * [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [`manual_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint) * [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) @@ -984,6 +986,28 @@ The minimum size (in bytes) to consider a type for passing by reference instead * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) +## `profiles` +Named profiles of disallowed items (unrelated to Cargo build profiles). + +#### Example + +```toml +[profiles.persistent] +disallowed-methods = [{ path = "std::env::temp_dir" }] +disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] + +[profiles.single_threaded] +disallowed-methods = [{ path = "std::thread::spawn" }] +``` + +**Default Value:** `{}` + +--- +**Affected lints:** +* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) +* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) + + ## `pub-underscore-fields-behavior` Lint "public" fields in a struct that are prefixed with an underscore based on their exported visibility, or whether they are marked as "pub". diff --git a/src/tools/clippy/book/src/lints.md b/src/tools/clippy/book/src/lints.md index 55b382c855651..8943274a38e81 100644 --- a/src/tools/clippy/book/src/lints.md +++ b/src/tools/clippy/book/src/lints.md @@ -5,10 +5,11 @@ and idiomatic Rust code. A full list of all lints, that can be filtered by category, lint level or keywords, can be found in the [Clippy lint documentation]. -This chapter will give an overview of the different lint categories, which kind -of lints they offer and recommended actions when you should see a lint out of +This chapter provides details about the different lint categories, which kind +of lints they offer, and recommended actions when you should see a lint out of that category. For examples, see the [Clippy lint documentation] and filter by -category. +category. For an overview of these categories, see the +[introduction](index.md). The different lint groups were defined in the [Clippy 1.0 RFC]. diff --git a/src/tools/clippy/build.rs b/src/tools/clippy/build.rs index b79d09b0dd2d2..4a28465c77151 100644 --- a/src/tools/clippy/build.rs +++ b/src/tools/clippy/build.rs @@ -1,6 +1,4 @@ fn main() { - // Forward the profile to the main compilation - println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap()); // Don't rebuild even if nothing changed println!("cargo:rerun-if-changed=build.rs"); rustc_tools_util::setup_version_info!(); diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 465e88a783ed8..47bc7b572c9d0 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,12 +1,13 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, - PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + DisallowedPath, DisallowedPathWithoutReplacement, DisallowedProfile, InherentImplLintScope, MacroMatcher, + MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -38,6 +39,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", + "SQLite", "MySQL", "PostgreSQL", "MariaDB", "MongoDB", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", @@ -221,12 +223,74 @@ macro_rules! deserialize { }}; } +macro_rules! parse_conf_value { + ( + $map:expr, + $ty:ty, + $errors:expr, + $file:expr, + $field_span:expr, + profiles @[$($profiles:expr)?], + disallowed @[$($disallowed:expr)?] + ) => { + parse_conf_value_impl!( + $map, + $ty, + $errors, + $file, + $field_span, + ($($profiles)?), + ($($disallowed)?) + ) + }; +} + +macro_rules! parse_conf_value_impl { + ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ()) => {{ + let _ = &$field_span; + deserialize!($map, $ty, $errors, $file) + }}; + ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, ($profiles:expr), ()) => {{ + let raw_value = $map.next_value::()?; + let value_span = $field_span.clone(); + let toml::Value::Table(table) = raw_value else { + $errors.push(ConfError::spanned( + $file, + "expected table with named profiles", + None, + value_span.clone(), + )); + continue; + }; + + let map = parse_profiles(table, $file, value_span.clone(), &mut $errors); + + (map, value_span) + }}; + ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ($disallowed:expr)) => {{ + let _ = &$field_span; + deserialize!($map, $ty, $errors, $file, $disallowed) + }}; + ( + $map:expr, + $ty:ty, + $errors:expr, + $file:expr, + $field_span:expr, + ($profiles:expr), + ($disallowed:expr) + ) => { + compile_error!("field cannot specify both profiles and disallowed-paths attributes") + }; +} + macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? + $(#[profiles = $profiles:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -281,10 +345,20 @@ macro_rules! define_Conf { match field { $(Field::$name => { + let field_span = name.span(); // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let (value, value_span) = - deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); + let (value, value_span) = parse_conf_value!( + map, + $ty, + errors, + self.0, + field_span, + // Disallowed-profile table parsing is special-cased to preserve spans for + // diagnostics in disallowed-path entries. + profiles @[$($profiles)?], + disallowed @[$($replacements_allowed)?] + ); // Was this field set previously? if $name.is_some() { errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); @@ -341,6 +415,121 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { ) } +fn parse_profiles( + table: toml::value::Table, + file: &SourceFile, + value_span: Range, + errors: &mut Vec, +) -> FxHashMap { + let mut profiles = FxHashMap::default(); + let config_span = span_from_toml_range(file, value_span.clone()); + + for (profile_name, profile_value) in table { + let toml::Value::Table(mut profile_table) = profile_value else { + errors.push(ConfError::spanned( + file, + format!("invalid profile `{profile_name}`: expected table"), + None, + value_span.clone(), + )); + continue; + }; + + let disallowed_methods = match profile_table + .remove("disallowed-methods") + .or_else(|| profile_table.remove("disallowed_methods")) + { + Some(value) => parse_profile_list( + file, + &profile_name, + "disallowed-methods", + value, + value_span.clone(), + config_span, + errors, + ), + None => Vec::new(), + }; + + let disallowed_types = match profile_table + .remove("disallowed-types") + .or_else(|| profile_table.remove("disallowed_types")) + { + Some(value) => parse_profile_list( + file, + &profile_name, + "disallowed-types", + value, + value_span.clone(), + config_span, + errors, + ), + None => Vec::new(), + }; + + if !profile_table.is_empty() { + let keys = profile_table.keys().map(String::as_str).collect::>().join(", "); + errors.push(ConfError::spanned( + file, + format!("profile `{profile_name}` has unknown keys: {keys}"), + None, + value_span.clone(), + )); + } + + profiles.insert( + profile_name, + DisallowedProfile { + disallowed_methods, + disallowed_types, + }, + ); + } + + profiles +} + +fn parse_profile_list( + file: &SourceFile, + profile_name: &str, + key_name: &str, + value: toml::Value, + value_span: Range, + config_span: Span, + errors: &mut Vec, +) -> Vec { + let toml::Value::Array(entries) = value else { + errors.push(ConfError::spanned( + file, + format!("profile `{profile_name}`: `{key_name}` must be an array"), + None, + value_span, + )); + return Vec::new(); + }; + + let mut disallowed = Vec::with_capacity(entries.len()); + for entry in entries { + match DisallowedPath::deserialize(entry.clone()) { + Ok(mut path) => { + path.set_span(config_span); + disallowed.push(path); + }, + Err(err) => errors.push(ConfError::spanned( + file, + format!( + "profile `{profile_name}`: {}", + err.to_string().replace('\n', " ").trim() + ), + None, + value_span.clone(), + )), + } + } + + disallowed +} + define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -713,7 +902,8 @@ define_Conf! { /// be filtering for common types. #[lints(manual_let_else)] matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes, - /// The maximum number of bool parameters a function can have + /// The maximum number of bool parameters a function can have. + /// Use `0` to lint on any function with a bool parameter. #[lints(fn_params_excessive_bools)] max_fn_params_bools: u64 = 3, /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -786,6 +976,7 @@ define_Conf! { manual_hash_one, manual_is_ascii_check, manual_is_power_of_two, + manual_isolate_lowest_one, manual_let_else, manual_midpoint, manual_non_exhaustive, @@ -838,6 +1029,21 @@ define_Conf! { /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. #[lints(large_types_passed_by_value)] pass_by_value_size_limit: u64 = 256, + /// Named profiles of disallowed items (unrelated to Cargo build profiles). + /// + /// #### Example + /// + /// ```toml + /// [profiles.persistent] + /// disallowed-methods = [{ path = "std::env::temp_dir" }] + /// disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] + /// + /// [profiles.single_threaded] + /// disallowed-methods = [{ path = "std::thread::spawn" }] + /// ``` + #[profiles = true] + #[lints(disallowed_methods, disallowed_types)] + profiles: FxHashMap = FxHashMap::default(), /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 8d9326a904b1e..5eaa44ddd51d7 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -57,6 +57,15 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, + #[serde(default, alias = "disallowed_types")] + pub disallowed_types: Vec, +} + // `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just // above. `DisallowedPathEnum` is not meant to be used outside of this file. #[derive(Debug, Deserialize, Serialize)] diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index a5e2050a3865e..4e44cad472ae0 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -157,20 +157,29 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let path = "clippy_lints/src/lib.rs"; let mut lib_rs = fs::read_to_string(path).context("reading")?; - let (comment, ctor_arg) = if lint.pass == Pass::Late { - ("// add late passes here", "_") - } else { - ("// add early passes here", "") - }; - let comment_start = lib_rs.find(comment).expect("Couldn't find comment"); let module_name = lint.name; let camel_name = to_camel_case(lint.name); - let new_lint = if enable_msrv { - format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ") + let (comment, new_lint) = if lint.pass == Pass::Late { + // Late passes are folded into the statically-combined struct, so a new + // entry is just `Field: Type = constructor` (see `combined_late_pass`). + let new_lint = if enable_msrv { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name}::new(conf),\n ") + } else { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name},\n ") + }; + ("// add late passes here", new_lint) } else { - format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ") + // Early passes are folded into the statically-combined struct, so a new + // entry is just `Field: Type = constructor` (see `combined_early_pass`). + let new_lint = if enable_msrv { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name}::new(conf),\n ") + } else { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name},\n ") + }; + ("// add early passes here", new_lint) }; + let comment_start = lib_rs.find(comment).expect("Couldn't find comment"); lib_rs.insert_str(comment_start, &new_lint); diff --git a/src/tools/clippy/clippy_dummy/Cargo.toml b/src/tools/clippy/clippy_dummy/Cargo.toml index 61bdd421c764e..5c7ed584309aa 100644 --- a/src/tools/clippy/clippy_dummy/Cargo.toml +++ b/src/tools/clippy/clippy_dummy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_dummy" # rename to clippy before publishing -version = "0.0.303" +version = "0.0.304" edition = "2024" readme = "crates-readme.md" description = "A bunch of helpful lints to avoid common pitfalls in Rust." @@ -13,4 +13,4 @@ keywords = ["clippy", "lint", "plugin"] categories = ["development-tools", "development-tools::cargo-plugins"] [build-dependencies] -term = "0.7" +term = "1" diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs index c0b14c2a4b66e..7ba7f803d2229 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs @@ -1,17 +1,20 @@ use super::INLINE_ALWAYS; +use super::utils::is_relevant_expr; use clippy_utils::diagnostics::span_lint; use rustc_hir::attrs::InlineAttr; -use rustc_hir::{Attribute, find_attr}; +use rustc_hir::{Attribute, BodyId, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; use rustc_span::symbol::Symbol; -pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { +pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute], body: Option) { if span.from_expansion() { return; } - if let Some(span) = find_attr!(attrs, Inline(InlineAttr::Always, span) => *span) { + if let Some(span) = find_attr!(attrs, Inline(InlineAttr::Always, span) => *span) + && body.is_none_or(|body| is_relevant_expr(cx, cx.tcx.hir_body(body).value)) + { span_lint( cx, INLINE_ALWAYS, diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 372defbb4d7e2..78701b3368226 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -14,14 +14,15 @@ mod useless_attribute; mod utils; use clippy_config::Conf; +use clippy_utils::check_clippy_attr; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; use rustc_ast::{self as ast, AttrArgs, AttrItemKind, AttrKind, Attribute, MetaItemInner, MetaItemKind}; -use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, TraitFn, TraitItem, TraitItemKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::sym; -use utils::{is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait}; +use utils::is_lint_level; declare_clippy_lint! { /// ### What it does @@ -512,23 +513,31 @@ impl Attributes { impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let attrs = cx.tcx.hir_attrs(item.hir_id()); - if let ItemKind::Fn { ident, .. } = item.kind - && is_relevant_item(cx, item) - { - inline_always::check(cx, item.span, ident.name, attrs); + if let ItemKind::Fn { ident, body, .. } = item.kind { + inline_always::check(cx, item.span, ident.name, attrs, Some(body)); } repr_attributes::check(cx, item.span, attrs, self.msrv); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - if is_relevant_impl(cx, item) { - inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); + if let ImplItemKind::Fn(_, body) = item.kind { + inline_always::check( + cx, + item.span, + item.ident.name, + cx.tcx.hir_attrs(item.hir_id()), + Some(body), + ); } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - if is_relevant_trait(cx, item) { - inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); + if let TraitItemKind::Fn(_, kind) = item.kind { + let body = match kind { + TraitFn::Required(_) => None, + TraitFn::Provided(body) => Some(body), + }; + inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id()), body); } } } @@ -574,6 +583,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + check_clippy_attr(cx.sess(), attr); if let Some(items) = &attr.meta_item_list() && let Some(name) = attr.name() { diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index 4822bdcb9bde2..9e56e019229ec 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -1,10 +1,7 @@ use clippy_utils::macros::{is_panic, macro_backtrace}; use rustc_ast::MetaItemInner; -use rustc_hir::{ - Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, -}; +use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, Level}; -use rustc_middle::ty; use rustc_span::sym; use rustc_span::symbol::Symbol; @@ -20,56 +17,26 @@ pub(super) fn is_lint_level(symbol: Symbol) -> bool { Level::from_symbol(symbol).is_some() } -pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if let ItemKind::Fn { body: eid, .. } = item.kind { - is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value) - } else { - false - } -} - -pub(super) fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool { - match item.kind { - ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value), - _ => false, - } -} - -pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { - match item.kind { - TraitItemKind::Fn(_, TraitFn::Required(_)) => true, - TraitItemKind::Fn(_, TraitFn::Provided(eid)) => { - is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value) - }, - _ => false, - } -} - -fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool { +fn is_relevant_block(cx: &LateContext<'_>, block: &Block<'_>) -> bool { block.stmts.first().map_or_else( - || { - block - .expr - .as_ref() - .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)) - }, + || block.expr.as_ref().is_some_and(|e| is_relevant_expr(cx, e)), |stmt| match &stmt.kind { StmtKind::Let(_) => true, - StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, expr), StmtKind::Item(_) => false, }, ) } -fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { +pub(super) fn is_relevant_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if macro_backtrace(expr.span).last().is_some_and(|macro_call| { is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable }) { return false; } match &expr.kind { - ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), - ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), + ExprKind::Block(block, _) => is_relevant_block(cx, block), + ExprKind::Ret(Some(e)) => is_relevant_expr(cx, e), ExprKind::Ret(None) | ExprKind::Break(_, None) => false, _ => true, } diff --git a/src/tools/clippy/clippy_lints/src/combined_early_pass.rs b/src/tools/clippy/clippy_lints/src/combined_early_pass.rs new file mode 100644 index 0000000000000..85de0b00a1b04 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/combined_early_pass.rs @@ -0,0 +1,92 @@ +//! A statically-combined early lint pass. +//! +//! The early-pass analogue of [`combined_late_pass`]. Folds clippy's early +//! passes into one concrete struct, one field per pass, with a single +//! `EarlyLintPass` impl that forwards each `check_*` to every field. Same +//! static-dispatch / DCE win as the late version: because the field types are +//! concrete and the forwards are `#[inline(always)]`, a pass that doesn't +//! override a `check_*` contributes only the empty default body, which is +//! DCE'd away. So the per-node, per-pass indirect (vtable) call into an empty +//! method disappears entirely, and the passes that do override become direct, +//! inlined calls. No vtable, no per-node dynamic dispatch. +//! +//! Unlike the late combine there is no `active` gate. rustc drops fully-disabled +//! late passes via `lints_that_dont_need_to_run`, but the early pass runner has +//! no such filtering, so a plain forward is equivalent and loses nothing. +//! +//! [`combined_late_pass`]: crate::combined_late_pass + +/// Run one field's `check_*`. +/// +/// Fully qualified through [`rustc_lint::EarlyLintPass`] since some passes impl +/// both `EarlyLintPass` and `LateLintPass` with like-named methods, which would +/// be ambiguous on the concrete field type. +#[macro_export] +macro_rules! run_combined_early_lint_pass_field { + ($self:ident, $field:ident, $name:ident, ($($arg:expr),* $(,)?)) => { + rustc_lint::EarlyLintPass::$name(&mut $self.$field, $($arg),*); + }; +} + +/// Forward one `check_*` method to every field of the combined pass. +#[macro_export] +macro_rules! expand_combined_early_lint_pass_method { + ([$($field:ident),*], $self:ident, $name:ident, $args:tt) => ({ + $($crate::run_combined_early_lint_pass_field!($self, $field, $name, $args);)* + }) +} + +/// Generate the combined `EarlyLintPass` impl's `check_*` methods, one per method +/// in rustc's early-pass method list. +#[macro_export] +macro_rules! expand_combined_early_lint_pass_methods { + ($fields:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(#[inline(always)] fn $name(&mut self, cx: &rustc_lint::EarlyContext<'_>, $($param: $arg),*) { + $crate::expand_combined_early_lint_pass_method!($fields, self, $name, (cx, $($param),*)); + })* + ) +} + +/// Declare the combined struct (one field per pass) plus its +/// `LintPass`/`EarlyLintPass` impls. The method list comes from +/// `rustc_lint::early_lint_methods!` so it can't drift from rustc's. +/// +/// Each entry is `Field: Type = constructor`; `new`'s params (`conf`, ...) come +/// from the caller so ctor exprs can name them without hygiene trouble. +#[macro_export] +macro_rules! combined_early_lint_pass { + ( + [$name:ident, ($($pname:ident: $pty:ty),* $(,)?), [$($field:ident: $fty:ty = $ctor:expr,)*]], + $methods:tt + ) => { + #[allow(non_snake_case)] + pub struct $name { + $($field: $fty,)* + } + + impl $name { + pub fn new($($pname: $pty,)*) -> Self { + Self { + $($field: $ctor,)* + } + } + } + + #[allow(rustc::lint_pass_impl_without_macro)] + impl rustc_lint::LintPass for $name { + fn name(&self) -> &'static str { + stringify!($name) + } + fn get_lints(&self) -> rustc_lint::LintVec { + // Reserve at least one slot per pass up front to skip the early reallocations. + let mut lints = Vec::with_capacity([$(stringify!($field)),*].len()); + $(lints.extend(self.$field.get_lints());)* + lints + } + } + + impl rustc_lint::EarlyLintPass for $name { + $crate::expand_combined_early_lint_pass_methods!([$($field),*], $methods); + } + }; +} diff --git a/src/tools/clippy/clippy_lints/src/combined_late_pass.rs b/src/tools/clippy/clippy_lints/src/combined_late_pass.rs new file mode 100644 index 0000000000000..135603fd15d08 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/combined_late_pass.rs @@ -0,0 +1,106 @@ +//! A statically-combined late lint pass. +//! +//! Folds clippy's ~300 late passes into one concrete struct, one field per pass. +//! The single `LateLintPass` impl forwards each `check_*` to every field; with +//! concrete types and `#[inline(always)]`, unoverridden methods are DCE'd and the +//! rest become direct calls, so there is no vtable or per-node dynamic dispatch. +//! +//! Mirrors rustc's `declare_combined_late_lint_pass!`, but wraps each field in +//! [`Gated`] with a precomputed `active` flag (the same "lint still needs to run" +//! predicate `rustc_lint::late` uses). Disabled passes are skipped by a branch +//! rather than dropped from a `Vec`, keeping clippy's allow-by-default fast path. + +use rustc_lint::{LintPass, LintVec}; + +/// A pass paired with its precomputed "still needs to run" flag. +pub struct Gated

{ + pub(crate) active: bool, + pub(crate) pass: P, +} + +impl Gated

{ + #[inline] + pub fn new bool>(is_active: &F, pass: P) -> Self { + let active = is_active(&pass.get_lints()); + Gated { active, pass } + } +} + +/// Run one field's `check_*`, if that field is active. +/// +/// Fully qualified through [`rustc_lint::LateLintPass`] since some passes impl +/// both `EarlyLintPass` and `LateLintPass` with like-named methods, which would +/// be ambiguous on the concrete field type. The args arrive as one `tt` and are +/// re-parsed here so the per-field and per-argument repetitions never share a +/// nesting level. +#[macro_export] +macro_rules! run_combined_late_lint_pass_field { + ($self:ident, $field:ident, $name:ident, ($($arg:expr),* $(,)?)) => { + if $self.$field.active { + rustc_lint::LateLintPass::$name(&mut $self.$field.pass, $($arg),*); + } + }; +} + +/// Forward one `check_*` method to every field of the combined pass. +#[macro_export] +macro_rules! expand_combined_late_lint_pass_method { + ([$($field:ident),*], $self:ident, $name:ident, $args:tt) => ({ + $($crate::run_combined_late_lint_pass_field!($self, $field, $name, $args);)* + }) +} + +/// Generate the combined `LateLintPass` impl's `check_*` methods, one per method +/// in rustc's late-pass method list. +#[macro_export] +macro_rules! expand_combined_late_lint_pass_methods { + ($fields:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(#[inline(always)] fn $name(&mut self, cx: &rustc_lint::LateContext<'tcx>, $($param: $arg),*) { + $crate::expand_combined_late_lint_pass_method!($fields, self, $name, (cx, $($param),*)); + })* + ) +} + +/// Declare the combined struct (one [`Gated`] field per pass) plus its +/// `LintPass`/`LateLintPass` impls. The method list comes from +/// `rustc_lint::late_lint_methods!` so it can't drift from rustc's. +/// +/// Each entry is `Field: Type = constructor`; `new`'s params (`tcx`, `conf`, ...) +/// come from the caller so ctor exprs can name them without hygiene trouble. +#[macro_export] +macro_rules! combined_late_lint_pass { + ( + [$name:ident, ($($pname:ident: $pty:ty),* $(,)?), [$($field:ident: $fty:ty = $ctor:expr,)*]], + $methods:tt + ) => { + #[allow(non_snake_case)] + pub struct $name<'tcx> { + $($field: $crate::combined_late_pass::Gated<$fty>,)* + } + + impl<'tcx> $name<'tcx> { + pub fn new bool>($($pname: $pty,)* is_active: &F) -> Self { + Self { + $($field: $crate::combined_late_pass::Gated::new(is_active, $ctor),)* + } + } + } + + #[allow(rustc::lint_pass_impl_without_macro)] + impl<'tcx> rustc_lint::LintPass for $name<'tcx> { + fn name(&self) -> &'static str { + stringify!($name) + } + fn get_lints(&self) -> rustc_lint::LintVec { + // Reserve at least one slot per pass up front to skip the early reallocations. + let mut lints = Vec::with_capacity([$(stringify!($field)),*].len()); + $(lints.extend(self.$field.pass.get_lints());)* + lints + } + } + + impl<'tcx> rustc_lint::LateLintPass<'tcx> for $name<'tcx> { + $crate::expand_combined_late_lint_pass_methods!([$($field),*], $methods); + } + }; +} diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index b5047c628d0f0..6daa70f390aaa 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -276,6 +276,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, crate::loops::EXPLICIT_ITER_LOOP_INFO, crate::loops::FOR_KV_MAP_INFO, + crate::loops::FOR_UNBOUNDED_RANGE_INFO, crate::loops::INFINITE_LOOP_INFO, crate::loops::ITER_NEXT_LOOP_INFO, crate::loops::MANUAL_FIND_INFO, @@ -385,7 +386,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::FLAT_MAP_IDENTITY_INFO, crate::methods::FLAT_MAP_OPTION_INFO, crate::methods::FORMAT_COLLECT_INFO, - crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO, crate::methods::GET_FIRST_INFO, crate::methods::GET_LAST_WITH_LEN_INFO, crate::methods::GET_UNWRAP_INFO, @@ -613,6 +613,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::INVALID_UPCAST_COMPARISONS_INFO, crate::operators::MANUAL_DIV_CEIL_INFO, crate::operators::MANUAL_IS_MULTIPLE_OF_INFO, + crate::operators::MANUAL_ISOLATE_LOWEST_ONE_INFO, crate::operators::MANUAL_MIDPOINT_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, crate::operators::MODULO_ARITHMETIC_INFO, @@ -807,6 +808,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, + crate::with_capacity_zero::WITH_CAPACITY_ZERO_INFO, crate::write::PRINT_LITERAL_INFO, crate::write::PRINT_STDERR_INFO, crate::write::PRINT_STDOUT_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 9ea70159eb9f1..649d8449e431b 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -56,7 +56,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { // Inline const supports type inference. let is_parent_const = matches!( cx.tcx.hir_body_const_context(cx.tcx.hir_body_owner_def_id(body.id())), - Some(ConstContext::Const { allow_const_fn_promotion: true } | ConstContext::Static(_)) + Some( + ConstContext::Const { + allow_const_fn_promotion: true + } | ConstContext::Static(_) + ) ); let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index ff92cd0598393..f1d481909d675 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -17,6 +17,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), + #[clippy::version = "1.98.0"] + ("clippy::from_iter_instead_of_collect", "lint has proved problematic"), #[clippy::version = "1.88.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), #[clippy::version = "pre 1.29.0"] diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 7a5150da6593a..26a06992d1a6f 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; +use clippy_utils::ty::{ + adjust_derefs_manually_drop, get_adt_inherent_method, implements_trait, is_manually_drop, peel_and_count_ty_refs, +}; use clippy_utils::{ DefinedTy, ExprUseNode, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym, }; @@ -17,7 +19,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults, Unnormalized}; +use rustc_middle::ty::{self, AssocTag, Ty, TyCtxt, TypeVisitableExt, TypeckResults, Unnormalized}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol, SyntaxContext}; use std::borrow::Cow; @@ -368,12 +370,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }, ExprUseNode::Callee | ExprUseNode::FieldAccess(_) if !use_site.moved_before_use => true, ExprUseNode::MethodArg(hir_id, _, 0) if !use_site.moved_before_use => { - // Check for calls to trait methods where the trait is implemented - // on a reference. - // Two cases need to be handled: + // Check for calls to trait methods where auto-borrow will not resolve. + // Three cases need to be handled: // * `self` methods on `&T` will never have auto-borrow // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take // priority. + // * `&self` methods on `T` can have auto-borrow, but if there's another method with the + // same name, it may take priority. if let Some(fn_id) = typeck.type_dependent_def_id(hir_id) && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) && let arg_ty = cx.tcx.erase_and_anonymize_regions(adjusted_ty) @@ -395,12 +398,42 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Trait methods taking `self` arg_ty } - && impl_ty.is_ref() - && implements_trait( - cx, - impl_ty, - trait_id, - &args[..cx.tcx.generics_of(trait_id).own_params.len() - 1], + && let method_name = cx.tcx.item_name(fn_id) + && ( + // If this trait impl is implemented on `&T`, then auto-borrowing won't work + (impl_ty.is_ref() + && implements_trait( + cx, + impl_ty, + trait_id, + &args[..cx.tcx.generics_of(trait_id).own_params.len() - 1], + )) + // If there's an inherent method, or a method from another trait, + // with the same name that's also implemented on this same type, + // then removing the borrow might cause that method to be chosen + // instead of the current one. + || get_adt_inherent_method(cx, impl_ty, method_name).is_some() + || cx.tcx.in_scope_traits(hir_id).is_some_and(|traits| { + traits + .iter() + .filter(|trait_| { + cx.tcx + .non_blanket_impls_for_ty(trait_.def_id, impl_ty) + .next() + .is_some() + || !cx + .tcx + .trait_impls_of(trait_.def_id) + .blanket_impls() + .is_empty() + }) + .any(|trait_| { + cx.tcx + .associated_items(trait_.def_id) + .filter_by_name_unhygienic(method_name) + .any(|item| item.tag() == AssocTag::Fn && item.def_id != fn_id) + }) + }) ) { false diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index e2fd71b7d990f..58cc318d57ccd 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -1,13 +1,18 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; use clippy_utils::paths::PathNS; +use clippy_utils::sym; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -55,6 +60,19 @@ declare_clippy_lint! { /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. /// xs.push(123); // Vec::push is _not_ disallowed in the config. /// ``` + /// + /// Disallowed profiles allow scoping different disallow lists: + /// ```toml + /// [profiles.forward_pass] + /// disallowed-methods = [{ path = "crate::devices::Buffer::copy_to_host", reason = "Forward code must not touch host buffers" }] + /// ``` + /// + /// ```rust,ignore + /// #[clippy::disallowed_profile("forward_pass")] + /// fn evaluate() { + /// // Method calls in this function use the `forward_pass` profile. + /// } + /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, style, @@ -64,12 +82,22 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); pub struct DisallowedMethods { - disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, + default: DefIdMap<(&'static str, &'static DisallowedPath)>, + /// Lookup per profile that declares a non-empty `disallowed_methods` list. Profiles + /// declared in `[profiles.*]` but without `disallowed_methods` entries are absent here. + profiles: FxHashMap>, + /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes + /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist + /// in config but only define entries for other lints (e.g. `disallowed_types`). + known_profiles: FxHashSet, + profile_cache: ProfileResolver, + warned_unknown_profiles: FxHashSet, } impl DisallowedMethods { + #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (disallowed, _) = create_disallowed_map( + let (default, _) = create_disallowed_map( tcx, &conf.disallowed_methods, PathNS::Value, @@ -82,7 +110,62 @@ impl DisallowedMethods { "function", false, ); - Self { disallowed } + + let mut profiles = FxHashMap::default(); + let mut known_profiles = FxHashSet::default(); + let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); + profile_entries.sort_by_key(|(a, _)| *a); + for (name, profile) in profile_entries { + let symbol = Symbol::intern(name.as_str()); + known_profiles.insert(symbol); + + let paths = profile.disallowed_methods.as_slice(); + if paths.is_empty() { + continue; + } + + let (map, _) = create_disallowed_map( + tcx, + paths, + PathNS::Value, + |def_kind| { + matches!( + def_kind, + DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn + ) + }, + "function", + false, + ); + profiles.insert(symbol, map); + } + + Self { + default, + profiles, + known_profiles, + profile_cache: ProfileResolver::default(), + warned_unknown_profiles: FxHashSet::default(), + } + } + + fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { + if self.warned_unknown_profiles.insert(entry.span) { + let attr_name = if entry.attr_name == sym::disallowed_profiles { + "clippy::disallowed_profiles" + } else { + "clippy::disallowed_profile" + }; + span_lint( + cx, + DISALLOWED_METHODS, + entry.span, + format!( + "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_methods`", + entry.name + ), + ); + } } } @@ -98,13 +181,43 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { + let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); + // Copy entries out of the cache before iterating: `warn_unknown_profile` takes + // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. + let entries: SmallVec<[ProfileEntry; 2]> = self + .profile_cache + .active_profiles(cx, expr.hir_id) + .map(|selection| selection.iter().copied().collect()) + .unwrap_or_default(); + for entry in &entries { + if self.profiles.contains_key(&entry.name) { + active_profiles.push(entry.name); + } else if !self.known_profiles.contains(&entry.name) { + self.warn_unknown_profile(cx, entry); + } + } + + if let Some((profile, &(path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { + self.profiles + .get(symbol) + .and_then(|map| map.get(&id).map(|info| (*symbol, info))) + }) { + let diag_amendment = disallowed_path.diag_amendment(span); + span_lint_and_then( + cx, + DISALLOWED_METHODS, + span, + format!("use of a disallowed method `{path}` (profile: {profile})"), + |diag| diag_amendment(diag), + ); + } else if let Some(&(path, disallowed_path)) = self.default.get(&id) { + let diag_amendment = disallowed_path.diag_amendment(span); span_lint_and_then( cx, DISALLOWED_METHODS, span, format!("use of a disallowed method `{path}`"), - disallowed_path.diag_amendment(span), + |diag| diag_amendment(diag), ); } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 2c520d053f439..37dd605617a7a 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,15 +1,18 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; use clippy_utils::paths::PathNS; -use rustc_data_structures::fx::FxHashMap; +use clippy_utils::sym; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -51,6 +54,17 @@ declare_clippy_lint! { /// // A similar type that is allowed by the config /// use std::collections::HashMap; /// ``` + /// + /// Disallowed profiles can scope lists to specific modules: + /// ```toml + /// [profiles.forward_pass] + /// disallowed-types = [{ path = "crate::buffers::HostBuffer", reason = "Prefer device buffers in forward computations" }] + /// ``` + /// + /// ```rust,ignore + /// #[clippy::disallowed_profile("forward_pass")] + /// fn forward_step(buffer: crate::buffers::DeviceBuffer) { /* ... */ } + /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, style, @@ -59,37 +73,127 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); -pub struct DisallowedTypes { +struct TypeLookup { def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, prim_tys: FxHashMap, } +impl TypeLookup { + fn from_config(tcx: TyCtxt<'_>, paths: &'static [DisallowedPath]) -> Self { + let (def_ids, prim_tys) = create_disallowed_map(tcx, paths, PathNS::Type, def_kind_predicate, "type", true); + Self { def_ids, prim_tys } + } + + fn find(&self, res: &Res) -> Option<(&'static str, &'static DisallowedPath)> { + match res { + Res::Def(_, did) => self.def_ids.get(did).copied(), + Res::PrimTy(prim) => self.prim_tys.get(prim).copied(), + _ => None, + } + } +} + +pub struct DisallowedTypes { + default: TypeLookup, + /// Lookup per profile that declares a non-empty `disallowed_types` list. Profiles + /// declared in `[profiles.*]` but without `disallowed_types` entries are absent here. + profiles: FxHashMap, + /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes + /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist + /// in config but only define entries for other lints (e.g. `disallowed_methods`). + known_profiles: FxHashSet, + profile_cache: ProfileResolver, + warned_unknown_profiles: FxHashSet, +} + impl DisallowedTypes { + #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, prim_tys) = create_disallowed_map( - tcx, - &conf.disallowed_types, - PathNS::Type, - def_kind_predicate, - "type", - true, - ); - Self { def_ids, prim_tys } + let default = TypeLookup::from_config(tcx, &conf.disallowed_types); + + let mut profiles = FxHashMap::default(); + let mut known_profiles = FxHashSet::default(); + let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); + profile_entries.sort_by_key(|(a, _)| *a); + for (name, profile) in profile_entries { + let symbol = Symbol::intern(name.as_str()); + known_profiles.insert(symbol); + + let paths = profile.disallowed_types.as_slice(); + if paths.is_empty() { + continue; + } + profiles.insert(symbol, TypeLookup::from_config(tcx, paths)); + } + + Self { + default, + profiles, + known_profiles, + profile_cache: ProfileResolver::default(), + warned_unknown_profiles: FxHashSet::default(), + } } - fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { - let (path, disallowed_path) = match res { - Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, - Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, - _ => return, - }; - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}`"), - disallowed_path.diag_amendment(span), - ); + fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { + if self.warned_unknown_profiles.insert(entry.span) { + let attr_name = if entry.attr_name == sym::disallowed_profiles { + "clippy::disallowed_profiles" + } else { + "clippy::disallowed_profile" + }; + span_lint( + cx, + DISALLOWED_TYPES, + entry.span, + format!( + "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_types`", + entry.name + ), + ); + } + } + + fn check_res_emit(&mut self, cx: &LateContext<'_>, hir_id: rustc_hir::HirId, res: &Res, span: Span) { + let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); + // Copy entries out of the cache before iterating: `warn_unknown_profile` takes + // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. + let entries: SmallVec<[ProfileEntry; 2]> = self + .profile_cache + .active_profiles(cx, hir_id) + .map(|selection| selection.iter().copied().collect()) + .unwrap_or_default(); + for entry in &entries { + if self.profiles.contains_key(&entry.name) { + active_profiles.push(entry.name); + } else if !self.known_profiles.contains(&entry.name) { + self.warn_unknown_profile(cx, entry); + } + } + + if let Some((profile, (path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { + self.profiles + .get(symbol) + .and_then(|lookup| lookup.find(res).map(|info| (*symbol, info))) + }) { + let diag_amendment = disallowed_path.diag_amendment(span); + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}` (profile: {profile})"), + |diag| diag_amendment(diag), + ); + } else if let Some((path, disallowed_path)) = self.default.find(res) { + let diag_amendment = disallowed_path.diag_amendment(span); + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + |diag| diag_amendment(diag), + ); + } } } @@ -111,17 +215,22 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind && let Some(res) = path.res.type_ns { - self.check_res_emit(cx, &res, item.span); + self.check_res_emit(cx, item.hir_id(), &res, item.span); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Path(path) = &ty.kind { - self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span); + self.check_res_emit(cx, ty.hir_id, &cx.qpath_res(path, ty.hir_id), ty.span); } } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); + self.check_res_emit( + cx, + poly.trait_ref.hir_ref_id, + &poly.trait_ref.path.res, + poly.trait_ref.path.span, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 1da0a010668bb..81da571bdd601 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -906,29 +906,35 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ return Some(DocHeaders::default()); } - check_for_code_clusters( - cx, - pulldown_cmark::Parser::new_with_broken_link_callback( + // Only emits the allow-by-default `DOC_LINK_CODE`; skip its extra markdown reparse when it's off. + if !clippy_utils::is_lint_allowed(cx, DOC_LINK_CODE, cx.last_node_with_lint_attrs) { + check_for_code_clusters( + cx, + pulldown_cmark::Parser::new_with_broken_link_callback( + &doc, + main_body_opts() - Options::ENABLE_SMART_PUNCTUATION, + Some(&mut fake_broken_link_callback), + ) + .into_offset_iter(), &doc, - main_body_opts() - Options::ENABLE_SMART_PUNCTUATION, - Some(&mut fake_broken_link_callback), - ) - .into_offset_iter(), - &doc, - Fragments { - doc: &doc, - fragments: &fragments, - }, - ); + Fragments { + doc: &doc, + fragments: &fragments, + }, + ); + } - doc_paragraphs_missing_punctuation::check( - cx, - &doc, - Fragments { - doc: &doc, - fragments: &fragments, - }, - ); + // Same for the allow-by-default `DOC_PARAGRAPHS_MISSING_PUNCTUATION`, which also reparses. + if !clippy_utils::is_lint_allowed(cx, DOC_PARAGRAPHS_MISSING_PUNCTUATION, cx.last_node_with_lint_attrs) { + doc_paragraphs_missing_punctuation::check( + cx, + &doc, + Fragments { + doc: &doc, + fragments: &fragments, + }, + ); + } // NOTE: check_doc uses it own cb function, // to avoid causing duplicated diagnostics for the broken link checker. diff --git a/src/tools/clippy/clippy_lints/src/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/empty_line_after.rs index b7b84c173f418..9df1919867e2e 100644 --- a/src/tools/clippy/clippy_lints/src/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/empty_line_after.rs @@ -93,14 +93,48 @@ impl_lint_pass!(EmptyLineAfter => [ EMPTY_LINE_AFTER_OUTER_ATTR, ]); +/// The kind of the item a doc comment or attribute applies to. Used in lint messages and to +/// detect the first item of a module or crate. `Other` holds the description string from +/// `ItemKind::descr` or `assoc_item_descr`. +#[derive(Debug, Clone, Copy)] +enum ItemKindDescr { + Crate, + Module, + Other(&'static str), +} + +impl ItemKindDescr { + fn as_str(self) -> &'static str { + match self { + Self::Crate => "crate", + Self::Module => "module", + Self::Other(descr) => descr, + } + } +} + #[derive(Debug)] struct ItemInfo { - kind: &'static str, + kind: ItemKindDescr, name: Option, span: Span, mod_items: Option, } +impl ItemInfo { + fn new(kind: ItemKindDescr, ident: Option, span: Span, mod_items: Option) -> Self { + Self { + kind, + name: ident.map(|ident| ident.name), + span: match ident { + Some(ident) => span.with_hi(ident.span.hi()), + None => span.shrink_to_lo(), + }, + mod_items, + } + } +} + pub struct EmptyLineAfter { items: Vec, } @@ -339,8 +373,8 @@ impl EmptyLineAfter { diag.span_label( info.span, match kind { - StopKind::Attr => format!("the attribute applies to this {}", info.kind), - StopKind::Doc(_) => format!("the comment documents this {}", info.kind), + StopKind::Attr => format!("the attribute applies to this {}", info.kind.as_str()), + StopKind::Doc(_) => format!("the comment documents this {}", info.kind.as_str()), }, ); @@ -364,7 +398,7 @@ impl EmptyLineAfter { stop.comment_out(cx, &mut suggestions); } let name = match info.name { - Some(name) => format!("{} `{name}`", info.kind).into(), + Some(name) => format!("{} `{name}`", info.kind.as_str()).into(), None => Cow::from("the following item"), }; diag.multipart_suggestion( @@ -400,7 +434,7 @@ impl EmptyLineAfter { /// them to inner attributes/docs fn suggest_inner(&self, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>], id: NodeId) { if let Some(parent) = self.items.iter().rev().nth(1) - && (parent.kind == "module" || parent.kind == "crate") + && matches!(parent.kind, ItemKindDescr::Module | ItemKindDescr::Crate) && parent.mod_items == Some(id) && let suggestions = gaps .iter() @@ -409,10 +443,9 @@ impl EmptyLineAfter { .collect::>() && !suggestions.is_empty() { - let desc = if parent.kind == "module" { - "parent module" - } else { - parent.kind + let desc = match parent.kind { + ItemKindDescr::Module => "parent module", + _ => parent.kind.as_str(), }; diag.multipart_suggestion( match kind { @@ -425,31 +458,8 @@ impl EmptyLineAfter { } } - fn check_item_kind( - &mut self, - cx: &EarlyContext<'_>, - kind: &ItemKind, - ident: Option, - span: Span, - attrs: &[Attribute], - id: NodeId, - ) { - self.items.push(ItemInfo { - kind: kind.descr(), - name: ident.map(|ident| ident.name), - span: match ident { - Some(ident) => span.with_hi(ident.span.hi()), - None => span.shrink_to_lo(), - }, - mod_items: match kind { - ItemKind::Mod(_, _, ModKind::Loaded(items, _, _)) => items - .iter() - .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) - .map(|i| i.id) - .next(), - _ => None, - }, - }); + fn check_item_kind(&mut self, cx: &EarlyContext<'_>, info: ItemInfo, attrs: &[Attribute], id: NodeId) { + self.items.push(info); let mut outer = attrs .iter() @@ -496,10 +506,21 @@ impl EmptyLineAfter { } } +fn assoc_item_descr(kind: &AssocItemKind) -> &'static str { + match kind { + AssocItemKind::Const(_) => "constant item", + AssocItemKind::Fn(_) => "function", + AssocItemKind::Type(_) => "type alias", + AssocItemKind::MacCall(_) => "item macro invocation", + AssocItemKind::Delegation(_) => "delegated function", + AssocItemKind::DelegationMac(_) => "delegation", + } +} + impl EarlyLintPass for EmptyLineAfter { fn check_crate(&mut self, _: &EarlyContext<'_>, krate: &Crate) { self.items.push(ItemInfo { - kind: "crate", + kind: ItemKindDescr::Crate, name: Some(kw::Crate), span: krate.spans.inner_span.with_hi(krate.spans.inner_span.lo()), mod_items: krate @@ -522,28 +543,31 @@ impl EarlyLintPass for EmptyLineAfter { } fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind( - cx, - &item.kind.clone().into(), - item.kind.ident(), - item.span, - &item.attrs, - item.id, - ); + let kind = ItemKindDescr::Other(assoc_item_descr(&item.kind)); + let info = ItemInfo::new(kind, item.kind.ident(), item.span, None); + self.check_item_kind(cx, info, &item.attrs, item.id); } fn check_trait_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind( - cx, - &item.kind.clone().into(), - item.kind.ident(), - item.span, - &item.attrs, - item.id, - ); + let kind = ItemKindDescr::Other(assoc_item_descr(&item.kind)); + let info = ItemInfo::new(kind, item.kind.ident(), item.span, None); + self.check_item_kind(cx, info, &item.attrs, item.id); } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind(cx, &item.kind, item.kind.ident(), item.span, &item.attrs, item.id); + let (kind, mod_items) = match &item.kind { + ItemKind::Mod(_, _, ModKind::Loaded(items, _, _)) => { + let first = items + .iter() + .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) + .map(|i| i.id) + .next(); + (ItemKindDescr::Module, first) + }, + ItemKind::Mod(..) => (ItemKindDescr::Module, None), + _ => (ItemKindDescr::Other(item.kind.descr()), None), + }; + let info = ItemInfo::new(kind, item.kind.ident(), item.span, mod_items); + self.check_item_kind(cx, info, &item.attrs, item.id); } } diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 2e88d78ce9095..e7df5fa020e34 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir; use rustc_abi::ExternAbi; use rustc_hir::def::DefKind; -use rustc_hir::{Body, FnDecl, HirId, HirIdSet, Node, Pat, PatKind, intravisit}; +use rustc_hir::{Body, FnDecl, HirId, HirIdSet, PatKind, intravisit}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; use rustc_span::Span; use rustc_span::def_id::LocalDefId; @@ -52,15 +52,16 @@ declare_clippy_lint! { impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]); -fn is_non_trait_box(ty: Ty<'_>) -> bool { - ty.boxed_ty().is_some_and(|boxed| !boxed.is_trait()) +/// Whether `ty` is a `Box` where `T` is not a trait object and is small enough to live on the +/// stack (large types are boxed to avoid stack overflows). +fn is_small_non_trait_box<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, too_large_for_stack: u64) -> bool { + ty.boxed_ty().is_some_and(|boxed| { + !boxed.is_trait() && cx.layout_of(boxed).map_or(0, |l| l.size.bytes()) <= too_large_for_stack + }) } -struct EscapeDelegate<'a, 'tcx> { - cx: &'a LateContext<'tcx>, +struct EscapeDelegate { set: HirIdSet, - trait_self_ty: Option>, - too_large_for_stack: u64, } impl<'tcx> LateLintPass<'tcx> for BoxedLocal { @@ -73,6 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { _: Span, fn_def_id: LocalDefId, ) { + // Skip closures + if matches!(fn_kind, intravisit::FnKind::Closure) { + return; + } + if let Some(header) = fn_kind.header() && header.abi != ExternAbi::Rust { @@ -97,12 +103,42 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { _ => {}, } - let mut v = EscapeDelegate { - cx, - set: HirIdSet::default(), - trait_self_ty, - too_large_for_stack: self.too_large_for_stack, - }; + let typeck_results = cx.tcx.typeck_body(body.id()); + + // Seed the set with the `Box` parameters that could be unboxed. The `ExprUseVisitor` walk + // below then removes any that escape by being moved or borrowed. + let set: HirIdSet = body + .params + .iter() + .filter_map(|param| { + // Only simple bindings (`x: Box<_>`) bind a local that the walk can track and report. + if !matches!(param.pat.kind, PatKind::Binding(..)) { + return None; + } + + let ty = typeck_results.pat_ty(param.pat); + if !is_small_non_trait_box(cx, ty, self.too_large_for_stack) { + return None; + } + + // skip `self` parameters whose type contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = trait_self_ty + && cx.tcx.hir_name(param.pat.hir_id) == kw::SelfLower + && ty.contains(trait_self_ty) + { + return None; + } + + Some(param.pat.hir_id) + }) + .collect(); + + // Without any candidate parameter, the expensive `ExprUseVisitor` walk can never lint. + if set.is_empty() { + return; + } + + let mut v = EscapeDelegate { set }; ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v) .consume_body(body) @@ -120,20 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { } } -// TODO: Replace with Map::is_argument(..) when it's fixed -fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { - match tcx.hir_node(id) { - Node::Pat(Pat { - kind: PatKind::Binding(..), - .. - }) => (), - _ => return false, - } - - matches!(tcx.parent_hir_node(id), Node::Param(_)) -} - -impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { +impl<'tcx> Delegate<'tcx> for EscapeDelegate { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { if cmt.place.projections.is_empty() && let PlaceBase::Local(lid) = cmt.place.base @@ -154,39 +177,7 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { } } - fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() && is_argument(self.cx.tcx, cmt.hir_id) { - // Skip closure arguments - let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); - if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { - return; - } - - // skip if there is a `self` parameter binding to a type - // that contains `Self` (i.e.: `self: Box`), see #4804 - if let Some(trait_self_ty) = self.trait_self_ty - && self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower - && cmt.place.ty().contains(trait_self_ty) - { - return; - } - - if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { - self.set.insert(cmt.hir_id); - } - } - } + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } - -impl<'tcx> EscapeDelegate<'_, 'tcx> { - fn is_large_box(&self, ty: Ty<'tcx>) -> bool { - // Large types need to be boxed to avoid stack overflows. - if let Some(boxed_ty) = ty.boxed_ty() { - self.cx.layout_of(boxed_ty).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack - } else { - false - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 65d0ff9b543f8..b44497ded301e 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -11,35 +11,51 @@ use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does - /// Checks for excessive use of - /// bools in function definitions. + /// Checks for excessive use of bools in function declarations. /// /// ### Why is this bad? - /// Calls to such functions - /// are confusing and error prone, because it's - /// hard to remember argument order and you have - /// no type system support to back you up. Using - /// two-variant enums instead of bools often makes - /// API easier to use. + /// Boolean parameters obscure meaning at the call site. The reader has to + /// remember what each `true` or `false` means and which position each flag + /// belongs in, with no help from the type system. + /// + /// The best replacement depends on what role the bool plays: + /// + /// - Use a two-variant enum when one parameter selects a mode. + /// - Split one function into two named operations when the choices are + /// distinct actions. + /// - Group multiple related flags into a struct or options type when they + /// travel together. + /// - Move the setting onto `self` or an existing config/context parameter + /// when it is really part of the surrounding state. + /// + /// This optimizes for readability of the calling code, which future readers + /// encounter more often than the function declaration. + /// + /// The `max-fn-params-bools` configuration accepts `0`, which lints on any + /// function with a bool parameter. /// /// ### Example /// ```rust,ignore - /// fn f(is_round: bool, is_hot: bool) { ... } + /// fn render_document(show_hidden: bool, is_draft: bool) { ... } /// ``` /// /// Use instead: /// ```rust,ignore - /// enum Shape { - /// Round, - /// Spiky, + /// enum Visibility { + /// ShowHidden, + /// HideHidden, /// } /// - /// enum Temperature { - /// Hot, - /// IceCold, + /// enum DocumentKind { + /// Draft, + /// Final, /// } /// - /// fn f(shape: Shape, temperature: Temperature) { ... } + /// fn render_document(visibility: Visibility, kind: DocumentKind) { ... } + /// + /// // or split the operation when the choices are distinct + /// fn render_draft() { ... } + /// fn render_final() { ... } /// ``` #[clippy::version = "1.43.0"] pub FN_PARAMS_EXCESSIVE_BOOLS, diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 7c1b3ef225c6a..a3e982bb09430 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use clippy_utils::attrs::is_proc_macro; -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_indent; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; @@ -145,31 +145,24 @@ fn check_needless_must_use( return; } if returns_unit(decl) { - if attrs.len() == 1 { - span_lint_and_then( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - |diag| { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see + // issue #12320. + // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, + // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this + // could be re-added. + if attrs.len() == 1 { diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable); - }, - ); - } else { - // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see - // issue #12320. - // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, - // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this - // could be re-added. - span_lint_and_help( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - Some(attr_span), - "remove `must_use`", - ); - } + } else { + diag.span_help(attr_span, "remove `must_use`"); + } + }, + ); } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type if sig.header.is_async() { @@ -181,13 +174,24 @@ fn check_needless_must_use( } } - span_lint_and_help( + span_lint_and_then( cx, DOUBLE_MUST_USE, fn_header_span, "this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`", - None, - "either add some descriptive message or remove the attribute", + |diag| { + // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see + // issue #12320. + // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, + // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this + // could be re-added. + if attrs.len() == 1 { + diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable); + } else { + diag.span_help(attr_span, "remove `must_use`"); + } + diag.note("alternatively, you may add an explicit reason to the `must_use` attribute"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index 829f054d8854e..331c6e23bddd7 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -2,10 +2,12 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use rustc_errors::Diag; use rustc_hir as hir; +use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefIdSet; use rustc_span::{Span, sym}; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::ty::{AdtVariantInfo, approx_ty_size}; @@ -23,17 +25,31 @@ fn result_err_ty<'tcx>( ) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> { if !item_span.in_external_macro(cx.sess().source_map()) && let hir::FnRetTy::Return(hir_ty) = decl.output - && let ty = cx - .tcx - .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output()) - && ty.is_diag_item(cx, sym::Result) - && let ty::Adt(_, args) = ty.kind() { - let err_ty = args.type_at(1); - Some((hir_ty, err_ty)) - } else { - None + let mut ty = cx + .tcx + .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output()); + + // for async functions, peel through `impl Future` to get `T` + if cx.tcx.ty_is_opaque_future(ty) + && let Some(future_output_ty) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .get_impl_future_output_ty(ty) + { + ty = future_output_ty; + } + + if let ty::Adt(adt_def, args) = ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) + { + let err_ty = args.type_at(1); + return Some((hir_ty, err_ty)); + } } + None } pub(super) fn check_item<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs index 99716253c53e7..3ffa23dcd6046 100644 --- a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{HasSession, snippet}; -use rustc_ast::NodeId; +use clippy_utils::sym; use rustc_ast::ast::{Fn, FnRetTy, GenericParam, GenericParamKind}; use rustc_ast::visit::{FnCtxt, FnKind}; +use rustc_ast::{HasAttrs as _, NodeId}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -37,9 +38,30 @@ declare_clippy_lint! { "enforce that `where` bounds are used for all trait bounds" } -declare_lint_pass!(InlineTraitBounds => [INLINE_TRAIT_BOUNDS]); +impl_lint_pass!(InlineTraitBounds => [INLINE_TRAIT_BOUNDS]); + +#[derive(Default)] +pub struct InlineTraitBounds { + /// The top-level item onto which `#[automatically_derived]` has been encountered + automatically_derived_at: Option, +} impl EarlyLintPass for InlineTraitBounds { + fn check_item(&mut self, _: &EarlyContext<'_>, item: &rustc_ast::Item) { + if self.automatically_derived_at.is_none() + && item + .attrs() + .iter() + .any(|attr| attr.has_name(sym::automatically_derived)) + { + self.automatically_derived_at = Some(item.id); + } + } + + fn check_item_post(&mut self, _: &EarlyContext<'_>, item: &rustc_ast::Item) { + self.automatically_derived_at.take_if(|id| *id == item.id); + } + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { let FnKind::Fn(ctxt, _vis, f) = kind else { return; @@ -50,6 +72,11 @@ impl EarlyLintPass for InlineTraitBounds { return; } + // Skip automatically derived functions + if self.automatically_derived_at.is_some() { + return; + } + if f.sig.span.in_external_macro(cx.sess().source_map()) { return; } diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index 80b49ecc7a40b..9835c1decceee 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -3,9 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_middle::ty::Unnormalized; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, Unnormalized}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; @@ -45,6 +44,31 @@ impl LargeConstArrays { maximum_allowed_size: conf.array_size_threshold, } } + + /// Checks recursively checks whether `ty` has an array exceeding allowed size + fn check_impl<'a>(&self, cx: &LateContext<'a>, ty: Ty<'a>) -> bool { + match ty.kind() { + ty::Adt(adt_def, args) => adt_def + .all_fields() + .any(|f| self.check_impl(cx, f.ty(cx.tcx, args).skip_norm_wip())), + ty::Array(element_type, cst) => { + let normalized = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(*cst)) + .unwrap_or(*cst); + if let Some(element_count) = normalized.try_to_target_usize(cx.tcx) + && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) + && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) + { + true + } else { + false + } + }, + ty::Tuple(fields) => fields.iter().any(|f| self.check_impl(cx, f)), + _ => false, + } + } } impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { @@ -56,13 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && generics.params.is_empty() && !generics.has_where_clause_predicates && !item.span.from_expansion() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip() - && let ty::Array(element_type, cst) = ty.kind() - && let Some(element_count) = cx.tcx - .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(*cst)) - .unwrap_or(*cst) - .try_to_target_usize(cx.tcx) - && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) - && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) + && self.check_impl(cx, ty) { let hi_pos = ident.span.lo() - BytePos::from_usize(1); let sugg_span = Span::new( diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs index 56ae1d04827e8..caa44d5ef1235 100644 --- a/src/tools/clippy/clippy_lints/src/large_futures.rs +++ b/src/tools/clippy/clippy_lints/src/large_futures.rs @@ -64,7 +64,9 @@ impl<'tcx> LateLintPass<'tcx> for LargeFuture { && let ty = cx.typeck_results().expr_ty(arg) && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, ty, future_trait_def_id, &[]) - && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().with_codegen_normalized(cx.tcx).as_query_input(ty)) + && let Ok(layout) = cx + .tcx + .layout_of(cx.typing_env().with_codegen_normalized(cx.tcx).as_query_input(ty)) && let size = layout.layout.size() && size >= Size::from_bytes(self.future_size_threshold) { diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index af25b1f7f1aac..4a7659a9cc210 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -55,6 +55,9 @@ extern crate declare_clippy_lint; mod utils; +mod combined_early_pass; +mod combined_late_pass; + pub mod declared_lints; pub mod deprecated_lints; @@ -400,6 +403,7 @@ mod vec_init_then_push; mod visibility; mod volatile_composites; mod wildcard_imports; +mod with_capacity_zero; mod write; mod zero_div_zero; mod zero_repeat_side_effects; @@ -410,10 +414,9 @@ mod zombie_processes; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync; -use rustc_lint::{EarlyLintPass, LateLintPass, Lint}; +use rustc_lint::Lint; use rustc_middle::ty::TyCtxt; -use utils::attr_collector::{AttrCollector, AttrStorage}; +use utils::attr_collector::AttrStorage; pub fn explain(name: &str) -> i32 { let target = format!("clippy::{}", name.to_ascii_uppercase()); @@ -440,7 +443,6 @@ pub fn explain(name: &str) -> i32 { /// Register all lints and lint groups with the rustc lint store /// /// Used in `./src/driver.rs`. -#[expect(clippy::too_many_lines)] pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Conf) { for (old_name, new_name) in deprecated_lints::RENAMED { store.register_renamed(old_name, new_name); @@ -457,421 +459,409 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co let format_args_storage = FormatArgsStorage::default(); let attr_storage = AttrStorage::default(); - let early_lints: [Box Box + sync::DynSend + sync::DynSync>; _] = [ - { - let format_args = format_args_storage.clone(); - Box::new(move || { - Box::new(utils::format_args_collector::FormatArgsCollector::new( - format_args.clone(), - )) - }) - }, - { - let attrs = attr_storage.clone(); - Box::new(move || Box::new(AttrCollector::new(attrs.clone()))) - }, - Box::new(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))), - Box::new(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)), - Box::new(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))), - Box::new(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))), - Box::new(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))), - Box::new(|| Box::new(functions::EarlyFunctions)), - Box::new(move || Box::new(doc::Documentation::new(conf))), - Box::new(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)), - Box::new(|| Box::new(double_parens::DoubleParens)), - Box::new(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)), - Box::new(|| Box::new(else_if_without_else::ElseIfWithoutElse)), - Box::new(|| Box::new(int_plus_one::IntPlusOne)), - Box::new(|| Box::new(formatting::Formatting)), - Box::new(|| Box::new(misc_early::MiscEarlyLints)), - Box::new(|| Box::new(unused_unit::UnusedUnit)), - Box::new(|| Box::new(precedence::Precedence)), - Box::new(|| Box::new(redundant_else::RedundantElse)), - Box::new(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)), - Box::new(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))), - Box::new(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))), - Box::new(|| Box::new(tabs_in_doc_comments::TabsInDocComments)), - Box::new(|| Box::::default()), - Box::new(|| Box::new(option_env_unwrap::OptionEnvUnwrap)), - Box::new(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))), - Box::new(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))), - Box::new(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)), - Box::new(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)), - Box::new(move || Box::new(module_style::ModStyle::default())), - Box::new(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))), - Box::new(|| Box::new(octal_escapes::OctalEscapes)), - Box::new(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)), - Box::new(|| Box::new(crate_in_macro_def::CrateInMacroDef)), - Box::new(|| Box::new(pub_use::PubUse)), - Box::new(move || Box::new(large_include_file::LargeIncludeFile::new(conf))), - Box::new(|| Box::::default()), - Box::new(|| Box::new(unused_rounding::UnusedRounding)), - Box::new(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))), - Box::new(|| Box::new(multi_assignments::MultiAssignments)), - Box::new(|| Box::new(partial_pub_fields::PartialPubFields)), - Box::new(|| Box::new(let_with_type_underscore::UnderscoreTyped)), - Box::new(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))), - Box::new(|| Box::new(ref_patterns::RefPatterns)), - Box::new(|| Box::new(needless_else::NeedlessElse)), - Box::new(move || Box::new(raw_strings::RawStrings::new(conf))), - Box::new(|| Box::new(visibility::Visibility)), - Box::new(|| Box::new(multiple_bound_locations::MultipleBoundLocations)), - Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)), - Box::new(|| Box::new(cfg_not_test::CfgNotTest)), - Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())), - Box::new(|| Box::new(inline_trait_bounds::InlineTraitBounds)), + { + let format_args = format_args_storage.clone(); + let attrs = attr_storage.clone(); + store.early_passes.push(Box::new(move || { + Box::new(CombinedEarlyLintPass::new(conf, format_args.clone(), attrs.clone())) + })); + } + + store.late_passes.push(Box::new(move |tcx: TyCtxt<'_>| { + let dont_need = tcx.lints_that_dont_need_to_run(()); + let is_active = |lints: &rustc_lint::LintVec| { + lints.is_empty() + || !lints + .iter() + .all(|lint| dont_need.contains(&rustc_lint::LintId::of(lint))) + }; + Box::new(CombinedLateLintPass::new( + tcx, + conf, + format_args_storage.clone(), + attr_storage.clone(), + &is_active, + )) + })); +} + +// Fold every early pass into one statically-combined struct (see +// `combined_early_pass`); the method list comes from `early_lint_methods!`. +#[rustfmt::skip] +rustc_lint::early_lint_methods!( + crate::combined_early_lint_pass, + [CombinedEarlyLintPass, (conf: &'static Conf, format_args: FormatArgsStorage, attrs: AttrStorage), [ + FormatArgsCollector: utils::format_args_collector::FormatArgsCollector = utils::format_args_collector::FormatArgsCollector::new(format_args.clone()), + AttrCollector: utils::attr_collector::AttrCollector = utils::attr_collector::AttrCollector::new(attrs.clone()), + PostExpansionEarlyAttributes: attrs::PostExpansionEarlyAttributes = attrs::PostExpansionEarlyAttributes::new(conf), + UnnecessarySelfImports: unnecessary_self_imports::UnnecessarySelfImports = unnecessary_self_imports::UnnecessarySelfImports, + RedundantStaticLifetimes: redundant_static_lifetimes::RedundantStaticLifetimes = redundant_static_lifetimes::RedundantStaticLifetimes::new(conf), + RedundantFieldNames: redundant_field_names::RedundantFieldNames = redundant_field_names::RedundantFieldNames::new(conf), + UnnestedOrPatterns: unnested_or_patterns::UnnestedOrPatterns = unnested_or_patterns::UnnestedOrPatterns::new(conf), + EarlyFunctions: functions::EarlyFunctions = functions::EarlyFunctions, + Documentation: doc::Documentation = doc::Documentation::new(conf), + SuspiciousOperationGroupings: suspicious_operation_groupings::SuspiciousOperationGroupings = suspicious_operation_groupings::SuspiciousOperationGroupings, + DoubleParens: double_parens::DoubleParens = double_parens::DoubleParens, + UnsafeNameRemoval: unsafe_removed_from_name::UnsafeNameRemoval = unsafe_removed_from_name::UnsafeNameRemoval, + ElseIfWithoutElse: else_if_without_else::ElseIfWithoutElse = else_if_without_else::ElseIfWithoutElse, + IntPlusOne: int_plus_one::IntPlusOne = int_plus_one::IntPlusOne, + Formatting: formatting::Formatting = formatting::Formatting, + MiscEarlyLints: misc_early::MiscEarlyLints = misc_early::MiscEarlyLints, + UnusedUnit: unused_unit::UnusedUnit = unused_unit::UnusedUnit, + Precedence: precedence::Precedence = precedence::Precedence, + RedundantElse: redundant_else::RedundantElse = redundant_else::RedundantElse, + NeedlessArbitrarySelfType: needless_arbitrary_self_type::NeedlessArbitrarySelfType = needless_arbitrary_self_type::NeedlessArbitrarySelfType, + LiteralDigitGrouping: literal_representation::LiteralDigitGrouping = literal_representation::LiteralDigitGrouping::new(conf), + DecimalLiteralRepresentation: literal_representation::DecimalLiteralRepresentation = literal_representation::DecimalLiteralRepresentation::new(conf), + TabsInDocComments: tabs_in_doc_comments::TabsInDocComments = tabs_in_doc_comments::TabsInDocComments, + SingleComponentPathImports: single_component_path_imports::SingleComponentPathImports = single_component_path_imports::SingleComponentPathImports::default(), + OptionEnvUnwrap: option_env_unwrap::OptionEnvUnwrap = option_env_unwrap::OptionEnvUnwrap, + NonExpressiveNames: non_expressive_names::NonExpressiveNames = non_expressive_names::NonExpressiveNames::new(conf), + MacroBraces: nonstandard_macro_braces::MacroBraces = nonstandard_macro_braces::MacroBraces::new(conf), + InlineAsmX86AttSyntax: asm_syntax::InlineAsmX86AttSyntax = asm_syntax::InlineAsmX86AttSyntax, + InlineAsmX86IntelSyntax: asm_syntax::InlineAsmX86IntelSyntax = asm_syntax::InlineAsmX86IntelSyntax, + ModStyle: module_style::ModStyle = module_style::ModStyle::default(), + DisallowedScriptIdents: disallowed_script_idents::DisallowedScriptIdents = disallowed_script_idents::DisallowedScriptIdents::new(conf), + OctalEscapes: octal_escapes::OctalEscapes = octal_escapes::OctalEscapes, + SingleCharLifetimeNames: single_char_lifetime_names::SingleCharLifetimeNames = single_char_lifetime_names::SingleCharLifetimeNames, + CrateInMacroDef: crate_in_macro_def::CrateInMacroDef = crate_in_macro_def::CrateInMacroDef, + PubUse: pub_use::PubUse = pub_use::PubUse, + LargeIncludeFile: large_include_file::LargeIncludeFile = large_include_file::LargeIncludeFile::new(conf), + DuplicateMod: duplicate_mod::DuplicateMod = duplicate_mod::DuplicateMod::default(), + UnusedRounding: unused_rounding::UnusedRounding = unused_rounding::UnusedRounding, + AlmostCompleteRange: almost_complete_range::AlmostCompleteRange = almost_complete_range::AlmostCompleteRange::new(conf), + MultiAssignments: multi_assignments::MultiAssignments = multi_assignments::MultiAssignments, + PartialPubFields: partial_pub_fields::PartialPubFields = partial_pub_fields::PartialPubFields, + UnderscoreTyped: let_with_type_underscore::UnderscoreTyped = let_with_type_underscore::UnderscoreTyped, + ExcessiveNesting: excessive_nesting::ExcessiveNesting = excessive_nesting::ExcessiveNesting::new(conf), + RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, + NeedlessElse: needless_else::NeedlessElse = needless_else::NeedlessElse, + RawStrings: raw_strings::RawStrings = raw_strings::RawStrings::new(conf), + Visibility: visibility::Visibility = visibility::Visibility, + MultipleBoundLocations: multiple_bound_locations::MultipleBoundLocations = multiple_bound_locations::MultipleBoundLocations, + FieldScopedVisibilityModifiers: field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers = field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers, + CfgNotTest: cfg_not_test::CfgNotTest = cfg_not_test::CfgNotTest, + EmptyLineAfter: empty_line_after::EmptyLineAfter = empty_line_after::EmptyLineAfter::new(), + InlineTraitBounds: inline_trait_bounds::InlineTraitBounds = inline_trait_bounds::InlineTraitBounds::default(), // add early passes here, used by `cargo dev new_lint` - ]; - store.early_passes.extend(early_lints); + ]] +); - #[expect(clippy::type_complexity)] - let late_lints: [Box< - dyn for<'tcx> Fn(TyCtxt<'tcx>) -> Box + 'tcx> + sync::DynSend + sync::DynSync, - >; _] = [ - Box::new(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))), - Box::new(|_| Box::new(utils::dump_hir::DumpHir)), - Box::new(|_| Box::new(utils::author::Author)), - Box::new(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))), - Box::new(|_| Box::new(serde_api::SerdeApi)), - Box::new(move |_| Box::new(types::Types::new(conf))), - Box::new(move |_| Box::new(booleans::NonminimalBool::new(conf))), - Box::new(|_| Box::new(enum_clike::UnportableVariant)), - Box::new(move |_| Box::new(float_literal::FloatLiteral::new(conf))), - Box::new(|_| Box::new(ptr::Ptr)), - Box::new(|_| Box::new(needless_bool::NeedlessBool)), - Box::new(|_| Box::new(bool_comparison::BoolComparison)), - Box::new(|_| Box::new(needless_for_each::NeedlessForEach)), - Box::new(|_| Box::new(misc::LintPass)), - Box::new(|_| Box::new(eta_reduction::EtaReduction)), - Box::new(|_| Box::new(mut_mut::MutMut::default())), - Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)), - Box::new(|_| Box::>::default()), - Box::new(move |_| Box::new(len_zero::LenZero::new(conf))), - Box::new(|_| Box::new(len_without_is_empty::LenWithoutIsEmpty)), - Box::new(move |_| Box::new(attrs::Attributes::new(conf))), - Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)), - Box::new(|_| Box::new(unicode::Unicode)), - Box::new(|_| Box::new(uninit_vec::UninitVec)), - Box::new(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)), - Box::new(|_| Box::new(strings::StringAdd)), - Box::new(|_| Box::new(implicit_return::ImplicitReturn)), - Box::new(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))), - Box::new(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)), - Box::new(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)), - Box::new(move |_| Box::new(approx_const::ApproxConstant::new(conf))), - Box::new(move |_| Box::new(matches::Matches::new(conf))), - Box::new(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))), - Box::new(move |_| Box::new(manual_strip::ManualStrip::new(conf))), - Box::new(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))), - Box::new(move |_| Box::new(mem_replace::MemReplace::new(conf))), - Box::new(move |_| Box::new(ranges::Ranges::new(conf))), - Box::new(move |_| Box::new(from_over_into::FromOverInto::new(conf))), - Box::new(move |_| Box::new(use_self::UseSelf::new(conf))), - Box::new(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))), - Box::new(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)), - Box::new(move |_| Box::new(casts::Casts::new(conf))), - Box::new(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)), - Box::new(|_| Box::new(same_name_method::SameNameMethod)), - Box::new(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))), - Box::new(|_| Box::::default()), - Box::new(move |_| { - Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( - conf, - )) - }), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))) - }, - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone()))) - }, - Box::new(move |_| Box::new(loops::Loops::new(conf))), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(lifetimes::Lifetimes::new(conf))), - Box::new(|_| Box::new(entry::HashMapPass)), - Box::new(|_| Box::new(minmax::MinMaxPass)), - Box::new(|_| Box::new(zero_div_zero::ZeroDiv)), - Box::new(|_| Box::new(mutex_atomic::Mutex)), - Box::new(|_| Box::new(needless_update::NeedlessUpdate)), - Box::new(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)), - Box::new(|_| Box::new(borrow_deref_ref::BorrowDerefRef)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(temporary_assignment::TemporaryAssignment)), - Box::new(move |_| Box::new(transmute::Transmute::new(conf))), - Box::new(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))), - Box::new(move |_| Box::new(escape::BoxedLocal::new(conf))), - Box::new(move |_| Box::new(useless_vec::UselessVec::new(conf))), - Box::new(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))), - Box::new(|_| Box::new(strings::StringLitAsBytes)), - Box::new(|_| Box::new(derive::Derive)), - Box::new(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))), - Box::new(|_| Box::new(drop_forget_ref::DropForgetRef)), - Box::new(|_| Box::new(empty_enums::EmptyEnums)), - Box::new(|_| Box::::default()), - Box::new(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))), - Box::new(|_| Box::new(copy_iterator::CopyIterator)), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(format::UselessFormat::new(format_args.clone()))) - }, - Box::new(|_| Box::new(swap::Swap)), - Box::new(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))), - Box::new(move |tcx| Box::new(functions::Functions::new(tcx, conf))), - Box::new(move |_| Box::new(doc::Documentation::new(conf))), - Box::new(|_| Box::new(neg_multiply::NegMultiply)), - Box::new(|_| Box::new(let_if_seq::LetIfSeq)), - Box::new(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)), - Box::new(move |_| Box::new(missing_doc::MissingDoc::new(conf))), - Box::new(|_| Box::new(missing_inline::MissingInline)), - Box::new(move |_| Box::new(exhaustive_items::ExhaustiveItems)), - Box::new(|_| Box::new(unused_result_ok::UnusedResultOk)), - Box::new(|_| Box::new(match_result_ok::MatchResultOk)), - Box::new(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)), - Box::new(|_| Box::new(unused_io_amount::UnusedIoAmount)), - Box::new(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))) - }, - Box::new(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)), - Box::new(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))), - Box::new(|_| Box::new(ref_option_ref::RefOptionRef)), - Box::new(|_| Box::new(infinite_iter::InfiniteIter)), - Box::new(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(implicit_hasher::ImplicitHasher)), - Box::new(|_| Box::new(fallible_impl_from::FallibleImplFrom)), - Box::new(move |_| Box::new(question_mark::QuestionMark::new(conf))), - Box::new(|_| Box::new(question_mark_used::QuestionMarkUsed)), - Box::new(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)), - Box::new(|_| Box::new(map_unit_fn::MapUnit)), - Box::new(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))), - Box::new(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)), - Box::new(move |_| Box::new(unwrap::Unwrap::new(conf))), - Box::new(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))), - Box::new(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))), - Box::new(|_| Box::new(redundant_clone::RedundantClone)), - Box::new(|_| Box::new(slow_vector_initialization::SlowVectorInit)), - Box::new(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))), - Box::new(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))), - Box::new(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)), - Box::new(|_| Box::new(inherent_to_string::InherentToString)), - Box::new(move |_| Box::new(trait_bounds::TraitBounds::new(conf))), - Box::new(|_| Box::new(comparison_chain::ComparisonChain)), - Box::new(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))), - Box::new(|_| Box::new(reference::DerefAddrOf)), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))) - }, - Box::new(|_| Box::new(redundant_closure_call::RedundantClosureCall)), - Box::new(|_| Box::new(unused_unit::UnusedUnit)), - Box::new(|_| Box::new(returns::Return)), - Box::new(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))), - Box::new(|_| Box::new(items_after_statements::ItemsAfterStatements)), - Box::new(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)), - Box::new(|_| Box::new(needless_continue::NeedlessContinue)), - Box::new(|_| Box::new(create_dir::CreateDir)), - Box::new(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))), - Box::new(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(unused_self::UnusedSelf::new(conf))), - Box::new(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)), - Box::new(|_| Box::new(exit::Exit)), - Box::new(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))), - Box::new(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))), - Box::new(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))), - Box::new(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)), - Box::new(|_| Box::new(as_conversions::AsConversions)), - Box::new(|_| Box::new(let_underscore::LetUnderscore)), - Box::new(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))), - Box::new(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))), - Box::new(|_| Box::::default()), - Box::new(|_| Box::>::default()), - Box::new(|_| Box::new(option_if_let_else::OptionIfLetElse)), - Box::new(|_| Box::new(future_not_send::FutureNotSend)), - Box::new(move |_| Box::new(large_futures::LargeFuture::new(conf))), - Box::new(|_| Box::new(if_let_mutex::IfLetMutex)), - Box::new(|_| Box::new(if_not_else::IfNotElse)), - Box::new(|_| Box::new(equatable_if_let::PatternEquality)), - Box::new(|_| Box::new(manual_async_fn::ManualAsyncFn)), - Box::new(|_| Box::new(panic_in_result_fn::PanicInResultFn)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)), - Box::new(|_| Box::new(async_yields_async::AsyncYieldsAsync)), - { - let attrs = attr_storage.clone(); - Box::new(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()))) - }, - Box::new(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))), - Box::new(|_| Box::new(empty_drop::EmptyDrop)), - Box::new(|_| Box::new(strings::StrToString)), - Box::new(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(redundant_slicing::RedundantSlicing)), - Box::new(|_| Box::new(from_str_radix_10::FromStrRadix10)), - Box::new(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))), - Box::new(|_| Box::new(bool_assert_comparison::BoolAssertComparison)), - Box::new(|_| Box::::default()), - Box::new(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))), - Box::new(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))), - Box::new(move |_| Box::new(strlen_on_c_strings::StrlenOnCStrings::new(conf))), - Box::new(move |_| Box::new(self_named_constructors::SelfNamedConstructors)), - Box::new(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)), - Box::new(move |_| Box::new(manual_assert::ManualAssert)), - Box::new(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))), - Box::new(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))), - { - let format_args = format_args_storage.clone(); - Box::new(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone()))) - }, - Box::new(|_| Box::new(trailing_empty_array::TrailingEmptyArray)), - Box::new(|_| Box::new(needless_late_init::NeedlessLateInit)), - Box::new(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)), - Box::new(|_| Box::new(init_numbered_fields::NumberedFields)), - Box::new(move |_| Box::new(manual_bits::ManualBits::new(conf))), - Box::new(|_| Box::new(default_union_representation::DefaultUnionRepresentation)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(dbg_macro::DbgMacro::new(conf))), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(write::Write::new(conf, format_args.clone()))) - }, - Box::new(move |_| Box::new(cargo::Cargo::new(conf))), - Box::new(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())), - Box::new(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(format_push_string::FormatPushString::new(format_args.clone()))) - }, - Box::new(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))), - Box::new(|_| Box::new(strings::TrimSplitWhitespace)), - Box::new(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)), - Box::new(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)), - Box::new(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)), - Box::new(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)), - Box::new(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)), - Box::new(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))), - Box::new(move |_| Box::new(manual_retain::ManualRetain::new(conf))), - Box::new(move |_| Box::new(manual_rotate::ManualRotate)), - Box::new(move |_| Box::new(operators::Operators::new(conf))), - Box::new(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))), - Box::new(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))), - Box::new(|_| Box::new(partialeq_to_none::PartialeqToNone)), - Box::new(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))), - Box::new(move |_| Box::new(manual_clamp::ManualClamp::new(conf))), - Box::new(|_| Box::new(manual_string_new::ManualStringNew)), - Box::new(|_| Box::new(unused_peekable::UnusedPeekable)), - Box::new(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)), - Box::new(|_| Box::new(box_default::BoxDefault)), - Box::new(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)), - Box::new(|_| Box::new(missing_trait_methods::MissingTraitMethods)), - Box::new(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)), - Box::new(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)), - Box::new(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))), - Box::new(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))), - Box::new(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)), - Box::new(|_| Box::new(size_of_ref::SizeOfRef)), - Box::new(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)), - Box::new(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))), - Box::new(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)), - Box::new(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)), - Box::new(|_| Box::new(missing_assert_message::MissingAssertMessage)), - Box::new(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)), - Box::new(|_| Box::new(redundant_async_block::RedundantAsyncBlock)), - Box::new(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))), - Box::new(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)), - Box::new(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))), - Box::new(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)), - Box::new(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))), - Box::new(|_| Box::new(items_after_test_module::ItemsAfterTestModule)), - Box::new(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)), - Box::new(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)), - Box::new(|_| Box::new(endian_bytes::EndianBytes)), - Box::new(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)), - Box::new(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)), - Box::new(|_| Box::new(needless_ifs::NeedlessIfs)), - Box::new(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))), - Box::new(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))), - Box::new(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)), - Box::new(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))), - Box::new(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))), - Box::new(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))), - Box::new(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))), - Box::new(|_| Box::new(manual_range_patterns::ManualRangePatterns)), - Box::new(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))), - Box::new(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))), - Box::new(|_| Box::new(four_forward_slashes::FourForwardSlashes)), - Box::new(|_| Box::new(error_impl_error::ErrorImplError)), - Box::new(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))), - Box::new(|_| Box::new(redundant_locals::RedundantLocals)), - Box::new(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)), - Box::new(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)), - Box::new(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)), - Box::new(move |_| { - Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( - conf, - )) - }), - Box::new(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))), - Box::new(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)), - Box::new(|_| Box::>::default()), - Box::new(|_| Box::new(iter_over_hash_type::IterOverHashType)), - Box::new(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)), - Box::new(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf))), - Box::new(|_| Box::new(uninhabited_references::UninhabitedReferences)), - Box::new(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))), - Box::new(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))), - Box::new(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))), - Box::new(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)), - Box::new(move |_| Box::new(assigning_clones::AssigningClones::new(conf))), - Box::new(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)), - Box::new(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))), - Box::new(move |_| Box::new(string_patterns::StringPatterns::new(conf))), - Box::new(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)), - Box::new(|_| Box::new(zombie_processes::ZombieProcesses)), - Box::new(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)), - Box::new(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))), - Box::new(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)), - Box::new(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)), - Box::new(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))), - Box::new(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)), - Box::new(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)), - Box::new(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))), - Box::new(|_| Box::new(useless_concat::UselessConcat)), - Box::new(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))), - Box::new(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))), - Box::new(|_| Box::new(single_option_map::SingleOptionMap)), - Box::new(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)), - Box::new(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))), - Box::new(|_| Box::new(infallible_try_from::InfallibleTryFrom)), - Box::new(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)), - Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)), - Box::new(|_| Box::new(volatile_composites::VolatileComposites)), - Box::new(|_| Box::::default()), - Box::new(move |tcx| Box::new(disallowed_fields::DisallowedFields::new(tcx, conf))), - Box::new(move |_| Box::new(manual_ilog2::ManualIlog2::new(conf))), - Box::new(|_| Box::new(same_length_and_capacity::SameLengthAndCapacity)), - Box::new(move |tcx| Box::new(duration_suboptimal_units::DurationSuboptimalUnits::new(tcx, conf))), - Box::new(move |_| Box::new(manual_take::ManualTake::new(conf))), - Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)), - Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))), - Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))), - Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)), - Box::new(|_| Box::new(manual_assert_eq::ManualAssertEq)), +// Fold every late pass into one statically-combined struct (see +// `combined_late_pass`); the method list comes from `late_lint_methods!`. +#[rustfmt::skip] +rustc_lint::late_lint_methods!( + crate::combined_late_lint_pass, + [CombinedLateLintPass, (tcx: TyCtxt<'tcx>, conf: &'static Conf, format_args: FormatArgsStorage, attrs: AttrStorage), [ + ArithmeticSideEffects: operators::arithmetic_side_effects::ArithmeticSideEffects = operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf), + DumpHir: utils::dump_hir::DumpHir = utils::dump_hir::DumpHir, + Author: utils::author::Author = utils::author::Author, + AwaitHolding: await_holding_invalid::AwaitHolding = await_holding_invalid::AwaitHolding::new(tcx, conf), + SerdeApi: serde_api::SerdeApi = serde_api::SerdeApi, + Types: types::Types = types::Types::new(conf), + NonminimalBool: booleans::NonminimalBool = booleans::NonminimalBool::new(conf), + UnportableVariant: enum_clike::UnportableVariant = enum_clike::UnportableVariant, + FloatLiteral: float_literal::FloatLiteral = float_literal::FloatLiteral::new(conf), + Ptr: ptr::Ptr = ptr::Ptr, + NeedlessBool: needless_bool::NeedlessBool = needless_bool::NeedlessBool, + BoolComparison: bool_comparison::BoolComparison = bool_comparison::BoolComparison, + NeedlessForEach: needless_for_each::NeedlessForEach = needless_for_each::NeedlessForEach, + LintPass: misc::LintPass = misc::LintPass, + EtaReduction: eta_reduction::EtaReduction = eta_reduction::EtaReduction, + MutMut: mut_mut::MutMut = mut_mut::MutMut::default(), + UnnecessaryMutPassed: unnecessary_mut_passed::UnnecessaryMutPassed = unnecessary_mut_passed::UnnecessaryMutPassed, + SignificantDropTightening: significant_drop_tightening::SignificantDropTightening<'tcx> = >::default(), + LenZero: len_zero::LenZero = len_zero::LenZero::new(conf), + LenWithoutIsEmpty: len_without_is_empty::LenWithoutIsEmpty = len_without_is_empty::LenWithoutIsEmpty, + Attributes: attrs::Attributes = attrs::Attributes::new(conf), + BlocksInConditions: blocks_in_conditions::BlocksInConditions = blocks_in_conditions::BlocksInConditions, + Unicode: unicode::Unicode = unicode::Unicode, + UninitVec: uninit_vec::UninitVec = uninit_vec::UninitVec, + UnitReturnExpectingOrd: unit_return_expecting_ord::UnitReturnExpectingOrd = unit_return_expecting_ord::UnitReturnExpectingOrd, + StringAdd: strings::StringAdd = strings::StringAdd, + ImplicitReturn: implicit_return::ImplicitReturn = implicit_return::ImplicitReturn, + ImplicitSaturatingSub: implicit_saturating_sub::ImplicitSaturatingSub = implicit_saturating_sub::ImplicitSaturatingSub::new(conf), + DefaultNumericFallback: default_numeric_fallback::DefaultNumericFallback = default_numeric_fallback::DefaultNumericFallback, + NonOctalUnixPermissions: non_octal_unix_permissions::NonOctalUnixPermissions = non_octal_unix_permissions::NonOctalUnixPermissions, + ApproxConstant: approx_const::ApproxConstant = approx_const::ApproxConstant::new(conf), + Matches: matches::Matches = matches::Matches::new(conf), + ManualNonExhaustive: manual_non_exhaustive::ManualNonExhaustive = manual_non_exhaustive::ManualNonExhaustive::new(conf), + ManualStrip: manual_strip::ManualStrip = manual_strip::ManualStrip::new(conf), + CheckedConversions: checked_conversions::CheckedConversions = checked_conversions::CheckedConversions::new(conf), + MemReplace: mem_replace::MemReplace = mem_replace::MemReplace::new(conf), + Ranges: ranges::Ranges = ranges::Ranges::new(conf), + FromOverInto: from_over_into::FromOverInto = from_over_into::FromOverInto::new(conf), + UseSelf: use_self::UseSelf = use_self::UseSelf::new(conf), + MissingConstForFn: missing_const_for_fn::MissingConstForFn = missing_const_for_fn::MissingConstForFn::new(conf), + NeedlessQuestionMark: needless_question_mark::NeedlessQuestionMark = needless_question_mark::NeedlessQuestionMark, + Casts: casts::Casts = casts::Casts::new(conf), + SizeOfInElementCount: size_of_in_element_count::SizeOfInElementCount = size_of_in_element_count::SizeOfInElementCount, + SameNameMethod: same_name_method::SameNameMethod = same_name_method::SameNameMethod, + IndexRefutableSlice: index_refutable_slice::IndexRefutableSlice = index_refutable_slice::IndexRefutableSlice::new(conf), + Shadow: shadow::Shadow = ::default(), + InconsistentStructConstructor: inconsistent_struct_constructor::InconsistentStructConstructor = inconsistent_struct_constructor::InconsistentStructConstructor::new( conf, ), + Methods: methods::Methods = methods::Methods::new(conf, format_args.clone()), + UnitTypes: unit_types::UnitTypes = unit_types::UnitTypes::new(format_args.clone()), + Loops: loops::Loops = loops::Loops::new(conf), + MainRecursion: main_recursion::MainRecursion = ::default(), + Lifetimes: lifetimes::Lifetimes = lifetimes::Lifetimes::new(conf), + HashMapPass: entry::HashMapPass = entry::HashMapPass, + MinMaxPass: minmax::MinMaxPass = minmax::MinMaxPass, + ZeroDiv: zero_div_zero::ZeroDiv = zero_div_zero::ZeroDiv, + Mutex: mutex_atomic::Mutex = mutex_atomic::Mutex, + NeedlessUpdate: needless_update::NeedlessUpdate = needless_update::NeedlessUpdate, + NeedlessBorrowedRef: needless_borrowed_ref::NeedlessBorrowedRef = needless_borrowed_ref::NeedlessBorrowedRef, + BorrowDerefRef: borrow_deref_ref::BorrowDerefRef = borrow_deref_ref::BorrowDerefRef, + NoEffect: no_effect::NoEffect = ::default(), + TemporaryAssignment: temporary_assignment::TemporaryAssignment = temporary_assignment::TemporaryAssignment, + Transmute: transmute::Transmute = transmute::Transmute::new(conf), + CognitiveComplexity: cognitive_complexity::CognitiveComplexity = cognitive_complexity::CognitiveComplexity::new(conf), + BoxedLocal: escape::BoxedLocal = escape::BoxedLocal::new(conf), + UselessVec: useless_vec::UselessVec = useless_vec::UselessVec::new(conf), + PanicUnimplemented: panic_unimplemented::PanicUnimplemented = panic_unimplemented::PanicUnimplemented::new(conf), + StringLitAsBytes: strings::StringLitAsBytes = strings::StringLitAsBytes, + Derive: derive::Derive = derive::Derive, + DerivableImpls: derivable_impls::DerivableImpls = derivable_impls::DerivableImpls::new(conf), + DropForgetRef: drop_forget_ref::DropForgetRef = drop_forget_ref::DropForgetRef, + EmptyEnums: empty_enums::EmptyEnums = empty_enums::EmptyEnums, + Regex: regex::Regex = ::default(), + CopyAndPaste: ifs::CopyAndPaste<'tcx> = ifs::CopyAndPaste::new(tcx, conf), + CopyIterator: copy_iterator::CopyIterator = copy_iterator::CopyIterator, + UselessFormat: format::UselessFormat = format::UselessFormat::new(format_args.clone()), + Swap: swap::Swap = swap::Swap, + PanickingOverflowChecks: panicking_overflow_checks::PanickingOverflowChecks = panicking_overflow_checks::PanickingOverflowChecks, + NewWithoutDefault: new_without_default::NewWithoutDefault = ::default(), + DisallowedNames: disallowed_names::DisallowedNames = disallowed_names::DisallowedNames::new(conf), + Functions: functions::Functions = functions::Functions::new(tcx, conf), + Documentation: doc::Documentation = doc::Documentation::new(conf), + NegMultiply: neg_multiply::NegMultiply = neg_multiply::NegMultiply, + LetIfSeq: let_if_seq::LetIfSeq = let_if_seq::LetIfSeq, + EvalOrderDependence: mixed_read_write_in_expression::EvalOrderDependence = mixed_read_write_in_expression::EvalOrderDependence, + MissingDoc: missing_doc::MissingDoc = missing_doc::MissingDoc::new(conf), + MissingInline: missing_inline::MissingInline = missing_inline::MissingInline, + ExhaustiveItems: exhaustive_items::ExhaustiveItems = exhaustive_items::ExhaustiveItems, + UnusedResultOk: unused_result_ok::UnusedResultOk = unused_result_ok::UnusedResultOk, + MatchResultOk: match_result_ok::MatchResultOk = match_result_ok::MatchResultOk, + PartialEqNeImpl: partialeq_ne_impl::PartialEqNeImpl = partialeq_ne_impl::PartialEqNeImpl, + UnusedIoAmount: unused_io_amount::UnusedIoAmount = unused_io_amount::UnusedIoAmount, + LargeEnumVariant: large_enum_variant::LargeEnumVariant = large_enum_variant::LargeEnumVariant::new(conf), + ExplicitWrite: explicit_write::ExplicitWrite = explicit_write::ExplicitWrite::new(format_args.clone()), + NeedlessPassByValue: needless_pass_by_value::NeedlessPassByValue = needless_pass_by_value::NeedlessPassByValue, + PassByRefOrValue: pass_by_ref_or_value::PassByRefOrValue = pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf), + RefOptionRef: ref_option_ref::RefOptionRef = ref_option_ref::RefOptionRef, + InfiniteIter: infinite_iter::InfiniteIter = infinite_iter::InfiniteIter, + InlineFnWithoutBody: inline_fn_without_body::InlineFnWithoutBody = inline_fn_without_body::InlineFnWithoutBody, + UselessConversion: useless_conversion::UselessConversion = ::default(), + ImplicitHasher: implicit_hasher::ImplicitHasher = implicit_hasher::ImplicitHasher, + FallibleImplFrom: fallible_impl_from::FallibleImplFrom = fallible_impl_from::FallibleImplFrom, + QuestionMark: question_mark::QuestionMark = question_mark::QuestionMark::new(conf), + QuestionMarkUsed: question_mark_used::QuestionMarkUsed = question_mark_used::QuestionMarkUsed, + SuspiciousImpl: suspicious_trait_impl::SuspiciousImpl = suspicious_trait_impl::SuspiciousImpl, + MapUnit: map_unit_fn::MapUnit = map_unit_fn::MapUnit, + MultipleInherentImpl: inherent_impl::MultipleInherentImpl = inherent_impl::MultipleInherentImpl::new(conf), + NoNegCompOpForPartialOrd: neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd = neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd, + Unwrap: unwrap::Unwrap = unwrap::Unwrap::new(conf), + IndexingSlicing: indexing_slicing::IndexingSlicing = indexing_slicing::IndexingSlicing::new(conf), + NonCopyConst: non_copy_const::NonCopyConst<'tcx> = non_copy_const::NonCopyConst::new(tcx, conf), + RedundantClone: redundant_clone::RedundantClone = redundant_clone::RedundantClone, + SlowVectorInit: slow_vector_initialization::SlowVectorInit = slow_vector_initialization::SlowVectorInit, + UnnecessaryWraps: unnecessary_wraps::UnnecessaryWraps = unnecessary_wraps::UnnecessaryWraps::new(conf), + AssertionsOnConstants: assertions_on_constants::AssertionsOnConstants = assertions_on_constants::AssertionsOnConstants::new(conf), + AssertionsOnResultStates: assertions_on_result_states::AssertionsOnResultStates = assertions_on_result_states::AssertionsOnResultStates, + InherentToString: inherent_to_string::InherentToString = inherent_to_string::InherentToString, + TraitBounds: trait_bounds::TraitBounds = trait_bounds::TraitBounds::new(conf), + ComparisonChain: comparison_chain::ComparisonChain = comparison_chain::ComparisonChain, + MutableKeyType: mut_key::MutableKeyType<'tcx> = mut_key::MutableKeyType::new(tcx, conf), + DerefAddrOf: reference::DerefAddrOf = reference::DerefAddrOf, + FormatImpl: format_impl::FormatImpl = format_impl::FormatImpl::new(format_args.clone()), + RedundantClosureCall: redundant_closure_call::RedundantClosureCall = redundant_closure_call::RedundantClosureCall, + UnusedUnit: unused_unit::UnusedUnit = unused_unit::UnusedUnit, + Return: returns::Return = returns::Return, + CollapsibleIf: collapsible_if::CollapsibleIf = collapsible_if::CollapsibleIf::new(conf), + ItemsAfterStatements: items_after_statements::ItemsAfterStatements = items_after_statements::ItemsAfterStatements, + NeedlessParensOnRangeLiterals: needless_parens_on_range_literals::NeedlessParensOnRangeLiterals = needless_parens_on_range_literals::NeedlessParensOnRangeLiterals, + NeedlessContinue: needless_continue::NeedlessContinue = needless_continue::NeedlessContinue, + CreateDir: create_dir::CreateDir = create_dir::CreateDir, + ItemNameRepetitions: item_name_repetitions::ItemNameRepetitions = item_name_repetitions::ItemNameRepetitions::new(conf), + UpperCaseAcronyms: upper_case_acronyms::UpperCaseAcronyms = upper_case_acronyms::UpperCaseAcronyms::new(conf), + Default: default::Default = ::default(), + UnusedSelf: unused_self::UnusedSelf = unused_self::UnusedSelf::new(conf), + DebugAssertWithMutCall: mutable_debug_assertion::DebugAssertWithMutCall = mutable_debug_assertion::DebugAssertWithMutCall, + Exit: exit::Exit = exit::Exit, + ToDigitIsSome: to_digit_is_some::ToDigitIsSome = to_digit_is_some::ToDigitIsSome::new(conf), + LargeStackArrays: large_stack_arrays::LargeStackArrays = large_stack_arrays::LargeStackArrays::new(conf), + LargeConstArrays: large_const_arrays::LargeConstArrays = large_const_arrays::LargeConstArrays::new(conf), + FloatingPointArithmetic: floating_point_arithmetic::FloatingPointArithmetic = floating_point_arithmetic::FloatingPointArithmetic, + AsConversions: as_conversions::AsConversions = as_conversions::AsConversions, + LetUnderscore: let_underscore::LetUnderscore = let_underscore::LetUnderscore, + ExcessiveBools: excessive_bools::ExcessiveBools = excessive_bools::ExcessiveBools::new(conf), + WildcardImports: wildcard_imports::WildcardImports = wildcard_imports::WildcardImports::new(conf), + RedundantPubCrate: redundant_pub_crate::RedundantPubCrate = ::default(), + Dereferencing: dereference::Dereferencing<'tcx> = >::default(), + OptionIfLetElse: option_if_let_else::OptionIfLetElse = option_if_let_else::OptionIfLetElse, + FutureNotSend: future_not_send::FutureNotSend = future_not_send::FutureNotSend, + LargeFuture: large_futures::LargeFuture = large_futures::LargeFuture::new(conf), + IfLetMutex: if_let_mutex::IfLetMutex = if_let_mutex::IfLetMutex, + IfNotElse: if_not_else::IfNotElse = if_not_else::IfNotElse, + PatternEquality: equatable_if_let::PatternEquality = equatable_if_let::PatternEquality, + ManualAsyncFn: manual_async_fn::ManualAsyncFn = manual_async_fn::ManualAsyncFn, + PanicInResultFn: panic_in_result_fn::PanicInResultFn = panic_in_result_fn::PanicInResultFn, + MacroUseImports: macro_use::MacroUseImports = ::default(), + PatternTypeMismatch: pattern_type_mismatch::PatternTypeMismatch = pattern_type_mismatch::PatternTypeMismatch, + UnwrapInResult: unwrap_in_result::UnwrapInResult = ::default(), + SemicolonIfNothingReturned: semicolon_if_nothing_returned::SemicolonIfNothingReturned = semicolon_if_nothing_returned::SemicolonIfNothingReturned, + AsyncYieldsAsync: async_yields_async::AsyncYieldsAsync = async_yields_async::AsyncYieldsAsync, + DisallowedMacros: disallowed_macros::DisallowedMacros = disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()), + DisallowedMethods: disallowed_methods::DisallowedMethods = disallowed_methods::DisallowedMethods::new(tcx, conf), + EmptyDrop: empty_drop::EmptyDrop = empty_drop::EmptyDrop, + StrToString: strings::StrToString = strings::StrToString, + ZeroSizedMapValues: zero_sized_map_values::ZeroSizedMapValues = zero_sized_map_values::ZeroSizedMapValues, + VecInitThenPush: vec_init_then_push::VecInitThenPush = ::default(), + RedundantSlicing: redundant_slicing::RedundantSlicing = redundant_slicing::RedundantSlicing, + FromStrRadix10: from_str_radix_10::FromStrRadix10 = from_str_radix_10::FromStrRadix10, + IfThenSomeElseNone: if_then_some_else_none::IfThenSomeElseNone = if_then_some_else_none::IfThenSomeElseNone::new(conf), + BoolAssertComparison: bool_assert_comparison::BoolAssertComparison = bool_assert_comparison::BoolAssertComparison, + UnusedAsync: unused_async::UnusedAsync = ::default(), + DisallowedTypes: disallowed_types::DisallowedTypes = disallowed_types::DisallowedTypes::new(tcx, conf), + ImportRename: missing_enforced_import_rename::ImportRename = missing_enforced_import_rename::ImportRename::new(tcx, conf), + StrlenOnCStrings: strlen_on_c_strings::StrlenOnCStrings = strlen_on_c_strings::StrlenOnCStrings::new(conf), + SelfNamedConstructors: self_named_constructors::SelfNamedConstructors = self_named_constructors::SelfNamedConstructors, + IterNotReturningIterator: iter_not_returning_iterator::IterNotReturningIterator = iter_not_returning_iterator::IterNotReturningIterator, + ManualAssert: manual_assert::ManualAssert = manual_assert::ManualAssert, + NonSendFieldInSendTy: non_send_fields_in_send_ty::NonSendFieldInSendTy = non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf), + UndocumentedUnsafeBlocks: undocumented_unsafe_blocks::UndocumentedUnsafeBlocks = undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf), + FormatArgs: format_args::FormatArgs<'tcx> = format_args::FormatArgs::new(tcx, conf, format_args.clone()), + TrailingEmptyArray: trailing_empty_array::TrailingEmptyArray = trailing_empty_array::TrailingEmptyArray, + NeedlessLateInit: needless_late_init::NeedlessLateInit = needless_late_init::NeedlessLateInit, + ReturnSelfNotMustUse: return_self_not_must_use::ReturnSelfNotMustUse = return_self_not_must_use::ReturnSelfNotMustUse, + NumberedFields: init_numbered_fields::NumberedFields = init_numbered_fields::NumberedFields, + ManualBits: manual_bits::ManualBits = manual_bits::ManualBits::new(conf), + DefaultUnionRepresentation: default_union_representation::DefaultUnionRepresentation = default_union_representation::DefaultUnionRepresentation, + OnlyUsedInRecursion: only_used_in_recursion::OnlyUsedInRecursion = ::default(), + DbgMacro: dbg_macro::DbgMacro = dbg_macro::DbgMacro::new(conf), + Write: write::Write = write::Write::new(conf, format_args.clone()), + Cargo: cargo::Cargo = cargo::Cargo::new(conf), + EmptyWithBrackets: empty_with_brackets::EmptyWithBrackets = empty_with_brackets::EmptyWithBrackets::default(), + UnnecessaryOwnedEmptyStrings: unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings = unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings, + FormatPushString: format_push_string::FormatPushString = format_push_string::FormatPushString::new(format_args.clone()), + LargeIncludeFile: large_include_file::LargeIncludeFile = large_include_file::LargeIncludeFile::new(conf), + TrimSplitWhitespace: strings::TrimSplitWhitespace = strings::TrimSplitWhitespace, + RcCloneInVecInit: rc_clone_in_vec_init::RcCloneInVecInit = rc_clone_in_vec_init::RcCloneInVecInit, + SwapPtrToRef: swap_ptr_to_ref::SwapPtrToRef = swap_ptr_to_ref::SwapPtrToRef, + TypeParamMismatch: mismatching_type_param_order::TypeParamMismatch = mismatching_type_param_order::TypeParamMismatch, + ReadZeroByteVec: read_zero_byte_vec::ReadZeroByteVec = read_zero_byte_vec::ReadZeroByteVec, + DefaultIterEmpty: default_instead_of_iter_empty::DefaultIterEmpty = default_instead_of_iter_empty::DefaultIterEmpty, + ManualRemEuclid: manual_rem_euclid::ManualRemEuclid = manual_rem_euclid::ManualRemEuclid::new(conf), + ManualRetain: manual_retain::ManualRetain = manual_retain::ManualRetain::new(conf), + ManualRotate: manual_rotate::ManualRotate = manual_rotate::ManualRotate, + Operators: operators::Operators = operators::Operators::new(conf), + StdReexports: std_instead_of_core::StdReexports = std_instead_of_core::StdReexports::new(conf), + UncheckedTimeSubtraction: time_subtraction::UncheckedTimeSubtraction = time_subtraction::UncheckedTimeSubtraction::new(conf), + PartialeqToNone: partialeq_to_none::PartialeqToNone = partialeq_to_none::PartialeqToNone, + ManualAbsDiff: manual_abs_diff::ManualAbsDiff = manual_abs_diff::ManualAbsDiff::new(conf), + ManualClamp: manual_clamp::ManualClamp = manual_clamp::ManualClamp::new(conf), + ManualStringNew: manual_string_new::ManualStringNew = manual_string_new::ManualStringNew, + UnusedPeekable: unused_peekable::UnusedPeekable = unused_peekable::UnusedPeekable, + BoolToIntWithIf: bool_to_int_with_if::BoolToIntWithIf = bool_to_int_with_if::BoolToIntWithIf, + BoxDefault: box_default::BoxDefault = box_default::BoxDefault, + ImplicitSaturatingAdd: implicit_saturating_add::ImplicitSaturatingAdd = implicit_saturating_add::ImplicitSaturatingAdd, + MissingTraitMethods: missing_trait_methods::MissingTraitMethods = missing_trait_methods::MissingTraitMethods, + FromRawWithVoidPtr: from_raw_with_void_ptr::FromRawWithVoidPtr = from_raw_with_void_ptr::FromRawWithVoidPtr, + ConfusingXorAndPow: suspicious_xor_used_as_pow::ConfusingXorAndPow = suspicious_xor_used_as_pow::ConfusingXorAndPow, + ManualIsAsciiCheck: manual_is_ascii_check::ManualIsAsciiCheck = manual_is_ascii_check::ManualIsAsciiCheck::new(conf), + SemicolonBlock: semicolon_block::SemicolonBlock = semicolon_block::SemicolonBlock::new(conf), + PermissionsSetReadonlyFalse: permissions_set_readonly_false::PermissionsSetReadonlyFalse = permissions_set_readonly_false::PermissionsSetReadonlyFalse, + SizeOfRef: size_of_ref::SizeOfRef = size_of_ref::SizeOfRef, + MultipleUnsafeOpsPerBlock: multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock = multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock, + ExtraUnusedTypeParameters: extra_unused_type_parameters::ExtraUnusedTypeParameters = extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf), + NoMangleWithRustAbi: no_mangle_with_rust_abi::NoMangleWithRustAbi = no_mangle_with_rust_abi::NoMangleWithRustAbi, + CollectionIsNeverRead: collection_is_never_read::CollectionIsNeverRead = collection_is_never_read::CollectionIsNeverRead, + MissingAssertMessage: missing_assert_message::MissingAssertMessage = missing_assert_message::MissingAssertMessage, + NeedlessMaybeSized: needless_maybe_sized::NeedlessMaybeSized = needless_maybe_sized::NeedlessMaybeSized, + RedundantAsyncBlock: redundant_async_block::RedundantAsyncBlock = redundant_async_block::RedundantAsyncBlock, + ManualMainSeparatorStr: manual_main_separator_str::ManualMainSeparatorStr = manual_main_separator_str::ManualMainSeparatorStr::new(conf), + UnnecessaryStruct: unnecessary_struct_initialization::UnnecessaryStruct = unnecessary_struct_initialization::UnnecessaryStruct, + UnnecessaryBoxReturns: unnecessary_box_returns::UnnecessaryBoxReturns = unnecessary_box_returns::UnnecessaryBoxReturns::new(conf), + TestsOutsideTestModule: tests_outside_test_module::TestsOutsideTestModule = tests_outside_test_module::TestsOutsideTestModule, + ManualSliceSizeCalculation: manual_slice_size_calculation::ManualSliceSizeCalculation = manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf), + ItemsAfterTestModule: items_after_test_module::ItemsAfterTestModule = items_after_test_module::ItemsAfterTestModule, + DefaultConstructedUnitStructs: default_constructed_unit_structs::DefaultConstructedUnitStructs = default_constructed_unit_structs::DefaultConstructedUnitStructs, + MissingFieldsInDebug: missing_fields_in_debug::MissingFieldsInDebug = missing_fields_in_debug::MissingFieldsInDebug, + EndianBytes: endian_bytes::EndianBytes = endian_bytes::EndianBytes, + RedundantTypeAnnotations: redundant_type_annotations::RedundantTypeAnnotations = redundant_type_annotations::RedundantTypeAnnotations, + ArcWithNonSendSync: arc_with_non_send_sync::ArcWithNonSendSync = arc_with_non_send_sync::ArcWithNonSendSync, + NeedlessIfs: needless_ifs::NeedlessIfs = needless_ifs::NeedlessIfs, + MinIdentChars: min_ident_chars::MinIdentChars = min_ident_chars::MinIdentChars::new(conf), + LargeStackFrames: large_stack_frames::LargeStackFrames = large_stack_frames::LargeStackFrames::new(conf), + SingleRangeInVecInit: single_range_in_vec_init::SingleRangeInVecInit = single_range_in_vec_init::SingleRangeInVecInit, + NeedlessPassByRefMut: needless_pass_by_ref_mut::NeedlessPassByRefMut<'tcx> = needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf), + NonCanonicalImpls: non_canonical_impls::NonCanonicalImpls = non_canonical_impls::NonCanonicalImpls::new(tcx), + SingleCallFn: single_call_fn::SingleCallFn = single_call_fn::SingleCallFn::new(conf), + LegacyNumericConstants: legacy_numeric_constants::LegacyNumericConstants = legacy_numeric_constants::LegacyNumericConstants::new(conf), + ManualRangePatterns: manual_range_patterns::ManualRangePatterns = manual_range_patterns::ManualRangePatterns, + TupleArrayConversions: tuple_array_conversions::TupleArrayConversions = tuple_array_conversions::TupleArrayConversions::new(conf), + ManualFloatMethods: manual_float_methods::ManualFloatMethods = manual_float_methods::ManualFloatMethods::new(conf), + FourForwardSlashes: four_forward_slashes::FourForwardSlashes = four_forward_slashes::FourForwardSlashes, + ErrorImplError: error_impl_error::ErrorImplError = error_impl_error::ErrorImplError, + AbsolutePaths: absolute_paths::AbsolutePaths = absolute_paths::AbsolutePaths::new(conf), + RedundantLocals: redundant_locals::RedundantLocals = redundant_locals::RedundantLocals, + IgnoredUnitPatterns: ignored_unit_patterns::IgnoredUnitPatterns = ignored_unit_patterns::IgnoredUnitPatterns, + ReserveAfterInitialization: reserve_after_initialization::ReserveAfterInitialization = ::default(), + ImpliedBoundsInImpls: implied_bounds_in_impls::ImpliedBoundsInImpls = implied_bounds_in_impls::ImpliedBoundsInImpls, + MissingAssertsForIndexing: missing_asserts_for_indexing::MissingAssertsForIndexing = missing_asserts_for_indexing::MissingAssertsForIndexing, + UnnecessaryMapOnConstructor: unnecessary_map_on_constructor::UnnecessaryMapOnConstructor = unnecessary_map_on_constructor::UnnecessaryMapOnConstructor, + NeedlessBorrowsForGenericArgs: needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs<'tcx> = needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( conf, ), + ManualHashOne: manual_hash_one::ManualHashOne = manual_hash_one::ManualHashOne::new(conf), + IterWithoutIntoIter: iter_without_into_iter::IterWithoutIntoIter = iter_without_into_iter::IterWithoutIntoIter, + PathbufThenPush: pathbuf_init_then_push::PathbufThenPush<'tcx> = >::default(), + IterOverHashType: iter_over_hash_type::IterOverHashType = iter_over_hash_type::IterOverHashType, + ImplHashWithBorrowStrBytes: impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes = impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes, + RepeatVecWithCapacity: repeat_vec_with_capacity::RepeatVecWithCapacity = repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf), + UninhabitedReferences: uninhabited_references::UninhabitedReferences = uninhabited_references::UninhabitedReferences, + IneffectiveOpenOptions: ineffective_open_options::IneffectiveOpenOptions = ineffective_open_options::IneffectiveOpenOptions, + UnconditionalRecursion: unconditional_recursion::UnconditionalRecursion = ::default(), + PubUnderscoreFields: pub_underscore_fields::PubUnderscoreFields = pub_underscore_fields::PubUnderscoreFields::new(conf), + MissingConstForThreadLocal: missing_const_for_thread_local::MissingConstForThreadLocal = missing_const_for_thread_local::MissingConstForThreadLocal::new(conf), + IncompatibleMsrv: incompatible_msrv::IncompatibleMsrv = incompatible_msrv::IncompatibleMsrv::new(tcx, conf), + ToStringTraitImpl: to_string_trait_impl::ToStringTraitImpl = to_string_trait_impl::ToStringTraitImpl, + AssigningClones: assigning_clones::AssigningClones = assigning_clones::AssigningClones::new(conf), + ZeroRepeatSideEffects: zero_repeat_side_effects::ZeroRepeatSideEffects = zero_repeat_side_effects::ZeroRepeatSideEffects, + ExprMetavarsInUnsafe: macro_metavars_in_unsafe::ExprMetavarsInUnsafe = macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf), + StringPatterns: string_patterns::StringPatterns = string_patterns::StringPatterns::new(conf), + SetContainsOrInsert: set_contains_or_insert::SetContainsOrInsert = set_contains_or_insert::SetContainsOrInsert, + ZombieProcesses: zombie_processes::ZombieProcesses = zombie_processes::ZombieProcesses, + PointersInNomemAsmBlock: pointers_in_nomem_asm_block::PointersInNomemAsmBlock = pointers_in_nomem_asm_block::PointersInNomemAsmBlock, + ManualIsPowerOfTwo: manual_is_power_of_two::ManualIsPowerOfTwo = manual_is_power_of_two::ManualIsPowerOfTwo::new(conf), + NonZeroSuggestions: non_zero_suggestions::NonZeroSuggestions = non_zero_suggestions::NonZeroSuggestions, + LiteralStringWithFormattingArg: literal_string_with_formatting_args::LiteralStringWithFormattingArg = literal_string_with_formatting_args::LiteralStringWithFormattingArg, + UnusedTraitNames: unused_trait_names::UnusedTraitNames = unused_trait_names::UnusedTraitNames::new(conf), + ManualIgnoreCaseCmp: manual_ignore_case_cmp::ManualIgnoreCaseCmp = manual_ignore_case_cmp::ManualIgnoreCaseCmp, + UnnecessaryLiteralBound: unnecessary_literal_bound::UnnecessaryLiteralBound = unnecessary_literal_bound::UnnecessaryLiteralBound, + ArbitrarySourceItemOrdering: arbitrary_source_item_ordering::ArbitrarySourceItemOrdering = arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf), + UselessConcat: useless_concat::UselessConcat = useless_concat::UselessConcat, + UnneededStructPattern: unneeded_struct_pattern::UnneededStructPattern = unneeded_struct_pattern::UnneededStructPattern, + UnnecessarySemicolon: unnecessary_semicolon::UnnecessarySemicolon = ::default(), + NonStdLazyStatic: non_std_lazy_statics::NonStdLazyStatic = non_std_lazy_statics::NonStdLazyStatic::new(conf), + ManualOptionAsSlice: manual_option_as_slice::ManualOptionAsSlice = manual_option_as_slice::ManualOptionAsSlice::new(conf), + SingleOptionMap: single_option_map::SingleOptionMap = single_option_map::SingleOptionMap, + RedundantTestPrefix: redundant_test_prefix::RedundantTestPrefix = redundant_test_prefix::RedundantTestPrefix, + ClonedRefToSliceRefs: cloned_ref_to_slice_refs::ClonedRefToSliceRefs<'tcx> = cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf), + InfallibleTryFrom: infallible_try_from::InfallibleTryFrom = infallible_try_from::InfallibleTryFrom, + CoerceContainerToAny: coerce_container_to_any::CoerceContainerToAny = coerce_container_to_any::CoerceContainerToAny, + ToplevelRefArg: toplevel_ref_arg::ToplevelRefArg = toplevel_ref_arg::ToplevelRefArg, + VolatileComposites: volatile_composites::VolatileComposites = volatile_composites::VolatileComposites, + ReplaceBox: replace_box::ReplaceBox = ::default(), + DisallowedFields: disallowed_fields::DisallowedFields = disallowed_fields::DisallowedFields::new(tcx, conf), + ManualIlog2: manual_ilog2::ManualIlog2 = manual_ilog2::ManualIlog2::new(conf), + SameLengthAndCapacity: same_length_and_capacity::SameLengthAndCapacity = same_length_and_capacity::SameLengthAndCapacity, + DurationSuboptimalUnits: duration_suboptimal_units::DurationSuboptimalUnits = duration_suboptimal_units::DurationSuboptimalUnits::new(tcx, conf), + ManualTake: manual_take::ManualTake = manual_take::ManualTake::new(conf), + ManualCheckedOps: manual_checked_ops::ManualCheckedOps = manual_checked_ops::ManualCheckedOps, + ManualPopIf: manual_pop_if::ManualPopIf = manual_pop_if::ManualPopIf::new(tcx, conf), + ManualNoopWaker: manual_noop_waker::ManualNoopWaker = manual_noop_waker::ManualNoopWaker::new(conf), + ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, + ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, + WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero, // add late passes here, used by `cargo dev new_lint` - ]; - store.late_passes.extend(late_lints); -} + ]] +); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 8b50ce279b4b1..2b4828872b202 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -9,14 +9,14 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, VisitorExt, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_unambig_ty, walk_where_predicate, + Visitor, VisitorExt, walk_fn_decl, walk_generic_args, walk_generic_param, walk_generics, walk_impl_item_ref, + walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_unambig_ty, walk_where_predicate, }; use rustc_hir::{ AmbigArg, BodyId, FnDecl, FnPtrTy, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeKind, LifetimeParamKind, Node, - PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, - WherePredicateKind, lang_items, + PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, TraitRef, Ty, TyKind, WhereBoundPredicate, + WherePredicate, WherePredicateKind, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -160,6 +160,10 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } } + fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly_trait_ref: &'tcx PolyTraitRef<'tcx>) { + report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + } + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if let ImplItemKind::Fn(ref sig, id) = item.kind { let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id).is_none(); @@ -579,9 +583,8 @@ impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> where F: NestedFilter<'tcx>, { - fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'_>) -> LifetimeChecker<'cx, 'tcx, F> { - let map = generics - .params + fn new(cx: &'cx LateContext<'tcx>, generic_params: &'tcx [GenericParam<'_>]) -> LifetimeChecker<'cx, 'tcx, F> { + let map = generic_params .iter() .filter_map(|par| match par.kind { GenericParamKind::Lifetime { @@ -590,6 +593,7 @@ where _ => None, }) .collect(); + Self { cx, map, @@ -703,8 +707,36 @@ fn is_candidate_for_elision(fd: &FnDecl<'_>) -> bool { } } +fn report_extra_trait_object_lifetimes<'tcx>( + cx: &LateContext<'tcx>, + generic_params: &'tcx [GenericParam<'_>], + trait_ref: &'tcx TraitRef<'tcx>, +) { + let mut checker = LifetimeChecker::::new(cx, generic_params); + + for param in generic_params { + walk_generic_param(&mut checker, param); + } + + walk_trait_ref(&mut checker, trait_ref); + + for (def_id, usages) in checker.map { + if usages + .iter() + .all(|usage| usage.in_where_predicate && !usage.in_bounded_ty && !usage.in_generics_arg) + { + span_lint( + cx, + EXTRA_UNUSED_LIFETIMES, + cx.tcx.def_span(def_id), + "this lifetime isn't used in the type", + ); + } + } +} + fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { - let mut checker = LifetimeChecker::::new(cx, generics); + let mut checker = LifetimeChecker::::new(cx, generics.params); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); @@ -725,7 +757,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, } fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { - let mut checker = LifetimeChecker::::new(cx, impl_.generics); + let mut checker = LifetimeChecker::::new(cx, impl_.generics.params); walk_generics(&mut checker, impl_.generics); if let Some(of_trait) = impl_.of_trait { diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs index b813a18b221ea..b10584fb9bd7c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::Range; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::{EMPTY, Sugg}; -use clippy_utils::{get_enclosing_block, is_integer_const, is_integer_literal_untyped}; +use clippy_utils::{get_enclosing_block, is_integer_literal, is_integer_literal_untyped}; use rustc_ast::{Label, RangeLimits}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( continue; } - let is_zero = is_integer_const(cx, initializer, 0); + let is_zero = is_integer_literal(initializer, 0); let mut applicability = Applicability::MaybeIncorrect; let span = expr.span.with_hi(arg.span.hi()); let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name)); @@ -88,7 +88,7 @@ pub(super) fn check<'tcx>( if pat_snippet == "_" && let Some(range) = Range::hir(cx, arg) && range.limits == RangeLimits::HalfOpen - && range.start.is_some_and(|start| is_integer_const(cx, start, 0)) + && range.start.is_some_and(|start| is_integer_literal(start, 0)) && let Some(end) = range.end { let end = snippet_with_applicability(cx, end.span, "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/loops/for_unbounded_range.rs b/src/tools/clippy/clippy_lints/src/loops/for_unbounded_range.rs new file mode 100644 index 0000000000000..4691ee02d9539 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/for_unbounded_range.rs @@ -0,0 +1,42 @@ +use super::FOR_UNBOUNDED_RANGE; +use super::infinite_loop::LoopVisitor; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; +use rustc_ast::Label; +use rustc_hir::Expr; +use rustc_hir::intravisit::Visitor as _; +use rustc_lint::LateContext; +use rustc_span::Span; + +pub fn check<'tcx>( + cx: &LateContext<'tcx>, + label: Option