From e0175f8b8b183b412dcf2f1aad827575a57df63b Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 10 Feb 2025 20:58:27 +0100 Subject: [PATCH 001/103] Document how to benchmark with lintcheck --perf Introducing a new chapter to the book, known as "Benchmarking Clippy". It explains the benchmarking capabilities of lintcheck --perf and gives a concrete example on how benchmark and compare a PR with master --- book/src/SUMMARY.md | 1 + .../infrastructure/benchmarking.md | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 book/src/development/infrastructure/benchmarking.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 19328fdd3cd47..39fe7358ed87a 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -30,6 +30,7 @@ - [Updating the Changelog](development/infrastructure/changelog_update.md) - [Release a New Version](development/infrastructure/release.md) - [The Clippy Book](development/infrastructure/book.md) + - [Benchmarking Clippy](development/infrastructure/benchmarking.md) - [Proposals](development/proposals/README.md) - [Roadmap 2021](development/proposals/roadmap-2021.md) - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md) diff --git a/book/src/development/infrastructure/benchmarking.md b/book/src/development/infrastructure/benchmarking.md new file mode 100644 index 0000000000000..0c5ecfc76279f --- /dev/null +++ b/book/src/development/infrastructure/benchmarking.md @@ -0,0 +1,55 @@ +# Benchmarking Clippy + +Benchmarking Clippy is similar to using our Lintcheck tool, in fact, it even +uses the same tool! Just by adding a `--perf` flag it will transform Lintcheck +into a very simple but powerful benchmarking tool! + +It requires having the [`perf` tool][perf] installed, as `perf` is what's actually +profiling Clippy under the hood. + +The lintcheck `--perf` tool generates a series of `perf.data` in the +`target/lintcheck/sources/-` directories. Each `perf.data` +corresponds to the package which is contained. + +Lintcheck uses the `-g` flag, meaning that you can get stack traces for richer +analysis, including with tools such as [flamegraph][flamegraph-perf] +(or [`flamegraph-rs`][flamegraph-rs]). + +Currently, we only measure instruction count, as it's the most reproducible metric +and rustc-perf also considers it the main number to focus on. + +## Benchmarking a PR + +Having a benchmarking tool directly implemented into lintcheck gives us the +ability to benchmark any given PR just by making a before and after + +Here's the way you can get into any PR, benchmark it, and then benchmark +`master`. + +The first `perf.data` will not have any numbers appended, but any subsequent +benchmark will be written to `perf.data.number` with a number growing for 0. +All benchmarks are compressed so that you can + +```bash +git fetch upstream pull//head: +git switch BRANCHNAME + +# Bench +cargo lintcheck --perf + +# Get last common commit, checkout that +LAST_COMMIT=$(git log BRANCHNAME..master --oneline | tail -1 | cut -c 1-11) +git switch -c temporary $LAST_COMMIT + +# We're now on master + +# Bench +cargo lintcheck --perf +perf diff ./target/lintcheck/sources/CRATE/perf.data ./target/lintcheck/sources/CRATE/perf.data.0 +``` + + +[perf]: https://perfwiki.github.io/main/ +[flamegraph-perf]: https://github.com/brendangregg/FlameGraph +[flamegraph-rs]: https://github.com/flamegraph-rs/flamegraph +[rustc-perf]: \ No newline at end of file From 0ee39a20150da33524df4d6471bc65b76e33816d Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 10 Feb 2025 23:29:19 +0100 Subject: [PATCH 002/103] Several improvements on lintcheck perf (desc.) - Now lintcheck perf deletes target directory after benchmarking, benchmarking with a cache isn't very useful or telling of any precise outcome. - Support for benchmarking several times without having to do a cargo clean. Now we can benchmark a PR and master (or a single change in the same commit) without having to move the perf.data files into an external directory. - Compress perf.data to allow for allowing multiple stacks and occupy much less space --- lintcheck/src/main.rs | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 8d0d41ab9450f..ab86f6ad6f917 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -120,14 +120,16 @@ impl Crate { if config.perf { cmd = Command::new("perf"); + let perf_data_filename = get_perf_data_filename(&self.path); cmd.args(&[ "record", "-e", "instructions", // Only count instructions "-g", // Enable call-graph, useful for flamegraphs and produces richer reports "--quiet", // Do not tamper with lintcheck's normal output + "--compression-level=22", "-o", - "perf.data", + &perf_data_filename, "--", "cargo", ]); @@ -165,7 +167,7 @@ impl Crate { return Vec::new(); } - if !config.fix { + if !config.fix && !config.perf { cmd.arg("--message-format=json"); } @@ -203,6 +205,11 @@ impl Crate { return Vec::new(); } + // We don't want to keep target directories if benchmarking + if config.perf { + let _ = fs::remove_dir_all(&shared_target_dir); + } + // get all clippy warnings and ICEs let mut entries: Vec = Message::parse_stream(stdout.as_bytes()) .filter_map(|msg| match msg { @@ -441,6 +448,35 @@ fn lintcheck(config: LintcheckConfig) { fs::write(&config.lintcheck_results_path, text).unwrap(); } +/// Traverse a directory looking for `perf.data.` files, and adds one +/// to the most recent of those files, returning the new most recent `perf.data` +/// file name. +fn get_perf_data_filename(source_path: &Path) -> String { + if source_path.join("perf.data").exists() { + let mut max_number = 0; + fs::read_dir(source_path) + .unwrap() + .filter_map(Result::ok) + .filter(|path| { + path.file_name() + .as_os_str() + .to_string_lossy() // We don't care about data loss, as we're checking for equality + .starts_with("perf.data") + }) + .for_each(|path| { + let file_name = path.file_name(); + let file_name = file_name.as_os_str().to_str().unwrap().split('.').next_back().unwrap(); + if let Ok(parsed_file_name) = file_name.parse::() { + if parsed_file_name >= max_number { + max_number = parsed_file_name + 1; + } + } + }); + return format!("perf.data.{max_number}"); + } + String::from("perf.data") +} + /// Returns the path to the Clippy project directory #[must_use] fn clippy_project_root() -> &'static Path { From 4035fc2b72e51534a573169accecbcc978f20238 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 10 Feb 2025 23:50:15 +0100 Subject: [PATCH 003/103] Follow remark guidelines --- book/src/development/infrastructure/benchmarking.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/development/infrastructure/benchmarking.md b/book/src/development/infrastructure/benchmarking.md index 0c5ecfc76279f..a3ebce922f6c9 100644 --- a/book/src/development/infrastructure/benchmarking.md +++ b/book/src/development/infrastructure/benchmarking.md @@ -16,7 +16,7 @@ analysis, including with tools such as [flamegraph][flamegraph-perf] (or [`flamegraph-rs`][flamegraph-rs]). Currently, we only measure instruction count, as it's the most reproducible metric -and rustc-perf also considers it the main number to focus on. +and [rustc-perf][rustc-perf] also considers it the main number to focus on. ## Benchmarking a PR @@ -52,4 +52,4 @@ perf diff ./target/lintcheck/sources/CRATE/perf.data ./target/lintcheck/sources/ [perf]: https://perfwiki.github.io/main/ [flamegraph-perf]: https://github.com/brendangregg/FlameGraph [flamegraph-rs]: https://github.com/flamegraph-rs/flamegraph -[rustc-perf]: \ No newline at end of file +[rustc-perf]: https://github.com/rust-lang/rustc-perf From 9c3b5cf9859a971839d3d116f07b955f163f6d07 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Wed, 19 Feb 2025 03:39:15 +0900 Subject: [PATCH 004/103] more natural suggestions for `cmp_owned` --- clippy_lints/src/operators/cmp_owned.rs | 2 +- tests/ui/cmp_owned/with_suggestion.fixed | 9 +++++++++ tests/ui/cmp_owned/with_suggestion.rs | 9 +++++++++ tests/ui/cmp_owned/with_suggestion.stderr | 14 +++++++++++++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index cf6b8992973a7..9b2cfd91b8535 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -98,7 +98,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let arg_snip = snippet(cx, arg_span, ".."); let expr_snip; let eq_impl; - if with_deref.is_implemented() { + if with_deref.is_implemented() && !arg_ty.peel_refs().is_str() { expr_snip = format!("*{arg_snip}"); eq_impl = with_deref; } else { diff --git a/tests/ui/cmp_owned/with_suggestion.fixed b/tests/ui/cmp_owned/with_suggestion.fixed index eb01633a25fd5..85d0991bef05d 100644 --- a/tests/ui/cmp_owned/with_suggestion.fixed +++ b/tests/ui/cmp_owned/with_suggestion.fixed @@ -74,3 +74,12 @@ impl ToOwned for Baz { Baz } } + +fn issue_8103() { + let foo1 = String::from("foo"); + let _ = foo1 == "foo"; + //~^ cmp_owned + let foo2 = "foo"; + let _ = foo1 == foo2; + //~^ cmp_owned +} diff --git a/tests/ui/cmp_owned/with_suggestion.rs b/tests/ui/cmp_owned/with_suggestion.rs index 82409f27b129c..2393757d76f2b 100644 --- a/tests/ui/cmp_owned/with_suggestion.rs +++ b/tests/ui/cmp_owned/with_suggestion.rs @@ -74,3 +74,12 @@ impl ToOwned for Baz { Baz } } + +fn issue_8103() { + let foo1 = String::from("foo"); + let _ = foo1 == "foo".to_owned(); + //~^ cmp_owned + let foo2 = "foo"; + let _ = foo1 == foo2.to_owned(); + //~^ cmp_owned +} diff --git a/tests/ui/cmp_owned/with_suggestion.stderr b/tests/ui/cmp_owned/with_suggestion.stderr index ca2ab44847274..dd9ffa70897ab 100644 --- a/tests/ui/cmp_owned/with_suggestion.stderr +++ b/tests/ui/cmp_owned/with_suggestion.stderr @@ -37,5 +37,17 @@ error: this creates an owned instance just for comparison LL | "abc".chars().filter(|c| c.to_owned() != 'X'); | ^^^^^^^^^^^^ help: try: `*c` -error: aborting due to 6 previous errors +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:80:21 + | +LL | let _ = foo1 == "foo".to_owned(); + | ^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:83:21 + | +LL | let _ = foo1 == foo2.to_owned(); + | ^^^^^^^^^^^^^^^ help: try: `foo2` + +error: aborting due to 8 previous errors From f04bfae764c90270dddeb5d0cf1e8254fb0098ce Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Sun, 23 Feb 2025 11:57:16 +0900 Subject: [PATCH 005/103] correct version attribute for `io_other_error` --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 291ddc1ce1712..f84f544ddba2e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4478,7 +4478,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = std::io::Error::other("bad".to_string()); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub IO_OTHER_ERROR, style, "calling `std::io::Error::new(std::io::ErrorKind::Other, _)`" From 517766c9740d54ac0778bc78225edb63647dc0e1 Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Sun, 23 Feb 2025 14:18:52 +0100 Subject: [PATCH 006/103] Update as_conversions.rs --- clippy_lints/src/as_conversions.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 78102772927c0..27e304a848e33 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -12,17 +12,17 @@ declare_clippy_lint! { /// regardless of whether good alternatives exist or not. If you want more /// precise lints for `as`, please consider using these separate lints: /// - /// - clippy::cast_lossless - /// - clippy::cast_possible_truncation - /// - clippy::cast_possible_wrap - /// - clippy::cast_precision_loss - /// - clippy::cast_sign_loss - /// - clippy::char_lit_as_u8 - /// - clippy::fn_to_numeric_cast - /// - clippy::fn_to_numeric_cast_with_truncation - /// - clippy::ptr_as_ptr - /// - clippy::unnecessary_cast - /// - invalid_reference_casting + /// - `clippy::cast_lossless` + /// - `clippy::cast_possible_truncation` + /// - `clippy::cast_possible_wrap` + /// - `clippy::cast_precision_loss` + /// - `clippy::cast_sign_loss` + /// - `clippy::char_lit_as_u8` + /// - `clippy::fn_to_numeric_cast` + /// - `clippy::fn_to_numeric_cast_with_truncation` + /// - `clippy::ptr_as_ptr` + /// - `clippy::unnecessary_cast` + /// - `invalid_reference_casting` /// /// There is a good explanation the reason why this lint should work in this /// way and how it is useful [in this From 92fc2bb2b8e2640baa9f8e6ac73d209e506ec99c Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 27 Feb 2025 14:21:12 +0000 Subject: [PATCH 007/103] Emit `collapsible_match` at the right node --- clippy_lints/src/matches/collapsible_match.rs | 4 ++-- tests/ui/collapsible_match.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 97e8423695d99..4c46084db875f 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; @@ -99,7 +99,7 @@ fn check_arm<'tcx>( } else { String::new() }; - span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, msg, |diag| { + span_lint_hir_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.hir_id, inner_expr.span, msg, |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 796cabd4b669a..55ef55844957a 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -303,6 +303,18 @@ pub fn test_2(x: Issue9647) { } } +// https://github.com/rust-lang/rust-clippy/issues/14281 +fn lint_emitted_at_right_node(opt: Option>) { + let n = match opt { + #[expect(clippy::collapsible_match)] + Some(n) => match n { + Ok(n) => n, + _ => return, + }, + None => return, + }; +} + fn make() -> T { unimplemented!() } From e1d6f1fc5ca8ec2a69033a7c506a0a7a824fc8e8 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 16 Feb 2025 17:21:08 +0100 Subject: [PATCH 008/103] Use a lintcheck specific Clippy configuration file in the CI By default, lintcheck will use the `clippy.toml` file found at the toplevel of the repository (`CARGO_MANIFEST_DIR`). This file is meant for configuration of Clippy applied to Clippy sources. This creates a new `lintcheck/ci-config/clippy.toml` file which is used by the CI when running lintcheck. By default this uses the default Clippy configuration. --- .github/workflows/lintcheck.yml | 4 ++-- lintcheck/ci-config/clippy.toml | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 lintcheck/ci-config/clippy.toml diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index d487c7d949857..70c805903d36e 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -66,7 +66,7 @@ jobs: - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml + run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON uses: actions/upload-artifact@v4 @@ -97,7 +97,7 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml + run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON uses: actions/upload-artifact@v4 diff --git a/lintcheck/ci-config/clippy.toml b/lintcheck/ci-config/clippy.toml new file mode 100644 index 0000000000000..d9eb2ef90dba2 --- /dev/null +++ b/lintcheck/ci-config/clippy.toml @@ -0,0 +1,6 @@ +# Configuration applied when running lintcheck from the CI +# +# The CI will set the `CLIPPY_CONF_DIR` environment variable +# to `$PWD/lintcheck/ci-config`. + +avoid-breaking-exported-api = false From 3caa1f25d883b8c5a5695b0c9a7717c8ff521a56 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 21 Feb 2025 08:29:10 +0100 Subject: [PATCH 009/103] Add regression tests for `suspicious_doc_comments` --- tests/ui/suspicious_doc_comments.fixed | 4 ++++ tests/ui/suspicious_doc_comments.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/tests/ui/suspicious_doc_comments.fixed b/tests/ui/suspicious_doc_comments.fixed index 3696b0e066d28..3faa4b21ee414 100644 --- a/tests/ui/suspicious_doc_comments.fixed +++ b/tests/ui/suspicious_doc_comments.fixed @@ -87,4 +87,8 @@ pub mod useless_outer_doc { use std::mem; } +// Do not lint, this is not a `///!` +#[doc = "! here's some docs !"] +fn issue14265() {} + fn main() {} diff --git a/tests/ui/suspicious_doc_comments.rs b/tests/ui/suspicious_doc_comments.rs index 4107f5526d132..4af6ed850c2bb 100644 --- a/tests/ui/suspicious_doc_comments.rs +++ b/tests/ui/suspicious_doc_comments.rs @@ -87,4 +87,8 @@ pub mod useless_outer_doc { use std::mem; } +// Do not lint, this is not a `///!` +#[doc = "! here's some docs !"] +fn issue14265() {} + fn main() {} From 10cf3cb945b413c0fb81cf13c4bf4db762ba5377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Mon, 3 Mar 2025 12:54:26 +0100 Subject: [PATCH 010/103] Add helper methods checking for "#[non_exhaustive] that's active" A check for `#[non_exhaustive]` is often done in combination with checking whether the type is local to the crate, in a variety of ways. Create a helper method and standardize on it as the way to check for this. --- clippy_lints/src/default.rs | 2 +- clippy_lints/src/needless_update.rs | 5 +++-- clippy_lints/src/unneeded_struct_pattern.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index ffdd946aadb8b..886c325b355fe 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { && let ty::Adt(adt, args) = *binding_type.kind() && adt.is_struct() && let variant = adt.non_enum_variant() - && (adt.did().is_local() || !variant.is_field_list_non_exhaustive()) + && !variant.field_list_has_applicable_non_exhaustive() && let module_did = cx.tcx.parent_module(stmt.hir_id) && variant .fields diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 0cba72bd2c6a9..cce0617ba3925 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -54,8 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() - && !def.variant(0_usize.into()).is_field_list_non_exhaustive() + let variant = def.non_enum_variant(); + if fields.len() == variant.fields.len() + && !variant.is_field_list_non_exhaustive() { span_lint( cx, diff --git a/clippy_lints/src/unneeded_struct_pattern.rs b/clippy_lints/src/unneeded_struct_pattern.rs index a74eab8b6ae50..3326dea8c5dfa 100644 --- a/clippy_lints/src/unneeded_struct_pattern.rs +++ b/clippy_lints/src/unneeded_struct_pattern.rs @@ -51,7 +51,7 @@ impl LateLintPass<'_> for UnneededStructPattern { let variant = cx.tcx.adt_def(enum_did).variant_with_id(did); let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty(); - let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive(); + let non_exhaustive_activated = variant.field_list_has_applicable_non_exhaustive(); if !has_only_fields_brackets || non_exhaustive_activated { return; } From 0af04455f1aec53fcd37f67c45d362df76364947 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 8 Mar 2025 10:54:45 -0500 Subject: [PATCH 011/103] Make `visit_map` happy path more evident --- clippy_config/src/conf.rs | 55 +++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 75f68d0dc5f9d..7d94525e6d25a 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -218,42 +218,53 @@ macro_rules! define_Conf { let mut value_spans = HashMap::new(); let mut errors = Vec::new(); let mut warnings = Vec::new(); + + // Declare a local variable for each field field available to a configuration file. $(let mut $name = None;)* + // could get `Field` here directly, but get `String` first for diagnostics while let Some(name) = map.next_key::>()? { - match Field::deserialize(name.get_ref().as_str().into_deserializer()) { + let field = match Field::deserialize(name.get_ref().as_str().into_deserializer()) { Err(e) => { let e: FieldError = e; errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span())); + continue; } - $(Ok(Field::$name) => { + Ok(field) => field + }; + + match field { + $(Field::$name => { + // 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 raw_value = map.next_value::>()?; let value_span = raw_value.span(); - match <$ty>::deserialize(raw_value.into_inner()) { - Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)), - Ok(value) => match $name { - Some(_) => { - errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); - } - None => { - $name = Some(value); - value_spans.insert(name.get_ref().as_str().to_string(), value_span); - // $new_conf is the same as one of the defined `$name`s, so - // this variable is defined in line 2 of this function. - $(match $new_conf { - Some(_) => errors.push(ConfError::spanned(self.0, concat!( - "duplicate field `", stringify!($new_conf), - "` (provided as `", stringify!($name), "`)" - ), None, name.span())), - None => $new_conf = $name.clone(), - })? - }, + let value = match <$ty>::deserialize(raw_value.into_inner()) { + Err(e) => { + errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)); + continue; } + Ok(value) => value + }; + // Was this field set previously? + if $name.is_some() { + errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); + continue; } + $name = Some(value); + value_spans.insert(name.get_ref().as_str().to_string(), value_span); + // If this is a deprecated field, was the new field (`$new_conf`) set previously? + // Note that `$new_conf` is one of the defined `$name`s. + $(match $new_conf { + Some(_) => errors.push(ConfError::spanned(self.0, concat!( + "duplicate field `", stringify!($new_conf), + "` (provided as `", stringify!($name), "`)" + ), None, name.span())), + None => $new_conf = $name.clone(), + })? })* // ignore contents of the third_party key - Ok(Field::third_party) => drop(map.next_value::()) + Field::third_party => drop(map.next_value::()) } } let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; From 1066ee09ed106a19cd902dfb1d4d44fdf472cb56 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Wed, 12 Mar 2025 21:55:31 +0800 Subject: [PATCH 012/103] fix: `redundant_clone` FP on enum cast --- clippy_utils/src/mir/mod.rs | 2 +- tests/ui/redundant_clone.fixed | 15 +++++++++++++++ tests/ui/redundant_clone.rs | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index ffcfcd240ea5a..9ba644fdd20ec 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -76,7 +76,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> { } if matches!( ctx, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move | NonMutatingUseContext::Inspect) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) ) { self.results[i].local_consume_or_mutate_locs.push(loc); diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 23c00b34a00a8..7d5195b62175b 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -259,3 +259,18 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +mod issue10074 { + #[derive(Debug, Clone)] + enum MyEnum { + A = 1, + } + + fn false_positive_on_as() { + let e = MyEnum::A; + let v = e.clone() as u16; + + println!("{e:?}"); + println!("{v}"); + } +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index f9fe8ba0236d0..0ea1024a5681a 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -259,3 +259,18 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +mod issue10074 { + #[derive(Debug, Clone)] + enum MyEnum { + A = 1, + } + + fn false_positive_on_as() { + let e = MyEnum::A; + let v = e.clone() as u16; + + println!("{e:?}"); + println!("{v}"); + } +} From 80ce9d1effd19b42a3eea3c571634960ab9a90ef Mon Sep 17 00:00:00 2001 From: yanglsh Date: Fri, 14 Mar 2025 00:15:02 +0800 Subject: [PATCH 013/103] fix: `borrow_deref_ref` suggests wrongly when coerce to mut --- clippy_lints/src/borrow_deref_ref.rs | 5 +++- tests/ui/borrow_deref_ref.fixed | 43 ++++++++++++++++++++++++++++ tests/ui/borrow_deref_ref.rs | 43 ++++++++++++++++++++++++++++ tests/ui/borrow_deref_ref.stderr | 8 +++++- 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 8892a9e6b6b08..cfe5e424ff750 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -2,7 +2,7 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,6 +73,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { } }) && !is_from_proc_macro(cx, e) + && let e_ty = cx.typeck_results().expr_ty_adjusted(e) + // check if the reference is coercing to a mutable reference + && (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target)) && let Some(deref_text) = deref_target.span.get_source_text(cx) { span_lint_and_then( diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed index 17c224f10bfea..765dd75fceb92 100644 --- a/tests/ui/borrow_deref_ref.fixed +++ b/tests/ui/borrow_deref_ref.fixed @@ -81,3 +81,46 @@ fn issue_13584() { let p = &raw const *s; let _ = p as *const i8; } + +mod issue_9905 { + use std::{fs, io}; + + pub enum File { + Stdio, + File(fs::File), + } + + impl io::Read for &'_ File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + File::Stdio => io::stdin().read(buf), + File::File(file) => (&*file).read(buf), + } + } + } +} + +mod issue_11346 { + struct Struct; + + impl Struct { + fn foo(self: &mut &Self) {} + } + + trait Trait { + fn bar(&mut self) {} + } + + impl Trait for &Struct {} + + fn bar() { + let s = &Struct; + (&*s).foo(); + (&*s).bar(); + + let mut s = &Struct; + s.foo(); // To avoid a warning about `s` not needing to be mutable + s.foo(); + //~^ borrow_deref_ref + } +} diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs index 130ed2903dc61..8ee66bfa881ab 100644 --- a/tests/ui/borrow_deref_ref.rs +++ b/tests/ui/borrow_deref_ref.rs @@ -81,3 +81,46 @@ fn issue_13584() { let p = &raw const *s; let _ = p as *const i8; } + +mod issue_9905 { + use std::{fs, io}; + + pub enum File { + Stdio, + File(fs::File), + } + + impl io::Read for &'_ File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + File::Stdio => io::stdin().read(buf), + File::File(file) => (&*file).read(buf), + } + } + } +} + +mod issue_11346 { + struct Struct; + + impl Struct { + fn foo(self: &mut &Self) {} + } + + trait Trait { + fn bar(&mut self) {} + } + + impl Trait for &Struct {} + + fn bar() { + let s = &Struct; + (&*s).foo(); + (&*s).bar(); + + let mut s = &Struct; + s.foo(); // To avoid a warning about `s` not needing to be mutable + (&*s).foo(); + //~^ borrow_deref_ref + } +} diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr index f5868aa874900..3d55da25b9b20 100644 --- a/tests/ui/borrow_deref_ref.stderr +++ b/tests/ui/borrow_deref_ref.stderr @@ -19,5 +19,11 @@ error: deref on an immutable reference LL | let addr_y = &&*x as *const _ as usize; // assert ok | ^^^ help: if you would like to reborrow, try removing `&*`: `x` -error: aborting due to 3 previous errors +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:123:9 + | +LL | (&*s).foo(); + | ^^^^^ help: if you would like to reborrow, try removing `&*`: `s` + +error: aborting due to 4 previous errors From 95d72812f83c0fadb4ffba9de6cd230ee85a10d6 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Fri, 14 Mar 2025 14:46:00 +0800 Subject: [PATCH 014/103] fix: `manual_find` suggests wrongly when early return --- clippy_lints/src/loops/manual_find.rs | 2 ++ tests/ui/manual_find.rs | 28 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index aa8a2934f89bd..35737f3eafe23 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -3,6 +3,7 @@ use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -35,6 +36,7 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) && path_res(cx, inner_ret) == Res::Local(binding_id) + && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) { let mut applicability = Applicability::MachineApplicable; diff --git a/tests/ui/manual_find.rs b/tests/ui/manual_find.rs index 20b557f21d141..7b9846cfe429d 100644 --- a/tests/ui/manual_find.rs +++ b/tests/ui/manual_find.rs @@ -23,4 +23,32 @@ fn tuple(arr: Vec<(String, i32)>) -> Option { None } +mod issue9521 { + fn condition(x: u32, y: u32) -> Result { + todo!() + } + + fn find_with_early_return(v: Vec) -> Option { + for x in v { + if condition(x, 10).ok()? { + return Some(x); + } + } + None + } + + fn find_with_early_break(v: Vec) -> Option { + for x in v { + if if x < 3 { + break; + } else { + x < 10 + } { + return Some(x); + } + } + None + } +} + fn main() {} From 9576c712ccc22ce09be4317b90813600d2af2533 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 13 Mar 2025 14:36:02 -0700 Subject: [PATCH 015/103] Teach rustfmt to handle postfix yield --- clippy_utils/src/ast_utils/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 707312a97f3bc..deda6030831ec 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -201,7 +201,8 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), - (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()), + (Yield(l, lk), Yield(r, rk)) => eq_expr_opt(l.as_ref(), r.as_ref()) && lk == rk, + (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()), (Break(ll, le), Break(rl, re)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_ref(), re.as_ref()), (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()), (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => { @@ -688,7 +689,7 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool { pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { use WherePredicateKind::*; - over(&l.attrs, &r.attrs, eq_attr) + over(&l.attrs, &r.attrs, eq_attr) && match (&l.kind, &r.kind) { (BoundPredicate(l), BoundPredicate(r)) => { over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { From ed1778c226591425b31f93ae01a2997fcbbcc738 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 13 Mar 2025 16:14:31 -0700 Subject: [PATCH 016/103] Fix clippy --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 0d809c17989de..206912d8de40d 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -528,7 +528,7 @@ fn ident_difference_expr_with_base_location( &strip_non_ident_wrappers(left).kind, &strip_non_ident_wrappers(right).kind, ) { - (Yield(_), Yield(_)) + (Yield(_, _), Yield(_, _)) | (Try(_), Try(_)) | (Paren(_), Paren(_)) | (Repeat(_, _), Repeat(_, _)) From 17351bd503b87758f52f9f3d8648fdee95cceacf Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sun, 16 Feb 2025 21:50:58 +0100 Subject: [PATCH 017/103] Change frequency to 3000 --- lintcheck/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index ab86f6ad6f917..c8b4a2c9e2073 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -128,6 +128,7 @@ impl Crate { "-g", // Enable call-graph, useful for flamegraphs and produces richer reports "--quiet", // Do not tamper with lintcheck's normal output "--compression-level=22", + "--freq=3000", // Slow down program to capture all events "-o", &perf_data_filename, "--", From ae3216e702eacef0b60d6df336cc3faf30d8ee4a Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Mar 2025 18:37:28 +0530 Subject: [PATCH 018/103] Add comment annotations --- book/src/development/adding_lints.md | 16 ++++++----- book/src/development/writing_tests.md | 38 +++++++++++++++++++++------ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 0b9010f01071f..dbf829cfcfcd7 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -99,6 +99,7 @@ struct A; impl A { pub fn fo(&self) {} pub fn foo(&self) {} + //~^ foo_functions pub fn food(&self) {} } @@ -106,12 +107,14 @@ impl A { trait B { fn fo(&self) {} fn foo(&self) {} + //~^ foo_functions fn food(&self) {} } // Plain functions fn fo() {} fn foo() {} +//~^ foo_functions fn food() {} fn main() { @@ -122,17 +125,18 @@ fn main() { } ``` -Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently -this test is meaningless though. +Note that we are adding comment annotations with the name of our lint to mark +lines where we expect an error. Once we have implemented our lint we can run +`TESTNAME=foo_functions cargo uibless` to generate the `.stderr` file. While we are working on implementing our lint, we can keep running the UI test. That allows us to check if the output is turning into what we want by checking the `.stderr` file that gets updated on every test run. -Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we -commit our lint, we need to commit the generated `.stderr` files, too. In -general, you should only commit files changed by `cargo bless` for the -specific lint you are creating/editing. +Once we have implemented our lint running `TESTNAME=foo_functions cargo uitest` +should pass on its own. When we commit our lint, we need to commit the generated + `.stderr` files, too. In general, you should only commit files changed by + `cargo bless` for the specific lint you are creating/editing. > _Note:_ you can run multiple test files by specifying a comma separated list: > `TESTNAME=foo_functions,test2,test3`. diff --git a/book/src/development/writing_tests.md b/book/src/development/writing_tests.md index d4cca2a72f0be..1da54322fcf72 100644 --- a/book/src/development/writing_tests.md +++ b/book/src/development/writing_tests.md @@ -41,20 +41,23 @@ Update the file with some examples to get started: struct A; impl A { pub fn fo(&self) {} - pub fn foo(&self) {} //~ ERROR: function called "foo" + pub fn foo(&self) {} + //~^ foo_functions pub fn food(&self) {} } // Default trait methods trait B { fn fo(&self) {} - fn foo(&self) {} //~ ERROR: function called "foo" + fn foo(&self) {} + //~^ foo_functions fn food(&self) {} } // Plain functions fn fo() {} -fn foo() {} //~ ERROR: function called "foo" +fn foo() {} +//~^ foo_functions fn food() {} fn main() { @@ -66,19 +69,38 @@ fn main() { ``` Without actual lint logic to emit the lint when we see a `foo` function name, -this test will just pass, because no lint will be emitted. However, we can now -run the test with the following command: +this test will fail, because we expect errors at lines marked with +`//~^ foo_functions`. However, we can now run the test with the following command: ```sh $ TESTNAME=foo_functions cargo uitest ``` -Clippy will compile and it will conclude with an `ok` for the tests: +Clippy will compile and it will fail complaining it didn't receive any errors: ``` ...Clippy warnings and test outputs... -test compile_test ... ok -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s +error: diagnostic code `clippy::foo_functions` not found on line 8 + --> tests/ui/foo_functions.rs:9:10 + | +9 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + +error: diagnostic code `clippy::foo_functions` not found on line 16 + --> tests/ui/foo_functions.rs:17:10 + | +17 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + +error: diagnostic code `clippy::foo_functions` not found on line 23 + --> tests/ui/foo_functions.rs:24:6 + | +24 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + ``` This is normal. After all, we wrote a bunch of Rust code but we haven't really From 5d7e55cc110e63cee8d24c0b9d60d3835cc5302b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 17 Mar 2025 17:55:26 +0530 Subject: [PATCH 019/103] Mention about .fixed files --- book/src/development/adding_lints.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index dbf829cfcfcd7..10ebe14b2eb45 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -127,7 +127,9 @@ fn main() { Note that we are adding comment annotations with the name of our lint to mark lines where we expect an error. Once we have implemented our lint we can run -`TESTNAME=foo_functions cargo uibless` to generate the `.stderr` file. +`TESTNAME=foo_functions cargo uibless` to generate the `.stderr` file. If our +lint makes use of structured suggestions then this command will also generate +the corresponding `.fixed` file. While we are working on implementing our lint, we can keep running the UI test. That allows us to check if the output is turning into what we want by checking the @@ -135,8 +137,8 @@ That allows us to check if the output is turning into what we want by checking t Once we have implemented our lint running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we commit our lint, we need to commit the generated - `.stderr` files, too. In general, you should only commit files changed by - `cargo bless` for the specific lint you are creating/editing. + `.stderr` and if applicable `.fixed` files, too. In general, you should only + commit files changed by `cargo bless` for the specific lint you are creating/editing. > _Note:_ you can run multiple test files by specifying a comma separated list: > `TESTNAME=foo_functions,test2,test3`. From 6cf2d721508ab8879be65bbfc5ca74ed64c87d05 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 14 Mar 2025 20:34:43 +0300 Subject: [PATCH 020/103] expand: Leave traces when expanding `cfg_attr` attributes --- clippy_lints/src/attrs/duplicated_attributes.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index 2ddbc7a6a76dc..5c486eb90cc2b 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -36,7 +36,11 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason { + if name == sym::doc + || name == sym::cfg_attr + || name == sym::cfg_attr_trace + || name == sym::rustc_on_unimplemented + || name == sym::reason { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. From e3f1bc8b06a1fe671bb21d17328584e5fc3c871c Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 18 Mar 2025 12:19:43 -0700 Subject: [PATCH 021/103] Refactor YieldKind so postfix yield must have an expression --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_utils/src/ast_utils/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 206912d8de40d..0d809c17989de 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -528,7 +528,7 @@ fn ident_difference_expr_with_base_location( &strip_non_ident_wrappers(left).kind, &strip_non_ident_wrappers(right).kind, ) { - (Yield(_, _), Yield(_, _)) + (Yield(_), Yield(_)) | (Try(_), Try(_)) | (Paren(_), Paren(_)) | (Repeat(_, _), Repeat(_, _)) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index deda6030831ec..54261079fcad8 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -201,7 +201,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), - (Yield(l, lk), Yield(r, rk)) => eq_expr_opt(l.as_ref(), r.as_ref()) && lk == rk, + (Yield(l), Yield(r)) => eq_expr_opt(l.expr(), r.expr()) && l.same_kind(r), (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()), (Break(ll, le), Break(rl, re)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_ref(), re.as_ref()), (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()), From 22be689b44d959370d5fadd7a3da0c5ac0f4c21b Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 18 Mar 2025 21:41:00 +0900 Subject: [PATCH 022/103] extend `obfuscated_if_else` to support `.then().unwrap_or_default()` and `.then_some().unwrap_or_default()` --- clippy_lints/src/methods/mod.rs | 24 ++++++++--- .../src/methods/obfuscated_if_else.rs | 18 ++++---- tests/ui/obfuscated_if_else.fixed | 12 ++++++ tests/ui/obfuscated_if_else.rs | 12 ++++++ tests/ui/obfuscated_if_else.stderr | 42 +++++++++++++++---- 5 files changed, 87 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 94d3657d9f123..be2c849f384c8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2429,7 +2429,7 @@ declare_clippy_lint! { /// /// ### Limitations /// This lint currently only looks for usages of - /// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded + /// `.{then, then_some}(..).{unwrap_or, unwrap_or_else, unwrap_or_default}(..)`, but will be expanded /// to account for similar patterns. /// /// ### Example @@ -5421,15 +5421,21 @@ impl Methods { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or"); + obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, "unwrap_or"); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, ("unwrap_or_default", []) => { - if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) { - manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); + match method_call(recv) { + Some(("map", m_recv, [arg], span, _)) => { + manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); + }, + Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, None, then_method, "unwrap_or_default"); + }, + _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, @@ -5441,7 +5447,15 @@ impl Methods { Some(("map", recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or_else"); + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + Some(u_arg), + then_method, + "unwrap_or_else", + ); }, _ => { unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs index 9a5ffdeaf4e8e..1cc56de48763e 100644 --- a/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -14,14 +14,17 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, - unwrap_arg: &'tcx hir::Expr<'_>, + unwrap_arg: Option<&'tcx hir::Expr<'_>>, then_method_name: &str, unwrap_method_name: &str, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { - let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) { + let then_eager = switch_to_eager_eval(cx, then_arg); + let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg)); + + let mut applicability = if then_eager && unwrap_eager { Applicability::MachineApplicable } else { Applicability::MaybeIncorrect @@ -36,16 +39,17 @@ pub(super) fn check<'tcx>( _ => return, }; - // FIXME: Add `unwrap_or_else` symbol + // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol let els = match unwrap_method_name { - "unwrap_or" => snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability), - "unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.kind => { + "unwrap_or" => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), + "unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.kind => { - snippet_with_applicability(cx, unwrap_arg.span, "_", &mut applicability) + "()" + "unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { + snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" }, + "unwrap_or_default" => "Default::default()".into(), _ => return, }; diff --git a/tests/ui/obfuscated_if_else.fixed b/tests/ui/obfuscated_if_else.fixed index 66f5070787b09..70ae090626b96 100644 --- a/tests/ui/obfuscated_if_else.fixed +++ b/tests/ui/obfuscated_if_else.fixed @@ -46,6 +46,18 @@ fn main() { let partial = true.then_some(1); partial.unwrap_or_else(|| n * 2); // not lint + + if true { () } else { Default::default() }; + //~^ obfuscated_if_else + + if true { () } else { Default::default() }; + //~^ obfuscated_if_else + + if true { 1 } else { Default::default() }; + //~^ obfuscated_if_else + + if true { 1 } else { Default::default() }; + //~^ obfuscated_if_else } fn issue11141() { diff --git a/tests/ui/obfuscated_if_else.rs b/tests/ui/obfuscated_if_else.rs index 4efd740eb60bb..8e1f57ca2c026 100644 --- a/tests/ui/obfuscated_if_else.rs +++ b/tests/ui/obfuscated_if_else.rs @@ -46,6 +46,18 @@ fn main() { let partial = true.then_some(1); partial.unwrap_or_else(|| n * 2); // not lint + + true.then_some(()).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then(|| ()).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then_some(1).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then(|| 1).unwrap_or_default(); + //~^ obfuscated_if_else } fn issue11141() { diff --git a/tests/ui/obfuscated_if_else.stderr b/tests/ui/obfuscated_if_else.stderr index d676c25669570..0de7259d8bb82 100644 --- a/tests/ui/obfuscated_if_else.stderr +++ b/tests/ui/obfuscated_if_else.stderr @@ -68,52 +68,76 @@ LL | true.then_some(1).unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:53:13 + --> tests/ui/obfuscated_if_else.rs:50:5 + | +LL | true.then_some(()).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:53:5 + | +LL | true.then(|| ()).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:56:5 + | +LL | true.then_some(1).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:59:5 + | +LL | true.then(|| 1).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:65:13 | LL | let _ = true.then_some(40).unwrap_or(17) | 2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(if true { 40 } else { 17 })` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:13 + --> tests/ui/obfuscated_if_else.rs:69:13 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(if true { 30 } else { 17 })` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:48 + --> tests/ui/obfuscated_if_else.rs:69:48 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 2 } else { 3 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:81 + --> tests/ui/obfuscated_if_else.rs:69:81 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 10 } else { 1 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:63:17 + --> tests/ui/obfuscated_if_else.rs:75:17 | LL | let _ = 2 | true.then_some(40).unwrap_or(17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 40 } else { 17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:67:13 + --> tests/ui/obfuscated_if_else.rs:79:13 | LL | let _ = true.then_some(42).unwrap_or(17) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 42 } else { 17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:71:14 + --> tests/ui/obfuscated_if_else.rs:83:14 | LL | let _ = *true.then_some(&42).unwrap_or(&17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { &42 } else { &17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:75:14 + --> tests/ui/obfuscated_if_else.rs:87:14 | LL | let _ = *true.then_some(&42).unwrap_or(&17) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { &42 } else { &17 }` -error: aborting due to 19 previous errors +error: aborting due to 23 previous errors From e4daa5a74a5af2cca3b02c286375e24f3dc1a33a Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 18 Mar 2025 21:47:44 +0900 Subject: [PATCH 023/103] apply `obfuscated_if_else` to clippy source --- clippy_lints/src/len_zero.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 98ba52f12707f..cbd39df8e7191 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -202,7 +202,11 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { expr.span, lhs_expr, peel_ref_operators(cx, rhs_expr), - (method.ident.name == sym::ne).then_some("!").unwrap_or_default(), + if method.ident.name == sym::ne { + "!" + } else { + Default::default() + }, ); } From dd92647f33e153b7a1feed1afbcd2c658c32b334 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 14 Mar 2025 09:03:23 +1100 Subject: [PATCH 024/103] Use `Option` for lowered param names. Parameter patterns are lowered to an `Ident` by `lower_fn_params_to_names`, which is used when lowering bare function types, trait methods, and foreign functions. Currently, there are two exceptional cases where the lowered param can become an empty `Ident`. - If the incoming pattern is an empty `Ident`. This occurs if the parameter is anonymous, e.g. in a bare function type. - If the incoming pattern is neither an ident nor an underscore. Any such parameter will have triggered a compile error (hence the `span_delayed_bug`), but lowering still occurs. This commit replaces these empty `Ident` results with `None`, which eliminates a number of `kw::Empty` uses, and makes it impossible to fail to check for these exceptional cases. Note: the `FIXME` comment in `is_unwrap_or_empty_symbol` is removed. It actually should have been removed in #138482, the precursor to this PR. That PR changed the lowering of wild patterns to `_` symbols instead of empty symbols, which made the mentioned underscore check load-bearing. --- .../src/functions/renamed_function_params.rs | 45 ++++++++++--------- clippy_lints/src/lifetimes.rs | 10 ++--- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index 5ad83f886e2ec..041f6228fba2d 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -5,7 +5,7 @@ use rustc_hir::hir_id::OwnerId; use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::symbol::{Ident, kw}; use super::RENAMED_FUNCTION_PARAMS; @@ -51,22 +51,33 @@ struct RenamedFnArgs(Vec<(Span, String)>); impl RenamedFnArgs { /// Comparing between an iterator of default names and one with current names, /// then collect the ones that got renamed. - fn new(default_names: &mut I, current_names: &mut T) -> Self + fn new(default_idents: &mut I1, current_idents: &mut I2) -> Self where - I: Iterator, - T: Iterator, + I1: Iterator>, + I2: Iterator>, { let mut renamed: Vec<(Span, String)> = vec![]; - debug_assert!(default_names.size_hint() == current_names.size_hint()); - while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { - let current_name = cur_name.name; - let default_name = def_name.name; - if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) { - continue; - } - if current_name != default_name { - renamed.push((cur_name.span, default_name.to_string())); + debug_assert!(default_idents.size_hint() == current_idents.size_hint()); + while let (Some(default_ident), Some(current_ident)) = + (default_idents.next(), current_idents.next()) + { + let has_name_to_check = |ident: Option| { + if let Some(ident) = ident + && ident.name != kw::Underscore + && !ident.name.as_str().starts_with('_') + { + Some(ident) + } else { + None + } + }; + + if let Some(default_ident) = has_name_to_check(default_ident) + && let Some(current_ident) = has_name_to_check(current_ident) + && default_ident.name != current_ident.name + { + renamed.push((current_ident.span, default_ident.to_string())); } } @@ -83,14 +94,6 @@ impl RenamedFnArgs { } } -fn is_unused_or_empty_symbol(symbol: Symbol) -> bool { - // FIXME: `body_param_names` currently returning empty symbols for `wild` as well, - // so we need to check if the symbol is empty first. - // Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now, - // but it would be nice to keep it here just to be future-proof. - symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_') -} - /// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option { items.iter().find_map(|item| { diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 3dd2de1fafc72..8d47c756fc53c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -189,7 +189,7 @@ fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, body: Option, - trait_sig: Option<&[Ident]>, + trait_sig: Option<&[Option]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, @@ -264,7 +264,7 @@ fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option, - trait_sig: Option<&[Ident]>, + trait_sig: Option<&[Option]>, named_generics: &'tcx [GenericParam<'_>], msrv: Msrv, ) -> Option<(Vec, Vec)> { @@ -310,7 +310,7 @@ fn could_use_elision<'tcx>( let body = cx.tcx.hir_body(body_id); let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); - if non_elidable_self_type(cx, func, first_ident, msrv) { + if non_elidable_self_type(cx, func, Some(first_ident), msrv) { return None; } @@ -384,8 +384,8 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option, msrv: Msrv) -> bool { - if let Some(ident) = ident +fn non_elidable_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option>, msrv: Msrv) -> bool { + if let Some(Some(ident)) = ident && ident.name == kw::SelfLower && !func.implicit_self.has_implicit_self() && let Some(self_ty) = func.inputs.first() From a463154bf0a4028fb6f4027ad58fc45a30379103 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Wed, 19 Mar 2025 21:37:42 +0900 Subject: [PATCH 025/103] set the applicability of `op_ref` to `MachineApplicable` --- clippy_lints/src/operators/op_ref.rs | 8 ++++---- tests/ui/op_ref.fixed | 12 ++++++++++++ tests/ui/op_ref.rs | 12 ++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index c3c09946c27d1..b03985d156542 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -86,7 +86,7 @@ pub(crate) fn check<'tcx>( left.span, "use the left value directly", lsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -105,7 +105,7 @@ pub(crate) fn check<'tcx>( right.span, "use the right value directly", rsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -137,7 +137,7 @@ pub(crate) fn check<'tcx>( left.span, "use the left value directly", lsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -164,7 +164,7 @@ pub(crate) fn check<'tcx>( right.span, "use the right value directly", rsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }); } diff --git a/tests/ui/op_ref.fixed b/tests/ui/op_ref.fixed index 46a59e419cce0..f412190b9fd9e 100644 --- a/tests/ui/op_ref.fixed +++ b/tests/ui/op_ref.fixed @@ -98,3 +98,15 @@ impl Mul for A { self * &rhs } } + +mod issue_2597 { + fn ex1() { + let a: &str = "abc"; + let b: String = "abc".to_owned(); + println!("{}", a > &b); + } + + pub fn ex2(array: &[T], val: &T, idx: usize) -> bool { + &array[idx] < val + } +} diff --git a/tests/ui/op_ref.rs b/tests/ui/op_ref.rs index e10840ff4b97b..a4bbd86c7e95b 100644 --- a/tests/ui/op_ref.rs +++ b/tests/ui/op_ref.rs @@ -98,3 +98,15 @@ impl Mul for A { self * &rhs } } + +mod issue_2597 { + fn ex1() { + let a: &str = "abc"; + let b: String = "abc".to_owned(); + println!("{}", a > &b); + } + + pub fn ex2(array: &[T], val: &T, idx: usize) -> bool { + &array[idx] < val + } +} From c86216ebe708d88dec9772e1f84556f5f529c6c6 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 20 Mar 2025 22:34:29 +0100 Subject: [PATCH 026/103] Merge commit '1e5237f4a56ae958af7e5824343eacf737b67083' into clippy-subtree-update --- .github/workflows/clippy_mq.yml | 2 + .github/workflows/remark.yml | 1 + CHANGELOG.md | 2 + COPYRIGHT | 2 +- Cargo.toml | 2 +- LICENSE-APACHE | 2 +- LICENSE-MIT | 2 +- README.md | 2 +- book/src/development/macro_expansions.md | 76 +++++ book/src/development/the_team.md | 3 +- book/src/lint_configuration.md | 14 + clippy_config/src/conf.rs | 79 ++++- clippy_config/src/types.rs | 108 ++++++- .../src/arbitrary_source_item_ordering.rs | 85 ++++-- clippy_lints/src/attrs/mod.rs | 4 +- clippy_lints/src/blocks_in_conditions.rs | 9 +- clippy_lints/src/casts/borrow_as_ptr.rs | 10 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/declared_lints.rs | 3 +- .../doc_comment_double_space_linebreaks.rs | 33 ++ clippy_lints/src/doc/mod.rs | 59 +++- clippy_lints/src/entry.rs | 47 ++- clippy_lints/src/escape.rs | 30 +- clippy_lints/src/excessive_bools.rs | 17 +- clippy_lints/src/format_args.rs | 5 +- clippy_lints/src/from_over_into.rs | 14 +- clippy_lints/src/ignored_unit_patterns.rs | 8 +- clippy_lints/src/implicit_saturating_sub.rs | 65 ++-- clippy_lints/src/incompatible_msrv.rs | 26 +- clippy_lints/src/inline_fn_without_body.rs | 6 +- clippy_lints/src/item_name_repetitions.rs | 283 ++++++++++-------- .../src/iter_not_returning_iterator.rs | 6 +- clippy_lints/src/legacy_numeric_constants.rs | 4 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 25 +- clippy_lints/src/manual_bits.rs | 6 +- clippy_lints/src/manual_div_ceil.rs | 2 +- clippy_lints/src/manual_ignore_case_cmp.rs | 2 +- clippy_lints/src/manual_let_else.rs | 37 +-- .../src/manual_slice_size_calculation.rs | 4 +- clippy_lints/src/matches/mod.rs | 8 +- clippy_lints/src/matches/single_match.rs | 38 ++- clippy_lints/src/methods/io_other_error.rs | 2 +- clippy_lints/src/methods/mod.rs | 4 +- clippy_lints/src/methods/needless_collect.rs | 78 ++++- .../src/methods/unnecessary_to_owned.rs | 5 +- .../src/misc_early/mixed_case_hex_literals.rs | 33 +- clippy_lints/src/missing_inline.rs | 10 +- clippy_lints/src/needless_late_init.rs | 9 +- clippy_lints/src/needless_pass_by_value.rs | 8 +- clippy_lints/src/needless_question_mark.rs | 2 +- clippy_lints/src/operators/mod.rs | 32 -- clippy_lints/src/operators/ptr_eq.rs | 62 ---- clippy_lints/src/option_if_let_else.rs | 86 +++++- clippy_lints/src/partialeq_ne_impl.rs | 4 +- clippy_lints/src/ptr.rs | 111 ++++++- clippy_lints/src/question_mark.rs | 55 +++- clippy_lints/src/redundant_async_block.rs | 4 +- clippy_lints/src/redundant_locals.rs | 2 +- clippy_lints/src/redundant_pub_crate.rs | 4 +- clippy_lints/src/size_of_in_element_count.rs | 1 - clippy_lints/src/size_of_ref.rs | 12 +- clippy_lints/src/strings.rs | 99 +++++- clippy_lints/src/types/mod.rs | 4 - .../src/undocumented_unsafe_blocks.rs | 151 +++++++--- clippy_lints/src/unnecessary_semicolon.rs | 2 +- clippy_lints/src/useless_conversion.rs | 28 +- .../almost_standard_lint_formulation.rs | 5 +- .../internal_lints/lint_without_lint_pass.rs | 6 +- .../src/utils/internal_lints/produce_ice.rs | 10 +- clippy_utils/README.md | 4 +- clippy_utils/src/ast_utils/mod.rs | 2 +- clippy_utils/src/diagnostics.rs | 10 +- clippy_utils/src/lib.rs | 45 ++- clippy_utils/src/mir/possible_borrower.rs | 3 +- clippy_utils/src/msrvs.rs | 3 +- clippy_utils/src/qualify_min_const_fn.rs | 40 ++- clippy_utils/src/sugg.rs | 41 ++- lintcheck/src/json.rs | 22 ++ rust-toolchain | 2 +- rustc_tools_util/README.md | 2 +- tests/compile-test.rs | 6 +- tests/lint_message_convention.rs | 1 + .../check_clippy_version_attribute.rs | 4 + .../check_clippy_version_attribute.stderr | 19 +- tests/ui-internal/check_formulation.rs | 2 + tests/ui-internal/check_formulation.stderr | 2 +- .../collapsible_span_lint_calls.rs | 5 + .../collapsible_span_lint_calls.stderr | 13 +- tests/ui-internal/custom_ice_message.rs | 1 + tests/ui-internal/custom_ice_message.stderr | 25 +- tests/ui-internal/default_lint.rs | 1 + tests/ui-internal/default_lint.stderr | 1 + tests/ui-internal/disallow_span_lint.rs | 2 + tests/ui-internal/disallow_span_lint.stderr | 2 +- .../interning_defined_symbol.fixed | 4 + tests/ui-internal/interning_defined_symbol.rs | 4 + .../interning_defined_symbol.stderr | 6 +- .../ui-internal/invalid_msrv_attr_impl.fixed | 1 + tests/ui-internal/invalid_msrv_attr_impl.rs | 1 + tests/ui-internal/invalid_paths.rs | 3 + tests/ui-internal/invalid_paths.stderr | 4 +- tests/ui-internal/lint_without_lint_pass.rs | 1 + .../ui-internal/lint_without_lint_pass.stderr | 1 + tests/ui-internal/outer_expn_data.fixed | 1 + tests/ui-internal/outer_expn_data.rs | 1 + tests/ui-internal/unnecessary_def_path.fixed | 15 + tests/ui-internal/unnecessary_def_path.rs | 15 + tests/ui-internal/unnecessary_def_path.stderr | 28 +- .../unnecessary_def_path_hardcoded_path.rs | 3 + ...unnecessary_def_path_hardcoded_path.stderr | 4 +- .../ui-internal/unnecessary_symbol_str.fixed | 5 + tests/ui-internal/unnecessary_symbol_str.rs | 5 + .../ui-internal/unnecessary_symbol_str.stderr | 8 +- .../bad_conf_4/clippy.toml | 1 + .../bad_conf_5/clippy.toml | 1 + .../bad_conf_6/clippy.toml | 1 + .../default_exp/clippy.toml | 2 +- .../ord_in_2/clippy.toml | 1 + .../ord_in_3/clippy.toml | 2 + .../ord_within/clippy.toml | 1 + .../ordering_good.bad_conf_4.stderr | 8 + .../ordering_good.bad_conf_5.stderr | 4 + .../ordering_good.bad_conf_6.stderr | 4 + .../ordering_good.rs | 8 +- .../ordering_mixed.default.stderr | 125 ++------ .../ordering_mixed.default_exp.stderr | 160 ++++++++++ .../ordering_mixed.ord_within.stderr | 235 +++++++++++++++ .../ordering_mixed.rs | 14 +- .../selective_ordering.default.stderr | 19 ++ .../selective_ordering.ord_in_2.stderr | 36 +++ .../selective_ordering.ord_in_3.stderr | 19 ++ .../selective_ordering.ord_within.stderr | 48 +++ .../selective_ordering.rs | 46 +++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + .../undocumented_unsafe_blocks.default.stderr | 46 ++- ...undocumented_unsafe_blocks.disabled.stderr | 62 +++- .../undocumented_unsafe_blocks.rs | 91 ++++++ tests/ui/blocks_in_conditions.fixed | 7 + tests/ui/blocks_in_conditions.rs | 7 + tests/ui/crashes/if_same_then_else.rs | 1 - ...needless_pass_by_value-w-late-bound.stderr | 6 +- .../doc_comment_double_space_linebreaks.fixed | 98 ++++++ .../doc_comment_double_space_linebreaks.rs | 98 ++++++ ...doc_comment_double_space_linebreaks.stderr | 50 ++++ tests/ui/entry_unfixable.rs | 94 ++++++ tests/ui/entry_unfixable.stderr | 41 +++ tests/ui/from_over_into.fixed | 11 + tests/ui/from_over_into.rs | 11 + tests/ui/from_over_into.stderr | 18 +- tests/ui/ifs_same_cond.rs | 7 +- tests/ui/ifs_same_cond.stderr | 16 +- tests/ui/implicit_saturating_sub.fixed | 24 ++ tests/ui/implicit_saturating_sub.rs | 24 ++ tests/ui/implicit_saturating_sub.stderr | 20 +- tests/ui/incompatible_msrv.rs | 39 +++ tests/ui/incompatible_msrv.stderr | 37 ++- tests/ui/io_other_error.fixed | 5 + tests/ui/io_other_error.rs | 5 + tests/ui/io_other_error.stderr | 14 +- tests/ui/literals.rs | 4 + tests/ui/literals.stderr | 45 ++- tests/ui/manual_arithmetic_check-2.rs | 9 + tests/ui/manual_arithmetic_check-2.stderr | 38 ++- tests/ui/manual_bits.stderr | 58 ++-- tests/ui/manual_let_else.rs | 34 +++ tests/ui/manual_let_else.stderr | 42 ++- .../ui/missing_const_for_fn/cant_be_const.rs | 88 ++++++ tests/ui/needless_collect.fixed | 23 ++ tests/ui/needless_collect.rs | 23 ++ tests/ui/needless_late_init.fixed | 6 + tests/ui/needless_late_init.rs | 6 + tests/ui/needless_late_init.stderr | 18 +- tests/ui/needless_pass_by_value.rs | 36 +++ tests/ui/needless_pass_by_value.stderr | 172 +++++++++-- tests/ui/needless_return.fixed | 12 +- tests/ui/needless_return.rs | 12 +- tests/ui/needless_return.stderr | 122 ++++---- tests/ui/never_loop.rs | 28 ++ tests/ui/never_loop.stderr | 70 ++++- tests/ui/never_loop_fixable.fixed | 20 ++ tests/ui/never_loop_fixable.rs | 20 ++ tests/ui/never_loop_fixable.stderr | 35 +++ tests/ui/option_if_let_else.fixed | 20 ++ tests/ui/option_if_let_else.rs | 20 ++ tests/ui/ptr_eq.fixed | 23 +- tests/ui/ptr_eq.rs | 15 + tests/ui/ptr_eq.stderr | 44 ++- tests/ui/ptr_eq_no_std.fixed | 12 +- tests/ui/ptr_eq_no_std.rs | 4 + tests/ui/ptr_eq_no_std.stderr | 26 +- tests/ui/question_mark.fixed | 55 ++++ tests/ui/question_mark.rs | 72 +++++ tests/ui/question_mark.stderr | 62 +++- tests/ui/same_functions_in_if_condition.rs | 7 +- .../ui/same_functions_in_if_condition.stderr | 24 +- tests/ui/single_match.fixed | 34 +-- tests/ui/single_match.rs | 10 +- tests/ui/single_match.stderr | 97 ++++-- tests/ui/single_match_else.fixed | 8 + tests/ui/single_match_else.rs | 12 + tests/ui/single_match_else.stderr | 43 ++- tests/ui/size_of_ref.stderr | 12 +- tests/ui/string_to_string.rs | 15 +- tests/ui/string_to_string.stderr | 18 +- tests/ui/string_to_string_in_map.fixed | 20 ++ tests/ui/string_to_string_in_map.rs | 20 ++ tests/ui/string_to_string_in_map.stderr | 38 +++ tests/ui/struct_fields.rs | 27 ++ tests/ui/struct_fields.stderr | 21 +- tests/ui/unnecessary_path_debug_formatting.rs | 6 + tests/ui/unnecessary_to_owned.fixed | 36 +-- tests/ui/unnecessary_to_owned.rs | 36 +-- tests/ui/unnecessary_to_owned.stderr | 86 ++---- triagebot.toml | 1 - util/gh-pages/index_template.html | 8 +- 216 files changed, 4611 insertions(+), 1144 deletions(-) create mode 100644 clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs delete mode 100644 clippy_lints/src/operators/ptr_eq.rs create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr create mode 100644 tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs create mode 100644 tests/ui/doc/doc_comment_double_space_linebreaks.fixed create mode 100644 tests/ui/doc/doc_comment_double_space_linebreaks.rs create mode 100644 tests/ui/doc/doc_comment_double_space_linebreaks.stderr create mode 100644 tests/ui/entry_unfixable.rs create mode 100644 tests/ui/entry_unfixable.stderr create mode 100644 tests/ui/never_loop_fixable.fixed create mode 100644 tests/ui/never_loop_fixable.rs create mode 100644 tests/ui/never_loop_fixable.stderr create mode 100644 tests/ui/string_to_string_in_map.fixed create mode 100644 tests/ui/string_to_string_in_map.rs create mode 100644 tests/ui/string_to_string_in_map.stderr diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index c337a96bdac52..741e745733177 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -27,6 +27,8 @@ jobs: host: x86_64-pc-windows-msvc - os: macos-13 host: x86_64-apple-darwin + - os: macos-latest + host: aarch64-apple-darwin runs-on: ${{ matrix.os }} diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 13902f78b541d..7e7e26818c094 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -37,6 +37,7 @@ jobs: - name: Linkcheck book run: | rustup toolchain install nightly --component rust-docs + rustup override set nightly curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh sh linkcheck.sh clippy --path ./book diff --git a/CHANGELOG.md b/CHANGELOG.md index 51441ab9fc0d8..1bf4b51ff0fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5570,6 +5570,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_comment_double_space_linebreaks`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreaks [`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_code @@ -6372,6 +6373,7 @@ Released 2018-09-13 [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items [`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings +[`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 [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior diff --git a/COPYRIGHT b/COPYRIGHT index 219693d63d973..f402dcf465a34 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,6 +1,6 @@ // REUSE-IgnoreStart -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/Cargo.toml b/Cargo.toml index c4588002dc990..94c170d73af34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.26.4" +ui_test = "0.29.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 506582c31d6d5..9990a0cec4743 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index 6d8ee9afb6163..5d6e36ef6bfc2 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2014-2024 The Rust Project Developers +Copyright (c) 2014-2025 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/README.md b/README.md index 32c1d33e2ed36..20a5e997e629b 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/book/src/development/macro_expansions.md b/book/src/development/macro_expansions.md index 36092f82e2607..ed547130b358e 100644 --- a/book/src/development/macro_expansions.md +++ b/book/src/development/macro_expansions.md @@ -150,9 +150,85 @@ if foo_span.in_external_macro(cx.sess().source_map()) { } ``` +### The `is_from_proc_macro` function +A common point of confusion is the existence of [`is_from_proc_macro`] +and how it differs from the other [`in_external_macro`]/[`from_expansion`] functions. + +While [`in_external_macro`] and [`from_expansion`] both work perfectly fine for detecting expanded code +from *declarative* macros (i.e. `macro_rules!` and macros 2.0), +detecting *proc macro*-generated code is a bit more tricky, as proc macros can (and often do) +freely manipulate the span of returned tokens. + +In practice, this often happens through the use of [`quote::quote_spanned!`] with a span from the input tokens. + +In those cases, there is no *reliable* way for the compiler (and tools like Clippy) +to distinguish code that comes from such a proc macro from code that the user wrote directly, +and [`in_external_macro`] will return `false`. + +This is usually not an issue for the compiler and actually helps proc macro authors create better error messages, +as it allows associating parts of the expansion with parts of the macro input and lets the compiler +point the user to the relevant code in case of a compile error. + +However, for Clippy this is inconvenient, because most of the time *we don't* want +to lint proc macro-generated code and this makes it impossible to tell what is and isn't proc macro code. + +> NOTE: this is specifically only an issue when a proc macro explicitly sets the span to that of an **input span**. +> +> For example, other common ways of creating `TokenStream`s, such as `"fn foo() {...}".parse::()`, +> sets each token's span to `Span::call_site()`, which already marks the span as coming from a proc macro +> and the usual span methods have no problem detecting that as a macro span. + +As such, Clippy has its own `is_from_proc_macro` function which tries to *approximate* +whether a span comes from a proc macro, by checking whether the source text at the given span +lines up with the given AST node. + +This function is typically used in combination with the other mentioned macro span functions, +but is usually called much later into the condition chain as it's a bit heavier than most other conditions, +so that the other cheaper conditions can fail faster. For example, the `borrow_deref_ref` lint: +```rs +impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { + if let ... = ... + && ... + && !e.span.from_expansion() + && ... + && ... + && !is_from_proc_macro(cx, e) + && ... + { + ... + } + } +} +``` + +### Testing lints with macro expansions +To test that all of these cases are handled correctly in your lint, +we have a helper auxiliary crate that exposes various macros, used by tests like so: +```rust +//@aux-build:proc_macros.rs + +extern crate proc_macros; + +fn main() { + proc_macros::external!{ code_that_should_trigger_your_lint } + proc_macros::with_span!{ span code_that_should_trigger_your_lint } +} +``` +This exercises two cases: +- `proc_macros::external!` is a simple proc macro that echos the input tokens back but with a macro span: +this represents the usual, common case where an external macro expands to code that your lint would trigger, +and is correctly handled by `in_external_macro` and `Span::from_expansion`. + +- `proc_macros::with_span!` echos back the input tokens starting from the second token +with the span of the first token: this is where the other functions will fail and `is_from_proc_macro` is needed + + [`ctxt`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.ctxt [expansion]: https://rustc-dev-guide.rust-lang.org/macro-expansion.html#expansion-and-ast-integration [`from_expansion`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [`in_external_macro`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.in_external_macro [Span]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html [SyntaxContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html +[`is_from_proc_macro`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.is_from_proc_macro.html +[`quote::quote_spanned!`]: https://docs.rs/quote/latest/quote/macro.quote_spanned.html diff --git a/book/src/development/the_team.md b/book/src/development/the_team.md index 6bc0783b166a9..da5d084ed97f3 100644 --- a/book/src/development/the_team.md +++ b/book/src/development/the_team.md @@ -102,8 +102,7 @@ is responsible for maintaining Clippy. 5. **Update the changelog** - This needs to be done for every release, every six weeks. This is usually - done by @xFrednet. + This needs to be done for every release, every six weeks. ### Membership diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 28b613ea32951..3726d6e8a8691 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -744,6 +744,19 @@ The named groupings of different source item kinds within modules. * [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) +## `module-items-ordered-within-groupings` +Whether the items within module groups should be ordered alphabetically or not. + +This option can be configured to "all", "none", or a list of specific grouping names that should be checked +(e.g. only "enums"). + +**Default Value:** `"none"` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` @@ -806,6 +819,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) * [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) +* [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) * [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index a61acbaa96bcf..798f8b3aa5a90 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -3,14 +3,17 @@ use crate::types::{ DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, + SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; +use itertools::Itertools; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; +use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; use std::ops::Range; use std::path::PathBuf; @@ -79,6 +82,7 @@ const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = { #[derive(Default)] struct TryConf { conf: Conf, + value_spans: HashMap>, errors: Vec, warnings: Vec, } @@ -87,6 +91,7 @@ impl TryConf { fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self { Self { conf: Conf::default(), + value_spans: HashMap::default(), errors: vec![ConfError::from_toml(file, error)], warnings: vec![], } @@ -210,6 +215,7 @@ macro_rules! define_Conf { } fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de> { + let mut value_spans = HashMap::new(); let mut errors = Vec::new(); let mut warnings = Vec::new(); $(let mut $name = None;)* @@ -232,6 +238,7 @@ macro_rules! define_Conf { } None => { $name = Some(value); + value_spans.insert(name.get_ref().as_str().to_string(), value_span); // $new_conf is the same as one of the defined `$name`s, so // this variable is defined in line 2 of this function. $(match $new_conf { @@ -250,7 +257,7 @@ macro_rules! define_Conf { } } let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; - Ok(TryConf { conf, errors, warnings }) + Ok(TryConf { conf, value_spans, errors, warnings }) } } @@ -596,6 +603,13 @@ define_Conf! { /// The named groupings of different source item kinds within modules. #[lints(arbitrary_source_item_ordering)] module_item_order_groupings: SourceItemOrderingModuleItemGroupings = DEFAULT_MODULE_ITEM_ORDERING_GROUPS.into(), + /// Whether the items within module groups should be ordered alphabetically or not. + /// + /// This option can be configured to "all", "none", or a list of specific grouping names that should be checked + /// (e.g. only "enums"). + #[lints(arbitrary_source_item_ordering)] + module_items_ordered_within_groupings: SourceItemOrderingWithinModuleItemGroupings = + SourceItemOrderingWithinModuleItemGroupings::None, /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = "current version"] #[lints( @@ -654,6 +668,7 @@ define_Conf! { option_as_ref_deref, option_map_unwrap_or, ptr_as_ptr, + question_mark, redundant_field_names, redundant_static_lifetimes, repeat_vec_with_capacity, @@ -815,6 +830,36 @@ fn deserialize(file: &SourceFile) -> TryConf { &mut conf.conf.allow_renamed_params_for, DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, ); + + // Confirms that the user has not accidentally configured ordering requirements for groups that + // aren't configured. + if let SourceItemOrderingWithinModuleItemGroupings::Custom(groupings) = + &conf.conf.module_items_ordered_within_groupings + { + for grouping in groupings { + if !conf.conf.module_item_order_groupings.is_grouping(grouping) { + // Since this isn't fixable by rustfix, don't emit a `Suggestion`. This just adds some useful + // info for the user instead. + + let names = conf.conf.module_item_order_groupings.grouping_names(); + let suggestion = suggest_candidate(grouping, names.iter().map(String::as_str)) + .map(|s| format!(" perhaps you meant `{s}`?")) + .unwrap_or_default(); + let names = names.iter().map(|s| format!("`{s}`")).join(", "); + let message = format!( + "unknown ordering group: `{grouping}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}" + ); + + let span = conf + .value_spans + .get("module_item_order_groupings") + .cloned() + .unwrap_or_default(); + conf.errors.push(ConfError::spanned(file, message, None, span)); + } + } + } + // TODO: THIS SHOULD BE TESTED, this comment will be gone soon if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") { conf.conf @@ -860,6 +905,7 @@ impl Conf { let TryConf { mut conf, + value_spans: _, errors, warnings, } = match path { @@ -950,17 +996,10 @@ impl serde::de::Error for FieldError { } } - let suggestion = expected - .iter() - .filter_map(|expected| { - let dist = edit_distance(field, expected, 4)?; - Some((dist, expected)) - }) - .min_by_key(|&(dist, _)| dist) - .map(|(_, suggestion)| Suggestion { - message: "perhaps you meant", - suggestion, - }); + let suggestion = suggest_candidate(field, expected).map(|suggestion| Suggestion { + message: "perhaps you meant", + suggestion, + }); Self { error: msg, suggestion } } @@ -998,6 +1037,22 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { (rows, column_widths) } +/// Given a user-provided value that couldn't be matched to a known option, finds the most likely +/// candidate among candidates that the user might have meant. +fn suggest_candidate<'a, I>(value: &str, candidates: I) -> Option<&'a str> +where + I: IntoIterator, +{ + candidates + .into_iter() + .filter_map(|expected| { + let dist = edit_distance(value, expected, 4)?; + Some((dist, expected)) + }) + .min_by_key(|&(dist, _)| dist) + .map(|(_, suggestion)| suggestion) +} + #[cfg(test)] mod tests { use serde::de::IgnoredAny; diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index c72291e9799f1..8faac9ecffea4 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -305,6 +305,7 @@ impl SourceItemOrderingModuleItemKind { pub struct SourceItemOrderingModuleItemGroupings { groups: Vec<(String, Vec)>, lut: HashMap, + back_lut: HashMap, } impl SourceItemOrderingModuleItemGroupings { @@ -320,6 +321,30 @@ impl SourceItemOrderingModuleItemGroupings { lut } + fn build_back_lut( + groups: &[(String, Vec)], + ) -> HashMap { + let mut lut = HashMap::new(); + for (group_name, items) in groups { + for item in items { + lut.insert(item.clone(), group_name.clone()); + } + } + lut + } + + pub fn grouping_name_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<&String> { + self.back_lut.get(item) + } + + pub fn grouping_names(&self) -> Vec { + self.groups.iter().map(|(name, _)| name.clone()).collect() + } + + pub fn is_grouping(&self, grouping: &str) -> bool { + self.groups.iter().any(|(g, _)| g == grouping) + } + pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option { self.lut.get(item).copied() } @@ -330,7 +355,8 @@ impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrdering let groups: Vec<(String, Vec)> = value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect(); let lut = Self::build_lut(&groups); - Self { groups, lut } + let back_lut = Self::build_back_lut(&groups); + Self { groups, lut, back_lut } } } @@ -348,6 +374,7 @@ impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { let groups = Vec::<(String, Vec)>::deserialize(deserializer)?; let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum(); let lut = Self::build_lut(&groups); + let back_lut = Self::build_back_lut(&groups); let mut expected_items = SourceItemOrderingModuleItemKind::all_variants(); for item in lut.keys() { @@ -370,7 +397,7 @@ impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { )); } - Ok(Self { groups, lut }) + Ok(Self { groups, lut, back_lut }) } else if items_total != all_items.len() { Err(de::Error::custom(format!( "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \ @@ -482,6 +509,83 @@ impl Serialize for SourceItemOrderingTraitAssocItemKinds { } } +/// Describes which specific groupings should have their items ordered +/// alphabetically. +/// +/// This is separate from defining and enforcing groupings. For example, +/// defining enums are grouped before structs still allows for an enum B to be +/// placed before an enum A. Only when enforcing ordering within the grouping, +/// will it be checked if A is placed before B. +#[derive(Clone, Debug)] +pub enum SourceItemOrderingWithinModuleItemGroupings { + /// All groupings should have their items ordered. + All, + + /// None of the groupings should have their order checked. + None, + + /// Only the specified groupings should have their order checked. + Custom(Vec), +} + +impl SourceItemOrderingWithinModuleItemGroupings { + pub fn ordered_within(&self, grouping_name: &String) -> bool { + match self { + SourceItemOrderingWithinModuleItemGroupings::All => true, + SourceItemOrderingWithinModuleItemGroupings::None => false, + SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => groups.contains(grouping_name), + } + } +} + +/// Helper struct for deserializing the [`SourceItemOrderingWithinModuleItemGroupings`]. +#[derive(Deserialize)] +#[serde(untagged)] +enum StringOrVecOfString { + String(String), + Vec(Vec), +} + +impl<'de> Deserialize<'de> for SourceItemOrderingWithinModuleItemGroupings { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let description = "The available options for configuring an ordering within module item groups are: \ + \"all\", \"none\", or a list of module item group names \ + (as configured with the `module-item-order-groupings` configuration option)."; + + match StringOrVecOfString::deserialize(deserializer) { + Ok(StringOrVecOfString::String(preset)) if preset == "all" => { + Ok(SourceItemOrderingWithinModuleItemGroupings::All) + }, + Ok(StringOrVecOfString::String(preset)) if preset == "none" => { + Ok(SourceItemOrderingWithinModuleItemGroupings::None) + }, + Ok(StringOrVecOfString::String(preset)) => Err(de::Error::custom(format!( + "Unknown configuration option: {preset}.\n{description}" + ))), + Ok(StringOrVecOfString::Vec(groupings)) => { + Ok(SourceItemOrderingWithinModuleItemGroupings::Custom(groupings)) + }, + Err(e) => Err(de::Error::custom(format!("{e}\n{description}"))), + } + } +} + +impl Serialize for SourceItemOrderingWithinModuleItemGroupings { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + match self { + SourceItemOrderingWithinModuleItemGroupings::All => serializer.serialize_str("all"), + SourceItemOrderingWithinModuleItemGroupings::None => serializer.serialize_str("none"), + SourceItemOrderingWithinModuleItemGroupings::Custom(vec) => vec.serialize(serializer), + } + } +} + // these impls are never actually called but are used by the various config options that default to // empty lists macro_rules! unimplemented_serialize { diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 39528a8f55c38..57cabe437034a 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -2,11 +2,12 @@ use clippy_config::Conf; use clippy_config::types::{ SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, + SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::{ - AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, - Variant, VariantData, + AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, Variant, + VariantData, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -36,7 +37,7 @@ declare_clippy_lint! { /// 2. Individual ordering rules per item kind. /// /// The item kinds that can be linted are: - /// - Module (with customized groupings, alphabetical within) + /// - Module (with customized groupings, alphabetical within - configurable) /// - Trait (with customized order of associated items, alphabetical within) /// - Enum, Impl, Struct (purely alphabetical) /// @@ -57,8 +58,31 @@ declare_clippy_lint! { /// | `PascalCase` | "ty_alias", "opaque_ty", "enum", "struct", "union", "trait", "trait_alias", "impl" | /// | `lower_snake_case` | "fn" | /// + /// The groups' names are arbitrary and can be changed to suit the + /// conventions that should be enforced for a specific project. + /// /// All item kinds must be accounted for to create an enforceable linting - /// rule set. + /// rule set. Following are some example configurations that may be useful. + /// + /// Example: *module inclusions and use statements to be at the top* + /// + /// ```toml + /// module-item-order-groupings = [ + /// [ "modules", [ "extern_crate", "mod", "foreign_mod" ], ], + /// [ "use", [ "use", ], ], + /// [ "everything_else", [ "macro", "global_asm", "static", "const", "ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl", "fn", ], ], + /// ] + /// ``` + /// + /// Example: *only consts and statics should be alphabetically ordered* + /// + /// It is also possible to configure a selection of module item groups that + /// should be ordered alphabetically. This may be useful if for example + /// statics and consts should be ordered, but the rest should be left open. + /// + /// ```toml + /// module-items-ordered-within-groupings = ["UPPER_SNAKE_CASE"] + /// ``` /// /// ### Known Problems /// @@ -143,6 +167,7 @@ pub struct ArbitrarySourceItemOrdering { enable_ordering_for_struct: bool, enable_ordering_for_trait: bool, module_item_order_groupings: SourceItemOrderingModuleItemGroupings, + module_items_ordered_within_groupings: SourceItemOrderingWithinModuleItemGroupings, } impl ArbitrarySourceItemOrdering { @@ -157,6 +182,7 @@ impl ArbitrarySourceItemOrdering { enable_ordering_for_struct: conf.source_item_ordering.contains(&Struct), enable_ordering_for_trait: conf.source_item_ordering.contains(&Trait), module_item_order_groupings: conf.module_item_order_groupings.clone(), + module_items_ordered_within_groupings: conf.module_items_ordered_within_groupings.clone(), } } @@ -176,11 +202,7 @@ impl ArbitrarySourceItemOrdering { } /// Produces a linting warning for incorrectly ordered item members. - fn lint_member_name( - cx: &T, - ident: &rustc_span::Ident, - before_ident: &rustc_span::Ident, - ) { + fn lint_member_name(cx: &T, ident: &rustc_span::Ident, before_ident: &rustc_span::Ident) { span_lint_and_note( cx, ARBITRARY_SOURCE_ITEM_ORDERING, @@ -191,7 +213,7 @@ impl ArbitrarySourceItemOrdering { ); } - fn lint_member_item(cx: &T, item: &Item<'_>, before_item: &Item<'_>) { + fn lint_member_item(cx: &T, item: &Item<'_>, before_item: &Item<'_>, msg: &'static str) { let span = if let Some(ident) = item.kind.ident() { ident.span } else { @@ -199,10 +221,7 @@ impl ArbitrarySourceItemOrdering { }; let (before_span, note) = if let Some(ident) = before_item.kind.ident() { - ( - ident.span, - format!("should be placed before `{}`", ident.as_str(),), - ) + (ident.span, format!("should be placed before `{}`", ident.as_str(),)) } else { ( before_item.span, @@ -215,14 +234,7 @@ impl ArbitrarySourceItemOrdering { return; } - span_lint_and_note( - cx, - ARBITRARY_SOURCE_ITEM_ORDERING, - span, - "incorrect ordering of items (must be alphabetically ordered)", - Some(before_span), - note, - ); + span_lint_and_note(cx, ARBITRARY_SOURCE_ITEM_ORDERING, span, msg, Some(before_span), note); } /// Produces a linting warning for incorrectly ordered trait items. @@ -376,6 +388,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { } let item_kind = convert_module_item_kind(&item.kind); + let grouping_name = self.module_item_order_groupings.grouping_name_of(&item_kind); let module_level_order = self .module_item_order_groupings .module_level_order_of(&item_kind) @@ -385,13 +398,27 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { use std::cmp::Ordering; // Better legibility. match module_level_order.cmp(&cur_t.order) { Ordering::Less => { - Self::lint_member_item(cx, item, cur_t.item); + Self::lint_member_item( + cx, + item, + cur_t.item, + "incorrect ordering of items (module item groupings specify another order)", + ); }, Ordering::Equal if item_kind == SourceItemOrderingModuleItemKind::Use => { // Skip ordering use statements, as these should be ordered by rustfmt. }, - Ordering::Equal if cur_t.name > get_item_name(item) => { - Self::lint_member_item(cx, item, cur_t.item); + Ordering::Equal + if (grouping_name.is_some_and(|grouping_name| { + self.module_items_ordered_within_groupings.ordered_within(grouping_name) + }) && cur_t.name > get_item_name(item)) => + { + Self::lint_member_item( + cx, + item, + cur_t.item, + "incorrect ordering of items (must be alphabetically ordered)", + ); }, Ordering::Equal | Ordering::Greater => { // Nothing to do in this case, they're already in the right order. @@ -501,6 +528,12 @@ fn get_item_name(item: &Item<'_>) -> String { }, // FIXME: `Ident::empty` for anonymous items is a bit strange, is there // a better way to do it? - _ => item.kind.ident().unwrap_or(rustc_span::Ident::empty()).name.as_str().to_owned(), + _ => item + .kind + .ident() + .unwrap_or(rustc_span::Ident::empty()) + .name + .as_str() + .to_owned(), } } diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 54ec8c7b2758b..e04d2ad5d13b7 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -466,7 +466,9 @@ 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) { + if let ItemKind::Fn { ident, .. } = item.kind + && is_relevant_item(cx, item) + { inline_always::check(cx, item.span, ident.name, attrs); } repr_attributes::check(cx, item.span, attrs, self.msrv); diff --git a/clippy_lints/src/blocks_in_conditions.rs b/clippy_lints/src/blocks_in_conditions.rs index aab0af0d743b9..011962846cb30 100644 --- a/clippy_lints/src/blocks_in_conditions.rs +++ b/clippy_lints/src/blocks_in_conditions.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_block_with_applicability; -use clippy_utils::{higher, is_from_proc_macro}; +use clippy_utils::{contains_return, higher, is_from_proc_macro}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -86,6 +86,13 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { if expr.span.from_expansion() || ex.span.from_expansion() { return; } + + // Linting should not be triggered to cases where `return` is included in the condition. + // #9911 + if contains_return(block.expr) { + return; + } + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index d143629da3a0d..64345c81a2482 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -2,11 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; +use clippy_utils::{is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -25,12 +24,7 @@ pub(super) fn check<'tcx>( let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0; // Fix #9884 - if !e.is_place_expr(|base| { - cx.typeck_results() - .adjustments() - .get(base.hir_id) - .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) - }) { + if is_expr_temporary_value(cx, e) { return false; } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 61c92d441d097..0e7f01e44b049 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -53,7 +53,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.40.0"] pub COMPARISON_CHAIN, - style, + pedantic, "`if`s that can be rewritten with `match` and `cmp`" } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0834618499c7b..7fa23dad69817 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -137,6 +137,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS_INFO, crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO, crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_CODE_INFO, @@ -614,7 +615,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::operators::MODULO_ONE_INFO, crate::operators::NEEDLESS_BITWISE_BOOL_INFO, crate::operators::OP_REF_INFO, - crate::operators::PTR_EQ_INFO, crate::operators::REDUNDANT_COMPARISONS_INFO, crate::operators::SELF_ASSIGNMENT_INFO, crate::operators::VERBOSE_BIT_MASK_INFO, @@ -641,6 +641,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::ptr::INVALID_NULL_PTR_USAGE_INFO, crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, + crate::ptr::PTR_EQ_INFO, crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, diff --git a/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs b/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs new file mode 100644 index 0000000000000..4cc0270223339 --- /dev/null +++ b/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs @@ -0,0 +1,33 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_span::{BytePos, Span}; + +use super::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS; + +pub fn check(cx: &LateContext<'_>, collected_breaks: &[Span]) { + if collected_breaks.is_empty() { + return; + } + + let breaks: Vec<_> = collected_breaks + .iter() + .map(|span| span.with_hi(span.lo() + BytePos(2))) + .collect(); + + span_lint_and_then( + cx, + DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, + breaks.clone(), + "doc comment uses two spaces for a hard line break", + |diag| { + let suggs: Vec<_> = breaks.iter().map(|span| (*span, "\\".to_string())).collect(); + diag.tool_only_multipart_suggestion( + "replace this double space with a backslash:", + suggs, + Applicability::MachineApplicable, + ); + diag.help("replace this double space with a backslash: `\\`"); + }, + ); +} diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 0296ff13112f3..36fd396cc1df8 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1,12 +1,10 @@ #![allow(clippy::lint_without_lint_pass)] -mod lazy_continuation; -mod too_long_first_doc_paragraph; - use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; @@ -33,12 +31,15 @@ use rustc_span::{Span, sym}; use std::ops::Range; use url::Url; +mod doc_comment_double_space_linebreaks; mod include_in_doc_without_cfg; +mod lazy_continuation; mod link_with_quotes; mod markdown; mod missing_headers; mod needless_doctest_main; mod suspicious_doc_comments; +mod too_long_first_doc_paragraph; declare_clippy_lint! { /// ### What it does @@ -567,6 +568,39 @@ declare_clippy_lint! { "link reference defined in list item or quote" } +declare_clippy_lint! { + /// ### What it does + /// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`). + /// + /// ### Why is this bad? + /// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may + /// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`) + /// is clearer in this regard. + /// + /// ### Example + /// The two replacement dots in this example represent a double space. + /// ```no_run + /// /// This command takes two numbers as inputs and·· + /// /// adds them together, and then returns the result. + /// fn add(l: i32, r: i32) -> i32 { + /// l + r + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// /// This command takes two numbers as inputs and\ + /// /// adds them together, and then returns the result. + /// fn add(l: i32, r: i32) -> i32 { + /// l + r + /// } + /// ``` + #[clippy::version = "1.87.0"] + pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, + pedantic, + "double-space used for doc comment linebreak instead of `\\`" +} + pub struct Documentation { valid_idents: FxHashSet, check_private_items: bool, @@ -598,6 +632,7 @@ impl_lint_pass!(Documentation => [ DOC_OVERINDENTED_LIST_ITEMS, TOO_LONG_FIRST_DOC_PARAGRAPH, DOC_INCLUDE_WITHOUT_CFG, + DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS ]); impl EarlyLintPass for Documentation { @@ -894,6 +929,7 @@ fn check_doc<'a, Events: Iterator, Range = Vec::new(); let mut is_first_paragraph = true; let mut containers = Vec::new(); @@ -1010,11 +1046,7 @@ fn check_doc<'a, Events: Iterator, Range range.start { - start - range.start - } else { - 0 - } + start.saturating_sub(range.start) } } else { 0 @@ -1073,6 +1105,14 @@ fn check_doc<'a, Events: Iterator, Range { paragraph_range.end = range.end; @@ -1123,6 +1163,9 @@ fn check_doc<'a, Events: Iterator, Range {} } } + + doc_comment_double_space_linebreaks::check(cx, &collected_breaks); + headers } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index f404bc59b3b88..dcfee0b6d3c62 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, @@ -84,14 +85,21 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { return; }; + let lint_msg = format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()); let mut app = Applicability::MachineApplicable; let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; + let sugg = if let Some(else_expr) = else_expr { let Some(else_search) = find_insert_calls(cx, &contains_expr, else_expr) else { return; }; + if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy { + span_lint(cx, MAP_ENTRY, expr.span, lint_msg); + return; + } + if then_search.edits.is_empty() && else_search.edits.is_empty() { // No insertions return; @@ -184,15 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } }; - span_lint_and_sugg( - cx, - MAP_ENTRY, - expr.span, - format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), - "try", - sugg, - app, - ); + span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); } } @@ -354,6 +354,8 @@ struct InsertSearcher<'cx, 'tcx> { key: &'tcx Expr<'tcx>, /// The context of the top level block. All insert calls must be in the same context. ctxt: SyntaxContext, + /// The spanless equality utility used to compare expressions. + spanless_eq: SpanlessEq<'cx, 'tcx>, /// Whether this expression can be safely moved into a closure. allow_insert_closure: bool, /// Whether this expression can use the entry api. @@ -364,6 +366,8 @@ struct InsertSearcher<'cx, 'tcx> { is_single_insert: bool, /// If the visitor has seen the map being used. is_map_used: bool, + /// If the visitor has seen the key being used. + is_key_used: bool, /// The locations where changes need to be made for the suggestion. edits: Vec>, /// A stack of loops the visitor is currently in. @@ -479,11 +483,11 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } match try_parse_insert(self.cx, expr) { - Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => { + Some(insert_expr) if self.spanless_eq.eq_expr(self.map, insert_expr.map) => { self.visit_insert_expr_arguments(&insert_expr); // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api. if self.is_map_used - || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key) + || !self.spanless_eq.eq_expr(self.key, insert_expr.key) || expr.span.ctxt() != self.ctxt { self.can_use_entry = false; @@ -502,9 +506,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { self.visit_non_tail_expr(insert_expr.value); self.is_single_insert = is_single_insert; }, - _ if is_any_expr_in_map_used(self.cx, self.map, expr) => { + _ if is_any_expr_in_map_used(self.cx, &mut self.spanless_eq, self.map, expr) => { self.is_map_used = true; }, + _ if self.spanless_eq.eq_expr(self.key, expr) => { + self.is_key_used = true; + }, _ => match expr.kind { ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { self.is_single_insert = false; @@ -568,9 +575,14 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { /// Check if the given expression is used for each sub-expression in the given map. /// For example, in map `a.b.c.my_map`, The expression `a.b.c.my_map`, `a.b.c`, `a.b`, and `a` are /// all checked. -fn is_any_expr_in_map_used<'tcx>(cx: &LateContext<'tcx>, map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn is_any_expr_in_map_used<'tcx>( + cx: &LateContext<'tcx>, + spanless_eq: &mut SpanlessEq<'_, 'tcx>, + map: &'tcx Expr<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> bool { for_each_expr(cx, map, |e| { - if SpanlessEq::new(cx).eq_expr(e, expr) { + if spanless_eq.eq_expr(e, expr) { return ControlFlow::Break(()); } ControlFlow::Continue(()) @@ -582,6 +594,7 @@ struct InsertSearchResults<'tcx> { edits: Vec>, allow_insert_closure: bool, is_single_insert: bool, + is_key_used_and_no_copy: bool, } impl<'tcx> InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option> { @@ -694,11 +707,13 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), + spanless_eq: SpanlessEq::new(cx), allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, is_map_used: false, + is_key_used: false, edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), @@ -706,10 +721,12 @@ fn find_insert_calls<'tcx>( s.visit_expr(expr); let allow_insert_closure = s.allow_insert_closure; let is_single_insert = s.is_single_insert; + let is_key_used_and_no_copy = s.is_key_used && !is_copy(cx, cx.typeck_results().expr_ty(contains_expr.key)); let edits = s.edits; s.can_use_entry.then_some(InsertSearchResults { edits, allow_insert_closure, is_single_insert, + is_key_used_and_no_copy, }) } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 3433b2cd857a9..de0fc2b1bf4bb 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -162,25 +162,23 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { } fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() { - if 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; - } + 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 { - if self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) { - 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 { + if 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); - } + if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { + self.set.insert(cmt.hir_id); } } } diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 2509c04cd86dd..38d115b878c71 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -15,12 +15,17 @@ declare_clippy_lint! { /// use of bools in structs. /// /// ### Why is this bad? - /// Excessive bools in a struct - /// is often a sign that it's used as a state machine, - /// which is much better implemented as an enum. - /// If it's not the case, excessive bools usually benefit - /// from refactoring into two-variant enums for better - /// readability and API. + /// Excessive bools in a struct is often a sign that + /// the type is being used to represent a state + /// machine, which is much better implemented as an + /// enum. + /// + /// The reason an enum is better for state machines + /// over structs is that enums more easily forbid + /// invalid states. + /// + /// Structs with too many booleans may benefit from refactoring + /// into multi variant enums for better readability and API. /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index fc5f76179f907..3862ff7921db8 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -9,7 +9,7 @@ use clippy_utils::macros::{ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{implements_trait, is_type_lang_item}; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro}; +use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test}; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -484,7 +484,8 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { fn check_unnecessary_debug_formatting(&self, name: Symbol, value: &Expr<'tcx>) { let cx = self.cx; - if !value.span.from_expansion() + if !is_in_test(cx.tcx, value.hir_id) + && !value.span.from_expansion() && !is_from_proc_macro(cx, value) && let ty = cx.typeck_results().expr_ty(value) && self.can_display_format(ty) diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 6da5567d9c709..be887b03ae4b6 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -176,8 +176,8 @@ fn convert_to_from( return None; }; let body = cx.tcx.hir_body(body_id); - let [input] = body.params else { return None }; - let PatKind::Binding(.., self_ident, None) = input.pat.kind else { + let [self_param] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = self_param.pat.kind else { return None; }; @@ -194,12 +194,12 @@ fn convert_to_from( // impl Into for U -> impl Into for T // ~ ~ (self_ty.span, into.to_owned()), - // fn into(self) -> T -> fn from(self) -> T - // ~~~~ ~~~~ + // fn into(self: U) -> T -> fn from(self) -> T + // ~~~~ ~~~~ (impl_item.ident.span, String::from("from")), - // fn into([mut] self) -> T -> fn into([mut] v: T) -> T - // ~~~~ ~~~~ - (self_ident.span, format!("val: {from}")), + // fn into([mut] self: U) -> T -> fn into([mut] val: T) -> T + // ~~~~~~~ ~~~~~~ + (self_ident.span.to(self_param.ty_span), format!("val: {from}")), ]; if let FnRetTy::Return(_) = sig.decl.output { diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index 54b8adbc8ac79..e4ace3bdabf0f 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -17,15 +17,15 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// match std::fs::create_dir("tmp-work-dir") { - /// Ok(_) => println!("Working directory created"), - /// Err(s) => eprintln!("Could not create directory: {s}"), + /// Ok(_) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), /// } /// ``` /// Use instead: /// ```no_run /// match std::fs::create_dir("tmp-work-dir") { - /// Ok(()) => println!("Working directory created"), - /// Err(s) => eprintln!("Could not create directory: {s}"), + /// Ok(()) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), /// } /// ``` #[clippy::version = "1.73.0"] diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index f2d16ff2e5649..cbc3e2ccd5b87 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_opt; +use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::{ - SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, + SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, }; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath}; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -170,22 +170,20 @@ fn check_gt( cx: &LateContext<'_>, condition_span: Span, expr_span: Span, - big_var: &Expr<'_>, - little_var: &Expr<'_>, + big_expr: &Expr<'_>, + little_expr: &Expr<'_>, if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: Msrv, is_composited: bool, ) { - if let Some(big_var) = Var::new(big_var) - && let Some(little_var) = Var::new(little_var) - { + if is_side_effect_free(cx, big_expr) && is_side_effect_free(cx, little_expr) { check_subtraction( cx, condition_span, expr_span, - big_var, - little_var, + big_expr, + little_expr, if_block, else_block, msrv, @@ -194,18 +192,8 @@ fn check_gt( } } -struct Var { - span: Span, - hir_id: HirId, -} - -impl Var { - fn new(expr: &Expr<'_>) -> Option { - path_to_local(expr).map(|hir_id| Self { - span: expr.span, - hir_id, - }) - } +fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + eq_expr_value(cx, expr, expr) } #[allow(clippy::too_many_arguments)] @@ -213,8 +201,8 @@ fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, expr_span: Span, - big_var: Var, - little_var: Var, + big_expr: &Expr<'_>, + little_expr: &Expr<'_>, if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: Msrv, @@ -234,8 +222,8 @@ fn check_subtraction( cx, condition_span, expr_span, - little_var, - big_var, + little_expr, + big_expr, else_block, if_block, msrv, @@ -247,17 +235,15 @@ fn check_subtraction( && let ExprKind::Binary(op, left, right) = if_block.kind && let BinOpKind::Sub = op.node { - let local_left = path_to_local(left); - let local_right = path_to_local(right); - if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right { + if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) { // This part of the condition is voluntarily split from the one before to ensure that // if `snippet_opt` fails, it won't try the next conditions. - if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) - && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) - && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) + if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_par) + && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { let sugg = format!( - "{}{big_var_snippet}.saturating_sub({little_var_snippet}){}", + "{}{big_expr_sugg}.saturating_sub({little_expr_sugg}){}", if is_composited { "{ " } else { "" }, if is_composited { " }" } else { "" } ); @@ -271,11 +257,12 @@ fn check_subtraction( Applicability::MachineApplicable, ); } - } else if Some(little_var.hir_id) == local_left - && Some(big_var.hir_id) == local_right - && let Some(big_var_snippet) = snippet_opt(cx, big_var.span) - && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + } else if eq_expr_value(cx, left, little_expr) + && eq_expr_value(cx, right, big_expr) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr) + && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { + let sugg = make_binop(BinOpKind::Sub, &big_expr_sugg, &little_expr_sugg); span_lint_and_then( cx, INVERTED_SATURATING_SUB, @@ -284,12 +271,12 @@ fn check_subtraction( |diag| { diag.span_note( if_block.span, - format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"), + format!("this subtraction underflows when `{little_expr_sugg} < {big_expr_sugg}`"), ); diag.span_suggestion( if_block.span, "try replacing it with", - format!("{big_var_snippet} - {little_var_snippet}"), + format!("{sugg}"), Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 12dfb14c454d2..e55edb1fcaa81 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -4,12 +4,12 @@ use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; use rustc_attr_parsing::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; -use rustc_span::{ExpnKind, Span}; +use rustc_span::{ExpnKind, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -93,6 +93,21 @@ impl IncompatibleMsrv { // Intentionally not using `.from_expansion()`, since we do still care about macro expansions return; } + + // Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the + // macros may have existed prior to the checked MSRV, but their expansion with a recent compiler + // might use recent functions or methods. Compiling with an older compiler would not use those. + if span.from_expansion() + && cx.tcx.crate_name(def_id.krate) == sym::core + && span + .ctxt() + .outer_expn_data() + .macro_def_id + .is_some_and(|def_id| cx.tcx.crate_name(def_id.krate) == sym::core) + { + return; + } + if (self.check_in_tests || !is_in_test(cx.tcx, node)) && let Some(current) = self.msrv.current(cx) && let version = self.get_def_id_version(cx.tcx, def_id) @@ -118,8 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span); } }, - ExprKind::Call(call, [_]) => { - if let ExprKind::Path(qpath) = call.kind + ExprKind::Call(call, _) => { + // Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should + // not be linted as they will not be generated in older compilers if the function is not available, + // and the compiler is allowed to call unstable functions. + if let ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) = call.kind && let Some(path_def_id) = cx.qpath_res(&qpath, call.hir_id).opt_def_id() { self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, call.span); diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 6a436fb4a9d1e..da5ca5e677218 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -32,11 +32,7 @@ declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]); impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind - && let Some(attr) = cx - .tcx - .hir_attrs(item.hir_id()) - .iter() - .find(|a| a.has_name(sym::inline)) + && let Some(attr) = cx.tcx.hir_attrs(item.hir_id()).iter().find(|a| a.has_name(sym::inline)) { span_lint_and_then( cx, diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 8de6125d1f2b3..977fd5fce15be 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -8,7 +8,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{Ident, Span}; use rustc_span::symbol::Symbol; declare_clippy_lint! { @@ -196,80 +195,176 @@ fn have_no_extra_prefix(prefixes: &[&str]) -> bool { prefixes.iter().all(|p| p == &"" || p == &"_") } -fn check_fields(cx: &LateContext<'_>, threshold: u64, ident: Ident, span: Span, fields: &[FieldDef<'_>]) { - if (fields.len() as u64) < threshold { - return; - } +impl ItemNameRepetitions { + /// Lint the names of enum variants against the name of the enum. + fn check_variants(&self, cx: &LateContext<'_>, item: &Item<'_>, def: &EnumDef<'_>) { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id) { + return; + } - check_struct_name_repetition(cx, ident, fields); + if (def.variants.len() as u64) < self.enum_threshold { + return; + } - // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them. - // this prevents linting in macros in which the location of the field identifier names differ - if !fields.iter().all(|field| ident.span.eq_ctxt(field.ident.span)) { - return; + let Some(ident) = item.kind.ident() else { + return; + }; + let item_name = ident.name.as_str(); + for var in def.variants { + check_enum_start(cx, item_name, var); + check_enum_end(cx, item_name, var); + } + + Self::check_enum_common_affix(cx, item, def); } - let mut pre: Vec<&str> = match fields.first() { - Some(first_field) => first_field.ident.name.as_str().split('_').collect(), - None => return, - }; - let mut post = pre.clone(); - post.reverse(); - for field in fields { - let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect(); - if field_split.len() == 1 { + /// Lint the names of struct fields against the name of the struct. + fn check_fields(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + if (fields.len() as u64) < self.struct_threshold { return; } - pre = pre - .into_iter() - .zip(field_split.iter()) - .take_while(|(a, b)| &a == b) - .map(|e| e.0) - .collect(); - post = post - .into_iter() - .zip(field_split.iter().rev()) - .take_while(|(a, b)| &a == b) - .map(|e| e.0) - .collect(); + self.check_struct_name_repetition(cx, item, fields); + self.check_struct_common_affix(cx, item, fields); } - let prefix = pre.join("_"); - post.reverse(); - let postfix = match post.last() { - Some(&"") => post.join("_") + "_", - Some(_) | None => post.join("_"), - }; - if fields.len() > 1 { - let (what, value) = match ( - prefix.is_empty() || prefix.chars().all(|c| c == '_'), - postfix.is_empty(), - ) { - (true, true) => return, - (false, _) => ("pre", prefix), - (true, false) => ("post", postfix), + + fn check_enum_common_affix(cx: &LateContext<'_>, item: &Item<'_>, def: &EnumDef<'_>) { + let first = match def.variants.first() { + Some(variant) => variant.ident.name.as_str(), + None => return, }; - if fields.iter().all(|field| is_bool(field.ty)) { - // If all fields are booleans, we don't want to emit this lint. - return; + let mut pre = camel_case_split(first); + let mut post = pre.clone(); + post.reverse(); + for var in def.variants { + let name = var.ident.name.as_str(); + + let variant_split = camel_case_split(name); + if variant_split.len() == 1 { + return; + } + + pre = pre + .iter() + .zip(variant_split.iter()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); + post = post + .iter() + .zip(variant_split.iter().rev()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); } + let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) { + (true, true) => return, + (false, _) => ("pre", pre.join("")), + (true, false) => { + post.reverse(); + ("post", post.join("")) + }, + }; span_lint_and_help( cx, - STRUCT_FIELD_NAMES, - span, - format!("all fields have the same {what}fix: `{value}`"), + ENUM_VARIANT_NAMES, + item.span, + format!("all variants have the same {what}fix: `{value}`"), None, - format!("remove the {what}fixes"), + format!( + "remove the {what}fixes and use full paths to \ + the variants instead of glob imports" + ), ); } -} -fn check_struct_name_repetition(cx: &LateContext<'_>, ident: Ident, fields: &[FieldDef<'_>]) { - let snake_name = to_snake_case(ident.name.as_str()); - let item_name_words: Vec<&str> = snake_name.split('_').collect(); - for field in fields { - if field.ident.span.eq_ctxt(ident.span) { - //consider linting only if the field identifier has the same SyntaxContext as the item(struct) + fn check_struct_common_affix(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them. + // this prevents linting in macros in which the location of the field identifier names differ + if !fields + .iter() + .all(|field| item.kind.ident().is_some_and(|i| i.span.eq_ctxt(field.ident.span))) + { + return; + } + + if self.avoid_breaking_exported_api + && fields + .iter() + .any(|field| cx.effective_visibilities.is_exported(field.def_id)) + { + return; + } + + let mut pre: Vec<&str> = match fields.first() { + Some(first_field) => first_field.ident.name.as_str().split('_').collect(), + None => return, + }; + let mut post = pre.clone(); + post.reverse(); + for field in fields { + let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect(); + if field_split.len() == 1 { + return; + } + + pre = pre + .into_iter() + .zip(field_split.iter()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + post = post + .into_iter() + .zip(field_split.iter().rev()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + } + let prefix = pre.join("_"); + post.reverse(); + let postfix = match post.last() { + Some(&"") => post.join("_") + "_", + Some(_) | None => post.join("_"), + }; + if fields.len() > 1 { + let (what, value) = match ( + prefix.is_empty() || prefix.chars().all(|c| c == '_'), + postfix.is_empty(), + ) { + (true, true) => return, + (false, _) => ("pre", prefix), + (true, false) => ("post", postfix), + }; + if fields.iter().all(|field| is_bool(field.ty)) { + // If all fields are booleans, we don't want to emit this lint. + return; + } + span_lint_and_help( + cx, + STRUCT_FIELD_NAMES, + item.span, + format!("all fields have the same {what}fix: `{value}`"), + None, + format!("remove the {what}fixes"), + ); + } + } + + fn check_struct_name_repetition(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + let Some(ident) = item.kind.ident() else { return }; + let snake_name = to_snake_case(ident.name.as_str()); + let item_name_words: Vec<&str> = snake_name.split('_').collect(); + for field in fields { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(field.def_id) { + continue; + } + + if !field.ident.span.eq_ctxt(ident.span) { + // consider linting only if the field identifier has the same SyntaxContext as the item(struct) + continue; + } + let field_words: Vec<&str> = field.ident.name.as_str().split('_').collect(); if field_words.len() >= item_name_words.len() { // if the field name is shorter than the struct name it cannot contain it @@ -337,65 +432,6 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) } } -fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_name: &str, span: Span) { - if (def.variants.len() as u64) < threshold { - return; - } - - for var in def.variants { - check_enum_start(cx, item_name, var); - check_enum_end(cx, item_name, var); - } - - let first = match def.variants.first() { - Some(variant) => variant.ident.name.as_str(), - None => return, - }; - let mut pre = camel_case_split(first); - let mut post = pre.clone(); - post.reverse(); - for var in def.variants { - let name = var.ident.name.as_str(); - - let variant_split = camel_case_split(name); - if variant_split.len() == 1 { - return; - } - - pre = pre - .iter() - .zip(variant_split.iter()) - .take_while(|(a, b)| a == b) - .map(|e| *e.0) - .collect(); - post = post - .iter() - .zip(variant_split.iter().rev()) - .take_while(|(a, b)| a == b) - .map(|e| *e.0) - .collect(); - } - let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) { - (true, true) => return, - (false, _) => ("pre", pre.join("")), - (true, false) => { - post.reverse(); - ("post", post.join("")) - }, - }; - span_lint_and_help( - cx, - ENUM_VARIANT_NAMES, - span, - format!("all variants have the same {what}fix: `{value}`"), - None, - format!( - "remove the {what}fixes and use full paths to \ - the variants instead of glob imports" - ), - ); -} - impl LateLintPass<'_> for ItemNameRepetitions { fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { let Some(_ident) = item.kind.ident() else { return }; @@ -462,13 +498,14 @@ impl LateLintPass<'_> for ItemNameRepetitions { } } } - if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) - && span_is_local(item.span) - { + + if span_is_local(item.span) { match item.kind { - ItemKind::Enum(_, def, _) => check_variant(cx, self.enum_threshold, &def, item_name, item.span), + ItemKind::Enum(_, def, _) => { + self.check_variants(cx, item, &def); + }, ItemKind::Struct(_, VariantData::Struct { fields, .. }, _) => { - check_fields(cx, self.struct_threshold, ident, item.span, fields); + self.check_fields(cx, item, fields); }, _ => (), } diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 4bc6ad0798c9c..753360906d666 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -28,9 +28,9 @@ declare_clippy_lint! { /// use std::str::Chars; /// struct Data {} /// impl Data { - /// fn iter(&self) -> Chars<'static> { - /// todo!() - /// } + /// fn iter(&self) -> Chars<'static> { + /// todo!() + /// } /// } /// ``` #[clippy::version = "1.57.0"] diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index ca51d8b618ee3..01b49403cac85 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -72,7 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { "importing a legacy numeric constant" }, |diag| { - if let UseKind::Single(ident) = kind && ident.name == kw::Underscore { + if let UseKind::Single(ident) = kind + && ident.name == kw::Underscore + { diag.help("remove this import"); return; } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index ed725a0398910..4b0bf5a4b3c94 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -469,7 +469,7 @@ declare_clippy_lint! { /// let item2 = 3; /// let mut vec: Vec = Vec::new(); /// for _ in 0..20 { - /// vec.push(item1); + /// vec.push(item1); /// } /// for _ in 0..30 { /// vec.push(item2); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index dd7a6f77acff5..c3a2a38b5ec25 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,11 +4,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; use std::iter::once; +use std::ops::ControlFlow; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -24,17 +26,23 @@ pub(super) fn check<'tcx>( arg: iterator, pat, span: for_span, + label, .. }) = for_loop { - // Suggests using an `if let` instead. This is `Unspecified` because the - // loop may (probably) contain `break` statements which would be invalid - // in an `if let`. + // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not + // appropriate. + let app = if !contains_any_break_or_continue(block) && label.is_none() { + Applicability::MachineApplicable + } else { + Applicability::Unspecified + }; + diag.span_suggestion_verbose( for_span.with_hi(iterator.span.hi()), "if you need the first element of the iterator, try writing", for_to_if_let_sugg(cx, iterator, pat), - Applicability::Unspecified, + app, ); } }); @@ -43,6 +51,15 @@ pub(super) fn check<'tcx>( } } +fn contains_any_break_or_continue(block: &Block<'_>) -> bool { + for_each_expr_without_closures(block, |e| match e.kind { + ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), + ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() +} + /// The `never_loop` analysis keeps track of three things: /// /// * Has any (reachable) code path hit a `continue` of the main loop? diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 39c4857b3e874..40fe88532729d 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -14,7 +14,7 @@ use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `std::mem::size_of::() * 8` when + /// Checks for usage of `size_of::() * 8` when /// `T::BITS` is available. /// /// ### Why is this bad? @@ -22,7 +22,7 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// std::mem::size_of::() * 8; + /// size_of::() * 8; /// ``` /// Use instead: /// ```no_run @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { cx, MANUAL_BITS, expr.span, - "usage of `mem::size_of::()` to obtain the size of `T` in bits", + "usage of `size_of::()` to obtain the size of `T` in bits", "consider using", sugg, app, diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index 9c1419175d55c..9944c4f880481 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { && check_int_ty_and_feature(cx, div_lhs) && check_int_ty_and_feature(cx, div_rhs) && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind - && self.msrv.meets(cx, msrvs::MANUAL_DIV_CEIL) + && self.msrv.meets(cx, msrvs::DIV_CEIL) { // (x + (y - 1)) / y if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index 506f4f6d9de1b..d92069edb6d00 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn compare(a: &str, b: &str) -> bool { - /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") + /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") /// } /// ``` #[clippy::version = "1.84.0"] diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 47939767212ef..d6ac6e106b4bd 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -5,7 +5,8 @@ use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_ast::BindingMode; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; @@ -113,7 +114,7 @@ fn emit_manual_let_else( cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, - ident_map: &FxHashMap>, + ident_map: &FxHashMap, BindingMode)>, pat: &Pat<'_>, else_body: &Expr<'_>, ) { @@ -167,7 +168,7 @@ fn emit_manual_let_else( fn replace_in_pattern( cx: &LateContext<'_>, span: Span, - ident_map: &FxHashMap>, + ident_map: &FxHashMap, BindingMode)>, pat: &Pat<'_>, app: &mut Applicability, top_level: bool, @@ -185,15 +186,16 @@ fn replace_in_pattern( match pat.kind { PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some(pat_to_put) = ident_map.get(&binding_name.name) else { + let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else { break 'a; }; + let sn_pfx = binding_mode.prefix_str(); let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); if let Some(subpt) = opt_subpt { let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); - return format!("{sn_ptp} @ {subpt}"); + return format!("{sn_pfx}{sn_ptp} @ {subpt}"); } - return sn_ptp.to_string(); + return format!("{sn_pfx}{sn_ptp}"); }, PatKind::Or(pats) => { let patterns = pats @@ -211,17 +213,18 @@ fn replace_in_pattern( .iter() .map(|fld| { if let PatKind::Binding(_, _, name, None) = fld.pat.kind - && let Some(pat_to_put) = ident_map.get(&name.name) + && let Some((pat_to_put, binding_mode)) = ident_map.get(&name.name) { + let sn_pfx = binding_mode.prefix_str(); let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app); let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); // TODO: this is a bit of a hack, but it does its job. Ideally, we'd check if pat_to_put is // a PatKind::Binding but that is also hard to get right. if sn_fld_name == sn_ptp { // Field init shorthand - return format!("{sn_fld_name}"); + return format!("{sn_pfx}{sn_fld_name}"); } - return format!("{sn_fld_name}: {sn_ptp}"); + return format!("{sn_fld_name}: {sn_pfx}{sn_ptp}"); } let (sn_fld, _) = snippet_with_context(cx, fld.span, span.ctxt(), "", app); sn_fld.into_owned() @@ -334,7 +337,7 @@ fn expr_simple_identity_map<'a, 'hir>( local_pat: &'a Pat<'hir>, let_pat: &'_ Pat<'hir>, expr: &'_ Expr<'hir>, -) -> Option>> { +) -> Option, BindingMode)>> { let peeled = peel_blocks(expr); let (sub_pats, paths) = match (local_pat.kind, peeled.kind) { (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) | (PatKind::Slice(pats, ..), ExprKind::Array(exprs)) => { @@ -351,9 +354,9 @@ fn expr_simple_identity_map<'a, 'hir>( return None; } - let mut pat_bindings = FxHashSet::default(); - let_pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| { - pat_bindings.insert(ident); + let mut pat_bindings = FxHashMap::default(); + let_pat.each_binding_or_first(&mut |binding_mode, _hir_id, _sp, ident| { + pat_bindings.insert(ident, binding_mode); }); if pat_bindings.len() < paths.len() { // This rebinds some bindings from the outer scope, or it repeats some copy-able bindings multiple @@ -366,12 +369,10 @@ fn expr_simple_identity_map<'a, 'hir>( for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) { if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind && let [path_seg] = path.segments + && let ident = path_seg.ident + && let Some(let_binding_mode) = pat_bindings.remove(&ident) { - let ident = path_seg.ident; - if !pat_bindings.remove(&ident) { - return None; - } - ident_map.insert(ident.name, sub_pat); + ident_map.insert(ident.name, (sub_pat, let_binding_mode)); } else { return None; } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 18901f7399d96..2dad0fa4925e7 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -24,12 +24,12 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// # let data : &[i32] = &[1, 2, 3]; - /// let newlen = data.len() * std::mem::size_of::(); + /// let newlen = data.len() * size_of::(); /// ``` /// Use instead: /// ```no_run /// # let data : &[i32] = &[1, 2, 3]; - /// let newlen = std::mem::size_of_val(data); + /// let newlen = size_of_val(data); /// ``` #[clippy::version = "1.70.0"] pub MANUAL_SLICE_SIZE_CALCULATION, diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 35caa7d1f3a6d..2b9173e6f4122 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1110,11 +1110,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } } - // If there are still comments, it means they are outside of the arms, therefore - // we should not lint. - if match_comments.is_empty() { - single_match::check(cx, ex, arms, expr); - } + // If there are still comments, it means they are outside of the arms. Tell the lint + // code about it. + single_match::check(cx, ex, arms, expr, !match_comments.is_empty()); match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 2f46eaaabb364..56fbd626eefc4 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{ @@ -6,7 +6,7 @@ use clippy_utils::{ }; use core::ops::ControlFlow; use rustc_arena::DroplessArena; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind}; @@ -32,7 +32,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { } #[rustfmt::skip] -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { if let [arm1, arm2] = arms && arm1.guard.is_none() && arm2.guard.is_none() @@ -77,15 +77,31 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tc } } - report_single_pattern(cx, ex, arm1, expr, els); + report_single_pattern(cx, ex, arm1, expr, els, contains_comments); } } } -fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) { +fn report_single_pattern( + cx: &LateContext<'_>, + ex: &Expr<'_>, + arm: &Arm<'_>, + expr: &Expr<'_>, + els: Option<&Expr<'_>>, + contains_comments: bool, +) { let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let ctxt = expr.span.ctxt(); - let mut app = Applicability::MachineApplicable; + let note = |diag: &mut Diag<'_, ()>| { + if contains_comments { + diag.note("you might want to preserve the comments from inside the `match`"); + } + }; + let mut app = if contains_comments { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; let els_str = els.map_or(String::new(), |els| { format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); @@ -109,7 +125,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } (sugg, "try") }; - span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + diag.span_suggestion(expr.span, help, sugg.to_string(), app); + note(diag); + }); return; } @@ -162,7 +181,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp (msg, sugg) }; - span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + diag.span_suggestion(expr.span, "try", sugg.to_string(), app); + note(diag); + }); } struct PatVisitor<'tcx> { diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs index 4659e9e163fe9..bdc834bd47a56 100644 --- a/clippy_lints/src/methods/io_other_error.rs +++ b/clippy_lints/src/methods/io_other_error.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args "use `std::io::Error::other`", vec![ (new_segment.ident.span, "other".to_owned()), - (error_kind.span.until(error.span), String::new()), + (error_kind.span.until(error.span.source_callsite()), String::new()), ], Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7dde21d3edb13..1d9296016e25f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4447,13 +4447,13 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// fn foo(values: &[u8]) -> bool { - /// values.iter().any(|&v| v == 10) + /// values.iter().any(|&v| v == 10) /// } /// ``` /// Use instead: /// ```no_run /// fn foo(values: &[u8]) -> bool { - /// values.contains(&10) + /// values.contains(&10) /// } /// ``` #[clippy::version = "1.86.0"] diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 45f79dd44f2a4..56ff7e2c61b22 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; @@ -9,9 +11,9 @@ use clippy_utils::{ }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, Pat, PatKind, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -103,6 +105,12 @@ pub(super) fn check<'tcx>( return; } + if let IterFunctionKind::IntoIter(hir_id) = iter_call.func + && !check_iter_expr_used_only_as_iterator(cx, hir_id, block) + { + return; + } + // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); @@ -253,7 +261,7 @@ struct IterFunction { impl IterFunction { fn get_iter_method(&self, cx: &LateContext<'_>) -> String { match &self.func { - IterFunctionKind::IntoIter => String::new(), + IterFunctionKind::IntoIter(_) => String::new(), IterFunctionKind::Len => String::from(".count()"), IterFunctionKind::IsEmpty => String::from(".next().is_none()"), IterFunctionKind::Contains(span) => { @@ -268,7 +276,7 @@ impl IterFunction { } fn get_suggestion_text(&self) -> &'static str { match &self.func { - IterFunctionKind::IntoIter => { + IterFunctionKind::IntoIter(_) => { "use the original Iterator instead of collecting it and then producing a new one" }, IterFunctionKind::Len => { @@ -284,7 +292,7 @@ impl IterFunction { } } enum IterFunctionKind { - IntoIter, + IntoIter(HirId), Len, IsEmpty, Contains(Span), @@ -343,7 +351,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { } match method_name.ident.name.as_str() { "into_iter" => self.uses.push(Some(IterFunction { - func: IterFunctionKind::IntoIter, + func: IterFunctionKind::IntoIter(expr.hir_id), span: expr.span, })), "len" => self.uses.push(Some(IterFunction { @@ -520,3 +528,61 @@ fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet { set } + +struct IteratorMethodCheckVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + hir_id_of_expr: HirId, + hir_id_of_let_binding: Option, +} + +impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> { + if let ExprKind::MethodCall(_method_name, recv, _args, _) = &expr.kind + && (recv.hir_id == self.hir_id_of_expr + || self + .hir_id_of_let_binding + .is_some_and(|hid| path_to_local_id(recv, hid))) + && !is_trait_method(self.cx, expr, sym::Iterator) + { + return ControlFlow::Break(()); + } else if let ExprKind::Assign(place, value, _span) = &expr.kind + && value.hir_id == self.hir_id_of_expr + && let Some(id) = path_to_local(place) + { + // our iterator was directly assigned to a variable + self.hir_id_of_let_binding = Some(id); + } + walk_expr(self, expr) + } + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) -> ControlFlow<()> { + if let StmtKind::Let(LetStmt { + init: Some(expr), + pat: + Pat { + kind: PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None), + .. + }, + .. + }) = &stmt.kind + && expr.hir_id == self.hir_id_of_expr + { + // our iterator was directly assigned to a variable + self.hir_id_of_let_binding = Some(*id); + } + walk_stmt(self, stmt) + } +} + +fn check_iter_expr_used_only_as_iterator<'tcx>( + cx: &LateContext<'tcx>, + hir_id_of_expr: HirId, + block: &'tcx Block<'tcx>, +) -> bool { + let mut visitor = IteratorMethodCheckVisitor { + cx, + hir_id_of_expr, + hir_id_of_let_binding: None, + }; + visitor.visit_block(block).is_continue() +} diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index a71b3659fd245..62ba3012643ce 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -6,7 +6,8 @@ use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, return_ty, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, + return_ty, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -219,6 +220,8 @@ fn check_into_iter_call_arg( && let Some(receiver_snippet) = receiver.span.get_source_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) + // Calling `iter()` on a temporary object can lead to false positives. #14242 + && !is_expr_temporary_value(cx, receiver) { if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 9151cc6332004..4fbd3c9874db1 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -1,32 +1,51 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_lint::EarlyContext; use rustc_span::Span; use super::MIXED_CASE_HEX_LITERALS; pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) { - let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { - return; // It's useless so shouldn't lint. + let num_end_idx = match lit_snip.strip_suffix(suffix) { + Some(p) if p.ends_with('_') => lit_snip.len() - (suffix.len() + 1), + Some(_) => lit_snip.len() - suffix.len(), + None => lit_snip.len(), }; - if maybe_last_sep_idx <= 2 { + + if num_end_idx <= 2 { // It's meaningless or causes range error. return; } + let mut seen = (false, false); - for ch in &lit_snip.as_bytes()[2..=maybe_last_sep_idx] { + for ch in &lit_snip.as_bytes()[2..num_end_idx] { match ch { b'a'..=b'f' => seen.0 = true, b'A'..=b'F' => seen.1 = true, _ => {}, } if seen.0 && seen.1 { - span_lint( + let raw_digits = &lit_snip[2..num_end_idx]; + let (sugg_lower, sugg_upper) = if suffix.is_empty() { + ( + format!("0x{}", raw_digits.to_lowercase()), + format!("0x{}", raw_digits.to_uppercase()), + ) + } else { + ( + format!("0x{}_{}", raw_digits.to_lowercase(), suffix), + format!("0x{}_{}", raw_digits.to_uppercase(), suffix), + ) + }; + + span_lint_and_help( cx, MIXED_CASE_HEX_LITERALS, lit_span, "inconsistent casing in hexadecimal literal", + None, + format!("consider using `{sugg_lower}` or `{sugg_upper}`"), ); - break; + return; } } } diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 8045ab97d3847..f49e03ea76528 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// /// struct Baz; /// impl Baz { - /// fn private() {} // ok + /// fn private() {} // ok /// } /// /// impl Bar for Baz { @@ -46,13 +46,13 @@ declare_clippy_lint! { /// /// pub struct PubBaz; /// impl PubBaz { - /// fn private() {} // ok - /// pub fn not_private() {} // missing #[inline] + /// fn private() {} // ok + /// pub fn not_private() {} // missing #[inline] /// } /// /// impl Bar for PubBaz { - /// fn bar() {} // missing #[inline] - /// fn def_bar() {} // missing #[inline] + /// fn bar() {} // missing #[inline] + /// fn def_bar() {} // missing #[inline] /// } /// ``` /// diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 3efbed0c2365e..a914267cf5002 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; -use clippy_utils::source::{SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; @@ -100,7 +100,6 @@ fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { #[derive(Debug)] struct LocalAssign { lhs_id: HirId, - lhs_span: Span, rhs_span: Span, span: Span, } @@ -118,7 +117,6 @@ impl LocalAssign { Some(Self { lhs_id: path_to_local(lhs)?, - lhs_span: lhs.span, rhs_span: rhs.span.source_callsite(), span, }) @@ -281,7 +279,10 @@ fn check<'tcx>( format!("move the declaration `{binding_name}` here"), vec![ (local_stmt.span, String::new()), - (assign.lhs_span, let_snippet.to_owned()), + ( + assign.span, + let_snippet.to_owned() + " = " + &snippet(cx, assign.rhs_span, ".."), + ), ], Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7bee89086b80f..55ca875edcee6 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_self; use clippy_utils::ptr::get_spans; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; +use clippy_utils::{is_self, peel_hir_ty_options}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; @@ -279,10 +279,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - diag.span_suggestion( - input.span, + diag.span_suggestion_verbose( + peel_hir_ty_options(cx, input).span.shrink_to_lo(), "consider taking a reference instead", - format!("&{}", snippet(cx, input.span, "_")), + '&', Applicability::MaybeIncorrect, ); }; diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 37463cfec9a21..72b0a80260e9f 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { /// } /// /// fn f(to: TO) -> Option { - /// to.magic + /// to.magic /// } /// /// struct TR { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 80459945094ed..f758d08d36633 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -18,7 +18,6 @@ mod modulo_one; mod needless_bitwise_bool; mod numeric_arithmetic; mod op_ref; -mod ptr_eq; mod self_assignment; mod verbose_bit_mask; @@ -768,35 +767,6 @@ declare_clippy_lint! { "Boolean expressions that use bitwise rather than lazy operators" } -declare_clippy_lint! { - /// ### What it does - /// Use `std::ptr::eq` when applicable - /// - /// ### Why is this bad? - /// `ptr::eq` can be used to compare `&T` references - /// (which coerce to `*const T` implicitly) by their address rather than - /// comparing the values they point to. - /// - /// ### Example - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(a as *const _ as usize == b as *const _ as usize); - /// ``` - /// Use instead: - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(std::ptr::eq(a, b)); - /// ``` - #[clippy::version = "1.49.0"] - pub PTR_EQ, - style, - "use `std::ptr::eq` when comparing raw pointers" -} - declare_clippy_lint! { /// ### What it does /// Checks for explicit self-assignments. @@ -902,7 +872,6 @@ impl_lint_pass!(Operators => [ MODULO_ONE, MODULO_ARITHMETIC, NEEDLESS_BITWISE_BOOL, - PTR_EQ, SELF_ASSIGNMENT, MANUAL_MIDPOINT, ]); @@ -921,7 +890,6 @@ impl<'tcx> LateLintPass<'tcx> for Operators { erasing_op::check(cx, e, op.node, lhs, rhs); identity_op::check(cx, e, op.node, lhs, rhs); needless_bitwise_bool::check(cx, e, op.node, lhs, rhs); - ptr_eq::check(cx, e, op.node, lhs, rhs); manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv); } self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs deleted file mode 100644 index 8118ad59bb71c..0000000000000 --- a/clippy_lints/src/operators/ptr_eq.rs +++ /dev/null @@ -1,62 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; -use clippy_utils::std_or_core; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::LateContext; - -use super::PTR_EQ; - -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: BinOpKind, - left: &'tcx Expr<'_>, - right: &'tcx Expr<'_>, -) { - if BinOpKind::Eq == op { - let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { - (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (left, right), - }; - - if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left) - && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right) - && let Some(left_snip) = left_var.span.get_source_text(cx) - && let Some(right_snip) = right_var.span.get_source_text(cx) - { - let Some(top_crate) = std_or_core(cx) else { return }; - span_lint_and_sugg( - cx, - PTR_EQ, - expr.span, - format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), - "try", - format!("{top_crate}::ptr::eq({left_snip}, {right_snip})"), - Applicability::MachineApplicable, - ); - } - } -} - -// If the given expression is a cast to a usize, return the lhs of the cast -// E.g., `foo as *const _ as usize` returns `foo as *const _`. -fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { - if let ExprKind::Cast(expr, _) = cast_expr.kind { - return Some(expr); - } - } - None -} - -// If the given expression is a cast to a `*const` pointer, return the lhs of the cast -// E.g., `foo as *const _` returns `foo`. -fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if cx.typeck_results().expr_ty(cast_expr).is_raw_ptr() { - if let ExprKind::Cast(expr, _) = cast_expr.kind { - return Some(expr); - } - } - None -} diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 75b18bc651e2a..6f302ea196217 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,16 +1,23 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, }; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::Res; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_path}; use rustc_hir::{ - Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp, + Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatExpr, PatExprKind, PatKind, Path, + QPath, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; use rustc_session::declare_lint_pass; use rustc_span::SyntaxContext; @@ -110,11 +117,12 @@ fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> Str ) } +#[expect(clippy::too_many_lines)] fn try_get_option_occurrence<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, pat: &Pat<'tcx>, - expr: &Expr<'_>, + expr: &'tcx Expr<'_>, if_then: &'tcx Expr<'_>, if_else: &'tcx Expr<'_>, ) -> Option { @@ -182,6 +190,26 @@ fn try_get_option_occurrence<'tcx>( Some(CaptureKind::Ref(Mutability::Not)) | None => (), } } + } else if !is_copy(cx, cx.typeck_results().expr_ty(expr)) + // TODO: Cover more match cases + && matches!( + expr.kind, + ExprKind::Field(_, _) | ExprKind::Path(_) | ExprKind::Index(_, _, _) + ) + { + let mut condition_visitor = ConditionVisitor { + cx, + identifiers: FxHashSet::default(), + }; + condition_visitor.visit_expr(cond_expr); + + let mut reference_visitor = ReferenceVisitor { + cx, + identifiers: condition_visitor.identifiers, + }; + if reference_visitor.visit_expr(none_body).is_break() { + return None; + } } let mut app = Applicability::Unspecified; @@ -219,6 +247,60 @@ fn try_get_option_occurrence<'tcx>( None } +/// This visitor looks for bindings in the block that mention a local variable. Then gets the +/// identifiers. The list of identifiers will then be used to check if the block mentions the +/// same local. See [`ReferenceVisitor`] for more. +struct ConditionVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for ConditionVisitor<'_, 'tcx> { + type NestedFilter = nested_filter::All; + + fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) { + if let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + { + self.identifiers.insert(local_id); + } + walk_path(self, path); + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} + +/// This visitor checks if the block contains references to the local variables that are +/// used in the block. See [`ConditionVisitor`] for more. +struct ReferenceVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { + type NestedFilter = nested_filter::All; + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> ControlFlow<()> { + if let ExprKind::Path(ref path) = expr.kind + && let QPath::Resolved(_, path) = path + && let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + && self.identifiers.contains(&local_id) + { + return ControlFlow::Break(()); + } + walk_expr(self, expr) + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} + fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 55676522419c6..65671b478ba74 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -19,8 +19,8 @@ declare_clippy_lint! { /// struct Foo; /// /// impl PartialEq for Foo { - /// fn eq(&self, other: &Foo) -> bool { true } - /// fn ne(&self, other: &Foo) -> bool { !(self == other) } + /// fn eq(&self, other: &Foo) -> bool { true } + /// fn ne(&self, other: &Foo) -> bool { !(self == other) } /// } /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index dae0709a5404a..55f1ece05593f 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -148,7 +148,36 @@ declare_clippy_lint! { "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); +declare_clippy_lint! { + /// ### What it does + /// Use `std::ptr::eq` when applicable + /// + /// ### Why is this bad? + /// `ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. + /// + /// ### Example + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` + /// Use instead: + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); + /// ``` + #[clippy::version = "1.49.0"] + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" +} + +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE, PTR_EQ]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { @@ -253,10 +282,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { if let ExprKind::Binary(op, l, r) = expr.kind && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) { - let non_null_path_snippet = match (is_null_path(cx, l), is_null_path(cx, r)) { - (true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), - (false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), - _ => return, + let non_null_path_snippet = match ( + is_lint_allowed(cx, CMP_NULL, expr.hir_id), + is_null_path(cx, l), + is_null_path(cx, r), + ) { + (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), + (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), + _ => return check_ptr_eq(cx, expr, op.node, l, r), }; span_lint_and_sugg( @@ -740,3 +773,71 @@ fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } } + +fn check_ptr_eq<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: BinOpKind, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, +) { + if expr.span.from_expansion() { + return; + } + + // Remove one level of usize conversion if any + let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs), + _ => (left, right), + }; + + // This lint concerns raw pointers + let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right)); + if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() { + return; + } + + let (left_var, right_var) = (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + + if let Some(left_snip) = left_var.span.get_source_text(cx) + && let Some(right_snip) = right_var.span.get_source_text(cx) + { + let Some(top_crate) = std_or_core(cx) else { return }; + let invert = if op == BinOpKind::Eq { "" } else { "!" }; + span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), + "try", + format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"), + Applicability::MachineApplicable, + ); + } +} + +// If the given expression is a cast to a usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize + && let ExprKind::Cast(expr, _) = cast_expr.kind + { + Some(expr) + } else { + None + } +} + +// Peel raw casts if the remaining expression can be coerced to it +fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> &'tcx Expr<'tcx> { + if let ExprKind::Cast(inner, _) = expr.kind + && let ty::RawPtr(target_ty, _) = expr_ty.kind() + && let inner_ty = cx.typeck_results().expr_ty(inner) + && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() + && target_ty == inner_target_ty + { + peel_raw_casts(cx, inner, inner_ty) + } else { + expr + } +} diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4f5f3eb6c15a0..a80e1f79bbc77 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -3,7 +3,7 @@ use crate::question_mark_used::QUESTION_MARK_USED; use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::msrvs::Msrv; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ @@ -145,8 +145,47 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { { let mut applicability = Applicability::MaybeIncorrect; let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability); - let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability); - let sugg = format!("let {receiver_str} = {init_expr_str}?;",); + // Take care when binding is `ref` + let sugg = if let PatKind::Binding( + BindingMode(ByRef::Yes(ref_mutability), binding_mutability), + _hir_id, + ident, + subpattern, + ) = inner_pat.kind + { + let (from_method, replace_to) = match ref_mutability { + Mutability::Mut => (".as_mut()", "&mut "), + Mutability::Not => (".as_ref()", "&"), + }; + + let mutability_str = match binding_mutability { + Mutability::Mut => "mut ", + Mutability::Not => "", + }; + + // Handle subpattern (@ subpattern) + let maybe_subpattern = match subpattern { + Some(Pat { + kind: PatKind::Binding(BindingMode(ByRef::Yes(_), _), _, subident, None), + .. + }) => { + // avoid `&ref` + // note that, because you can't have aliased, mutable references, we don't have to worry about + // the outer and inner mutability being different + format!(" @ {subident}") + }, + Some(subpattern) => { + let substr = snippet_with_applicability(cx, subpattern.span, "..", &mut applicability); + format!(" @ {replace_to}{substr}") + }, + None => String::new(), + }; + + format!("let {mutability_str}{ident}{maybe_subpattern} = {init_expr_str}{from_method}?;") + } else { + let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability); + format!("let {receiver_str} = {init_expr_str}?;") + }; span_lint_and_sugg( cx, QUESTION_MARK, @@ -230,7 +269,7 @@ fn expr_return_none_or_err( /// /// ```ignore /// if option.is_none() { -/// return None; +/// return None; /// } /// ``` /// @@ -485,7 +524,8 @@ fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) { + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) || !self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) + { return; } @@ -501,7 +541,10 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + if !self.inside_try_block() + && !is_in_const_context(cx) + && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + && self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) { check_is_none_or_err_and_early_return(cx, expr); check_if_let_some_or_err_and_early_return(cx, expr); diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs index bc5e8fd2c2584..8289ec47bc7e1 100644 --- a/clippy_lints/src/redundant_async_block.rs +++ b/clippy_lints/src/redundant_async_block.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let f = async { - /// 1 + 2 + /// 1 + 2 /// }; /// let fut = async { /// f.await @@ -32,7 +32,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// let f = async { - /// 1 + 2 + /// 1 + 2 /// }; /// let fut = f; /// ``` diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index defb6684cffbe..8f33a47e29089 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// let a = a; /// /// fn foo(b: i32) { - /// let b = b; + /// let b = b; /// } /// ``` /// Use instead: diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 3a5f44db8720a..f2fdac5a8afaf 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -55,7 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { // FIXME: `DUMMY_SP` isn't right here, because it causes the // resulting span to begin at the start of the file. let span = item.span.with_hi( - item.kind.ident().map(|ident| ident.span.hi()).unwrap_or(rustc_span::DUMMY_SP.hi()) + item.kind + .ident() + .map_or(rustc_span::DUMMY_SP.hi(), |ident| ident.span.hi()), ); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); span_lint_and_then( diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index dc19236011bd4..835ec1e4ca1c7 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -18,7 +18,6 @@ declare_clippy_lint! { /// ### Example /// ```rust,no_run /// # use std::ptr::copy_nonoverlapping; - /// # use std::mem::size_of; /// const SIZE: usize = 128; /// let x = [2u8; SIZE]; /// let mut y = [2u8; SIZE]; diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index b3d32a6d7d84c..60d923bcd77e7 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -8,7 +8,7 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does /// - /// Checks for calls to `std::mem::size_of_val()` where the argument is + /// Checks for calls to `size_of_val()` where the argument is /// a reference to a reference. /// /// ### Why is this bad? @@ -29,7 +29,7 @@ declare_clippy_lint! { /// // is already a reference, `&self` is a double-reference. /// // The return value of `size_of_val()` therefore is the /// // size of the reference-type, not the size of `self`. - /// std::mem::size_of_val(&self) + /// size_of_val(&self) /// } /// } /// ``` @@ -42,14 +42,14 @@ declare_clippy_lint! { /// impl Foo { /// fn size(&self) -> usize { /// // Correct - /// std::mem::size_of_val(self) + /// size_of_val(self) /// } /// } /// ``` #[clippy::version = "1.68.0"] pub SIZE_OF_REF, suspicious, - "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" + "Argument to `size_of_val()` is a double-reference, which is almost certainly unintended" } declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); @@ -65,9 +65,9 @@ impl LateLintPass<'_> for SizeOfRef { cx, SIZE_OF_REF, expr.span, - "argument to `std::mem::size_of_val()` is a reference to a reference", + "argument to `size_of_val()` is a reference to a reference", None, - "dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type", + "dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type", ); } } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 4a5f143a2d344..27c548bed9f64 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -14,6 +14,8 @@ use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::sym; +use std::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Checks for string appends of the form `x = x + y` (without @@ -438,27 +440,94 @@ declare_clippy_lint! { declare_lint_pass!(StringToString => [STRING_TO_STRING]); +fn is_parent_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(name, _, _, parent_span) = parent_expr.kind + && name.ident.name == sym::map + && let Some(caller_def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) + && (clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Result) + || clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Option) + || clippy_utils::is_diag_trait_item(cx, caller_def_id, sym::Iterator)) + { + Some(parent_span) + } else { + None + } +} + +fn is_called_from_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // Look for a closure as parent of `expr`, discarding simple blocks + let parent_closure = cx + .tcx + .hir_parent_iter(expr.hir_id) + .try_fold(expr.hir_id, |child_hir_id, (_, node)| match node { + // Check that the child expression is the only expression in the block + Node::Block(block) if block.stmts.is_empty() && block.expr.map(|e| e.hir_id) == Some(child_hir_id) => { + ControlFlow::Continue(block.hir_id) + }, + Node::Expr(expr) if matches!(expr.kind, ExprKind::Block(..)) => ControlFlow::Continue(expr.hir_id), + Node::Expr(expr) if matches!(expr.kind, ExprKind::Closure(_)) => ControlFlow::Break(Some(expr)), + _ => ControlFlow::Break(None), + }) + .break_value()?; + is_parent_map_like(cx, parent_closure?) +} + +fn suggest_cloned_string_to_string(cx: &LateContext<'_>, span: rustc_span::Span) { + span_lint_and_sugg( + cx, + STRING_TO_STRING, + span, + "`to_string()` called on a `String`", + "try", + "cloned()".to_string(), + Applicability::MachineApplicable, + ); +} + impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if expr.span.from_expansion() { return; } - if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind - && path.ident.name == sym::to_string - && let ty = cx.typeck_results().expr_ty(self_arg) - && is_type_lang_item(cx, ty, LangItem::String) - { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - STRING_TO_STRING, - expr.span, - "`to_string()` called on a `String`", - |diag| { - diag.help("consider using `.clone()`"); - }, - ); + match &expr.kind { + ExprKind::MethodCall(path, self_arg, [], _) => { + if path.ident.name == sym::to_string + && let ty = cx.typeck_results().expr_ty(self_arg) + && is_type_lang_item(cx, ty.peel_refs(), LangItem::String) + { + if let Some(parent_span) = is_called_from_map_like(cx, expr) { + suggest_cloned_string_to_string(cx, parent_span); + } else { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + |diag| { + diag.help("consider using `.clone()`"); + }, + ); + } + } + }, + ExprKind::Path(QPath::TypeRelative(ty, segment)) => { + if segment.ident.name == sym::to_string + && let rustc_hir::TyKind::Path(QPath::Resolved(_, path)) = ty.peel_refs().kind + && let rustc_hir::def::Res::Def(_, def_id) = path.res + && cx + .tcx + .lang_items() + .get(LangItem::String) + .is_some_and(|lang_id| lang_id == def_id) + && let Some(parent_span) = is_parent_map_like(cx, expr) + { + suggest_cloned_string_to_string(cx, parent_span); + } + }, + _ => {}, } } } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 2e974374c99e7..b6f4c4d7f0a41 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -61,10 +61,6 @@ declare_clippy_lint! { /// `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. /// - /// ### Known problems - /// Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), - /// 1st comment). - /// /// ### Example /// ```no_run /// struct X { diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 4f1a017522e40..a2938c86c76a9 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -312,6 +312,25 @@ fn expr_has_unnecessary_safety_comment<'tcx>( }, _, ) => ControlFlow::Break(()), + // `_ = foo()` is desugared to `{ let _ = foo(); }` + hir::ExprKind::Block( + Block { + rules: BlockCheckMode::DefaultBlock, + stmts: + [ + hir::Stmt { + kind: + hir::StmtKind::Let(hir::LetStmt { + source: hir::LocalSource::AssignDesugar(_), + .. + }), + .. + }, + ], + .. + }, + _, + ) => ControlFlow::Continue(Descend::Yes), // statements will be handled by check_stmt itself again hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No), _ => ControlFlow::Continue(Descend::Yes), @@ -339,6 +358,33 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { .is_none_or(|src| !src.starts_with("unsafe")) } +fn find_unsafe_block_parent_in_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, +) -> Option<(Span, HirId)> { + match cx.tcx.parent_hir_node(expr.hir_id) { + Node::LetStmt(hir::LetStmt { span, hir_id, .. }) + | Node::Expr(hir::Expr { + hir_id, + kind: hir::ExprKind::Assign(_, _, span), + .. + }) => Some((*span, *hir_id)), + Node::Expr(expr) => find_unsafe_block_parent_in_expr(cx, expr), + node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + && is_const_or_static(&node) => + { + Some((span, hir_id)) + }, + + _ => { + if is_branchy(expr) { + return None; + } + Some((expr.span, expr.hir_id)) + }, + } +} + // Checks if any parent {expression, statement, block, local, const, static} // has a safety comment fn block_parents_have_safety_comment( @@ -348,21 +394,7 @@ fn block_parents_have_safety_comment( id: HirId, ) -> bool { let (span, hir_id) = match cx.tcx.parent_hir_node(id) { - Node::Expr(expr) => match cx.tcx.parent_hir_node(expr.hir_id) { - Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - span, - owner_id, - .. - }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), - _ => { - if is_branchy(expr) { - return false; - } - (expr.span, expr.hir_id) - }, - }, + Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner, Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) @@ -371,12 +403,13 @@ fn block_parents_have_safety_comment( .. }) | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - span, - owner_id, - .. - }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), + + node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + && is_const_or_static(&node) => + { + (span, hir_id) + }, + _ => return false, }; // if unsafe block is part of a let/const/static statement, @@ -427,11 +460,12 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { - span.to(cx - .tcx - .hir_attrs(hir_id) - .iter() - .fold(span, |acc, attr| acc.to(attr.span()))) + span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { + if attr.is_doc_comment() { + return acc; + } + acc.to(attr.span()) + })) } enum HasSafetyComment { @@ -603,31 +637,35 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span fn get_body_search_span(cx: &LateContext<'_>) -> Option { let body = cx.enclosing_body?; - let mut span = cx.tcx.hir_body(body).value.span; - let mut maybe_global_var = false; - for (_, node) in cx.tcx.hir_parent_iter(body.hir_id) { - match node { - Node::Expr(e) => span = e.span, - Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - .. - }) => maybe_global_var = true, + let mut maybe_mod_item = None; + + for (_, parent_node) in cx.tcx.hir_parent_iter(body.hir_id) { + match parent_node { + Node::Crate(mod_) => return Some(mod_.spans.inner_span), Node::Item(hir::Item { - kind: ItemKind::Mod(..), - span: item_span, + kind: ItemKind::Mod(_, mod_), + span, .. }) => { - span = *item_span; - break; + return maybe_mod_item + .and_then(|item| comment_start_before_item_in_mod(cx, mod_, *span, &item)) + .map(|comment_start| mod_.spans.inner_span.with_lo(comment_start)) + .or(Some(*span)); }, - Node::Crate(mod_) if maybe_global_var => { - span = mod_.spans.inner_span; + node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node) + && !is_const_or_static(&node) => + { + return Some(span); + }, + Node::Item(item) => { + maybe_mod_item = Some(*item); + }, + _ => { + maybe_mod_item = None; }, - _ => break, } } - Some(span) + None } fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { @@ -716,3 +754,28 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos } } } + +fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> { + match node { + Node::Item(item) => Some((item.span, item.owner_id.into())), + Node::TraitItem(ti) => Some((ti.span, ti.owner_id.into())), + Node::ImplItem(ii) => Some((ii.span, ii.owner_id.into())), + _ => None, + } +} + +fn is_const_or_static(node: &Node<'_>) -> bool { + matches!( + node, + Node::Item(hir::Item { + kind: ItemKind::Const(..) | ItemKind::Static(..), + .. + }) | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const(..), + .. + }) | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..), + .. + }) + ) +} diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs index e5267620c4fb9..f1d1a76d0c2df 100644 --- a/clippy_lints/src/unnecessary_semicolon.rs +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// ```no_run /// # let a: u32 = 42; /// if a > 10 { - /// println!("a is greater than 10"); + /// println!("a is greater than 10"); /// } /// ``` #[clippy::version = "1.86.0"] diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5e452c6d2ac09..57bb2fc27f145 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, }; @@ -13,7 +13,7 @@ use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -412,24 +412,14 @@ pub fn check_function_application(cx: &LateContext<'_>, expr: &Expr<'_>, recv: & } fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { - let recv_ty = cx.typeck_results().expr_ty(recv); - if is_inherent_method_call(cx, expr) - && let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) - { - if let Some(diag_name) = cx.tcx.get_diagnostic_name(recv_ty_defid) - && matches!(diag_name, sym::Option | sym::Result) - { - return true; - } - - if cx.tcx.is_diagnostic_item(sym::ControlFlow, recv_ty_defid) { - return true; - } - } - if is_trait_method(cx, expr, sym::Iterator) { - return true; + if is_inherent_method_call(cx, expr) { + matches!( + get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)), + Some(sym::Option | sym::Result | sym::ControlFlow) + ) + } else { + is_trait_method(cx, expr, sym::Iterator) } - false } fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String { diff --git a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs index bfcce81c498a8..0a01a364a75b9 100644 --- a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs @@ -44,11 +44,10 @@ impl AlmostStandardFormulation { impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let mut check_next = false; - if let ItemKind::Static(ty, Mutability::Not, _) = item.kind { + if let ItemKind::Static(_, ty, Mutability::Not, _) = item.kind { let lines = cx .tcx - .hir() - .attrs(item.hir_id()) + .hir_attrs(item.hir_id()) .iter() .filter_map(|attr| Attribute::doc_str(attr).map(|sym| (sym, attr))); if is_lint_ref_type(cx, ty) { diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 16d51fa09025a..94a2e598522b2 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { return; } - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + if let hir::ItemKind::Static(ident, ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { check_invalid_clippy_version_attribute(cx, item); @@ -133,10 +133,10 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, DEFAULT_LINT, item.span, - format!("the lint `{}` has the default lint description", item.ident.name), + format!("the lint `{}` has the default lint description", ident.name), ); } - self.declared_lints.insert(item.ident.name, item.span); + self.declared_lints.insert(ident.name, item.span); } } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints/src/utils/internal_lints/produce_ice.rs index 9169e2968eb7d..0a07919d659fe 100644 --- a/clippy_lints/src/utils/internal_lints/produce_ice.rs +++ b/clippy_lints/src/utils/internal_lints/produce_ice.rs @@ -1,6 +1,6 @@ use rustc_ast::ast::NodeId; use rustc_ast::visit::FnKind; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -24,8 +24,12 @@ declare_clippy_lint! { declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); + fn check_fn(&mut self, ctx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + if is_trigger_fn(fn_kind) { + ctx.sess() + .dcx() + .span_delayed_bug(span, "Would you like some help with that?"); + } } } diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 5dd31b52f8800..7c665b4249776 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-02-27 +nightly-2025-03-20 ``` @@ -30,7 +30,7 @@ Function signatures can change or be removed without replacement without any pri -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 <[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 707312a97f3bc..edebee289e1c7 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -688,7 +688,7 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool { pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { use WherePredicateKind::*; - over(&l.attrs, &r.attrs, eq_attr) + over(&l.attrs, &r.attrs, eq_attr) && match (&l.kind, &r.kind) { (BoundPredicate(l), BoundPredicate(r)) => { over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index ddb7a6635e063..292792408c642 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -85,7 +85,7 @@ fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir`] instead. /// @@ -128,7 +128,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// @@ -241,7 +241,7 @@ pub fn span_lint_and_note( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: M, f: F) @@ -358,7 +358,7 @@ pub fn span_lint_hir_and_then( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index eb4e1a7722f3c..1307ff79bc5dd 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -106,10 +106,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, - ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, - PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, - TraitItemRef, TraitRef, TyKind, UnOp, def, + Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, + ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, + Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, + TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -434,7 +434,7 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator Some(ty.as_unambig_ty()), + GenericArg::Type(ty) => Some(ty.as_unambig_ty()), _ => None, }) } @@ -1420,8 +1420,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id; match cx.tcx.hir_node_by_def_id(parent_id) { Node::Item(item) => item.kind.ident().map(|ident| ident.name), - Node::TraitItem(TraitItem { ident, .. }) - | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name), + Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name), _ => None, } } @@ -2334,6 +2333,18 @@ pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..)) } +/// Checks if the expression is a temporary value. +// This logic is the same as the one used in rustc's `check_named_place_expr function`. +// https://github.com/rust-lang/rust/blob/3ed2a10d173d6c2e0232776af338ca7d080b1cd4/compiler/rustc_hir_typeck/src/expr.rs#L482-L499 +pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + !expr.is_place_expr(|base| { + cx.typeck_results() + .adjustments() + .get(base.hir_id) + .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) + }) +} + pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { if !is_no_std_crate(cx) { Some("std") @@ -3548,7 +3559,7 @@ pub fn is_block_like(expr: &Expr<'_>) -> bool { pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool { fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool { match expr.kind { - ExprKind::Binary(_, lhs, _) => contains_block(lhs, true), + ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true), _ if is_block_like(expr) => is_operand, _ => false, } @@ -3695,3 +3706,21 @@ pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { true } } + +/// Peel `Option<…>` from `hir_ty` as long as the HIR name is `Option` and it corresponds to the +/// `core::Option<_>` type. +pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else { + return hir_ty; + }; + while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind + && let Some(segment) = path.segments.last() + && segment.ident.name == sym::Option + && let Res::Def(DefKind::Enum, def_id) = segment.res + && def_id == option_def_id + && let [GenericArg::Type(arg_ty)] = segment.args().args + { + hir_ty = arg_ty.as_unambig_ty(); + } + hir_ty +} diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index db07b6404169e..152b4272c26ca 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -6,8 +6,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{self, Mutability}; -use rustc_middle::ty::TypeVisitor; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitor}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::{Analysis, ResultsCursor}; use std::borrow::Cow; diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 0316de172de7f..86f4f190b950a 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -33,7 +33,7 @@ msrv_aliases! { 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,75,0 { OPTION_AS_SLICE } 1,74,0 { REPR_RUST, IO_ERROR_OTHER } - 1,73,0 { MANUAL_DIV_CEIL } + 1,73,0 { DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } @@ -74,6 +74,7 @@ msrv_aliases! { 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } 1,15,0 { MAYBE_BOUND_IN_WHERE } + 1,13,0 { QUESTION_MARK_OPERATOR } } /// `#[clippy::msrv]` attributes are rarely used outside of Clippy's test suite, as a basic diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 8e6f4d4a317eb..5d0401010db6a 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -395,24 +395,32 @@ fn check_terminator<'tcx>( fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { cx.tcx.is_const_fn(def_id) - && cx.tcx.lookup_const_stability(def_id).is_none_or(|const_stab| { - if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level { - // Checking MSRV is manually necessary because `rustc` has no such concept. This entire - // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. - // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. + && cx + .tcx + .lookup_const_stability(def_id) + .or_else(|| { + cx.tcx + .trait_of_item(def_id) + .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) + }) + .is_none_or(|const_stab| { + if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level { + // Checking MSRV is manually necessary because `rustc` has no such concept. This entire + // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. + // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. - let const_stab_rust_version = match since { - StableSince::Version(version) => version, - StableSince::Current => RustcVersion::CURRENT, - StableSince::Err => return false, - }; + let const_stab_rust_version = match since { + StableSince::Version(version) => version, + StableSince::Current => RustcVersion::CURRENT, + StableSince::Err => return false, + }; - msrv.meets(cx, const_stab_rust_version) - } else { - // Unstable const fn, check if the feature is enabled. - cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() - } - }) + msrv.meets(cx, const_stab_rust_version) + } else { + // Unstable const fn, check if the feature is enabled. + cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() + } + }) } fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 9cc66593dcc3f..68a1de96a3515 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -4,8 +4,8 @@ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; -use rustc_ast::util::parser::AssocOp; use rustc_ast::ast; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; @@ -444,7 +444,7 @@ impl<'a> Not for Sugg<'a> { type Output = Sugg<'a>; fn not(self) -> Sugg<'a> { use AssocOp::Binary; - use ast::BinOpKind::{Eq, Gt, Ge, Lt, Le, Ne}; + use ast::BinOpKind::{Eq, Ge, Gt, Le, Lt, Ne}; if let Sugg::BinOp(op, lhs, rhs) = self { let to_op = match op { @@ -515,10 +515,10 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> op, AssocOp::Binary( ast::BinOpKind::Add - | ast::BinOpKind::Sub - | ast::BinOpKind::Mul - | ast::BinOpKind::Div - | ast::BinOpKind::Rem + | ast::BinOpKind::Sub + | ast::BinOpKind::Mul + | ast::BinOpKind::Div + | ast::BinOpKind::Rem ) ) } @@ -578,10 +578,8 @@ enum Associativity { /// associative. #[must_use] fn associativity(op: AssocOp) -> Associativity { + use ast::BinOpKind::{Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub}; use rustc_ast::util::parser::AssocOp::{Assign, AssignOp, Binary, Cast, Range}; - use ast::BinOpKind::{ - Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub, - }; match op { Assign | AssignOp(_) => Associativity::Right, @@ -994,6 +992,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { mod test { use super::Sugg; + use rustc_ast as ast; use rustc_ast::util::parser::AssocOp; use std::borrow::Cow; @@ -1011,15 +1010,15 @@ mod test { #[test] fn binop_maybe_par() { - let sugg = Sugg::BinOp(AssocOp::Add, "1".into(), "1".into()); + let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "1".into(), "1".into()); assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); - let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into(), "(1 + 1)".into()); + let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into()); assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); } #[test] fn not_op() { - use AssocOp::{Add, Equal, Greater, GreaterEqual, LAnd, LOr, Less, LessEqual, NotEqual}; + use ast::BinOpKind::{Add, And, Eq, Ge, Gt, Le, Lt, Ne, Or}; fn test_not(op: AssocOp, correct: &str) { let sugg = Sugg::BinOp(op, "x".into(), "y".into()); @@ -1027,16 +1026,16 @@ mod test { } // Invert the comparison operator. - test_not(Equal, "x != y"); - test_not(NotEqual, "x == y"); - test_not(Less, "x >= y"); - test_not(LessEqual, "x > y"); - test_not(Greater, "x <= y"); - test_not(GreaterEqual, "x < y"); + test_not(AssocOp::Binary(Eq), "x != y"); + test_not(AssocOp::Binary(Ne), "x == y"); + test_not(AssocOp::Binary(Lt), "x >= y"); + test_not(AssocOp::Binary(Le), "x > y"); + test_not(AssocOp::Binary(Gt), "x <= y"); + test_not(AssocOp::Binary(Ge), "x < y"); // Other operators are inverted like !(..). - test_not(Add, "!(x + y)"); - test_not(LAnd, "!(x && y)"); - test_not(LOr, "!(x || y)"); + test_not(AssocOp::Binary(Add), "!(x + y)"); + test_not(AssocOp::Binary(And), "!(x && y)"); + test_not(AssocOp::Binary(Or), "!(x || y)"); } } diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs index 3a68f2c924356..8ea0a41ed368a 100644 --- a/lintcheck/src/json.rs +++ b/lintcheck/src/json.rs @@ -1,3 +1,9 @@ +//! JSON output and comparison functionality for Clippy warnings. +//! +//! This module handles serialization of Clippy warnings to JSON format, +//! loading warnings from JSON files, and generating human-readable diffs +//! between different linting runs. + use std::fs; use std::path::Path; @@ -8,8 +14,10 @@ use crate::ClippyWarning; /// This is the total number. 300 warnings results in 100 messages per section. const DEFAULT_LIMIT_PER_LINT: usize = 300; +/// Target for total warnings to display across all lints when truncating output. const TRUNCATION_TOTAL_TARGET: usize = 1000; +/// Representation of a single Clippy warning for JSON serialization. #[derive(Debug, Deserialize, Serialize)] struct LintJson { /// The lint name e.g. `clippy::bytes_nth` @@ -21,10 +29,12 @@ struct LintJson { } impl LintJson { + /// Returns a tuple of name and `file_line` for sorting and comparison. fn key(&self) -> impl Ord + '_ { (self.name.as_str(), self.file_line.as_str()) } + /// Formats the warning information with an action verb for display. fn info_text(&self, action: &str) -> String { format!("{action} `{}` at [`{}`]({})", self.name, self.file_line, self.file_url) } @@ -53,12 +63,17 @@ pub(crate) fn output(clippy_warnings: Vec) -> String { serde_json::to_string(&lints).unwrap() } +/// Loads lint warnings from a JSON file at the given path. fn load_warnings(path: &Path) -> Vec { let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display())); serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display())) } +/// Generates and prints a diff between two sets of lint warnings. +/// +/// Compares warnings from `old_path` and `new_path`, then displays a summary table +/// and detailed information about added, removed, and changed warnings. pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { let old_warnings = load_warnings(old_path); let new_warnings = load_warnings(new_path); @@ -116,6 +131,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { } } +/// Container for grouped lint warnings organized by status (added/removed/changed). #[derive(Debug)] struct LintWarnings { name: String, @@ -124,6 +140,7 @@ struct LintWarnings { changed: Vec<(LintJson, LintJson)>, } +/// Prints a formatted report for a single lint type with its warnings. fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { let name = &lint.name; let html_id = to_html_id(name); @@ -145,6 +162,7 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { print_changed_diff(&lint.changed, truncate_after / 3); } +/// Prints a summary table of all lints with counts of added, removed, and changed warnings. fn print_summary_table(lints: &[LintWarnings]) { println!("| Lint | Added | Removed | Changed |"); println!("| ------------------------------------------ | ------: | ------: | ------: |"); @@ -160,6 +178,7 @@ fn print_summary_table(lints: &[LintWarnings]) { } } +/// Prints a section of warnings with a header and formatted code blocks. fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { if warnings.is_empty() { return; @@ -180,6 +199,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { } } +/// Prints a section of changed warnings with unified diff format. fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { if changed.is_empty() { return; @@ -213,6 +233,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { } } +/// Truncates a list to a maximum number of items and prints a message about truncation. fn truncate(list: &[T], truncate_after: usize) -> &[T] { if list.len() > truncate_after { println!( @@ -227,6 +248,7 @@ fn truncate(list: &[T], truncate_after: usize) -> &[T] { } } +/// Prints a level 3 heading with an appropriate HTML ID for linking. fn print_h3(lint: &str, title: &str) { let html_id = to_html_id(lint); // We have to use HTML here to be able to manually add an id. diff --git a/rust-toolchain b/rust-toolchain index a4931499c8028..fcaeedc9a66b5 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-02-27" +channel = "nightly-2025-03-20" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index ff4ca6f830e6e..f47a4c69c2c32 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under: -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/tests/compile-test.rs b/tests/compile-test.rs index f44cf7a7c25a0..956a05288f358 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -16,7 +16,7 @@ use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::{Args, CommandBuilder, Config, Match, OutputConflictHandling, status_emitter}; +use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; @@ -142,7 +142,7 @@ impl TestContext { fn base_config(&self, test_dir: &str, mandatory_annotations: bool) -> Config { let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())); let mut config = Config { - output_conflict_handling: OutputConflictHandling::Error, + output_conflict_handling: error_on_output_conflict, filter_files: env::var("TESTNAME") .map(|filters| filters.split(',').map(str::to_string).collect()) .unwrap_or_default(), @@ -220,7 +220,7 @@ fn run_internal_tests(cx: &TestContext) { if !RUN_INTERNAL_TESTS { return; } - let mut config = cx.base_config("ui-internal", false); + let mut config = cx.base_config("ui-internal", true); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 7ed1f485c1cfd..9229e2e8c496b 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -44,6 +44,7 @@ impl Message { ".*AT&T x86 assembly syntax used", "note: Clippy version: .*", "the compiler unexpectedly panicked. this is a bug.", + "internal compiler error:", ]) .unwrap() }); diff --git a/tests/ui-internal/check_clippy_version_attribute.rs b/tests/ui-internal/check_clippy_version_attribute.rs index 31acac89cc635..e5f6001b74d09 100644 --- a/tests/ui-internal/check_clippy_version_attribute.rs +++ b/tests/ui-internal/check_clippy_version_attribute.rs @@ -38,6 +38,7 @@ declare_tool_lint! { // Invalid attributes /////////////////////// declare_tool_lint! { +//~^ invalid_clippy_version_attribute #[clippy::version = "1.2.3.4.5.6"] pub clippy::INVALID_ONE, Warn, @@ -46,6 +47,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ invalid_clippy_version_attribute #[clippy::version = "I'm a string"] pub clippy::INVALID_TWO, Warn, @@ -57,6 +59,7 @@ declare_tool_lint! { // Missing attribute test /////////////////////// declare_tool_lint! { +//~^ missing_clippy_version_attribute #[clippy::version] pub clippy::MISSING_ATTRIBUTE_ONE, Warn, @@ -65,6 +68,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ missing_clippy_version_attribute pub clippy::MISSING_ATTRIBUTE_TWO, Warn, "Two", diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 631c292f5249e..1129c35d1d01b 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -2,10 +2,10 @@ error: this item has an invalid `clippy::version` attribute --> tests/ui-internal/check_clippy_version_attribute.rs:40:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version = "1.2.3.4.5.6"] LL | | pub clippy::INVALID_ONE, -LL | | Warn, -LL | | "One", +... | LL | | report_in_external_macro: true LL | | } | |_^ @@ -20,13 +20,13 @@ LL | #![deny(clippy::internal)] = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute - --> tests/ui-internal/check_clippy_version_attribute.rs:48:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:49:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version = "I'm a string"] LL | | pub clippy::INVALID_TWO, -LL | | Warn, -LL | | "Two", +... | LL | | report_in_external_macro: true LL | | } | |_^ @@ -35,13 +35,13 @@ LL | | } = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value - --> tests/ui-internal/check_clippy_version_attribute.rs:59:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:61:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version] LL | | pub clippy::MISSING_ATTRIBUTE_ONE, -LL | | Warn, -LL | | "Two", +... | LL | | report_in_external_macro: true LL | | } | |_^ @@ -51,9 +51,10 @@ LL | | } = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value - --> tests/ui-internal/check_clippy_version_attribute.rs:67:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:70:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::MISSING_ATTRIBUTE_TWO, LL | | Warn, LL | | "Two", diff --git a/tests/ui-internal/check_formulation.rs b/tests/ui-internal/check_formulation.rs index 43fc996033eac..8265a78769d16 100644 --- a/tests/ui-internal/check_formulation.rs +++ b/tests/ui-internal/check_formulation.rs @@ -21,6 +21,7 @@ declare_tool_lint! { declare_tool_lint! { /// # What it does /// Check for lint formulations that are correct + //~^ almost_standard_lint_formulation #[clippy::version = "pre 1.29.0"] pub clippy::INVALID1, Warn, @@ -31,6 +32,7 @@ declare_tool_lint! { declare_tool_lint! { /// # What it does /// Detects uses of incorrect formulations + //~^ almost_standard_lint_formulation #[clippy::version = "pre 1.29.0"] pub clippy::INVALID2, Warn, diff --git a/tests/ui-internal/check_formulation.stderr b/tests/ui-internal/check_formulation.stderr index 12514370e6ded..b16e1bf868737 100644 --- a/tests/ui-internal/check_formulation.stderr +++ b/tests/ui-internal/check_formulation.stderr @@ -9,7 +9,7 @@ LL | /// Check for lint formulations that are correct = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:33:5 + --> tests/ui-internal/check_formulation.rs:34:5 | LL | /// Detects uses of incorrect formulations | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs index 1baf6142b3499..2f289ae2b4819 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -33,18 +33,23 @@ impl EarlyLintPass for Pass { let predicate = true; span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_help(expr.span, help_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.help(help_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_note(expr.span, note_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.note(note_msg); }); diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 104995918de2e..a2be1f1cd367d 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -2,6 +2,7 @@ error: this call is collapsible --> tests/ui-internal/collapsible_span_lint_calls.rs:35:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); LL | | }); | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` @@ -14,33 +15,37 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:38:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:39:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_help(expr.span, help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:41:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:43:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.help(help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:44:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:47:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_note(expr.span, note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:47:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:51:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.note(note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 9b0db660c9975..71819fe370701 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -10,5 +10,6 @@ #![allow(clippy::missing_clippy_version_attribute)] fn it_looks_like_you_are_trying_to_kill_clippy() {} +//~^ ice: Would you like some help with that? fn main() {} diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index 801b0f340de93..589e1190a907e 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,9 +1,18 @@ - -thread '' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: -Would you like some help with that? -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace - -error: the compiler unexpectedly panicked. this is a bug. +note: no errors encountered even though delayed bugs were created + +note: those delayed bugs will now be shown as internal compiler errors + +error: internal compiler error: Would you like some help with that? + --> tests/ui-internal/custom_ice_message.rs:12:1 + | +LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: delayed at clippy_lints/src/utils/internal_lints/produce_ice.rs - disabled backtrace + --> tests/ui-internal/custom_ice_message.rs:12:1 + | +LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml @@ -13,9 +22,5 @@ note: rustc running on note: compiler flags: -Z ui-testing -Z deduplicate-diagnostics=no -query stack during panic: -#0 [early_lint_checks] perform lints prior to AST lowering -#1 [hir_crate] getting the crate HIR -... and 3 other queries... use `env RUST_BACKTRACE=1` to see the full query stack note: Clippy version: foo diff --git a/tests/ui-internal/default_lint.rs b/tests/ui-internal/default_lint.rs index da29aedb2a3ae..959bfd27e3899 100644 --- a/tests/ui-internal/default_lint.rs +++ b/tests/ui-internal/default_lint.rs @@ -16,6 +16,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ default_lint pub clippy::TEST_LINT_DEFAULT, Warn, "default lint description", diff --git a/tests/ui-internal/default_lint.stderr b/tests/ui-internal/default_lint.stderr index c939125e875c4..9d4c2e15349f6 100644 --- a/tests/ui-internal/default_lint.stderr +++ b/tests/ui-internal/default_lint.stderr @@ -2,6 +2,7 @@ error: the lint `TEST_LINT_DEFAULT` has the default lint description --> tests/ui-internal/default_lint.rs:18:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::TEST_LINT_DEFAULT, LL | | Warn, LL | | "default lint description", diff --git a/tests/ui-internal/disallow_span_lint.rs b/tests/ui-internal/disallow_span_lint.rs index ca71dddcc24f1..3fed38cab64d4 100644 --- a/tests/ui-internal/disallow_span_lint.rs +++ b/tests/ui-internal/disallow_span_lint.rs @@ -12,12 +12,14 @@ use rustc_middle::ty::TyCtxt; pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into, msg: impl Into) { cx.span_lint(lint, span, |lint| { + //~^ disallowed_methods lint.primary_message(msg); }); } pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into, msg: impl Into) { tcx.node_span_lint(lint, hir_id, span, |lint| { + //~^ disallowed_methods lint.primary_message(msg); }); } diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index 16e1487f2bbca..9a7a7ecbbff92 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -9,7 +9,7 @@ LL | cx.span_lint(lint, span, |lint| { = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:20:9 + --> tests/ui-internal/disallow_span_lint.rs:21:9 | LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index 3bcabb4ab2d36..92d3b1537e0c8 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -15,15 +15,19 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern let _ = rustc_span::sym::f32; + //~^ interning_defined_symbol // Using a sym macro let _ = rustc_span::sym::f32; + //~^ interning_defined_symbol // Correct suggestion when symbol isn't stringified constant name let _ = rustc_span::sym::proc_dash_macro; + //~^ interning_defined_symbol // interning a keyword let _ = rustc_span::kw::SelfLower; + //~^ interning_defined_symbol // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs index 92e92d4fbc16a..d1e6f9cb1c416 100644 --- a/tests/ui-internal/interning_defined_symbol.rs +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -15,15 +15,19 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern let _ = Symbol::intern("f32"); + //~^ interning_defined_symbol // Using a sym macro let _ = sym!(f32); + //~^ interning_defined_symbol // Correct suggestion when symbol isn't stringified constant name let _ = Symbol::intern("proc-macro"); + //~^ interning_defined_symbol // interning a keyword let _ = Symbol::intern("self"); + //~^ interning_defined_symbol // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr index c4d0308979f64..c84a566436a8e 100644 --- a/tests/ui-internal/interning_defined_symbol.stderr +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -12,19 +12,19 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:20:13 + --> tests/ui-internal/interning_defined_symbol.rs:21:13 | LL | let _ = sym!(f32); | ^^^^^^^^^ help: try: `rustc_span::sym::f32` error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:23:13 + --> tests/ui-internal/interning_defined_symbol.rs:25:13 | LL | let _ = Symbol::intern("proc-macro"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:26:13 + --> tests/ui-internal/interning_defined_symbol.rs:29:13 | LL | let _ = Symbol::intern("self"); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::kw::SelfLower` diff --git a/tests/ui-internal/invalid_msrv_attr_impl.fixed b/tests/ui-internal/invalid_msrv_attr_impl.fixed index 7011ef518f20f..6804e2bbae83c 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -27,6 +27,7 @@ impl_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { extract_msrv_attr!(); + //~^ missing_msrv_attr_impl fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {} } diff --git a/tests/ui-internal/invalid_msrv_attr_impl.rs b/tests/ui-internal/invalid_msrv_attr_impl.rs index 323061decd23c..c625a5d9a4590 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -26,6 +26,7 @@ struct Pass { impl_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { + //~^ missing_msrv_attr_impl fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {} } diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index 9a9790a4bae51..abfb111f938e4 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -13,12 +13,15 @@ mod paths { // Path with empty segment pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + //~^ invalid_paths // Path with bad crate pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + //~^ invalid_paths // Path with bad module pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + //~^ invalid_paths // Path to method on an enum inherent impl pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr index fc530a2efa378..7bde37667be42 100644 --- a/tests/ui-internal/invalid_paths.stderr +++ b/tests/ui-internal/invalid_paths.stderr @@ -8,13 +8,13 @@ LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute" = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` error: invalid path - --> tests/ui-internal/invalid_paths.rs:18:5 + --> tests/ui-internal/invalid_paths.rs:19:5 | LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid path - --> tests/ui-internal/invalid_paths.rs:21:5 + --> tests/ui-internal/invalid_paths.rs:23:5 | LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/lint_without_lint_pass.rs b/tests/ui-internal/lint_without_lint_pass.rs index d59e9cbbb6173..69591523432c8 100644 --- a/tests/ui-internal/lint_without_lint_pass.rs +++ b/tests/ui-internal/lint_without_lint_pass.rs @@ -10,6 +10,7 @@ extern crate rustc_lint; use rustc_lint::{LintPass, LintVec}; declare_tool_lint! { +//~^ lint_without_lint_pass pub clippy::TEST_LINT, Warn, "", diff --git a/tests/ui-internal/lint_without_lint_pass.stderr b/tests/ui-internal/lint_without_lint_pass.stderr index 187bba97fd419..9cca96ca16020 100644 --- a/tests/ui-internal/lint_without_lint_pass.stderr +++ b/tests/ui-internal/lint_without_lint_pass.stderr @@ -2,6 +2,7 @@ error: the lint `TEST_LINT` is not added to any `LintPass` --> tests/ui-internal/lint_without_lint_pass.rs:12:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::TEST_LINT, LL | | Warn, LL | | "", diff --git a/tests/ui-internal/outer_expn_data.fixed b/tests/ui-internal/outer_expn_data.fixed index cef16cf6ca5bf..cb7680b8bb142 100644 --- a/tests/ui-internal/outer_expn_data.fixed +++ b/tests/ui-internal/outer_expn_data.fixed @@ -21,6 +21,7 @@ declare_lint_pass!(Pass => [TEST_LINT]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { let _ = expr.span.ctxt().outer_expn_data(); + //~^ outer_expn_expn_data } } diff --git a/tests/ui-internal/outer_expn_data.rs b/tests/ui-internal/outer_expn_data.rs index fb453be661c8b..41d735110b5a0 100644 --- a/tests/ui-internal/outer_expn_data.rs +++ b/tests/ui-internal/outer_expn_data.rs @@ -21,6 +21,7 @@ declare_lint_pass!(Pass => [TEST_LINT]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { let _ = expr.span.ctxt().outer_expn().expn_data(); + //~^ outer_expn_expn_data } } diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index d3fab60f9e3ed..577fad9341b64 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -35,28 +35,43 @@ const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_diagnostic_item(cx, ty, sym::Option); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Result); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Result); + //~^ unnecessary_def_path #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = is_type_diagnostic_item(cx, ty, sym::Rc); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Option); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::Result); + //~^ unnecessary_def_path let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); + //~^ unnecessary_def_path let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); + //~^ unnecessary_def_path let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); + //~^ unnecessary_def_path let _ = cx.tcx.is_diagnostic_item(sym::Option, did); + //~^ unnecessary_def_path let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); + //~^ unnecessary_def_path let _ = is_trait_method(cx, expr, sym::AsRef); + //~^ unnecessary_def_path let _ = is_path_diagnostic_item(cx, expr, sym::Option); + //~^ unnecessary_def_path let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); + //~^ unnecessary_def_path let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); + //~^ unnecessary_def_path } fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 1b36f6b09e9c4..d4deb3626d0b6 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -35,28 +35,43 @@ const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = match_type(cx, ty, &OPTION); + //~^ unnecessary_def_path let _ = match_type(cx, ty, RESULT); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &["core", "result", "Result"]); + //~^ unnecessary_def_path #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &paths::OPTION); + //~^ unnecessary_def_path let _ = match_type(cx, ty, paths::RESULT); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + //~^ unnecessary_def_path let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + //~^ unnecessary_def_path let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + //~^ unnecessary_def_path let _ = match_def_path(cx, did, &["core", "option", "Option"]); + //~^ unnecessary_def_path let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + //~^ unnecessary_def_path let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + //~^ unnecessary_def_path let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + //~^ unnecessary_def_path let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + //~^ unnecessary_def_path let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); + //~^ unnecessary_def_path } fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index 79521c5037a80..0053ba321bbe7 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -12,61 +12,61 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:38:13 + --> tests/ui-internal/unnecessary_def_path.rs:39:13 | LL | let _ = match_type(cx, ty, RESULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:39:13 + --> tests/ui-internal/unnecessary_def_path.rs:41:13 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:43:13 + --> tests/ui-internal/unnecessary_def_path.rs:46:13 | LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:45:13 + --> tests/ui-internal/unnecessary_def_path.rs:49:13 | LL | let _ = match_type(cx, ty, &paths::OPTION); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:46:13 + --> tests/ui-internal/unnecessary_def_path.rs:51:13 | LL | let _ = match_type(cx, ty, paths::RESULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:48:13 + --> tests/ui-internal/unnecessary_def_path.rs:54:13 | LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:49:13 + --> tests/ui-internal/unnecessary_def_path.rs:56:13 | LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:51:13 + --> tests/ui-internal/unnecessary_def_path.rs:59:13 | LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:52:13 + --> tests/ui-internal/unnecessary_def_path.rs:61:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:53:13 + --> tests/ui-internal/unnecessary_def_path.rs:63:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` @@ -74,25 +74,25 @@ LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:55:13 + --> tests/ui-internal/unnecessary_def_path.rs:66:13 | LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:57:13 + --> tests/ui-internal/unnecessary_def_path.rs:69:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:58:13 + --> tests/ui-internal/unnecessary_def_path.rs:71:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:59:13 + --> tests/ui-internal/unnecessary_def_path.rs:73:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs index f6abb3cc3d713..4801d76bd2685 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -8,8 +8,11 @@ use rustc_hir::LangItem; fn main() { const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + //~^ unnecessary_def_path const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + //~^ unnecessary_def_path const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + //~^ unnecessary_def_path // Don't lint, not a diagnostic or language item const OPS_MOD: [&str; 2] = ["core", "ops"]; diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 9d7c00088fe8f..b938395193234 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -9,7 +9,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` error: hardcoded path to a language item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:11:40 + --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40 | LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"] = help: convert all references to use `LangItem::DerefMut` error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43 + --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:14:43 | LL | const OPS_MOD: [&str; 5] = ["core", "ops"]; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed index 3d9deb705ace8..dc564daef8293 100644 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -14,8 +14,13 @@ use rustc_span::symbol::{Ident, Symbol}; fn main() { Symbol::intern("foo") == rustc_span::sym::clippy; + //~^ unnecessary_symbol_str Symbol::intern("foo") == rustc_span::kw::SelfLower; + //~^ unnecessary_symbol_str Symbol::intern("foo") != rustc_span::kw::SelfUpper; + //~^ unnecessary_symbol_str Ident::empty().name == rustc_span::sym::clippy; + //~^ unnecessary_symbol_str rustc_span::sym::clippy == Ident::empty().name; + //~^ unnecessary_symbol_str } diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs index 9aeeb9aaf3aa3..d74262d1294b7 100644 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -14,8 +14,13 @@ use rustc_span::symbol::{Ident, Symbol}; fn main() { Symbol::intern("foo").as_str() == "clippy"; + //~^ unnecessary_symbol_str Symbol::intern("foo").to_string() == "self"; + //~^ unnecessary_symbol_str Symbol::intern("foo").to_ident_string() != "Self"; + //~^ unnecessary_symbol_str &*Ident::empty().as_str() == "clippy"; + //~^ unnecessary_symbol_str "clippy" == Ident::empty().to_string(); + //~^ unnecessary_symbol_str } diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr index 1742603eff6d9..517a395e93f2c 100644 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_string() == "self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::kw::SelfLower` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::kw::SelfUpper` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:22:5 | LL | &*Ident::empty().as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:24:5 | LL | "clippy" == Ident::empty().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml new file mode 100644 index 0000000000000..baf7041138f92 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = true diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml new file mode 100644 index 0000000000000..1fa80c730126a --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["madules"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml new file mode 100644 index 0000000000000..cee857a155b9c --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["entirely garbled"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml index ddca5cfa57796..af3aa1cc62a49 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml +++ b/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml @@ -9,4 +9,4 @@ module-item-order-groupings = [ ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]] ] - +module-items-ordered-within-groupings = "none" diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml new file mode 100644 index 0000000000000..fd961c82d6f21 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["PascalCase"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml new file mode 100644 index 0000000000000..de2a7307eca6e --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml @@ -0,0 +1,2 @@ +source-item-ordering = ["module"] +module-items-ordered-within-groupings = ["PascalCase"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml new file mode 100644 index 0000000000000..e7640efde10ca --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = "all" diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr new file mode 100644 index 0000000000000..c38c54ffeb0d5 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: data did not match any variant of untagged enum StringOrVecOfString The available options for configuring an ordering within module item groups are: "all", "none", or a list of module item group names (as configured with the `module-item-order-groupings` configuration option). + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml:1:41 + | +LL | module-items-ordered-within-groupings = true + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr new file mode 100644 index 0000000000000..7b1dafb6d0d5b --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file: unknown ordering group: `madules` was not specified in `module-items-ordered-within-groupings`, perhaps you meant `modules`? expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr new file mode 100644 index 0000000000000..996cabeeed4a7 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file: unknown ordering group: `entirely garbled` was not specified in `module-items-ordered-within-groupings`, expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs index 05eb40506db49..b43791521cb5a 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs @@ -1,15 +1,21 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs -//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 +//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 bad_conf_4 bad_conf_5 bad_conf_6 //@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default //@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp //@[bad_conf_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1 //@[bad_conf_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2 //@[bad_conf_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3 +//@[bad_conf_4] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4 +//@[bad_conf_5] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5 +//@[bad_conf_6] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6 //@[default] check-pass //@[default_exp] check-pass //@[bad_conf_1] error-in-other-file: //@[bad_conf_2] error-in-other-file: //@[bad_conf_3] error-in-other-file: +//@[bad_conf_4] error-in-other-file: +//@[bad_conf_5] error-in-other-file: +//@[bad_conf_6] error-in-other-file: #![allow(dead_code)] #![warn(clippy::arbitrary_source_item_ordering)] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr index 3605952bddc9b..50567e32b1bb9 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr @@ -1,223 +1,160 @@ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:22:14 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 | LL | use std::rc::Weak; | ^^^^ | note: should be placed before `SNAKE_CASE` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:20:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 | LL | const SNAKE_CASE: &str = "zzzzzzzz"; | ^^^^^^^^^^ = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:67:1 - | -LL | / impl CloneSelf for StructOrdered { -LL | | -LL | | fn clone_self(&self) -> Self { -LL | | Self { -... | -LL | | } - | |_^ - | -note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:1 - | -LL | / impl Default for StructOrdered { -LL | | fn default() -> Self { -LL | | Self { -LL | | a: true, -... | -LL | | } - | |_^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:145:7 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:163:1 - | -LL | impl BasicEmptyTrait for StructOrdered {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:1 - | -LL | / impl TraitUnordered for StructUnordered { -LL | | const A: bool = false; -LL | | const C: bool = false; -LL | | const B: bool = false; -... | -LL | | } - | |_^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:184:5 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:179:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 | LL | fn main() { | ^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:194:7 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:192:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 - | -LL | const AFTER: i8 = 0; - | ^^^^^ - | -note: should be placed before `BEFORE` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:10:11 - | -LL | const BEFORE: i8 = 0; - | ^^^^^^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:40:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 | LL | B, | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:39:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 | LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:92:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:91:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:101:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:100:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:128:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:127:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:135:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:133:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:151:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:150:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:158:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:157:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:169:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:167:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:11 - | -LL | const A: i8 = 1; - | ^ - | -note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:186:11 - | -LL | const C: i8 = 0; - | ^ - -error: aborting due to 17 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr new file mode 100644 index 0000000000000..50567e32b1bb9 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr @@ -0,0 +1,160 @@ +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr new file mode 100644 index 0000000000000..ae5261dcc6df8 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr @@ -0,0 +1,235 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + | +note: should be placed before `ZNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:22:7 + | +LL | const ZNAKE_CASE: &str = "123"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:71:1 + | +LL | / impl CloneSelf for StructOrdered { +LL | | +LL | | fn clone_self(&self) -> Self { +LL | | Self { +... | +LL | | } + | |_^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:61:1 + | +LL | / impl Default for StructOrdered { +LL | | fn default() -> Self { +LL | | Self { +LL | | a: true, +... | +LL | | } + | |_^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:167:1 + | +LL | impl BasicEmptyTrait for StructOrdered {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:1 + | +LL | / impl TraitUnordered for StructUnordered { +LL | | const A: bool = false; +LL | | const C: bool = false; +LL | | const B: bool = false; +... | +LL | | } + | |_^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:14:11 + | +LL | const AFTER: i8 = 0; + | ^^^^^ + | +note: should be placed before `BEFORE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 + | +LL | const BEFORE: i8 = 0; + | ^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:191:11 + | +LL | const A: i8 = 1; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:190:11 + | +LL | const C: i8 = 0; + | ^ + +error: aborting due to 18 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs index 9e65a9cca0da1..90399470d4c09 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs @@ -1,6 +1,8 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs -//@revisions: default +//@revisions: default default_exp ord_within //@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp +//@[ord_within] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_within #![allow(dead_code)] #![warn(clippy::arbitrary_source_item_ordering)] @@ -10,14 +12,16 @@ mod i_am_just_right { const BEFORE: i8 = 0; const AFTER: i8 = 0; - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering } // Use statements should not be linted internally - this is normally auto-sorted using rustfmt. use std::rc::Rc; use std::sync::{Arc, Barrier, RwLock}; +const ZNAKE_CASE: &str = "123"; const SNAKE_CASE: &str = "zzzzzzzz"; +//~[ord_within]^ arbitrary_source_item_ordering use std::rc::Weak; //~^ arbitrary_source_item_ordering @@ -65,7 +69,7 @@ impl Default for StructOrdered { } impl CloneSelf for StructOrdered { - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering fn clone_self(&self) -> Self { Self { a: true, @@ -161,7 +165,7 @@ impl TraitUnordered for StructUnordered { // Trait impls should be located just after the type they implement it for. impl BasicEmptyTrait for StructOrdered {} -//~^ arbitrary_source_item_ordering +//~[ord_within]^ arbitrary_source_item_ordering impl TraitUnorderedItemKinds for StructUnordered { type SomeType = (); @@ -185,7 +189,7 @@ mod this_is_in_the_wrong_position { //~^ arbitrary_source_item_ordering const C: i8 = 0; const A: i8 = 1; - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering } #[derive(Default, std::clone::Clone)] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr new file mode 100644 index 0000000000000..7fc216b30d508 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr @@ -0,0 +1,19 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr new file mode 100644 index 0000000000000..1f75f5099ecc1 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr @@ -0,0 +1,36 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr new file mode 100644 index 0000000000000..8027f55add673 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr @@ -0,0 +1,19 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr new file mode 100644 index 0000000000000..333a601f6a952 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr @@ -0,0 +1,48 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:45:4 + | +LL | fn before_main() {} + | ^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:41:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs new file mode 100644 index 0000000000000..e32b921dd9659 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs @@ -0,0 +1,46 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default ord_within ord_in_2 ord_in_3 +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[ord_within] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_within +//@[ord_in_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_2 +//@[ord_in_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_3 + +#![allow(dead_code)] +#![deny(clippy::arbitrary_source_item_ordering)] + +#[allow(clippy::arbitrary_source_item_ordering)] +struct Ordered { + a: bool, + b: bool, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +struct Unordered { + b: bool, + a: bool, +} + +#[deny(clippy::arbitrary_source_item_ordering)] +struct OrderedChecked { + //~[ord_within]^ arbitrary_source_item_ordering + //~[ord_in_2]| arbitrary_source_item_ordering + //~[ord_in_3]| arbitrary_source_item_ordering + a: bool, + b: bool, +} + +#[deny(clippy::arbitrary_source_item_ordering)] +struct UnorderedChecked { + b: bool, + a: bool, + //~[ord_within]^ arbitrary_source_item_ordering + //~[default]| arbitrary_source_item_ordering + //~[ord_in_2]| arbitrary_source_item_ordering +} + +fn main() { + // test code goes here +} + +fn before_main() {} +//~[ord_within]^ arbitrary_source_item_ordering diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index acfe739277cc7..fee5b01b68982 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -60,6 +60,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect min-ident-chars-threshold missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior @@ -152,6 +153,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect min-ident-chars-threshold missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior @@ -244,6 +246,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni min-ident-chars-threshold missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 32ed78151d237..8a2f201009a92 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -310,5 +310,49 @@ LL | let bar = unsafe {}; | = help: consider adding a safety comment on the preceding line -error: aborting due to 35 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52 + | +LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40 + | +LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + | +LL | _ = bar(); + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + | +LL | // SAFETY: unnecessary_safety_comment triggers here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 40 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 83a6986addf26..e9c5e5f9f1146 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -390,5 +390,65 @@ LL | unsafe {} | = help: consider adding a safety comment on the preceding line -error: aborting due to 45 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52 + | +LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40 + | +LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:673:9 + | +LL | unsafe { here_is_another_variable_with_long_name }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:702:9 + | +LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + | +LL | _ = bar(); + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + | +LL | // SAFETY: unnecessary_safety_comment triggers here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 52 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index 6a3fda3df5c38..91a02bc3d7c65 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -632,4 +632,95 @@ mod issue_11246 { // Safety: Another safety comment const FOO: () = unsafe {}; +// trait items and impl items +mod issue_11709 { + trait MyTrait { + const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + //~^ ERROR: unsafe block missing a safety comment + + // SAFETY: safe + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + + // SAFETY: unrelated + unsafe fn unsafe_fn() {} + + const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + //~^ ERROR: unsafe block missing a safety comment + } + + struct UnsafeStruct; + + impl MyTrait for UnsafeStruct { + // SAFETY: safe in this impl + const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 2 }; + + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + //~^ ERROR: unsafe block missing a safety comment + } + + impl UnsafeStruct { + const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + //~^ ERROR: unsafe block missing a safety comment + } +} + +fn issue_13024() { + let mut just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters = 0; + let here_is_another_variable_with_long_name = 100; + + // SAFETY: fail ONLY if `accept-comment-above-statement = false` + just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters = + unsafe { here_is_another_variable_with_long_name }; + //~[disabled]^ undocumented_unsafe_blocks +} + +// https://docs.rs/time/0.3.36/src/time/offset_date_time.rs.html#35 +mod issue_11709_regression { + use std::num::NonZeroI32; + + struct Date { + value: NonZeroI32, + } + + impl Date { + const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { + Self { + // Safety: The caller must guarantee that `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) }, + } + } + + const fn into_julian_day_just_make_this_line_longer(self) -> i32 { + 42 + } + } + + /// The Julian day of the Unix epoch. + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + #[allow(unsafe_code)] + const UNIX_EPOCH_JULIAN_DAY: i32 = + unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); + //~[disabled]^ undocumented_unsafe_blocks +} + +fn issue_13039() { + unsafe fn foo() -> usize { + 1234 + } + + fn bar() -> usize { + 1234 + } + + // SAFETY: unnecessary_safety_comment should not trigger here + _ = unsafe { foo() }; + + // SAFETY: unnecessary_safety_comment triggers here + _ = bar(); + //~^ unnecessary_safety_comment + + // SAFETY: unnecessary_safety_comment should not trigger here + _ = unsafe { foo() } +} + fn main() {} diff --git a/tests/ui/blocks_in_conditions.fixed b/tests/ui/blocks_in_conditions.fixed index df375e370573c..cd307e803d0c9 100644 --- a/tests/ui/blocks_in_conditions.fixed +++ b/tests/ui/blocks_in_conditions.fixed @@ -118,6 +118,13 @@ mod issue_12016 { } } +fn issue_9911() { + if { return } {} + + let a = 1; + if { if a == 1 { return } else { true } } {} +} + fn in_closure() { let v = vec![1, 2, 3]; if v.into_iter() diff --git a/tests/ui/blocks_in_conditions.rs b/tests/ui/blocks_in_conditions.rs index 1d9c9dd424603..6a211c8edfd4f 100644 --- a/tests/ui/blocks_in_conditions.rs +++ b/tests/ui/blocks_in_conditions.rs @@ -118,6 +118,13 @@ mod issue_12016 { } } +fn issue_9911() { + if { return } {} + + let a = 1; + if { if a == 1 { return } else { true } } {} +} + fn in_closure() { let v = vec![1, 2, 3]; if v.into_iter() diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs index 58ee751948b52..8a27b3c6d47db 100644 --- a/tests/ui/crashes/if_same_then_else.rs +++ b/tests/ui/crashes/if_same_then_else.rs @@ -1,6 +1,5 @@ //@ check-pass -#![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] // Test for https://github.com/rust-lang/rust-clippy/issues/2426 diff --git a/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr b/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr index 2847957000692..b4fb1222539a2 100644 --- a/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr +++ b/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr @@ -2,7 +2,7 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:7:12 | LL | fn test(x: Foo<'_>) {} - | ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>` + | ^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:5:1 @@ -11,6 +11,10 @@ LL | struct Foo<'a>(&'a [(); 100]); | ^^^^^^^^^^^^^^ = note: `-D clippy::needless-pass-by-value` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_value)]` +help: consider taking a reference instead + | +LL | fn test(x: &Foo<'_>) {} + | + error: aborting due to 1 previous error diff --git a/tests/ui/doc/doc_comment_double_space_linebreaks.fixed b/tests/ui/doc/doc_comment_double_space_linebreaks.fixed new file mode 100644 index 0000000000000..6a616b2c7e180 --- /dev/null +++ b/tests/ui/doc/doc_comment_double_space_linebreaks.fixed @@ -0,0 +1,98 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![warn(clippy::doc_comment_double_space_linebreaks)] +#![allow(unused, clippy::empty_docs)] + +//~v doc_comment_double_space_linebreaks +//! Should warn on double space linebreaks\ +//! in file/module doc comment + +/// Should not warn on single-line doc comments +fn single_line() {} + +/// Should not warn on single-line doc comments +/// split across multiple lines +fn single_line_split() {} + +// Should not warn on normal comments + +// note: cargo fmt can remove double spaces from normal and block comments +// Should not warn on normal comments +// with double spaces at the end of a line + +#[doc = "This is a doc attribute, which should not be linted"] +fn normal_comment() { + /* + Should not warn on block comments + */ + + /* + Should not warn on block comments + with double space at the end of a line + */ +} + +//~v doc_comment_double_space_linebreaks +/// Should warn when doc comment uses double space\ +/// as a line-break, even when there are multiple\ +/// in a row +fn double_space_doc_comment() {} + +/// Should not warn when back-slash is used \ +/// as a line-break +fn back_slash_doc_comment() {} + +//~v doc_comment_double_space_linebreaks +/// 🌹 are 🟥\ +/// 🌷 are 🟦\ +/// 📎 is 😎\ +/// and so are 🫵\ +/// (hopefully no formatting weirdness linting this) +fn multi_byte_chars_tada() {} + +macro_rules! macro_that_makes_function { + () => { + /// Shouldn't lint on this! + /// (hopefully) + fn my_macro_created_function() {} + } +} + +macro_that_makes_function!(); + +// dont lint when its alone on a line +/// +fn alone() {} + +/// | First column | Second column | +/// | ------------ | ------------- | +/// | Not a line | break when | +/// | after a line | in a table | +fn table() {} + +/// ```text +/// It's also not a hard line break if +/// there's two spaces at the end of a +/// line in a block code. +/// ``` +fn codeblock() {} + +/// It's also not a hard line break `if +/// there's` two spaces in the middle of inline code. +fn inline() {} + +/// It's also not a hard line break [when]( +/// https://example.com) in a URL. +fn url() {} + +//~v doc_comment_double_space_linebreaks +/// here we mix\ +/// double spaces\ +/// and also\ +/// adding backslash\ +/// to some of them\ +/// to see how that looks +fn mixed() {} + +fn main() {} diff --git a/tests/ui/doc/doc_comment_double_space_linebreaks.rs b/tests/ui/doc/doc_comment_double_space_linebreaks.rs new file mode 100644 index 0000000000000..e36cf7dea2363 --- /dev/null +++ b/tests/ui/doc/doc_comment_double_space_linebreaks.rs @@ -0,0 +1,98 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![warn(clippy::doc_comment_double_space_linebreaks)] +#![allow(unused, clippy::empty_docs)] + +//~v doc_comment_double_space_linebreaks +//! Should warn on double space linebreaks +//! in file/module doc comment + +/// Should not warn on single-line doc comments +fn single_line() {} + +/// Should not warn on single-line doc comments +/// split across multiple lines +fn single_line_split() {} + +// Should not warn on normal comments + +// note: cargo fmt can remove double spaces from normal and block comments +// Should not warn on normal comments +// with double spaces at the end of a line + +#[doc = "This is a doc attribute, which should not be linted"] +fn normal_comment() { + /* + Should not warn on block comments + */ + + /* + Should not warn on block comments + with double space at the end of a line + */ +} + +//~v doc_comment_double_space_linebreaks +/// Should warn when doc comment uses double space +/// as a line-break, even when there are multiple +/// in a row +fn double_space_doc_comment() {} + +/// Should not warn when back-slash is used \ +/// as a line-break +fn back_slash_doc_comment() {} + +//~v doc_comment_double_space_linebreaks +/// 🌹 are 🟥 +/// 🌷 are 🟦 +/// 📎 is 😎 +/// and so are 🫵 +/// (hopefully no formatting weirdness linting this) +fn multi_byte_chars_tada() {} + +macro_rules! macro_that_makes_function { + () => { + /// Shouldn't lint on this! + /// (hopefully) + fn my_macro_created_function() {} + } +} + +macro_that_makes_function!(); + +// dont lint when its alone on a line +/// +fn alone() {} + +/// | First column | Second column | +/// | ------------ | ------------- | +/// | Not a line | break when | +/// | after a line | in a table | +fn table() {} + +/// ```text +/// It's also not a hard line break if +/// there's two spaces at the end of a +/// line in a block code. +/// ``` +fn codeblock() {} + +/// It's also not a hard line break `if +/// there's` two spaces in the middle of inline code. +fn inline() {} + +/// It's also not a hard line break [when]( +/// https://example.com) in a URL. +fn url() {} + +//~v doc_comment_double_space_linebreaks +/// here we mix +/// double spaces\ +/// and also +/// adding backslash\ +/// to some of them +/// to see how that looks +fn mixed() {} + +fn main() {} diff --git a/tests/ui/doc/doc_comment_double_space_linebreaks.stderr b/tests/ui/doc/doc_comment_double_space_linebreaks.stderr new file mode 100644 index 0000000000000..08c6956c3d003 --- /dev/null +++ b/tests/ui/doc/doc_comment_double_space_linebreaks.stderr @@ -0,0 +1,50 @@ +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:8:43 + | +LL | //! Should warn on double space linebreaks + | ^^ + | + = help: replace this double space with a backslash: `\` + = note: `-D clippy::doc-comment-double-space-linebreaks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_comment_double_space_linebreaks)]` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:37:51 + | +LL | /// Should warn when doc comment uses double space + | ^^ +LL | /// as a line-break, even when there are multiple + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:47:12 + | +LL | /// 🌹 are 🟥 + | ^^ +LL | /// 🌷 are 🟦 + | ^^ +LL | /// 📎 is 😎 + | ^^ +LL | /// and so are 🫵 + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:90:16 + | +LL | /// here we mix + | ^^ +LL | /// double spaces\ +LL | /// and also + | ^^ +LL | /// adding backslash\ +LL | /// to some of them + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/entry_unfixable.rs b/tests/ui/entry_unfixable.rs new file mode 100644 index 0000000000000..dbdacf950569c --- /dev/null +++ b/tests/ui/entry_unfixable.rs @@ -0,0 +1,94 @@ +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] +//@no-rustfix + +use std::collections::HashMap; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + +mod issue13306 { + use std::collections::HashMap; + + struct Env { + enclosing: Option>, + values: HashMap, + } + + impl Env { + fn assign(&mut self, name: String, value: usize) -> bool { + if !self.values.contains_key(&name) { + //~^ map_entry + self.values.insert(name, value); + true + } else if let Some(enclosing) = &mut self.enclosing { + enclosing.assign(name, value) + } else { + false + } + } + } +} + +fn issue9925(mut hm: HashMap) { + let key = "hello".to_string(); + if hm.contains_key(&key) { + //~^ map_entry + let bval = hm.get_mut(&key).unwrap(); + *bval = false; + } else { + hm.insert(key, true); + } +} + +mod issue9470 { + use std::collections::HashMap; + use std::sync::Mutex; + + struct Interner(i32); + + impl Interner { + const fn new() -> Self { + Interner(0) + } + + fn resolve(&self, name: String) -> Option { + todo!() + } + } + + static INTERNER: Mutex = Mutex::new(Interner::new()); + + struct VM { + stack: Vec, + globals: HashMap, + } + + impl VM { + fn stack_top(&self) -> &i32 { + self.stack.last().unwrap() + } + + fn resolve(&mut self, name: String, value: i32) -> Result<(), String> { + if self.globals.contains_key(&name) { + //~^ map_entry + self.globals.insert(name, value); + } else { + let interner = INTERNER.lock().unwrap(); + return Err(interner.resolve(name).unwrap().to_owned()); + } + + Ok(()) + } + } +} + +fn main() {} diff --git a/tests/ui/entry_unfixable.stderr b/tests/ui/entry_unfixable.stderr new file mode 100644 index 0000000000000..9f9956d351b29 --- /dev/null +++ b/tests/ui/entry_unfixable.stderr @@ -0,0 +1,41 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:28:13 + | +LL | / if !self.values.contains_key(&name) { +LL | | +LL | | self.values.insert(name, value); +LL | | true +... | +LL | | false +LL | | } + | |_____________^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:43:5 + | +LL | / if hm.contains_key(&key) { +LL | | +LL | | let bval = hm.get_mut(&key).unwrap(); +LL | | *bval = false; +LL | | } else { +LL | | hm.insert(key, true); +LL | | } + | |_____^ + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:81:13 + | +LL | / if self.globals.contains_key(&name) { +LL | | +LL | | self.globals.insert(name, value); +LL | | } else { +LL | | let interner = INTERNER.lock().unwrap(); +LL | | return Err(interner.resolve(name).unwrap().to_owned()); +LL | | } + | |_____________^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 7d6780a0e02c8..7229e5a2d3589 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -105,4 +105,15 @@ fn issue_12138() { } } +fn issue_112502() { + struct MyInt(i64); + + impl From for i64 { + //~^ from_over_into + fn from(val: MyInt) -> Self { + val.0 + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 387ddde359c16..9c75969c5c134 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -105,4 +105,15 @@ fn issue_12138() { } } +fn issue_112502() { + struct MyInt(i64); + + impl Into for MyInt { + //~^ from_over_into + fn into(self: MyInt) -> i64 { + self.0 + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index a564bccbaf71f..fe779544dd578 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -107,5 +107,21 @@ LL | LL ~ fn from(val: Hello) {} | -error: aborting due to 7 previous errors +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> tests/ui/from_over_into.rs:111:5 + | +LL | impl Into for MyInt { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implementation with `From` + | +LL ~ impl From for i64 { +LL | +LL ~ fn from(val: MyInt) -> Self { +LL ~ val.0 + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index 6ecb7cb1eba9d..ebc3acb1b77f6 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -1,10 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow( - clippy::if_same_then_else, - clippy::comparison_chain, - clippy::needless_if, - clippy::needless_else -)] // all empty blocks +#![allow(clippy::if_same_then_else, clippy::needless_if, clippy::needless_else)] // all empty blocks fn ifs_same_cond() { let a = 0; diff --git a/tests/ui/ifs_same_cond.stderr b/tests/ui/ifs_same_cond.stderr index 81fbb921e8463..df21e6f1b8262 100644 --- a/tests/ui/ifs_same_cond.stderr +++ b/tests/ui/ifs_same_cond.stderr @@ -1,11 +1,11 @@ error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:14:15 + --> tests/ui/ifs_same_cond.rs:9:15 | LL | } else if b { | ^ | note: same as this - --> tests/ui/ifs_same_cond.rs:13:8 + --> tests/ui/ifs_same_cond.rs:8:8 | LL | if b { | ^ @@ -13,37 +13,37 @@ LL | if b { = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:19:15 + --> tests/ui/ifs_same_cond.rs:14:15 | LL | } else if a == 1 { | ^^^^^^ | note: same as this - --> tests/ui/ifs_same_cond.rs:18:8 + --> tests/ui/ifs_same_cond.rs:13:8 | LL | if a == 1 { | ^^^^^^ error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:25:15 + --> tests/ui/ifs_same_cond.rs:20:15 | LL | } else if 2 * a == 1 { | ^^^^^^^^^^ | note: same as this - --> tests/ui/ifs_same_cond.rs:23:8 + --> tests/ui/ifs_same_cond.rs:18:8 | LL | if 2 * a == 1 { | ^^^^^^^^^^ error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:58:15 + --> tests/ui/ifs_same_cond.rs:53:15 | LL | } else if a.contains("ah") { | ^^^^^^^^^^^^^^^^ | note: same as this - --> tests/ui/ifs_same_cond.rs:57:8 + --> tests/ui/ifs_same_cond.rs:52:8 | LL | if a.contains("ah") { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index 136238f9ecacc..1aab6c54407e6 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -228,3 +228,27 @@ fn regression_13524(a: usize, b: usize, c: bool) -> usize { 123 } else { b.saturating_sub(a) } } + +fn with_side_effect(a: u64) -> u64 { + println!("a = {a}"); + a +} + +fn arbitrary_expression() { + let (a, b) = (15u64, 20u64); + + let _ = (a * 2).saturating_sub(b); + //~^ implicit_saturating_sub + + let _ = a.saturating_sub(b * 2); + //~^ implicit_saturating_sub + + let _ = a.saturating_sub(b * 2); + //~^ implicit_saturating_sub + + let _ = if with_side_effect(a) > a { + with_side_effect(a) - a + } else { + 0 + }; +} diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 140cf0338602d..7ca57a2902db8 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -302,3 +302,27 @@ fn regression_13524(a: usize, b: usize, c: bool) -> usize { b - a } } + +fn with_side_effect(a: u64) -> u64 { + println!("a = {a}"); + a +} + +fn arbitrary_expression() { + let (a, b) = (15u64, 20u64); + + let _ = if a * 2 > b { a * 2 - b } else { 0 }; + //~^ implicit_saturating_sub + + let _ = if a > b * 2 { a - b * 2 } else { 0 }; + //~^ implicit_saturating_sub + + let _ = if a < b * 2 { 0 } else { a - b * 2 }; + //~^ implicit_saturating_sub + + let _ = if with_side_effect(a) > a { + with_side_effect(a) - a + } else { + 0 + }; +} diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index f9c94d3ad8416..0c225856fd07c 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -220,5 +220,23 @@ LL | | b - a LL | | } | |_____^ help: replace it with: `{ b.saturating_sub(a) }` -error: aborting due to 24 previous errors +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:314:13 + | +LL | let _ = if a * 2 > b { a * 2 - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `(a * 2).saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:317:13 + | +LL | let _ = if a > b * 2 { a - b * 2 } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)` + +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:320:13 + | +LL | let _ = if a < b * 2 { 0 } else { a - b * 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)` + +error: aborting due to 27 previous errors diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index b4fea4cae5edf..99101b2bb8f26 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -1,5 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] +#![feature(panic_internals)] #![clippy::msrv = "1.3.0"] use std::collections::HashMap; @@ -34,4 +35,42 @@ async fn issue12273(v: impl Future) { v.await; } +fn core_special_treatment(p: bool) { + // Do not lint code coming from `core` macros expanding into `core` function calls + if p { + panic!("foo"); // Do not lint + } + + // But still lint code calling `core` functions directly + if p { + core::panicking::panic("foo"); + //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` + } + + // Lint code calling `core` from non-`core` macros + macro_rules! my_panic { + ($msg:expr) => { + core::panicking::panic($msg) + }; //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` + } + my_panic!("foo"); + + // Lint even when the macro comes from `core` and calls `core` functions + assert!(core::panicking::panic("out of luck")); + //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` +} + +#[clippy::msrv = "1.26.0"] +fn lang_items() { + // Do not lint lang items. `..=` will expand into `RangeInclusive::new()`, which was introduced + // in Rust 1.27.0. + let _ = 1..=3; +} + +#[clippy::msrv = "1.80.0"] +fn issue14212() { + let _ = std::iter::repeat_n((), 5); + //~^ ERROR: is `1.80.0` but this item is stable since `1.82.0` +} + fn main() {} diff --git a/tests/ui/incompatible_msrv.stderr b/tests/ui/incompatible_msrv.stderr index 56c9eae5aafa7..5ea2bb9cc58b1 100644 --- a/tests/ui/incompatible_msrv.stderr +++ b/tests/ui/incompatible_msrv.stderr @@ -1,5 +1,5 @@ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0` - --> tests/ui/incompatible_msrv.rs:13:39 + --> tests/ui/incompatible_msrv.rs:14:39 | LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); | ^^^^^ @@ -8,16 +8,45 @@ LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); = help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]` error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.12.0` - --> tests/ui/incompatible_msrv.rs:17:11 + --> tests/ui/incompatible_msrv.rs:18:11 | LL | v.into_key(); | ^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.4.0` - --> tests/ui/incompatible_msrv.rs:21:5 + --> tests/ui/incompatible_msrv.rs:22:5 | LL | sleep(Duration::new(1, 0)); | ^^^^^ -error: aborting due to 3 previous errors +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:46:9 + | +LL | core::panicking::panic("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:53:13 + | +LL | core::panicking::panic($msg) + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | my_panic!("foo"); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `my_panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:59:13 + | +LL | assert!(core::panicking::panic("out of luck")); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.80.0` but this item is stable since `1.82.0` + --> tests/ui/incompatible_msrv.rs:72:13 + | +LL | let _ = std::iter::repeat_n((), 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors diff --git a/tests/ui/io_other_error.fixed b/tests/ui/io_other_error.fixed index 0054c56fb6282..ce7e8ef281f70 100644 --- a/tests/ui/io_other_error.fixed +++ b/tests/ui/io_other_error.fixed @@ -53,3 +53,8 @@ mod paths { fn under_msrv() { let _err = std::io::Error::new(std::io::ErrorKind::Other, E); } + +pub fn issue14346(x: i32) -> std::io::Error { + std::io::Error::other(format!("{x}")) + //~^ ERROR: this can be `std::io::Error::other(_)` +} diff --git a/tests/ui/io_other_error.rs b/tests/ui/io_other_error.rs index 8529fb9a77f98..b66e7f88ab143 100644 --- a/tests/ui/io_other_error.rs +++ b/tests/ui/io_other_error.rs @@ -53,3 +53,8 @@ mod paths { fn under_msrv() { let _err = std::io::Error::new(std::io::ErrorKind::Other, E); } + +pub fn issue14346(x: i32) -> std::io::Error { + std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) + //~^ ERROR: this can be `std::io::Error::other(_)` +} diff --git a/tests/ui/io_other_error.stderr b/tests/ui/io_other_error.stderr index e79e05ecd406b..b37e3d15064f5 100644 --- a/tests/ui/io_other_error.stderr +++ b/tests/ui/io_other_error.stderr @@ -48,5 +48,17 @@ LL - let _err = io::Error::new(io::ErrorKind::Other, super::E); LL + let _err = io::Error::other(super::E); | -error: aborting due to 4 previous errors +error: this can be `std::io::Error::other(_)` + --> tests/ui/io_other_error.rs:58:5 + | +LL | std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `std::io::Error::other` + | +LL - std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) +LL + std::io::Error::other(format!("{x}")) + | + +error: aborting due to 5 previous errors diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index d21d49310a077..ba343a8f62f82 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -30,6 +30,10 @@ fn main() { //~^ separated_literal_suffix //~| mixed_case_hex_literals + let fail2 = 0xab_CD_isize; + //~^ separated_literal_suffix + //~| mixed_case_hex_literals + let fail_multi_zero = 000_123usize; //~^ unseparated_literal_suffix //~| zero_prefixed_literal diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index d65cd3c89ebbe..6bfeb91625b32 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -25,6 +25,7 @@ error: inconsistent casing in hexadecimal literal LL | let fail1 = 0xabCD; | ^^^^^^ | + = help: consider using `0xabcd` or `0xABCD` = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mixed_case_hex_literals)]` @@ -39,6 +40,8 @@ error: inconsistent casing in hexadecimal literal | LL | let fail2 = 0xabCD_u32; | ^^^^^^^^^^ + | + = help: consider using `0xabcd_u32` or `0xABCD_u32` error: integer type suffix should not be separated by an underscore --> tests/ui/literals.rs:29:17 @@ -51,9 +54,25 @@ error: inconsistent casing in hexadecimal literal | LL | let fail2 = 0xabCD_isize; | ^^^^^^^^^^^^ + | + = help: consider using `0xabcd_isize` or `0xABCD_isize` + +error: integer type suffix should not be separated by an underscore + --> tests/ui/literals.rs:33:17 + | +LL | let fail2 = 0xab_CD_isize; + | ^^^^^^^^^^^^^ help: remove the underscore: `0xab_CDisize` + +error: inconsistent casing in hexadecimal literal + --> tests/ui/literals.rs:33:17 + | +LL | let fail2 = 0xab_CD_isize; + | ^^^^^^^^^^^^^ + | + = help: consider using `0xab_cd_isize` or `0xAB_CD_isize` error: integer type suffix should be separated by an underscore - --> tests/ui/literals.rs:33:27 + --> tests/ui/literals.rs:37:27 | LL | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ help: add an underscore: `000_123_usize` @@ -62,7 +81,7 @@ LL | let fail_multi_zero = 000_123usize; = help: to override `-D warnings` add `#[allow(clippy::unseparated_literal_suffix)]` error: this is a decimal constant - --> tests/ui/literals.rs:33:27 + --> tests/ui/literals.rs:37:27 | LL | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ @@ -81,13 +100,13 @@ LL + let fail_multi_zero = 0o123usize; | error: integer type suffix should not be separated by an underscore - --> tests/ui/literals.rs:38:16 + --> tests/ui/literals.rs:42:16 | LL | let ok10 = 0_i64; | ^^^^^ help: remove the underscore: `0i64` error: this is a decimal constant - --> tests/ui/literals.rs:41:17 + --> tests/ui/literals.rs:45:17 | LL | let fail8 = 0123; | ^^^^ @@ -103,13 +122,13 @@ LL | let fail8 = 0o123; | + error: integer type suffix should not be separated by an underscore - --> tests/ui/literals.rs:51:16 + --> tests/ui/literals.rs:55:16 | LL | let ok17 = 0x123_4567_8901_usize; | ^^^^^^^^^^^^^^^^^^^^^ help: remove the underscore: `0x123_4567_8901usize` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:56:18 + --> tests/ui/literals.rs:60:18 | LL | let fail19 = 12_3456_21; | ^^^^^^^^^^ help: consider: `12_345_621` @@ -118,19 +137,19 @@ LL | let fail19 = 12_3456_21; = help: to override `-D warnings` add `#[allow(clippy::inconsistent_digit_grouping)]` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:59:18 + --> tests/ui/literals.rs:63:18 | LL | let fail22 = 3__4___23; | ^^^^^^^^^ help: consider: `3_423` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:62:18 + --> tests/ui/literals.rs:66:18 | LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` error: digits of hex, binary or octal literal not in groups of equal size - --> tests/ui/literals.rs:65:18 + --> tests/ui/literals.rs:69:18 | LL | let fail24 = 0xAB_ABC_AB; | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` @@ -139,7 +158,7 @@ LL | let fail24 = 0xAB_ABC_AB; = help: to override `-D warnings` add `#[allow(clippy::unusual_byte_groupings)]` error: this is a decimal constant - --> tests/ui/literals.rs:75:13 + --> tests/ui/literals.rs:79:13 | LL | let _ = 08; | ^^ @@ -151,7 +170,7 @@ LL + let _ = 8; | error: this is a decimal constant - --> tests/ui/literals.rs:78:13 + --> tests/ui/literals.rs:82:13 | LL | let _ = 09; | ^^ @@ -163,7 +182,7 @@ LL + let _ = 9; | error: this is a decimal constant - --> tests/ui/literals.rs:81:13 + --> tests/ui/literals.rs:85:13 | LL | let _ = 089; | ^^^ @@ -174,5 +193,5 @@ LL - let _ = 089; LL + let _ = 89; | -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/manual_arithmetic_check-2.rs b/tests/ui/manual_arithmetic_check-2.rs index 749d15f1cbd81..b094b2021b32c 100644 --- a/tests/ui/manual_arithmetic_check-2.rs +++ b/tests/ui/manual_arithmetic_check-2.rs @@ -24,6 +24,15 @@ fn main() { let result = if b <= a { 0 } else { a - b }; //~^ inverted_saturating_sub + let result = if b * 2 <= a { 0 } else { a - b * 2 }; + //~^ inverted_saturating_sub + + let result = if b <= a * 2 { 0 } else { a * 2 - b }; + //~^ inverted_saturating_sub + + let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + //~^ inverted_saturating_sub + let af = 12f32; let bf = 13f32; // Should not lint! diff --git a/tests/ui/manual_arithmetic_check-2.stderr b/tests/ui/manual_arithmetic_check-2.stderr index 8841210befda7..fb0f0da772c9f 100644 --- a/tests/ui/manual_arithmetic_check-2.stderr +++ b/tests/ui/manual_arithmetic_check-2.stderr @@ -71,5 +71,41 @@ note: this subtraction underflows when `a < b` LL | let result = if b <= a { 0 } else { a - b }; | ^^^^^ -error: aborting due to 6 previous errors +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:27:27 + | +LL | let result = if b * 2 <= a { 0 } else { a - b * 2 }; + | ^^ --------- help: try replacing it with: `b * 2 - a` + | +note: this subtraction underflows when `a < b * 2` + --> tests/ui/manual_arithmetic_check-2.rs:27:45 + | +LL | let result = if b * 2 <= a { 0 } else { a - b * 2 }; + | ^^^^^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:30:23 + | +LL | let result = if b <= a * 2 { 0 } else { a * 2 - b }; + | ^^ --------- help: try replacing it with: `b - a * 2` + | +note: this subtraction underflows when `a * 2 < b` + --> tests/ui/manual_arithmetic_check-2.rs:30:45 + | +LL | let result = if b <= a * 2 { 0 } else { a * 2 - b }; + | ^^^^^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:33:27 + | +LL | let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + | ^^ ----------------- help: try replacing it with: `b + 3 - (a + 2)` + | +note: this subtraction underflows when `a + 2 < b + 3` + --> tests/ui/manual_arithmetic_check-2.rs:33:49 + | +LL | let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index a50975ed474ec..44c4cf9239c8f 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,4 +1,4 @@ -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:14:5 | LL | size_of::() * 8; @@ -7,169 +7,169 @@ LL | size_of::() * 8; = note: `-D clippy::manual-bits` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_bits)]` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:16:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:18:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:20:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:22:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:24:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:27:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:29:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:31:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:33:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:35:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:37:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:40:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:42:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:44:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:46:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:48:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:50:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:53:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:55:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:57:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:59:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:61:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:63:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:74:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:79:18 | LL | let _: u32 = (size_of::() * 8) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:81:18 | LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:83:13 | LL | let _ = (size_of::() * 8).pow(5); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:85:14 | LL | let _ = &(size_of::() * 8); diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index d2350d97ed003..a753566b34cb0 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -480,3 +480,37 @@ fn issue12337() { //~^ manual_let_else }; } + +mod issue13768 { + enum Foo { + Str(String), + None, + } + + fn foo(value: Foo) { + let signature = match value { + //~^ manual_let_else + Foo::Str(ref val) => val, + _ => { + println!("No signature found"); + return; + }, + }; + } + + enum Bar { + Str { inner: String }, + None, + } + + fn bar(mut value: Bar) { + let signature = match value { + //~^ manual_let_else + Bar::Str { ref mut inner } => inner, + _ => { + println!("No signature found"); + return; + }, + }; + } +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 8f5cba64d545c..ef0421921141e 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -489,5 +489,45 @@ error: this could be rewritten as `let...else` LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` -error: aborting due to 31 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:491:9 + | +LL | / let signature = match value { +LL | | +LL | | Foo::Str(ref val) => val, +LL | | _ => { +... | +LL | | }, +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Foo::Str(ref signature) = value else { +LL + println!("No signature found"); +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:507:9 + | +LL | / let signature = match value { +LL | | +LL | | Bar::Str { ref mut inner } => inner, +LL | | _ => { +... | +LL | | }, +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Bar::Str { inner: ref mut signature } = value else { +LL + println!("No signature found"); +LL + return; +LL + }; + | + +error: aborting due to 33 previous errors diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index bd6339b78700e..40e7a5d745cce 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -218,3 +218,91 @@ mod with_ty_alias { fn mut_add(x: &mut i32) { *x += 1; } + +#[clippy::msrv = "1.87"] +mod issue14020 { + use std::ops::Add; + + fn f(a: T, b: T) -> ::Output { + a + b + } +} + +#[clippy::msrv = "1.87"] +mod issue14290 { + use std::ops::{Deref, DerefMut}; + + struct Wrapper { + t: T, + } + + impl Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.t + } + } + impl DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.t + } + } + + struct Example(bool); + + fn do_something(mut a: Wrapper) { + a.0 = !a.0; + } + + pub struct Stream(Vec); + + impl Stream { + pub fn bytes(&self) -> &[u8] { + &self.0 + } + } +} + +#[clippy::msrv = "1.87"] +mod issue14091 { + use std::mem::ManuallyDrop; + + struct BucketSlotGuard<'a> { + id: u32, + free_list: &'a mut Vec, + } + + impl BucketSlotGuard<'_> { + fn into_inner(self) -> u32 { + let this = ManuallyDrop::new(self); + this.id + } + } + + use std::ops::{Deref, DerefMut}; + + struct Wrap(T); + + impl Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + + impl DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } + } + + fn smart_two_field(v: &mut Wrap<(i32, i32)>) { + let _a = &mut v.0; + let _b = &mut v.1; + } + + fn smart_destructure(v: &mut Wrap<(i32, i32)>) { + let (ref mut _head, ref mut _tail) = **v; + } +} diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index bad8c307586cf..6551fa56b42ce 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -98,6 +98,29 @@ fn main() { let mut out = vec![]; values.iter().cloned().map(|x| out.push(x)).collect::>(); let _y = values.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Don't write a warning if we call `clone()` on the iterator + // https://github.com/rust-lang/rust-clippy/issues/13430 + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _cloned = my_collection.into_iter().clone(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _cloned = my_iter.clone(); + // Same for `as_slice()`, for same reason. + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _sliced = my_collection.into_iter().as_slice(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _sliced = my_iter.as_slice(); + // Assignment outside of main scope + { + let x; + { + let xxx: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + x = xxx.into_iter(); + for i in x.as_slice() {} + } + } } fn foo(_: impl IntoIterator) {} diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 3dfb5194f4048..973c41c687544 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -98,6 +98,29 @@ fn main() { let mut out = vec![]; values.iter().cloned().map(|x| out.push(x)).collect::>(); let _y = values.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Don't write a warning if we call `clone()` on the iterator + // https://github.com/rust-lang/rust-clippy/issues/13430 + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _cloned = my_collection.into_iter().clone(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _cloned = my_iter.clone(); + // Same for `as_slice()`, for same reason. + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _sliced = my_collection.into_iter().as_slice(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _sliced = my_iter.as_slice(); + // Assignment outside of main scope + { + let x; + { + let xxx: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + x = xxx.into_iter(); + for i in x.as_slice() {} + } + } } fn foo(_: impl IntoIterator) {} diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index eb67e6cc82821..391d4bc3fcc74 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -297,3 +297,9 @@ fn issue13776() { let x; issue13776_mac!(x, 10); // should not lint } + +fn issue9895() { + + //~^ needless_late_init + let r = 5; +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 7de2202dc3234..6096e8300e1a1 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -297,3 +297,9 @@ fn issue13776() { let x; issue13776_mac!(x, 10); // should not lint } + +fn issue9895() { + let r; + //~^ needless_late_init + (r = 5); +} diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 02fd2811da58b..e7c36136847b0 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -275,5 +275,21 @@ LL | }, LL ~ }; | -error: aborting due to 16 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:302:5 + | +LL | let r; + | ^^^^^^ created here +LL | +LL | (r = 5); + | ^^^^^^^ initialised here + | +help: move the declaration `r` here + | +LL ~ +LL | +LL ~ let r = 5; + | + +error: aborting due to 17 previous errors diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 885fb409417b1..aef7fff287058 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -189,6 +189,42 @@ struct Obj(String); fn prefix_test(_unused_with_prefix: Obj) {} +// Regression test for . +// It's more idiomatic to write `Option<&T>` rather than `&Option`. +fn option_inner_ref(x: Option) { + //~^ ERROR: this argument is passed by value, but not consumed in the function body + assert!(x.is_some()); +} + +mod non_standard { + #[derive(Debug)] + pub struct Option(T); +} + +fn non_standard_option(x: non_standard::Option) { + //~^ needless_pass_by_value + dbg!(&x); +} + +fn option_by_name(x: Option>>>) { + //~^ needless_pass_by_value + dbg!(&x); +} + +type OptStr = Option; + +fn non_option(x: OptStr) { + //~^ needless_pass_by_value + dbg!(&x); +} + +type Opt = Option; + +fn non_option_either(x: Opt) { + //~^ needless_pass_by_value + dbg!(&x); +} + fn main() { // This should not cause an ICE either // https://github.com/rust-lang/rust-clippy/issues/3144 diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 4ac4fdce972d2..e4381d1db53ad 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -17,43 +17,78 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:35:22 | LL | fn bar(x: String, y: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn bar(x: String, y: &Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:44:71 | LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { - | ^ help: consider taking a reference instead: `&V` + | ^ + | +help: consider taking a reference instead + | +LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: &V) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:58:18 | LL | fn test_match(x: Option>, y: Option>) { - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option>` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_match(x: Option>, y: Option>) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:73:24 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_destructure(x: &Wrapper, y: Wrapper, z: Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:73:36 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_destructure(x: Wrapper, y: &Wrapper, z: Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:92:49 | LL | fn test_blanket_ref(vals: T, serializable: S) {} - | ^ help: consider taking a reference instead: `&T` + | ^ + | +help: consider taking a reference instead + | +LL | fn test_blanket_ref(vals: &T, serializable: S) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:18 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { - | ^^^^^^ help: consider taking a reference instead: `&String` + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | fn issue_2114(s: &String, t: String, u: Vec, v: Vec) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:29 @@ -76,7 +111,12 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:40 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { - | ^^^^^^^^ help: consider taking a reference instead: `&Vec` + | ^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn issue_2114(s: String, t: String, u: &Vec, v: Vec) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:53 @@ -105,79 +145,175 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:115:12 | LL | t: String, - | ^^^^^^ help: consider taking a reference instead: `&String` + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | t: &String, + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:125:23 | LL | fn baz(&self, uu: U, ss: Self) {} - | ^ help: consider taking a reference instead: `&U` + | ^ + | +help: consider taking a reference instead + | +LL | fn baz(&self, uu: &U, ss: Self) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:125:30 | LL | fn baz(&self, uu: U, ss: Self) {} - | ^^^^ help: consider taking a reference instead: `&Self` + | ^^^^ + | +help: consider taking a reference instead + | +LL | fn baz(&self, uu: U, ss: &Self) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:149:24 | LL | fn bar_copy(x: u32, y: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn bar_copy(x: u32, y: &CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:29 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: &CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:45 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: CopyWrapper, y: &CopyWrapper, z: CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:61 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: &CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:173:40 | LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} - | ^ help: consider taking a reference instead: `&S` + | ^ + | +help: consider taking a reference instead + | +LL | fn some_fun<'b, S: Bar<'b, ()>>(items: &S) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:179:20 | LL | fn more_fun(items: impl Club<'static, i32>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn more_fun(items: &impl Club<'static, i32>) {} + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:194:24 + | +LL | fn option_inner_ref(x: Option) { + | ^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn option_inner_ref(x: Option<&String>) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:204:27 + | +LL | fn non_standard_option(x: non_standard::Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_standard_option(x: &non_standard::Option) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:209:22 + | +LL | fn option_by_name(x: Option>>>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn option_by_name(x: Option>>>) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:216:18 + | +LL | fn non_option(x: OptStr) { + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_option(x: &OptStr) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:223:25 + | +LL | fn non_option_either(x: Opt) { + | ^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_option_either(x: &Opt) { + | + -error: aborting due to 22 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index efc073ebe8747..ad625ad6d5076 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -6,7 +6,8 @@ clippy::single_match, clippy::needless_bool, clippy::equatable_if_let, - clippy::needless_else + clippy::needless_else, + clippy::missing_safety_doc )] #![warn(clippy::needless_return)] @@ -442,3 +443,12 @@ fn b(x: Option) -> Option { }, } } + +unsafe fn todo() -> *const u8 { + todo!() +} + +pub unsafe fn issue_12157() -> *const i32 { + (unsafe { todo() } as *const i32) + //~^ needless_return +} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 283d86f25fe55..41d7e5bdd5065 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -6,7 +6,8 @@ clippy::single_match, clippy::needless_bool, clippy::equatable_if_let, - clippy::needless_else + clippy::needless_else, + clippy::missing_safety_doc )] #![warn(clippy::needless_return)] @@ -451,3 +452,12 @@ fn b(x: Option) -> Option { }, } } + +unsafe fn todo() -> *const u8 { + todo!() +} + +pub unsafe fn issue_12157() -> *const i32 { + return unsafe { todo() } as *const i32; + //~^ needless_return +} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 04c97a41d676b..80863b9b62b20 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> tests/ui/needless_return.rs:29:5 + --> tests/ui/needless_return.rs:30:5 | LL | return true; | ^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:34:5 + --> tests/ui/needless_return.rs:35:5 | LL | return true; | ^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:40:5 + --> tests/ui/needless_return.rs:41:5 | LL | return true;;; | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:46:5 + --> tests/ui/needless_return.rs:47:5 | LL | return true;; ; ; | ^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:52:9 + --> tests/ui/needless_return.rs:53:9 | LL | return true; | ^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:55:9 + --> tests/ui/needless_return.rs:56:9 | LL | return false; | ^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + false | error: unneeded `return` statement - --> tests/ui/needless_return.rs:62:17 + --> tests/ui/needless_return.rs:63:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + true => false, | error: unneeded `return` statement - --> tests/ui/needless_return.rs:65:13 + --> tests/ui/needless_return.rs:66:13 | LL | return true; | ^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:73:9 + --> tests/ui/needless_return.rs:74:9 | LL | return true; | ^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:76:16 + --> tests/ui/needless_return.rs:77:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + let _ = || true; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:81:5 + --> tests/ui/needless_return.rs:82:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:85:21 + --> tests/ui/needless_return.rs:86:21 | LL | fn test_void_fun() { | _____________________^ @@ -148,7 +148,7 @@ LL + fn test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:91:11 + --> tests/ui/needless_return.rs:92:11 | LL | if b { | ___________^ @@ -163,7 +163,7 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:94:13 + --> tests/ui/needless_return.rs:95:13 | LL | } else { | _____________^ @@ -178,7 +178,7 @@ LL + } else { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:103:14 + --> tests/ui/needless_return.rs:104:14 | LL | _ => return, | ^^^^^^ @@ -190,7 +190,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:112:24 + --> tests/ui/needless_return.rs:113:24 | LL | let _ = 42; | ________________________^ @@ -205,7 +205,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:116:14 + --> tests/ui/needless_return.rs:117:14 | LL | _ => return, | ^^^^^^ @@ -217,7 +217,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:130:9 + --> tests/ui/needless_return.rs:131:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + String::from("test") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:133:9 + --> tests/ui/needless_return.rs:134:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + String::new() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:156:32 + --> tests/ui/needless_return.rs:157:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -253,7 +253,7 @@ LL + bar.unwrap_or_else(|_| {}) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:161:21 + --> tests/ui/needless_return.rs:162:21 | LL | let _ = || { | _____________________^ @@ -268,7 +268,7 @@ LL + let _ = || { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:165:20 + --> tests/ui/needless_return.rs:166:20 | LL | let _ = || return; | ^^^^^^ @@ -280,7 +280,7 @@ LL + let _ = || {}; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:172:32 + --> tests/ui/needless_return.rs:173:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -292,7 +292,7 @@ LL + res.unwrap_or_else(|_| Foo) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:182:5 + --> tests/ui/needless_return.rs:183:5 | LL | return true; | ^^^^^^^^^^^ @@ -304,7 +304,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:187:5 + --> tests/ui/needless_return.rs:188:5 | LL | return true; | ^^^^^^^^^^^ @@ -316,7 +316,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:193:9 + --> tests/ui/needless_return.rs:194:9 | LL | return true; | ^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:196:9 + --> tests/ui/needless_return.rs:197:9 | LL | return false; | ^^^^^^^^^^^^ @@ -340,7 +340,7 @@ LL + false | error: unneeded `return` statement - --> tests/ui/needless_return.rs:203:17 + --> tests/ui/needless_return.rs:204:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -352,7 +352,7 @@ LL + true => false, | error: unneeded `return` statement - --> tests/ui/needless_return.rs:206:13 + --> tests/ui/needless_return.rs:207:13 | LL | return true; | ^^^^^^^^^^^ @@ -364,7 +364,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:214:9 + --> tests/ui/needless_return.rs:215:9 | LL | return true; | ^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:217:16 + --> tests/ui/needless_return.rs:218:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -388,7 +388,7 @@ LL + let _ = || true; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:222:5 + --> tests/ui/needless_return.rs:223:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -400,7 +400,7 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:226:33 + --> tests/ui/needless_return.rs:227:33 | LL | async fn async_test_void_fun() { | _________________________________^ @@ -415,7 +415,7 @@ LL + async fn async_test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:232:11 + --> tests/ui/needless_return.rs:233:11 | LL | if b { | ___________^ @@ -430,7 +430,7 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:235:13 + --> tests/ui/needless_return.rs:236:13 | LL | } else { | _____________^ @@ -445,7 +445,7 @@ LL + } else { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:244:14 + --> tests/ui/needless_return.rs:245:14 | LL | _ => return, | ^^^^^^ @@ -457,7 +457,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:258:9 + --> tests/ui/needless_return.rs:259:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -469,7 +469,7 @@ LL + String::from("test") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:261:9 + --> tests/ui/needless_return.rs:262:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -481,7 +481,7 @@ LL + String::new() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:278:5 + --> tests/ui/needless_return.rs:279:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -493,7 +493,7 @@ LL + format!("Hello {}", "world!") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:320:9 + --> tests/ui/needless_return.rs:321:9 | LL | return true; | ^^^^^^^^^^^ @@ -508,7 +508,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:323:9 + --> tests/ui/needless_return.rs:324:9 | LL | return false; | ^^^^^^^^^^^^ @@ -521,7 +521,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:331:13 + --> tests/ui/needless_return.rs:332:13 | LL | return 10; | ^^^^^^^^^ @@ -536,7 +536,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:335:13 + --> tests/ui/needless_return.rs:336:13 | LL | return 100; | ^^^^^^^^^^ @@ -550,7 +550,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:344:9 + --> tests/ui/needless_return.rs:345:9 | LL | return 0; | ^^^^^^^^ @@ -563,7 +563,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:352:13 + --> tests/ui/needless_return.rs:353:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -579,7 +579,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:355:13 + --> tests/ui/needless_return.rs:356:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -593,7 +593,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:363:20 + --> tests/ui/needless_return.rs:364:20 | LL | let _ = 42; | ____________________^ @@ -608,7 +608,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:370:20 + --> tests/ui/needless_return.rs:371:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -620,7 +620,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:383:9 + --> tests/ui/needless_return.rs:384:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -632,7 +632,7 @@ LL + Ok(format!("ok!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:386:9 + --> tests/ui/needless_return.rs:387:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -644,7 +644,7 @@ LL + Err(format!("err!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:393:9 + --> tests/ui/needless_return.rs:394:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -656,7 +656,7 @@ LL + if true { 1 } else { 2 } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:398:9 + --> tests/ui/needless_return.rs:399:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -668,7 +668,7 @@ LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else | error: unneeded `return` statement - --> tests/ui/needless_return.rs:420:5 + --> tests/ui/needless_return.rs:421:5 | LL | return { "a".to_string() } + "b" + { "c" }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -680,7 +680,7 @@ LL + ({ "a".to_string() } + "b" + { "c" }) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:425:5 + --> tests/ui/needless_return.rs:426:5 | LL | return "".split("").next().unwrap().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -691,5 +691,17 @@ LL - return "".split("").next().unwrap().to_string(); LL + "".split("").next().unwrap().to_string() | -error: aborting due to 54 previous errors +error: unneeded `return` statement + --> tests/ui/needless_return.rs:461:5 + | +LL | return unsafe { todo() } as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `return` and wrap the sequence with parentheses + | +LL - return unsafe { todo() } as *const i32; +LL + (unsafe { todo() } as *const i32) + | + +error: aborting due to 55 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 2d8e04c192e40..e0f54ef899b05 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -422,6 +422,34 @@ pub fn issue12205() -> Option<()> { } } +fn stmt_after_return() { + for v in 0..10 { + //~^ never_loop + break; + println!("{v}"); + } +} + +fn loop_label() { + 'outer: for v in 0..10 { + //~^ never_loop + loop { + //~^ never_loop + break 'outer; + } + return; + } + + for v in 0..10 { + //~^ never_loop + 'inner: loop { + //~^ never_loop + break 'inner; + } + return; + } +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index f6d6d109542b5..bc9a7ec48b487 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -164,5 +164,73 @@ LL | | unimplemented!("not yet"); LL | | } | |_____^ -error: aborting due to 16 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:426:5 + | +LL | / for v in 0..10 { +LL | | +LL | | break; +LL | | println!("{v}"); +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:434:5 + | +LL | / 'outer: for v in 0..10 { +LL | | +LL | | loop { +... | +LL | | return; +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - 'outer: for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:436:9 + | +LL | / loop { +LL | | +LL | | break 'outer; +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:443:5 + | +LL | / for v in 0..10 { +LL | | +LL | | 'inner: loop { +... | +LL | | return; +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:445:9 + | +LL | / 'inner: loop { +LL | | +LL | | break 'inner; +LL | | } + | |_________^ + +error: aborting due to 21 previous errors diff --git a/tests/ui/never_loop_fixable.fixed b/tests/ui/never_loop_fixable.fixed new file mode 100644 index 0000000000000..5bc9ff1bb4df0 --- /dev/null +++ b/tests/ui/never_loop_fixable.fixed @@ -0,0 +1,20 @@ +#![allow(clippy::iter_next_slice, clippy::needless_return)] + +fn no_break_or_continue_loop() { + if let Some(i) = [1, 2, 3].iter().next() { + //~^ never_loop + return; + } +} + +fn no_break_or_continue_loop_outer() { + if let Some(i) = [1, 2, 3].iter().next() { + //~^ never_loop + return; + loop { + if true { + continue; + } + } + } +} diff --git a/tests/ui/never_loop_fixable.rs b/tests/ui/never_loop_fixable.rs new file mode 100644 index 0000000000000..9782bc107e9a6 --- /dev/null +++ b/tests/ui/never_loop_fixable.rs @@ -0,0 +1,20 @@ +#![allow(clippy::iter_next_slice, clippy::needless_return)] + +fn no_break_or_continue_loop() { + for i in [1, 2, 3].iter() { + //~^ never_loop + return; + } +} + +fn no_break_or_continue_loop_outer() { + for i in [1, 2, 3].iter() { + //~^ never_loop + return; + loop { + if true { + continue; + } + } + } +} diff --git a/tests/ui/never_loop_fixable.stderr b/tests/ui/never_loop_fixable.stderr new file mode 100644 index 0000000000000..ee02d9a429760 --- /dev/null +++ b/tests/ui/never_loop_fixable.stderr @@ -0,0 +1,35 @@ +error: this loop never actually loops + --> tests/ui/never_loop_fixable.rs:4:5 + | +LL | / for i in [1, 2, 3].iter() { +LL | | +LL | | return; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default +help: if you need the first element of the iterator, try writing + | +LL - for i in [1, 2, 3].iter() { +LL + if let Some(i) = [1, 2, 3].iter().next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop_fixable.rs:11:5 + | +LL | / for i in [1, 2, 3].iter() { +LL | | +LL | | return; +LL | | loop { +... | +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for i in [1, 2, 3].iter() { +LL + if let Some(i) = [1, 2, 3].iter().next() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index f5a869cf28318..ee30988960175 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -268,3 +268,23 @@ fn issue11893() { panic!("Haven't thought about this condition."); } } + +mod issue13964 { + #[derive(Debug)] + struct A(Option); + + fn foo(a: A) { + let _ = match a.0 { + Some(x) => x, + None => panic!("{a:?} is invalid."), + }; + } + + fn bar(a: A) { + let _ = if let Some(x) = a.0 { + x + } else { + panic!("{a:?} is invalid.") + }; + } +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index d48272e618acc..525a5df4371c2 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -331,3 +331,23 @@ fn issue11893() { panic!("Haven't thought about this condition."); } } + +mod issue13964 { + #[derive(Debug)] + struct A(Option); + + fn foo(a: A) { + let _ = match a.0 { + Some(x) => x, + None => panic!("{a:?} is invalid."), + }; + } + + fn bar(a: A) { + let _ = if let Some(x) = a.0 { + x + } else { + panic!("{a:?} is invalid.") + }; + } +} diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed index 1ccd2c2237dee..df6305ed497e8 100644 --- a/tests/ui/ptr_eq.fixed +++ b/tests/ui/ptr_eq.fixed @@ -20,8 +20,10 @@ fn main() { //~^ ptr_eq let _ = std::ptr::eq(a, b); //~^ ptr_eq - let _ = a.as_ptr() == b as *const _; - let _ = a.as_ptr() == b.as_ptr(); + let _ = std::ptr::eq(a.as_ptr(), b as *const _); + //~^ ptr_eq + let _ = std::ptr::eq(a.as_ptr(), b.as_ptr()); + //~^ ptr_eq // Do not lint @@ -31,9 +33,22 @@ fn main() { let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - let _ = a.as_mut_ptr() == b.as_mut_ptr(); + let _ = std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); + //~^ ptr_eq + let _ = std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); + + let (x, y) = (&0u32, &mut 1u32); + let _ = std::ptr::eq(x, y); + //~^ ptr_eq + + let _ = !std::ptr::eq(x, y); + //~^ ptr_eq + + #[allow(clippy::eq_op)] + let _issue14337 = std::ptr::eq(main as *const (), main as *const ()); + //~^ ptr_eq } diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs index 0bc58a57fa53d..0ed0ff0d13716 100644 --- a/tests/ui/ptr_eq.rs +++ b/tests/ui/ptr_eq.rs @@ -21,7 +21,9 @@ fn main() { let _ = a as *const _ == b as *const _; //~^ ptr_eq let _ = a.as_ptr() == b as *const _; + //~^ ptr_eq let _ = a.as_ptr() == b.as_ptr(); + //~^ ptr_eq // Do not lint @@ -32,8 +34,21 @@ fn main() { let b = &mut [1, 2, 3]; let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + //~^ ptr_eq let _ = a.as_mut_ptr() == b.as_mut_ptr(); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); + + let (x, y) = (&0u32, &mut 1u32); + let _ = x as *const u32 == y as *mut u32 as *const u32; + //~^ ptr_eq + + let _ = x as *const u32 != y as *mut u32 as *const u32; + //~^ ptr_eq + + #[allow(clippy::eq_op)] + let _issue14337 = main as *const () == main as *const (); + //~^ ptr_eq } diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr index 8e8b34f26ff77..33190df284a3f 100644 --- a/tests/ui/ptr_eq.stderr +++ b/tests/ui/ptr_eq.stderr @@ -13,5 +13,47 @@ error: use `std::ptr::eq` when comparing raw pointers LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` -error: aborting due to 2 previous errors +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:23:13 + | +LL | let _ = a.as_ptr() == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b as *const _)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:25:13 + | +LL | let _ = a.as_ptr() == b.as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b.as_ptr())` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:36:13 + | +LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:38:13 + | +LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:45:13 + | +LL | let _ = x as *const u32 == y as *mut u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:48:13 + | +LL | let _ = x as *const u32 != y as *mut u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:52:23 + | +LL | let _issue14337 = main as *const () == main as *const (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(main as *const (), main as *const ())` + +error: aborting due to 9 previous errors diff --git a/tests/ui/ptr_eq_no_std.fixed b/tests/ui/ptr_eq_no_std.fixed index b3e82fae38f30..d8ee4ea88f843 100644 --- a/tests/ui/ptr_eq_no_std.fixed +++ b/tests/ui/ptr_eq_no_std.fixed @@ -32,8 +32,10 @@ fn main() { //~^ ptr_eq let _ = core::ptr::eq(a, b); //~^ ptr_eq - let _ = a.as_ptr() == b as *const _; - let _ = a.as_ptr() == b.as_ptr(); + let _ = core::ptr::eq(a.as_ptr(), b as *const _); + //~^ ptr_eq + let _ = core::ptr::eq(a.as_ptr(), b.as_ptr()); + //~^ ptr_eq // Do not lint @@ -43,8 +45,10 @@ fn main() { let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - let _ = a.as_mut_ptr() == b.as_mut_ptr(); + let _ = core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); + //~^ ptr_eq + let _ = core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.rs b/tests/ui/ptr_eq_no_std.rs index ba78f5ee5f84f..a236314c29b77 100644 --- a/tests/ui/ptr_eq_no_std.rs +++ b/tests/ui/ptr_eq_no_std.rs @@ -33,7 +33,9 @@ fn main() { let _ = a as *const _ == b as *const _; //~^ ptr_eq let _ = a.as_ptr() == b as *const _; + //~^ ptr_eq let _ = a.as_ptr() == b.as_ptr(); + //~^ ptr_eq // Do not lint @@ -44,7 +46,9 @@ fn main() { let b = &mut [1, 2, 3]; let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + //~^ ptr_eq let _ = a.as_mut_ptr() == b.as_mut_ptr(); + //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.stderr b/tests/ui/ptr_eq_no_std.stderr index 8c7b1ff76661f..5b8135dc8e8bc 100644 --- a/tests/ui/ptr_eq_no_std.stderr +++ b/tests/ui/ptr_eq_no_std.stderr @@ -13,5 +13,29 @@ error: use `core::ptr::eq` when comparing raw pointers LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` -error: aborting due to 2 previous errors +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:35:13 + | +LL | let _ = a.as_ptr() == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b as *const _)` + +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:37:13 + | +LL | let _ = a.as_ptr() == b.as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b.as_ptr())` + +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:48:13 + | +LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` + +error: use `core::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq_no_std.rs:50:13 + | +LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` + +error: aborting due to 6 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 8dfef3202be9c..fff41f578284d 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -375,3 +375,58 @@ fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { //~^^^ question_mark Some(()) } + +struct StructWithOptionString { + opt_x: Option, +} + +struct WrapperStructWithString(String); + +#[allow(clippy::disallowed_names)] +fn issue_13417(foo: &mut StructWithOptionString) -> Option { + let x = foo.opt_x.as_ref()?; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +fn issue_13417_mut(foo: &mut StructWithOptionString) -> Option { + let x = foo.opt_x.as_mut()?; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +#[allow(unused)] +fn issue_13417_weirder(foo: &mut StructWithOptionString, mut bar: Option) -> Option<()> { + let x @ y = foo.opt_x.as_ref()?; + //~^^^ question_mark + let x @ &WrapperStructWithString(_) = bar.as_ref()?; + //~^^^ question_mark + let x @ &mut WrapperStructWithString(_) = bar.as_mut()?; + //~^^^ question_mark + Some(()) +} + +#[clippy::msrv = "1.12"] +fn msrv_1_12(arg: Option) -> Option { + if arg.is_none() { + return None; + } + let val = match arg { + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} + +#[clippy::msrv = "1.13"] +fn msrv_1_13(arg: Option) -> Option { + arg?; + let val = arg?; + println!("{}", val); + Some(val) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index fffaa803f39c2..c71c8ee984edd 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -452,3 +452,75 @@ fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { //~^^^ question_mark Some(()) } + +struct StructWithOptionString { + opt_x: Option, +} + +struct WrapperStructWithString(String); + +#[allow(clippy::disallowed_names)] +fn issue_13417(foo: &mut StructWithOptionString) -> Option { + let Some(ref x) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +fn issue_13417_mut(foo: &mut StructWithOptionString) -> Option { + let Some(ref mut x) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +#[allow(unused)] +fn issue_13417_weirder(foo: &mut StructWithOptionString, mut bar: Option) -> Option<()> { + let Some(ref x @ ref y) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let Some(ref x @ WrapperStructWithString(_)) = bar else { + return None; + }; + //~^^^ question_mark + let Some(ref mut x @ WrapperStructWithString(_)) = bar else { + return None; + }; + //~^^^ question_mark + Some(()) +} + +#[clippy::msrv = "1.12"] +fn msrv_1_12(arg: Option) -> Option { + if arg.is_none() { + return None; + } + let val = match arg { + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} + +#[clippy::msrv = "1.13"] +fn msrv_1_13(arg: Option) -> Option { + if arg.is_none() { + //~^ question_mark + return None; + } + let val = match arg { + //~^ question_mark + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index c4db0fbc30229..183b8866a7481 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -215,5 +215,65 @@ LL | | return None; LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` -error: aborting due to 22 previous errors +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:464:5 + | +LL | / let Some(ref x) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:474:5 + | +LL | / let Some(ref mut x) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:485:5 + | +LL | / let Some(ref x @ ref y) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:489:5 + | +LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:493:5 + | +LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:515:5 + | +LL | / if arg.is_none() { +LL | | +LL | | return None; +LL | | } + | |_____^ help: replace it with: `arg?;` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:519:15 + | +LL | let val = match arg { + | _______________^ +LL | | +LL | | Some(val) => val, +LL | | None => return None, +LL | | }; + | |_____^ help: try instead: `arg?` + +error: aborting due to 29 previous errors diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index b7ed3aab00434..a98b73c9e1c82 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -3,12 +3,7 @@ // ifs_same_cond warning is different from `ifs_same_cond`. // clippy::if_same_then_else, clippy::comparison_chain -- all empty blocks #![allow(incomplete_features)] -#![allow( - clippy::comparison_chain, - clippy::if_same_then_else, - clippy::ifs_same_cond, - clippy::uninlined_format_args -)] +#![allow(clippy::if_same_then_else, clippy::ifs_same_cond, clippy::uninlined_format_args)] use std::marker::ConstParamTy; diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 6cd4f96c13e32..35dcbadce5953 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,11 +1,11 @@ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:39:15 + --> tests/ui/same_functions_in_if_condition.rs:34:15 | LL | } else if function() { | ^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:38:8 + --> tests/ui/same_functions_in_if_condition.rs:33:8 | LL | if function() { | ^^^^^^^^^^ @@ -16,61 +16,61 @@ LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:44:15 + --> tests/ui/same_functions_in_if_condition.rs:39:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:43:8 + --> tests/ui/same_functions_in_if_condition.rs:38:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:49:15 + --> tests/ui/same_functions_in_if_condition.rs:44:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:48:8 + --> tests/ui/same_functions_in_if_condition.rs:43:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:54:15 + --> tests/ui/same_functions_in_if_condition.rs:49:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:53:8 + --> tests/ui/same_functions_in_if_condition.rs:48:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:60:15 + --> tests/ui/same_functions_in_if_condition.rs:55:15 | LL | } else if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:59:8 + --> tests/ui/same_functions_in_if_condition.rs:54:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:65:15 + --> tests/ui/same_functions_in_if_condition.rs:60:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> tests/ui/same_functions_in_if_condition.rs:64:8 + --> tests/ui/same_functions_in_if_condition.rs:59:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index c6ffe93eb7ab3..0e198ec79344a 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -1,3 +1,4 @@ +//@require-annotations-for-level: WARN #![warn(clippy::single_match)] #![allow( unused, @@ -18,13 +19,9 @@ fn single_match() { //~^^^^^^ single_match let x = Some(1u8); - match x { - // Note the missing block braces. - // We suggest `if let Some(y) = x { .. }` because the macro - // is expanded before we can do anything. - Some(y) => println!("{:?}", y), - _ => (), - } + if let Some(y) = x { println!("{:?}", y) } + //~^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` let z = (1u8, 1u8); if let (2..=3, 7..=9) = z { dummy() }; @@ -358,21 +355,14 @@ fn irrefutable_match() { let mut x = vec![1i8]; - // Should not lint. - match x.pop() { - // bla - Some(u) => println!("{u}"), - // more comments! - None => {}, - } - // Should not lint. - match x.pop() { - // bla - Some(u) => { - // bla - println!("{u}"); - }, + if let Some(u) = x.pop() { println!("{u}") } + //~^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` + + if let Some(u) = x.pop() { // bla - None => {}, + println!("{u}"); } + //~^^^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` } diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index dc758fa4281c8..fcac65f8aaf5e 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,3 +1,4 @@ +//@require-annotations-for-level: WARN #![warn(clippy::single_match)] #![allow( unused, @@ -28,6 +29,8 @@ fn single_match() { Some(y) => println!("{:?}", y), _ => (), } + //~^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` let z = (1u8, 1u8); match z { @@ -437,14 +440,15 @@ fn irrefutable_match() { let mut x = vec![1i8]; - // Should not lint. match x.pop() { // bla Some(u) => println!("{u}"), // more comments! None => {}, } - // Should not lint. + //~^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` + match x.pop() { // bla Some(u) => { @@ -454,4 +458,6 @@ fn irrefutable_match() { // bla None => {}, } + //~^^^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index c882969594892..2467423b9c17d 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:15:5 + --> tests/ui/single_match.rs:16:5 | LL | / match x { LL | | Some(y) => { @@ -19,7 +19,18 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:33:5 + --> tests/ui/single_match.rs:25:5 + | +LL | / match x { +... | +LL | | _ => (), +LL | | } + | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:36:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -28,7 +39,7 @@ LL | | }; | |_____^ help: try: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:63:5 + --> tests/ui/single_match.rs:66:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -37,7 +48,7 @@ LL | | }; | |_____^ help: try: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:69:5 + --> tests/ui/single_match.rs:72:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -46,7 +57,7 @@ LL | | }; | |_____^ help: try: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:77:5 + --> tests/ui/single_match.rs:80:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -55,7 +66,7 @@ LL | | }; | |_____^ help: try: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:99:5 + --> tests/ui/single_match.rs:102:5 | LL | / match x { LL | | "test" => println!(), @@ -64,7 +75,7 @@ LL | | } | |_____^ help: try: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:113:5 + --> tests/ui/single_match.rs:116:5 | LL | / match x { LL | | Foo::A => println!(), @@ -73,7 +84,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:120:5 + --> tests/ui/single_match.rs:123:5 | LL | / match x { LL | | FOO_C => println!(), @@ -82,7 +93,7 @@ LL | | } | |_____^ help: try: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:126:5 + --> tests/ui/single_match.rs:129:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -91,7 +102,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:133:5 + --> tests/ui/single_match.rs:136:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -100,7 +111,7 @@ LL | | } | |_____^ help: try: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:151:5 + --> tests/ui/single_match.rs:154:5 | LL | / match x { LL | | Bar::A => println!(), @@ -109,7 +120,7 @@ LL | | } | |_____^ help: try: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:160:5 + --> tests/ui/single_match.rs:163:5 | LL | / match x { LL | | None => println!(), @@ -118,7 +129,7 @@ LL | | }; | |_____^ help: try: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:183:5 + --> tests/ui/single_match.rs:186:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -127,7 +138,7 @@ LL | | } | |_____^ help: try: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:190:5 + --> tests/ui/single_match.rs:193:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -136,7 +147,7 @@ LL | | } | |_____^ help: try: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:197:5 + --> tests/ui/single_match.rs:200:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -145,7 +156,7 @@ LL | | } | |_____^ help: try: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:270:5 + --> tests/ui/single_match.rs:273:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -165,7 +176,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:279:5 + --> tests/ui/single_match.rs:282:5 | LL | / match bar { LL | | #[rustfmt::skip] @@ -187,7 +198,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:360:5 + --> tests/ui/single_match.rs:363:5 | LL | / match Ok::<_, u32>(Some(A)) { LL | | Ok(Some(A)) => println!(), @@ -196,7 +207,7 @@ LL | | } | |_____^ help: try: `if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:376:5 + --> tests/ui/single_match.rs:379:5 | LL | / match &Some(A) { LL | | Some(A | B) => println!(), @@ -205,7 +216,7 @@ LL | | } | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:384:5 + --> tests/ui/single_match.rs:387:5 | LL | / match &s[0..3] { LL | | b"foo" => println!(), @@ -214,7 +225,7 @@ LL | | } | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:398:5 + --> tests/ui/single_match.rs:401:5 | LL | / match DATA { LL | | DATA => println!(), @@ -223,7 +234,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:404:5 + --> tests/ui/single_match.rs:407:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -232,7 +243,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:411:5 + --> tests/ui/single_match.rs:414:5 | LL | / match i { LL | | i => { @@ -252,7 +263,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:420:5 + --> tests/ui/single_match.rs:423:5 | LL | / match i { LL | | i => {}, @@ -261,7 +272,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:426:5 + --> tests/ui/single_match.rs:429:5 | LL | / match i { LL | | i => (), @@ -270,7 +281,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:432:5 + --> tests/ui/single_match.rs:435:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -278,5 +289,37 @@ LL | | _ => {}, LL | | } | |_____^ help: try: `println!();` -error: aborting due to 26 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:443:5 + | +LL | / match x.pop() { +LL | | // bla +LL | | Some(u) => println!("{u}"), +... | +LL | | } + | |_____^ help: try: `if let Some(u) = x.pop() { println!("{u}") }` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:452:5 + | +LL | / match x.pop() { +LL | | // bla +LL | | Some(u) => { +... | +LL | | None => {}, +LL | | } + | |_____^ + | + = note: you might want to preserve the comments from inside the `match` +help: try + | +LL ~ if let Some(u) = x.pop() { +LL + // bla +LL + println!("{u}"); +LL + } + | + +error: aborting due to 29 previous errors diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index 64782bf62a782..fde13fb90dbb2 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -1,4 +1,5 @@ //@aux-build: proc_macros.rs +//@require-annotations-for-level: WARN #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] @@ -90,6 +91,13 @@ fn main() { } //~^^^^^^^ single_match_else + if let Some(a) = Some(1) { println!("${:?}", a) } else { + println!("else block"); + return; + } + //~^^^^^^^^ single_match_else + //~| NOTE: you might want to preserve the comments from inside the `match` + // lint here use std::convert::Infallible; if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 3f86f4d51803f..ca282200067ce 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,4 +1,5 @@ //@aux-build: proc_macros.rs +//@require-annotations-for-level: WARN #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] @@ -99,6 +100,17 @@ fn main() { } //~^^^^^^^ single_match_else + match Some(1) { + Some(a) => println!("${:?}", a), + // This is an inner comment + None => { + println!("else block"); + return; + }, + } + //~^^^^^^^^ single_match_else + //~| NOTE: you might want to preserve the comments from inside the `match` + // lint here use std::convert::Infallible; match Result::::Ok(1) { diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 7d4ba5fb75ef8..570480f9a3f0a 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:17:13 + --> tests/ui/single_match_else.rs:18:13 | LL | let _ = match ExprNode::Butterflies { | _____________^ @@ -22,7 +22,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:83:5 + --> tests/ui/single_match_else.rs:84:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -42,7 +42,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:93:5 + --> tests/ui/single_match_else.rs:94:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -62,7 +62,28 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:104:5 + --> tests/ui/single_match_else.rs:103:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | // This is an inner comment +LL | | None => { +... | +LL | | }, +LL | | } + | |_____^ + | + = note: you might want to preserve the comments from inside the `match` +help: try + | +LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL + println!("else block"); +LL + return; +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match_else.rs:116:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -81,7 +102,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:114:5 + --> tests/ui/single_match_else.rs:126:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), @@ -100,7 +121,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:125:5 + --> tests/ui/single_match_else.rs:137:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -123,7 +144,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:137:5 + --> tests/ui/single_match_else.rs:149:5 | LL | / match bar { LL | | Some(v) => { @@ -147,7 +168,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:150:5 + --> tests/ui/single_match_else.rs:162:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -171,7 +192,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:163:5 + --> tests/ui/single_match_else.rs:175:5 | LL | / match bar { LL | | #[rustfmt::skip] @@ -196,7 +217,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match_else.rs:213:5 + --> tests/ui/single_match_else.rs:225:5 | LL | / match ExprNode::Butterflies { LL | | ExprNode::Butterflies => Some(&NODE), @@ -207,5 +228,5 @@ LL | | }, LL | | } | |_____^ help: try: `Some(&NODE)` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/size_of_ref.stderr b/tests/ui/size_of_ref.stderr index 6ac0b0dd2f065..46af9f55deaf6 100644 --- a/tests/ui/size_of_ref.stderr +++ b/tests/ui/size_of_ref.stderr @@ -1,28 +1,28 @@ -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:13:5 | LL | size_of_val(&&x); | ^^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type = note: `-D clippy::size-of-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::size_of_ref)]` -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:16:5 | LL | size_of_val(&y); | ^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:28:9 | LL | std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type error: aborting due to 3 previous errors diff --git a/tests/ui/string_to_string.rs b/tests/ui/string_to_string.rs index 94174e1253b8c..7c5bd8a897baa 100644 --- a/tests/ui/string_to_string.rs +++ b/tests/ui/string_to_string.rs @@ -1,8 +1,21 @@ #![warn(clippy::string_to_string)] -#![allow(clippy::redundant_clone)] +#![allow(clippy::redundant_clone, clippy::unnecessary_literal_unwrap)] fn main() { let mut message = String::from("Hello"); let mut v = message.to_string(); //~^ string_to_string + + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.map(|x| { + println!(); + x.to_string() + }); + //~^^ string_to_string + + let x = Some(String::new()); + let _ = x.unwrap_or_else(|| v.to_string()); + //~^ string_to_string } diff --git a/tests/ui/string_to_string.stderr b/tests/ui/string_to_string.stderr index ae80597d1f841..99eea06f18ebf 100644 --- a/tests/ui/string_to_string.stderr +++ b/tests/ui/string_to_string.stderr @@ -8,5 +8,21 @@ LL | let mut v = message.to_string(); = note: `-D clippy::string-to-string` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::string_to_string)]` -error: aborting due to 1 previous error +error: `to_string()` called on a `String` + --> tests/ui/string_to_string.rs:14:9 + | +LL | x.to_string() + | ^^^^^^^^^^^^^ + | + = help: consider using `.clone()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string.rs:19:33 + | +LL | let _ = x.unwrap_or_else(|| v.to_string()); + | ^^^^^^^^^^^^^ + | + = help: consider using `.clone()` + +error: aborting due to 3 previous errors diff --git a/tests/ui/string_to_string_in_map.fixed b/tests/ui/string_to_string_in_map.fixed new file mode 100644 index 0000000000000..efc085539f154 --- /dev/null +++ b/tests/ui/string_to_string_in_map.fixed @@ -0,0 +1,20 @@ +#![deny(clippy::string_to_string)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] + +fn main() { + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.cloned(); + //~^ string_to_string + let _ = variable2.cloned(); + //~^ string_to_string + #[rustfmt::skip] + let _ = variable2.cloned(); + //~^ string_to_string + + let _ = vec![String::new()].iter().cloned().collect::>(); + //~^ string_to_string + let _ = vec![String::new()].iter().cloned().collect::>(); + //~^ string_to_string +} diff --git a/tests/ui/string_to_string_in_map.rs b/tests/ui/string_to_string_in_map.rs new file mode 100644 index 0000000000000..5bf1d7ba5a2e6 --- /dev/null +++ b/tests/ui/string_to_string_in_map.rs @@ -0,0 +1,20 @@ +#![deny(clippy::string_to_string)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] + +fn main() { + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.map(String::to_string); + //~^ string_to_string + let _ = variable2.map(|x| x.to_string()); + //~^ string_to_string + #[rustfmt::skip] + let _ = variable2.map(|x| { x.to_string() }); + //~^ string_to_string + + let _ = vec![String::new()].iter().map(String::to_string).collect::>(); + //~^ string_to_string + let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); + //~^ string_to_string +} diff --git a/tests/ui/string_to_string_in_map.stderr b/tests/ui/string_to_string_in_map.stderr new file mode 100644 index 0000000000000..35aeed656eed0 --- /dev/null +++ b/tests/ui/string_to_string_in_map.stderr @@ -0,0 +1,38 @@ +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:8:23 + | +LL | let _ = variable2.map(String::to_string); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + | +note: the lint level is defined here + --> tests/ui/string_to_string_in_map.rs:1:9 + | +LL | #![deny(clippy::string_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:10:23 + | +LL | let _ = variable2.map(|x| x.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:13:23 + | +LL | let _ = variable2.map(|x| { x.to_string() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:16:40 + | +LL | let _ = vec![String::new()].iter().map(String::to_string).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:18:40 + | +LL | let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/struct_fields.rs b/tests/ui/struct_fields.rs index 3dce530efffaa..e7ff2e6573b2a 100644 --- a/tests/ui/struct_fields.rs +++ b/tests/ui/struct_fields.rs @@ -344,4 +344,31 @@ struct Use { //~^ struct_field_names } +// should lint on private fields of public structs (renaming them is not breaking-exported-api) +pub struct PubStructFieldNamedAfterStruct { + pub_struct_field_named_after_struct: bool, + //~^ ERROR: field name starts with the struct's name + other1: bool, + other2: bool, +} +pub struct PubStructFieldPrefix { + //~^ ERROR: all fields have the same prefix: `field` + field_foo: u8, + field_bar: u8, + field_baz: u8, +} +// ...but should not lint on structs with public fields. +pub struct PubStructPubAndPrivateFields { + /// One could argue that this field should be linted, but currently, any public field stops all + /// checking. + pub_struct_pub_and_private_fields_1: bool, + pub pub_struct_pub_and_private_fields_2: bool, +} +// nor on common prefixes if one of the involved fields is public +pub struct PubStructPubAndPrivateFieldPrefix { + pub field_foo: u8, + field_bar: u8, + field_baz: u8, +} + fn main() {} diff --git a/tests/ui/struct_fields.stderr b/tests/ui/struct_fields.stderr index 79186cc1cfd8f..a5ff1b1259074 100644 --- a/tests/ui/struct_fields.stderr +++ b/tests/ui/struct_fields.stderr @@ -281,5 +281,24 @@ error: field name starts with the struct's name LL | use_baz: bool, | ^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: field name starts with the struct's name + --> tests/ui/struct_fields.rs:349:5 + | +LL | pub_struct_field_named_after_struct: bool, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: all fields have the same prefix: `field` + --> tests/ui/struct_fields.rs:354:1 + | +LL | / pub struct PubStructFieldPrefix { +LL | | +LL | | field_foo: u8, +LL | | field_bar: u8, +LL | | field_baz: u8, +LL | | } + | |_^ + | + = help: remove the prefixes + +error: aborting due to 26 previous errors diff --git a/tests/ui/unnecessary_path_debug_formatting.rs b/tests/ui/unnecessary_path_debug_formatting.rs index 02adeece2809b..f14f6085c9a14 100644 --- a/tests/ui/unnecessary_path_debug_formatting.rs +++ b/tests/ui/unnecessary_path_debug_formatting.rs @@ -41,3 +41,9 @@ fn main() { let deref_path = DerefPath { path }; println!("{:?}", &*deref_path); //~ unnecessary_debug_formatting } + +#[test] +fn issue_14345() { + let input = std::path::Path::new("/foo/bar"); + assert!(input.ends_with("baz"), "{input:?}"); +} diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index bf271aef763bb..5410033dbd8f4 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -195,19 +195,11 @@ fn main() { //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned let _ = check_files(&[FileType::Account]); @@ -317,19 +309,6 @@ fn get_file_path(_file_type: &FileType) -> Result impl IntoIterator { cow.into_owned().into_iter() } + +mod issue_14242 { + use std::rc::Rc; + + #[derive(Copy, Clone)] + struct Foo; + + fn rc_slice_provider() -> Rc<[Foo]> { + Rc::from([Foo]) + } + + fn iterator_provider() -> impl Iterator { + rc_slice_provider().to_vec().into_iter() + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 95b95ab6bd222..0619dd4ddec09 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -195,19 +195,11 @@ fn main() { //~^ unnecessary_to_owned let _ = slice.to_owned().into_iter(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); - //~^ unnecessary_to_owned let _ = IntoIterator::into_iter(slice.to_vec()); //~^ unnecessary_to_owned let _ = IntoIterator::into_iter(slice.to_owned()); //~^ unnecessary_to_owned - let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); - //~^ unnecessary_to_owned - let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); - //~^ unnecessary_to_owned let _ = check_files(&[FileType::Account]); @@ -317,19 +309,6 @@ fn get_file_path(_file_type: &FileType) -> Result impl IntoIterator { cow.into_owned().into_iter() } + +mod issue_14242 { + use std::rc::Rc; + + #[derive(Copy, Clone)] + struct Foo; + + fn rc_slice_provider() -> Rc<[Foo]> { + Rc::from([Foo]) + } + + fn iterator_provider() -> impl Iterator { + rc_slice_provider().to_vec().into_iter() + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 4daa3876e60eb..8926db34da8c8 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:225:64 + --> tests/ui/unnecessary_to_owned.rs:217:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:225:20 + --> tests/ui/unnecessary_to_owned.rs:217:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,49 +13,49 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:227:40 + --> tests/ui/unnecessary_to_owned.rs:219:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:227:21 + --> tests/ui/unnecessary_to_owned.rs:219:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:229:48 + --> tests/ui/unnecessary_to_owned.rs:221:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:229:19 + --> tests/ui/unnecessary_to_owned.rs:221:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:231:35 + --> tests/ui/unnecessary_to_owned.rs:223:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:231:18 + --> tests/ui/unnecessary_to_owned.rs:223:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:233:39 + --> tests/ui/unnecessary_to_owned.rs:225:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:233:20 + --> tests/ui/unnecessary_to_owned.rs:225:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ @@ -442,43 +442,19 @@ LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:198:13 - | -LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:200:13 - | -LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:203:13 + --> tests/ui/unnecessary_to_owned.rs:199:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:205:13 + --> tests/ui/unnecessary_to_owned.rs:201:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:207:13 - | -LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:209:13 - | -LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:237:26 + --> tests/ui/unnecessary_to_owned.rs:229:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +466,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:239:26 + --> tests/ui/unnecessary_to_owned.rs:231:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +478,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:241:26 + --> tests/ui/unnecessary_to_owned.rs:233:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:299:14 + --> tests/ui/unnecessary_to_owned.rs:291:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -526,65 +502,53 @@ LL | LL ~ let path = match get_file_path(t) { | -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:323:14 - | -LL | let _ = &["x"][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` - -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:329:14 - | -LL | let _ = &["x"][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` - error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:378:24 + --> tests/ui/unnecessary_to_owned.rs:357:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:488:12 + --> tests/ui/unnecessary_to_owned.rs:467:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:632:37 + --> tests/ui/unnecessary_to_owned.rs:611:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:643:18 + --> tests/ui/unnecessary_to_owned.rs:622:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:648:14 + --> tests/ui/unnecessary_to_owned.rs:627:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:650:14 + --> tests/ui/unnecessary_to_owned.rs:629:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:656:14 + --> tests/ui/unnecessary_to_owned.rs:635:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:658:14 + --> tests/ui/unnecessary_to_owned.rs:637:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` -error: aborting due to 88 previous errors +error: aborting due to 82 previous errors diff --git a/triagebot.toml b/triagebot.toml index 3d35116ebc178..33d3b0728f3d1 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -34,5 +34,4 @@ users_on_vacation = [ "@Jarcho", "@blyxyas", "@y21", - "@Centri3", ] diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index a9b6462800307..19dc1ec0b0cc6 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -19,10 +19,10 @@ {# #} - {# #} - {# #} - {# #} - {# #} + {# #} + {# #} + {# #} + {# #} {# #} {# #} {# #} From 19c7c46d48dc659de44d85845d53ed594b95c286 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Thu, 20 Mar 2025 11:30:45 +0900 Subject: [PATCH 027/103] rename `rust-toolchain` to `rust-toolchain.toml` --- book/src/development/basics.md | 2 +- book/src/development/infrastructure/sync.md | 2 +- clippy_dev/src/main.rs | 2 +- clippy_dev/src/setup/toolchain.rs | 2 +- clippy_dev/src/sync.rs | 2 +- rust-toolchain => rust-toolchain.toml | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename rust-toolchain => rust-toolchain.toml (100%) diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 931e5c3a2942a..4219724ed5df9 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -147,7 +147,7 @@ following: First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in -`/rust-toolchain`. We will use this override to install Clippy into the right +`/rust-toolchain.toml`. We will use this override to install Clippy into the right toolchain. > Tip: You can view the active toolchain for the current directory with `rustup diff --git a/book/src/development/infrastructure/sync.md b/book/src/development/infrastructure/sync.md index da1ad586607f9..2bbdf47a83581 100644 --- a/book/src/development/infrastructure/sync.md +++ b/book/src/development/infrastructure/sync.md @@ -86,7 +86,7 @@ to be run inside the `rust` directory): 4. Bump the nightly version in the Clippy repository by running these commands: ```bash cargo dev sync update_nightly - git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain clippy_utils/README.md + git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain.toml clippy_utils/README.md ``` 5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 074dea4ab77b6..0380b7e6a6dc9 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -334,7 +334,7 @@ struct SyncCommand { #[derive(Subcommand)] enum SyncSubcommand { #[command(name = "update_nightly")] - /// Update nightly version in rust-toolchain and `clippy_utils` + /// Update nightly version in `rust-toolchain.toml` and `clippy_utils` UpdateNightly, } diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs index 2966629cf70a3..ecd80215f7e8f 100644 --- a/clippy_dev/src/setup/toolchain.rs +++ b/clippy_dev/src/setup/toolchain.rs @@ -62,7 +62,7 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) { println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`"); if !standalone { - println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes"); + println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain.toml` changes"); } } diff --git a/clippy_dev/src/sync.rs b/clippy_dev/src/sync.rs index 3522d182e90ac..a6b65e561c223 100644 --- a/clippy_dev/src/sync.rs +++ b/clippy_dev/src/sync.rs @@ -10,7 +10,7 @@ pub fn update_nightly() { let date = Utc::now().format("%Y-%m-%d").to_string(); replace_region_in_file( UpdateMode::Change, - Path::new("rust-toolchain"), + Path::new("rust-toolchain.toml"), "# begin autogenerated nightly\n", "# end autogenerated nightly", |res| { diff --git a/rust-toolchain b/rust-toolchain.toml similarity index 100% rename from rust-toolchain rename to rust-toolchain.toml From aadda467525d6bb6ade388312cb71f20f8c0e2f4 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jan 2025 18:03:54 -0500 Subject: [PATCH 028/103] Add `ignore_without_reason` lint --- CHANGELOG.md | 1 + clippy_lints/src/attrs/mod.rs | 45 ++++++++++++++++++++++++++- clippy_lints/src/declared_lints.rs | 1 + tests/ui/ignore_without_reason.rs | 14 +++++++++ tests/ui/ignore_without_reason.stderr | 12 +++++++ 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/ui/ignore_without_reason.rs create mode 100644 tests/ui/ignore_without_reason.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf4b51ff0fe2..0d00638f2ee7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5681,6 +5681,7 @@ Released 2018-09-13 [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else [`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`ignore_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignore_without_reason [`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns [`impl_hash_borrow_with_str_and_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_hash_borrow_with_str_and_bytes [`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index e04d2ad5d13b7..7ba7b47661766 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -14,8 +14,9 @@ mod useless_attribute; mod utils; use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; -use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind}; +use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -448,6 +449,31 @@ declare_clippy_lint! { "duplicated attribute" } +declare_clippy_lint! { + /// ### What it does + /// Checks for ignored tests without messages. + /// + /// ### Why is this bad? + /// The reason for ignoring the test may not be obvious. + /// + /// ### Example + /// ```no_run + /// #[test] + /// #[ignore] + /// fn test() {} + /// ``` + /// Use instead: + /// ```no_run + /// #[test] + /// #[ignore = "Some good reason"] + /// fn test() {} + /// ``` + #[clippy::version = "1.85.0"] + pub IGNORE_WITHOUT_REASON, + restriction, + "ignored tests without messages" +} + pub struct Attributes { msrv: Msrv, } @@ -532,6 +558,7 @@ impl_lint_pass!(PostExpansionEarlyAttributes => [ ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, DEPRECATED_SEMVER, + IGNORE_WITHOUT_REASON, USELESS_ATTRIBUTE, BLANKET_CLIPPY_RESTRICTION_LINTS, SHOULD_PANIC_WITHOUT_EXPECT, @@ -575,6 +602,22 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if attr.has_name(sym::should_panic) { should_panic_without_expect::check(cx, attr); } + + if attr.has_name(sym::ignore) + && match &attr.kind { + AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }), + AttrKind::DocComment(..) => true, + } + { + span_lint_and_help( + cx, + IGNORE_WITHOUT_REASON, + attr.span, + "`#[ignore]` without reason", + None, + "add a reason with `= \"..\"`", + ); + } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7fa23dad69817..a9b6b369c4c39 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -52,6 +52,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, crate::attrs::DUPLICATED_ATTRIBUTES_INFO, + crate::attrs::IGNORE_WITHOUT_REASON_INFO, crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, diff --git a/tests/ui/ignore_without_reason.rs b/tests/ui/ignore_without_reason.rs new file mode 100644 index 0000000000000..eb8aaf179bedd --- /dev/null +++ b/tests/ui/ignore_without_reason.rs @@ -0,0 +1,14 @@ +#![warn(clippy::ignore_without_reason)] + +fn main() {} + +#[test] +fn unignored_test() {} + +#[test] +#[ignore = "Some good reason"] +fn ignored_with_reason() {} + +#[test] +#[ignore] +fn ignored_without_reason() {} diff --git a/tests/ui/ignore_without_reason.stderr b/tests/ui/ignore_without_reason.stderr new file mode 100644 index 0000000000000..4c0210c2bbc08 --- /dev/null +++ b/tests/ui/ignore_without_reason.stderr @@ -0,0 +1,12 @@ +error: `#[ignore]` without reason + --> tests/ui/ignore_without_reason.rs:13:1 + | +LL | #[ignore] + | ^^^^^^^^^ + | + = help: add a reason with `= ".."` + = note: `-D clippy::ignore-without-reason` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ignore_without_reason)]` + +error: aborting due to 1 previous error + From 475d10d3b05be14263dae9f624887d6eb2f2fa2d Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 3 Mar 2025 12:54:34 -0500 Subject: [PATCH 029/103] Add test annotation --- tests/ui/ignore_without_reason.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/ignore_without_reason.rs b/tests/ui/ignore_without_reason.rs index eb8aaf179bedd..53ac34c27248e 100644 --- a/tests/ui/ignore_without_reason.rs +++ b/tests/ui/ignore_without_reason.rs @@ -10,5 +10,5 @@ fn unignored_test() {} fn ignored_with_reason() {} #[test] -#[ignore] +#[ignore] //~ ignore_without_reason fn ignored_without_reason() {} From 721ac284de4b31ba2e4dd05e6afae08596bbc541 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Fri, 7 Mar 2025 15:05:11 +0800 Subject: [PATCH 030/103] fix: `filter_map_bool_then` suggest wrongly when contain return --- .../src/methods/filter_map_bool_then.rs | 27 ++++++---- tests/ui/filter_map_bool_then_unfixable.rs | 52 +++++++++++++++++++ .../ui/filter_map_bool_then_unfixable.stderr | 50 ++++++++++++++++++ 3 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 tests/ui/filter_map_bool_then_unfixable.rs create mode 100644 tests/ui/filter_map_bool_then_unfixable.stderr diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index f7e116c5310ed..3461f539b0986 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,8 +1,8 @@ use super::FILTER_MAP_BOOL_THEN; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; -use clippy_utils::{is_from_proc_macro, is_trait_method, peel_blocks}; +use clippy_utils::{contains_return, is_from_proc_macro, is_trait_method, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; @@ -44,17 +44,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(filter) = recv.span.get_source_text(cx) && let Some(map) = then_body.span.get_source_text(cx) { - span_lint_and_sugg( + span_lint_and_then( cx, FILTER_MAP_BOOL_THEN, call_span, "usage of `bool::then` in `filter_map`", - "use `filter` then `map` instead", - format!( - "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", - derefs = "*".repeat(needed_derefs) - ), - Applicability::MachineApplicable, + |diag| { + if contains_return(recv) { + diag.help("consider using `filter` then `map` instead"); + } else { + diag.span_suggestion( + call_span, + "use `filter` then `map` instead", + format!( + "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", + derefs = "*".repeat(needed_derefs) + ), + Applicability::MachineApplicable, + ); + } + }, ); } } diff --git a/tests/ui/filter_map_bool_then_unfixable.rs b/tests/ui/filter_map_bool_then_unfixable.rs new file mode 100644 index 0000000000000..f65b0b66f836e --- /dev/null +++ b/tests/ui/filter_map_bool_then_unfixable.rs @@ -0,0 +1,52 @@ +#![allow(clippy::question_mark, unused)] +#![warn(clippy::filter_map_bool_then)] +//@no-rustfix + +mod issue14368 { + + fn do_something(_: ()) -> bool { + true + } + + fn option_with_early_return(x: &[Option]) { + let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); + //~^ filter_map_bool_then + let _ = x + .iter() + .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); + //~^ filter_map_bool_then + let _ = x.iter().filter_map(|&x| { + //~^ filter_map_bool_then + match x { + Some(x) => x, + None => return None, + } + .then(|| do_something(())) + }); + } + + #[derive(Copy, Clone)] + enum Foo { + One(bool), + Two, + Three(Option), + } + + fn nested_type_with_early_return(x: &[Foo]) { + let _ = x.iter().filter_map(|&x| { + //~^ filter_map_bool_then + match x { + Foo::One(x) => x, + Foo::Two => return None, + Foo::Three(inner) => { + if inner? == 0 { + return Some(false); + } else { + true + } + }, + } + .then(|| do_something(())) + }); + } +} diff --git a/tests/ui/filter_map_bool_then_unfixable.stderr b/tests/ui/filter_map_bool_then_unfixable.stderr new file mode 100644 index 0000000000000..f72bcac8a7501 --- /dev/null +++ b/tests/ui/filter_map_bool_then_unfixable.stderr @@ -0,0 +1,50 @@ +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:12:26 + | +LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `filter` then `map` instead + = note: `-D clippy::filter-map-bool-then` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]` + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:16:14 + | +LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:18:26 + | +LL | let _ = x.iter().filter_map(|&x| { + | __________________________^ +LL | | +LL | | match x { +LL | | Some(x) => x, +... | +LL | | .then(|| do_something(())) +LL | | }); + | |__________^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:36:26 + | +LL | let _ = x.iter().filter_map(|&x| { + | __________________________^ +LL | | +LL | | match x { +LL | | Foo::One(x) => x, +... | +LL | | .then(|| do_something(())) +LL | | }); + | |__________^ + | + = help: consider using `filter` then `map` instead + +error: aborting due to 4 previous errors + From 3f9ecbe341a7d1975d688946e58929402399189c Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 11 Mar 2025 19:44:30 +0800 Subject: [PATCH 031/103] fix: `filter_map_bool_then`: suggests wrongly when mut capture in then --- .../src/methods/filter_map_bool_then.rs | 57 +++++++++++++++++-- tests/ui/filter_map_bool_then_unfixable.rs | 11 ++++ .../ui/filter_map_bool_then_unfixable.stderr | 29 +++++++--- 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 3461f539b0986..965993808f6b5 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -2,9 +2,13 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; -use clippy_utils::{contains_return, is_from_proc_macro, is_trait_method, peel_blocks}; +use clippy_utils::{ + CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, +}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, HirId, Param, Pat}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Binder; use rustc_middle::ty::adjustment::Adjust; @@ -50,9 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & call_span, "usage of `bool::then` in `filter_map`", |diag| { - if contains_return(recv) { - diag.help("consider using `filter` then `map` instead"); - } else { + if can_filter_and_then_move_to_closure(cx, ¶m, recv, then_body) { diag.span_suggestion( call_span, "use `filter` then `map` instead", @@ -62,8 +64,53 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & ), Applicability::MachineApplicable, ); + } else { + diag.help("consider using `filter` then `map` instead"); } }, ); } } + +/// Returns a set of all bindings found in the given pattern. +fn find_bindings_from_pat(pat: &Pat<'_>) -> FxHashSet { + let mut bindings = FxHashSet::default(); + pat.walk(|p| { + if let rustc_hir::PatKind::Binding(_, hir_id, _, _) = p.kind { + bindings.insert(hir_id); + } + true + }); + bindings +} + +/// Returns true if we can take a closure parameter and have it in both the `filter` function and +/// the`map` function. This is not the case if: +/// +/// - The `filter` would contain an early return, +/// - `filter` and `then` contain captures, and any of those are &mut +fn can_filter_and_then_move_to_closure<'tcx>( + cx: &LateContext<'tcx>, + param: &Param<'tcx>, + filter: &'tcx Expr<'tcx>, + then: &'tcx Expr<'tcx>, +) -> bool { + if contains_return(filter) { + return false; + } + + let Some(filter_captures) = can_move_expr_to_closure(cx, filter) else { + return true; + }; + let Some(then_captures) = can_move_expr_to_closure(cx, then) else { + return true; + }; + + let param_bindings = find_bindings_from_pat(param.pat); + filter_captures.iter().all(|(hir_id, filter_cap)| { + param_bindings.contains(hir_id) + || !then_captures + .get(hir_id) + .is_some_and(|then_cap| matches!(*filter_cap | *then_cap, CaptureKind::Ref(Mutability::Mut))) + }) +} diff --git a/tests/ui/filter_map_bool_then_unfixable.rs b/tests/ui/filter_map_bool_then_unfixable.rs index f65b0b66f836e..68294292502ac 100644 --- a/tests/ui/filter_map_bool_then_unfixable.rs +++ b/tests/ui/filter_map_bool_then_unfixable.rs @@ -2,6 +2,17 @@ #![warn(clippy::filter_map_bool_then)] //@no-rustfix +fn issue11617() { + let mut x: Vec = vec![0; 10]; + let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { + //~^ filter_map_bool_then + (x[i] != *v).then(|| { + x[i] = i; + i + }) + }); +} + mod issue14368 { fn do_something(_: ()) -> bool { diff --git a/tests/ui/filter_map_bool_then_unfixable.stderr b/tests/ui/filter_map_bool_then_unfixable.stderr index f72bcac8a7501..2025958136ba6 100644 --- a/tests/ui/filter_map_bool_then_unfixable.stderr +++ b/tests/ui/filter_map_bool_then_unfixable.stderr @@ -1,15 +1,30 @@ error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:12:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:7:48 | -LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { + | ________________________________________________^ +LL | | +LL | | (x[i] != *v).then(|| { +LL | | x[i] = i; +LL | | i +LL | | }) +LL | | }); + | |______^ | = help: consider using `filter` then `map` instead = note: `-D clippy::filter-map-bool-then` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:16:14 + --> tests/ui/filter_map_bool_then_unfixable.rs:23:26 + | +LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:27:14 | LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +32,7 @@ LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }. = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:18:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:29:26 | LL | let _ = x.iter().filter_map(|&x| { | __________________________^ @@ -32,7 +47,7 @@ LL | | }); = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:36:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:47:26 | LL | let _ = x.iter().filter_map(|&x| { | __________________________^ @@ -46,5 +61,5 @@ LL | | }); | = help: consider using `filter` then `map` instead -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From 6c8400e342609de052ec287fecaf67cef04da1d2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 14 Mar 2025 12:47:08 +0100 Subject: [PATCH 032/103] remove `feature(inline_const_pat)` --- clippy_lints/src/matches/redundant_guards.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index ab53ad98572e4..9bbef8da0a466 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -246,7 +246,6 @@ fn emit_redundant_guards<'tcx>( fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { for_each_expr_without_closures(expr, |expr| { if match expr.kind { - ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat(), ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => { // Allow ctors matches!(cx.qpath_res(&qpath, c.hir_id), Res::Def(DefKind::Ctor(..), ..)) From da4f5a58a929145127b5cf82ed271ca7279244b0 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Fri, 21 Mar 2025 21:20:39 +0900 Subject: [PATCH 033/103] expand `neg_multiply` to lint float numbers as well --- clippy_lints/src/neg_multiply.rs | 10 +++---- tests/ui/neg_multiply.fixed | 29 ++++++++++++++++++ tests/ui/neg_multiply.rs | 29 ++++++++++++++++++ tests/ui/neg_multiply.stderr | 50 +++++++++++++++++++++++++++++++- 4 files changed, 112 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 429afff9b6642..86eb833becc20 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -16,9 +16,6 @@ declare_clippy_lint! { /// ### Why is this bad? /// It's more readable to just negate. /// - /// ### Known problems - /// This only catches integers (for now). - /// /// ### Example /// ```rust,ignore /// let a = x * -1; @@ -53,8 +50,11 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { if let ExprKind::Lit(l) = lit.kind - && consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1) - && cx.typeck_results().expr_ty(exp).is_integral() + && matches!( + consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)), + Constant::Int(1) | Constant::F16(1.0) | Constant::F32(1.0) | Constant::F64(1.0) | Constant::F128(1.0) + ) + && cx.typeck_results().expr_ty(exp).is_numeric() { let mut applicability = Applicability::MachineApplicable; let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); diff --git a/tests/ui/neg_multiply.fixed b/tests/ui/neg_multiply.fixed index 995470493bfb7..ff6e08300e298 100644 --- a/tests/ui/neg_multiply.fixed +++ b/tests/ui/neg_multiply.fixed @@ -53,3 +53,32 @@ fn main() { X * -1; // should be ok -1 * X; // should also be ok } + +fn float() { + let x = 0.0; + + -x; + //~^ neg_multiply + + -x; + //~^ neg_multiply + + 100.0 + -x; + //~^ neg_multiply + + -(100.0 + x); + //~^ neg_multiply + + -17.0; + //~^ neg_multiply + + 0.0 + -0.0; + //~^ neg_multiply + + -(3.0_f32 as f64); + //~^ neg_multiply + -(3.0_f32 as f64); + //~^ neg_multiply + + -1.0 * -1.0; // should be ok +} diff --git a/tests/ui/neg_multiply.rs b/tests/ui/neg_multiply.rs index 95b94e29517fa..b0f4e85c78e5d 100644 --- a/tests/ui/neg_multiply.rs +++ b/tests/ui/neg_multiply.rs @@ -53,3 +53,32 @@ fn main() { X * -1; // should be ok -1 * X; // should also be ok } + +fn float() { + let x = 0.0; + + x * -1.0; + //~^ neg_multiply + + -1.0 * x; + //~^ neg_multiply + + 100.0 + x * -1.0; + //~^ neg_multiply + + (100.0 + x) * -1.0; + //~^ neg_multiply + + -1.0 * 17.0; + //~^ neg_multiply + + 0.0 + 0.0 * -1.0; + //~^ neg_multiply + + 3.0_f32 as f64 * -1.0; + //~^ neg_multiply + (3.0_f32 as f64) * -1.0; + //~^ neg_multiply + + -1.0 * -1.0; // should be ok +} diff --git a/tests/ui/neg_multiply.stderr b/tests/ui/neg_multiply.stderr index 9efa5d3ba1f1d..2ef7e32ce05e1 100644 --- a/tests/ui/neg_multiply.stderr +++ b/tests/ui/neg_multiply.stderr @@ -49,5 +49,53 @@ error: this multiplication by -1 can be written more succinctly LL | (3_usize as i32) * -1; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3_usize as i32)` -error: aborting due to 8 previous errors +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:60:5 + | +LL | x * -1.0; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:63:5 + | +LL | -1.0 * x; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:66:13 + | +LL | 100.0 + x * -1.0; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:69:5 + | +LL | (100.0 + x) * -1.0; + | ^^^^^^^^^^^^^^^^^^ help: consider using: `-(100.0 + x)` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:72:5 + | +LL | -1.0 * 17.0; + | ^^^^^^^^^^^ help: consider using: `-17.0` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:75:11 + | +LL | 0.0 + 0.0 * -1.0; + | ^^^^^^^^^^ help: consider using: `-0.0` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:78:5 + | +LL | 3.0_f32 as f64 * -1.0; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:80:5 + | +LL | (3.0_f32 as f64) * -1.0; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` + +error: aborting due to 16 previous errors From 5b234c965d0bd3122b253933f0d2acf3ef74ae5d Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 21 Mar 2025 11:10:49 -0400 Subject: [PATCH 034/103] Change category to pedantic --- clippy_lints/src/attrs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 7ba7b47661766..6f8a9a6ecbe3d 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -470,7 +470,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.85.0"] pub IGNORE_WITHOUT_REASON, - restriction, + pedantic, "ignored tests without messages" } From 82381608c993262b562e56b8b52ff47b42650c8e Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 16 Feb 2025 16:05:02 +0100 Subject: [PATCH 035/103] Lint more cases in `collapsible_if` Replace the use of `Sugg::ast()` which prevented combining `if` together when they contained comments by span manipulation. A new configuration option `lint_commented_code`, which is `true` by default, opts out from this behavior. --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 11 ++ clippy_config/src/conf.rs | 4 + clippy_lints/src/collapsible_if.rs | 186 +++++++++++------- clippy_lints/src/lib.rs | 2 +- tests/ui-toml/collapsible_if/clippy.toml | 1 + .../collapsible_if/collapsible_if.fixed | 34 ++++ .../ui-toml/collapsible_if/collapsible_if.rs | 38 ++++ .../collapsible_if/collapsible_if.stderr | 80 ++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/collapsible_if.fixed | 102 +++++----- tests/ui/collapsible_if.rs | 52 ++--- tests/ui/collapsible_if.stderr | 91 ++++++--- 13 files changed, 410 insertions(+), 195 deletions(-) create mode 100644 tests/ui-toml/collapsible_if/clippy.toml create mode 100644 tests/ui-toml/collapsible_if/collapsible_if.fixed create mode 100644 tests/ui-toml/collapsible_if/collapsible_if.rs create mode 100644 tests/ui-toml/collapsible_if/collapsible_if.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf4b51ff0fe2..1babf0bd61440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6362,6 +6362,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold +[`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code [`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 3726d6e8a8691..36f3985fc0281 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -613,6 +613,17 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) +## `lint-commented-code` +Whether collapsible `if` chains are linted if they contain comments inside the parts +that would be collapsed. + +**Default Value:** `false` + +--- +**Affected lints:** +* [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if) + + ## `lint-inconsistent-struct-field-initializers` Whether to suggest reordering constructor fields when initializers are present. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 798f8b3aa5a90..eeb462dedc974 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -549,6 +549,10 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether collapsible `if` chains are linted if they contain comments inside the parts + /// that would be collapsed. + #[lints(collapsible_if)] + lint_commented_code: bool = false, /// Whether to suggest reordering constructor fields when initializers are present. /// /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index e73bfc6ebf7a1..b100e2408de3d 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,10 +1,13 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; -use clippy_utils::sugg::Sugg; +use clippy_utils::source::{ + HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability, +}; +use clippy_utils::span_contains_comment; use rustc_ast::ast; 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! { @@ -75,7 +78,95 @@ declare_clippy_lint! { "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } -declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); +pub struct CollapsibleIf { + lint_commented_code: bool, +} + +impl CollapsibleIf { + pub fn new(conf: &'static Conf) -> Self { + Self { + lint_commented_code: conf.lint_commented_code, + } + } + + fn check_collapsible_else_if(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { + if let ast::ExprKind::Block(ref block, _) = else_.kind + && !block_starts_with_comment(cx, block) + && let Some(else_) = expr_block(block) + && else_.attrs.is_empty() + && !else_.span.from_expansion() + && let ast::ExprKind::If(..) = else_.kind + { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { + !c.is_whitespace() + } else { + false + }; + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COLLAPSIBLE_ELSE_IF, + block.span, + "this `else { if .. }` block can be collapsed", + "collapse nested if block", + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + ), + applicability, + ); + } + } + + fn check_collapsible_if_if(&self, cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { + if let Some(inner) = expr_block(then) + && inner.attrs.is_empty() + && let ast::ExprKind::If(check_inner, _, None) = &inner.kind + // Prevent triggering on `if c { if let a = b { .. } }`. + && !matches!(check_inner.kind, ast::ExprKind::Let(..)) + && let ctxt = expr.span.ctxt() + && inner.span.ctxt() == ctxt + && let contains_comment = span_contains_comment(cx.sess().source_map(), check.span.to(check_inner.span)) + && (!contains_comment || self.lint_commented_code) + { + span_lint_and_then( + cx, + COLLAPSIBLE_IF, + expr.span, + "this `if` statement can be collapsed", + |diag| { + let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span(); + let then_closing_bracket = { + let end = then.span.shrink_to_hi(); + end.with_lo(end.lo() - rustc_span::BytePos(1)) + .with_leading_whitespace(cx) + .into_span() + }; + let inner_if = inner.span.split_at(2).0; + let mut sugg = vec![ + // Remove the outer then block `{` + (then_open_bracket, String::new()), + // Remove the outer then block '}' + (then_closing_bracket, String::new()), + // Replace inner `if` by `&&` + (inner_if, String::from("&&")), + ]; + sugg.extend(parens_around(check)); + sugg.extend(parens_around(check_inner)); + + diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable); + }, + ); + } + } +} + +impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { @@ -83,9 +174,10 @@ impl EarlyLintPass for CollapsibleIf { && !expr.span.from_expansion() { if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, then.span, else_); + Self::check_collapsible_else_if(cx, then.span, else_); } else if !matches!(cond.kind, ast::ExprKind::Let(..)) { - check_collapsible_no_if_let(cx, expr, cond, then); + // Prevent triggering on `if c { if let a = b { .. } }`. + self.check_collapsible_if_if(cx, expr, cond, then); } } } @@ -99,74 +191,6 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { - if let ast::ExprKind::Block(ref block, _) = else_.kind - && !block_starts_with_comment(cx, block) - && let Some(else_) = expr_block(block) - && else_.attrs.is_empty() - && !else_.span.from_expansion() - && let ast::ExprKind::If(..) = else_.kind - { - // Prevent "elseif" - // Check that the "else" is followed by whitespace - let up_to_else = then_span.between(block.span); - let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { - !c.is_whitespace() - } else { - false - }; - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - COLLAPSIBLE_ELSE_IF, - block.span, - "this `else { if .. }` block can be collapsed", - "collapse nested if block", - format!( - "{}{}", - if requires_space { " " } else { "" }, - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) - ), - applicability, - ); - } -} - -fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { - if !block_starts_with_comment(cx, then) - && let Some(inner) = expr_block(then) - && inner.attrs.is_empty() - && let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind - // Prevent triggering on `if c { if let a = b { .. } }`. - && !matches!(check_inner.kind, ast::ExprKind::Let(..)) - && let ctxt = expr.span.ctxt() - && inner.span.ctxt() == ctxt - { - span_lint_and_then( - cx, - COLLAPSIBLE_IF, - expr.span, - "this `if` statement can be collapsed", - |diag| { - let mut app = Applicability::MachineApplicable; - let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); - let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); - diag.span_suggestion( - expr.span, - "collapse nested if block", - format!( - "if {} {}", - lhs.and(&rhs), - snippet_block(cx, content.span, "..", Some(expr.span)), - ), - app, // snippet - ); - }, - ); - } -} - /// If the block contains only one expression, return it. fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { if let [stmt] = &*block.stmts @@ -177,3 +201,17 @@ fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { None } } + +/// If the expression is a `||`, suggest parentheses around it. +fn parens_around(expr: &ast::Expr) -> Vec<(Span, String)> { + if let ast::ExprKind::Binary(op, _, _) = expr.kind + && op.node == ast::BinOpKind::Or + { + vec![ + (expr.span.shrink_to_lo(), String::from("(")), + (expr.span.shrink_to_hi(), String::from(")")), + ] + } else { + vec![] + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3fe3cd67e1671..2d390be5248c6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -772,7 +772,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); + store.register_early_pass(move || Box::new(collapsible_if::CollapsibleIf::new(conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); diff --git a/tests/ui-toml/collapsible_if/clippy.toml b/tests/ui-toml/collapsible_if/clippy.toml new file mode 100644 index 0000000000000..592cea90cff5c --- /dev/null +++ b/tests/ui-toml/collapsible_if/clippy.toml @@ -0,0 +1 @@ +lint-commented-code = true diff --git a/tests/ui-toml/collapsible_if/collapsible_if.fixed b/tests/ui-toml/collapsible_if/collapsible_if.fixed new file mode 100644 index 0000000000000..f695f9804d59c --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_if.fixed @@ -0,0 +1,34 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" + // Comment must be kept + && y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_if + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" // Inner comment + && y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_if + + if x == "hello" + /* Inner comment */ + && y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_if + + if x == "hello" /* Inner comment */ + && y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_if +} diff --git a/tests/ui-toml/collapsible_if/collapsible_if.rs b/tests/ui-toml/collapsible_if/collapsible_if.rs new file mode 100644 index 0000000000000..868b4adcde502 --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_if.rs @@ -0,0 +1,38 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" { + // Comment must be kept + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_if + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" { // Inner comment + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_if + + if x == "hello" { + /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_if + + if x == "hello" { /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_if +} diff --git a/tests/ui-toml/collapsible_if/collapsible_if.stderr b/tests/ui-toml/collapsible_if/collapsible_if.stderr new file mode 100644 index 0000000000000..a12c2112f5877 --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_if.stderr @@ -0,0 +1,80 @@ +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:8:5 + | +LL | / if x == "hello" { +LL | | // Comment must be kept +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if x == "hello" +LL | // Comment must be kept +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:17:5 + | +LL | / if x == "hello" { // Inner comment +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" // Inner comment +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:24:5 + | +LL | / if x == "hello" { +LL | | /* Inner comment */ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" +LL | /* Inner comment */ +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:32:5 + | +LL | / if x == "hello" { /* Inner comment */ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" /* Inner comment */ +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index fee5b01b68982..de0eefc422e70 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -49,6 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-commented-code lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else @@ -142,6 +143,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-commented-code lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else @@ -235,6 +237,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold + lint-commented-code lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 6e994018aef01..df281651ab14f 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -12,34 +12,40 @@ fn main() { let x = "hello"; let y = "world"; - if x == "hello" && y == "world" { - println!("Hello world!"); - } + if x == "hello" + && y == "world" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if (x == "hello" || x == "world") && (y == "world" || y == "hello") { - println!("Hello world!"); - } + if (x == "hello" || x == "world") + && (y == "world" || y == "hello") { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if x == "hello" && x == "world" && (y == "world" || y == "hello") { - println!("Hello world!"); - } + if x == "hello" && x == "world" + && (y == "world" || y == "hello") { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if (x == "hello" || x == "world") && y == "world" && y == "hello" { - println!("Hello world!"); - } + if (x == "hello" || x == "world") + && y == "world" && y == "hello" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if x == "hello" && x == "world" && y == "world" && y == "hello" { - println!("Hello world!"); - } + if x == "hello" && x == "world" + && y == "world" && y == "hello" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if 42 == 1337 && 'a' != 'A' { - println!("world!") - } + if 42 == 1337 + && 'a' != 'A' { + println!("world!") + } //~^^^^^ collapsible_if // Works because any if with an else statement cannot be collapsed. @@ -71,37 +77,17 @@ fn main() { assert!(true); // assert! is just an `if` } - - // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" {// Not collapsible - if y == "world" { + if x == "hello" + && y == "world" { // Collapsible println!("Hello world!"); } - } - - if x == "hello" { // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { - // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" && y == "world" { // Collapsible - println!("Hello world!"); - } //~^^^^^ collapsible_if if x == "hello" { print!("Hello "); } else { // Not collapsible - if y == "world" { + if let Some(42) = Some(42) { println!("world!") } } @@ -110,21 +96,8 @@ fn main() { print!("Hello "); } else { // Not collapsible - if let Some(42) = Some(42) { - println!("world!") - } - } - - if x == "hello" { - /* Not collapsible */ if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { /* Not collapsible */ - if y == "world" { - println!("Hello world!"); + println!("world!") } } @@ -150,11 +123,13 @@ fn main() { } // Fix #5962 - if matches!(true, true) && matches!(true, true) {} + if matches!(true, true) + && matches!(true, true) {} //~^^^ collapsible_if // Issue #9375 - if matches!(true, true) && truth() && matches!(true, true) {} + if matches!(true, true) && truth() + && matches!(true, true) {} //~^^^ collapsible_if if true { @@ -163,4 +138,17 @@ fn main() { println!("Hello world!"); } } + + if true + && true { + println!("No comment, linted"); + } + //~^^^^^ collapsible_if + + if true { + // Do not collapse because of this comment + if true { + println!("Hello world!"); + } + } } diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 5cf591a658c7a..ce979568cc895 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -83,27 +83,6 @@ fn main() { assert!(true); // assert! is just an `if` } - - // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" {// Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { - // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - if x == "hello" { if y == "world" { // Collapsible println!("Hello world!"); @@ -115,7 +94,7 @@ fn main() { print!("Hello "); } else { // Not collapsible - if y == "world" { + if let Some(42) = Some(42) { println!("world!") } } @@ -124,21 +103,8 @@ fn main() { print!("Hello "); } else { // Not collapsible - if let Some(42) = Some(42) { - println!("world!") - } - } - - if x == "hello" { - /* Not collapsible */ if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { /* Not collapsible */ - if y == "world" { - println!("Hello world!"); + println!("world!") } } @@ -181,4 +147,18 @@ fn main() { println!("Hello world!"); } } + + if true { + if true { + println!("No comment, linted"); + } + } + //~^^^^^ collapsible_if + + if true { + // Do not collapse because of this comment + if true { + println!("Hello world!"); + } + } } diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 3cc3fe5534f25..559db238cb182 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -12,9 +12,10 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` help: collapse nested if block | -LL ~ if x == "hello" && y == "world" { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -29,9 +30,10 @@ LL | | } | help: collapse nested if block | -LL ~ if (x == "hello" || x == "world") && (y == "world" || y == "hello") { -LL + println!("Hello world!"); -LL + } +LL ~ if (x == "hello" || x == "world") { +LL ~ && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -46,9 +48,10 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && x == "world" && (y == "world" || y == "hello") { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" && x == "world" +LL ~ && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -63,9 +66,10 @@ LL | | } | help: collapse nested if block | -LL ~ if (x == "hello" || x == "world") && y == "world" && y == "hello" { -LL + println!("Hello world!"); -LL + } +LL ~ if (x == "hello" || x == "world") { +LL ~ && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -80,9 +84,10 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && x == "world" && y == "world" && y == "hello" { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" && x == "world" +LL ~ && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -97,13 +102,14 @@ LL | | } | help: collapse nested if block | -LL ~ if 42 == 1337 && 'a' != 'A' { -LL + println!("world!") -LL + } +LL ~ if 42 == 1337 +LL ~ && 'a' != 'A' { +LL | println!("world!") +LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:107:5 + --> tests/ui/collapsible_if.rs:86:5 | LL | / if x == "hello" { LL | | if y == "world" { // Collapsible @@ -114,26 +120,57 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && y == "world" { // Collapsible -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" +LL ~ && y == "world" { // Collapsible +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:167:5 + --> tests/ui/collapsible_if.rs:133:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} LL | | } - | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + | |_____^ + | +help: collapse nested if block + | +LL ~ if matches!(true, true) +LL ~ && matches!(true, true) {} + | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:173:5 + --> tests/ui/collapsible_if.rs:139:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} LL | | } - | |_____^ help: collapse nested if block: `if matches!(true, true) && truth() && matches!(true, true) {}` + | |_____^ + | +help: collapse nested if block + | +LL ~ if matches!(true, true) && truth() +LL ~ && matches!(true, true) {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:151:5 + | +LL | / if true { +LL | | if true { +LL | | println!("No comment, linted"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | println!("No comment, linted"); +LL ~ } + | -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors From 282b61b64174f8dca382932c5f5f3385c28c4a11 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Mon, 17 Mar 2025 23:28:43 +0800 Subject: [PATCH 036/103] fix: `nonminimal_bool` wrongly showed the macro definition --- clippy_lints/src/booleans.rs | 12 ++++++++---- tests/ui/nonminimal_bool.rs | 20 ++++++++++++++++++++ tests/ui/nonminimal_bool.stderr | 10 ++++++++-- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 48b5d4da88860..36c1aea263781 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -13,7 +13,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does @@ -349,9 +349,13 @@ impl SuggestContext<'_, '_, '_> { if let Some(str) = simplify_not(self.cx, self.msrv, terminal) { self.output.push_str(&str); } else { - self.output.push('!'); - self.output - .push_str(&Sugg::hir_opt(self.cx, terminal)?.maybe_par().to_string()); + let mut app = Applicability::MachineApplicable; + let snip = Sugg::hir_with_context(self.cx, terminal, SyntaxContext::root(), "", &mut app); + // Ignore the case If the expression is inside a macro expansion, or the default snippet is used + if app != Applicability::MachineApplicable { + return None; + } + self.output.push_str(&(!snip).to_string()); } }, True | False | Not(_) => { diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index a155ff3508be0..1eecc3dee3dc5 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -216,3 +216,23 @@ fn issue14184(a: f32, b: bool) { println!("Hi"); } } + +mod issue14404 { + enum TyKind { + Ref(i32, i32, i32), + Other, + } + + struct Expr; + + fn is_mutable(expr: &Expr) -> bool { + todo!() + } + + fn should_not_give_macro(ty: TyKind, expr: Expr) { + if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { + //~^ nonminimal_bool + todo!() + } + } +} diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index 336cce40abf0d..0e3e4cf7988e2 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -227,7 +227,13 @@ error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:214:8 | LL | if !(a < 2.0 && !b) { - | ^^^^^^^^^^^^^^^^ help: try: `!(a < 2.0) || b` + | ^^^^^^^^^^^^^^^^ help: try: `a >= 2.0 || b` -error: aborting due to 30 previous errors +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool.rs:233:12 + | +LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` + +error: aborting due to 31 previous errors From 8bc164b81824a1d13a6cb10ebe6d169cea82916e Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 23 Mar 2025 12:49:03 +0800 Subject: [PATCH 037/103] fix: `option_if_let_else` suggests wrongly when coercion --- clippy_lints/src/option_if_let_else.rs | 13 +++++++++++-- tests/ui/option_if_let_else.fixed | 14 ++++++++++++++ tests/ui/option_if_let_else.rs | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 6f302ea196217..fa5481f27b490 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -4,8 +4,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, - is_res_lang_ctor, peel_blocks, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, + is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -212,6 +212,15 @@ fn try_get_option_occurrence<'tcx>( } } + let some_body_ty = cx.typeck_results().expr_ty(some_body); + let none_body_ty = cx.typeck_results().expr_ty(none_body); + // Check if coercion is needed for the `None` arm. If so, we cannot suggest because it will + // introduce a type mismatch. A special case is when both arms have the same type, then + // coercion is fine. + if some_body_ty != none_body_ty && expr_requires_coercion(cx, none_body) { + return None; + } + let mut app = Applicability::Unspecified; let (none_body, is_argless_call) = match none_body.kind { diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index ee30988960175..fe3ac9e8f92c3 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -288,3 +288,17 @@ mod issue13964 { }; } } + +mod issue11059 { + use std::fmt::Debug; + + fn box_coercion_unsize(o: Option) -> Box { + if let Some(o) = o { Box::new(o) } else { Box::new("foo") } + } + + static S: String = String::new(); + + fn deref_with_overload(o: Option<&str>) -> &str { + if let Some(o) = o { o } else { &S } + } +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 525a5df4371c2..5b7498bc8e23b 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -351,3 +351,17 @@ mod issue13964 { }; } } + +mod issue11059 { + use std::fmt::Debug; + + fn box_coercion_unsize(o: Option) -> Box { + if let Some(o) = o { Box::new(o) } else { Box::new("foo") } + } + + static S: String = String::new(); + + fn deref_with_overload(o: Option<&str>) -> &str { + if let Some(o) = o { o } else { &S } + } +} From 63e7815390c420c16e1dde7efa919cc15e15afec Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Mon, 10 Mar 2025 15:11:29 +0500 Subject: [PATCH 038/103] Fix example for unconditional_recursion --- clippy_lints/src/unconditional_recursion.rs | 28 ++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index a443043bef902..392a9c18a5f37 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -23,8 +23,8 @@ declare_clippy_lint! { /// implementations. /// /// ### Why is this bad? - /// This is a hard to find infinite recursion that will crash any code - /// using it. + /// Infinite recursion in trait implementation will either cause crashes + /// or result in an infinite loop, and it is hard to detect. /// /// ### Example /// ```no_run @@ -39,9 +39,31 @@ declare_clippy_lint! { /// } /// } /// ``` + /// /// Use instead: /// - /// In such cases, either use `#[derive(PartialEq)]` or don't implement it. + /// ```no_run + /// #[derive(PartialEq)] + /// enum Foo { + /// A, + /// B, + /// } + /// ``` + /// + /// As an alternative, rewrite the logic without recursion: + /// + /// ```no_run + /// enum Foo { + /// A, + /// B, + /// } + /// + /// impl PartialEq for Foo { + /// fn eq(&self, other: &Self) -> bool { + /// matches!((self, other), (Foo::A, Foo::A) | (Foo::B, Foo::B)) + /// } + /// } + /// ``` #[clippy::version = "1.77.0"] pub UNCONDITIONAL_RECURSION, suspicious, From d8dff61275fba20d8440a8373f062b3809fc2dcd Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Fri, 21 Mar 2025 23:53:40 +0900 Subject: [PATCH 039/103] set concurrency for the `deploy` job --- .github/workflows/deploy.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b42f3e7712f10..ede19c11257ee 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,6 +8,10 @@ on: tags: - rust-1.** +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + env: TARGET_BRANCH: 'gh-pages' SHA: '${{ github.sha }}' From 3786c07463bc777b310de71adef9d3e75196ea09 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Sun, 23 Mar 2025 21:09:28 +0900 Subject: [PATCH 040/103] remove the notation of the `deploy` job --- book/src/development/infrastructure/release.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/book/src/development/infrastructure/release.md b/book/src/development/infrastructure/release.md index 8b080c099b81c..a429e0d953c46 100644 --- a/book/src/development/infrastructure/release.md +++ b/book/src/development/infrastructure/release.md @@ -88,9 +88,6 @@ git push upstream stable After updating the `stable` branch, tag the HEAD commit and push it to the Clippy repo. -> Note: Only push the tag once the Deploy GitHub action of the `beta` branch is -> finished. Otherwise the deploy for the tag might fail. - ```bash git tag rust-1.XX.0 # XX should be exchanged with the corresponding version git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote From 88b590bf4626df68b73fdb03840c7ce616ac5929 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Feb 2025 19:33:39 -0500 Subject: [PATCH 041/103] `lint-inconsistent-...` -> `check-inconsistent-...` --- CHANGELOG.md | 2 +- book/src/lint_configuration.md | 54 +++++++++---------- clippy.toml | 2 +- clippy_config/src/conf.rs | 40 ++++++++------ .../src/inconsistent_struct_constructor.rs | 6 +-- .../clippy.toml | 2 +- .../toml_unknown_key/conf_unknown_key.stderr | 3 ++ 7 files changed, 59 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3caf78581c1c3..3e6f00e857f70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6347,6 +6347,7 @@ Released 2018-09-13 [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish [`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests +[`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros @@ -6364,7 +6365,6 @@ Released 2018-09-13 [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold [`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code -[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 36f3985fc0281..859b2827b329f 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -425,6 +425,33 @@ Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. * [`incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv) +## `check-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `check-private-items` Whether to also run the listed lints on private items. @@ -624,33 +651,6 @@ that would be collapsed. * [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if) -## `lint-inconsistent-struct-field-initializers` -Whether to suggest reordering constructor fields when initializers are present. - -Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the -suggested code would compile, it can change semantics if the initializer expressions have side effects. The -following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: - -```rust -struct MyStruct { - vector: Vec, - length: usize -} -fn main() { - let vector = vec![1,2,3]; - MyStruct { length: vector.len(), vector}; -} -``` - -[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 - -**Default Value:** `false` - ---- -**Affected lints:** -* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) - - ## `literal-representation-threshold` The lower bound for linting decimal literals diff --git a/clippy.toml b/clippy.toml index f4789c9d03035..7872933552b43 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,6 +1,6 @@ avoid-breaking-exported-api = false -lint-inconsistent-struct-field-initializers = true +check-inconsistent-struct-field-initializers = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index eeb462dedc974..929e9c679ea88 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -474,6 +474,26 @@ define_Conf! { /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] check_incompatible_msrv_in_tests: bool = false, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + check_inconsistent_struct_field_initializers: bool = false, /// Whether to also run the listed lints on private items. #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] check_private_items: bool = false, @@ -554,24 +574,10 @@ define_Conf! { #[lints(collapsible_if)] lint_commented_code: bool = false, /// Whether to suggest reordering constructor fields when initializers are present. + /// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers /// - /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the - /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The - /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: - /// - /// ```rust - /// struct MyStruct { - /// vector: Vec, - /// length: usize - /// } - /// fn main() { - /// let vector = vec![1,2,3]; - /// MyStruct { length: vector.len(), vector}; - /// } - /// ``` - /// - /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 - #[lints(inconsistent_struct_constructor)] + /// Use the `check-inconsistent-struct-field-initializers` configuration instead. + #[conf_deprecated("Please use `check-inconsistent-struct-field-initializers` instead", check_inconsistent_struct_field_initializers)] lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index e1dd7872b9d48..e6129757e560f 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -65,13 +65,13 @@ declare_clippy_lint! { } pub struct InconsistentStructConstructor { - lint_inconsistent_struct_field_initializers: bool, + check_inconsistent_struct_field_initializers: bool, } impl InconsistentStructConstructor { pub fn new(conf: &'static Conf) -> Self { Self { - lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + check_inconsistent_struct_field_initializers: conf.check_inconsistent_struct_field_initializers, } } } @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); let applicability = if all_fields_are_shorthand { Applicability::MachineApplicable - } else if self.lint_inconsistent_struct_field_initializers { + } else if self.check_inconsistent_struct_field_initializers { Applicability::MaybeIncorrect } else { return; diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml index f43c9d97e825d..3cb8523562a8f 100644 --- a/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -1 +1 @@ -lint-inconsistent-struct-field-initializers = true +check-inconsistent-struct-field-initializers = true diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index de0eefc422e70..cd2c849909258 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -32,6 +32,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold cyclomatic-complexity-threshold @@ -126,6 +127,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold cyclomatic-complexity-threshold @@ -220,6 +222,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold cyclomatic-complexity-threshold From 969b5ad65c393ba1e7e8ca2e18d6030867ef42c3 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Feb 2025 19:34:30 -0500 Subject: [PATCH 042/103] Don't suggests deprecated congurations --- clippy_config/src/conf.rs | 18 +++++++++++++++++- .../toml_unknown_key/conf_unknown_key.stderr | 9 --------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 929e9c679ea88..1b9ea464dc3c6 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -991,7 +991,23 @@ impl serde::de::Error for FieldError { // set and allows it. use fmt::Write; - let mut expected = expected.to_vec(); + let metadata = get_configuration_metadata(); + let deprecated = metadata + .iter() + .filter_map(|conf| { + if conf.deprecation_reason.is_some() { + Some(conf.name.as_str()) + } else { + None + } + }) + .collect::>(); + + let mut expected = expected + .iter() + .copied() + .filter(|name| !deprecated.contains(name)) + .collect::>(); expected.sort_unstable(); let (rows, column_widths) = calculate_dimensions(&expected); diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index cd2c849909258..f2eaa66a4ae41 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -29,13 +29,11 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -51,7 +49,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect ignore-interior-mutability large-error-threshold lint-commented-code - lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -124,13 +121,11 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -146,7 +141,6 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect ignore-interior-mutability large-error-threshold lint-commented-code - lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -219,13 +213,11 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -241,7 +233,6 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni ignore-interior-mutability large-error-threshold lint-commented-code - lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools From de3aa1d9b1e0d780b999b2e4fd5ded8fa1072a41 Mon Sep 17 00:00:00 2001 From: Aaron Ang Date: Thu, 20 Feb 2025 23:18:52 -0800 Subject: [PATCH 043/103] Add new lint `manual_dangling_ptr` --- CHANGELOG.md | 1 + clippy_lints/src/casts/manual_dangling_ptr.rs | 82 +++++++++++++++++++ clippy_lints/src/casts/mod.rs | 32 ++++++++ clippy_lints/src/declared_lints.rs | 1 + clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/paths.rs | 1 + tests/ui/manual_dangling_ptr.fixed | 44 ++++++++++ tests/ui/manual_dangling_ptr.rs | 44 ++++++++++ tests/ui/manual_dangling_ptr.stderr | 65 +++++++++++++++ 9 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/casts/manual_dangling_ptr.rs create mode 100644 tests/ui/manual_dangling_ptr.fixed create mode 100644 tests/ui/manual_dangling_ptr.rs create mode 100644 tests/ui/manual_dangling_ptr.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 3caf78581c1c3..77c397f04c6e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5790,6 +5790,7 @@ Released 2018-09-13 [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains +[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr [`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs new file mode 100644 index 0000000000000..8ace27eca895e --- /dev/null +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::ty::is_normalizable; +use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core}; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; + +use super::MANUAL_DANGLING_PTR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { + if let TyKind::Ptr(ref ptr_ty) = to.kind { + let init_expr = expr_or_init(cx, from); + if is_expr_const_aligned(cx, init_expr, ptr_ty.ty) + && let Some(std_or_core) = std_or_core(cx) + { + let sugg_fn = match ptr_ty.mutbl { + Mutability::Not => "ptr::dangling", + Mutability::Mut => "ptr::dangling_mut", + }; + + let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { + format!("{std_or_core}::{sugg_fn}()") + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") + } else { + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_DANGLING_PTR, + expr.span, + "manual creation of a dangling pointer", + "use", + sugg, + Applicability::MachineApplicable, + ); + } + } +} + +// Checks if the given expression is a call to `align_of` whose generic argument matches the target +// type, or a positive constant literal that matches the target type's alignment. +fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool { + match expr.kind { + ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to), + ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to), + _ => false, + } +} + +fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind + && let Some(fun_id) = path_def_id(cx, fun) + && match_def_path(cx, fun_id, &paths::ALIGN_OF) + && let Some(args) = path.segments.last().and_then(|seg| seg.args) + && let [GenericArg::Type(generic_ty)] = args.args + { + let typeck = cx.typeck_results(); + return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id); + } + false +} + +fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) -> bool { + let LitKind::Int(val, _) = lit.node else { return false }; + if val == 0 { + return false; + } + let to_mid_ty = cx.typeck_results().node_type(to.hir_id); + is_normalizable(cx, cx.param_env, to_mid_ty) + && cx + .tcx + .layout_of(cx.typing_env().as_query_input(to_mid_ty)) + .is_ok_and(|layout| { + let align = u128::from(layout.align.abi.bytes()); + u128::from(val) <= align + }) +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index dc2a1fa85bf5c..38352977bde64 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -17,6 +17,7 @@ mod char_lit_as_u8; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; +mod manual_dangling_ptr; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -759,6 +760,32 @@ declare_clippy_lint! { "detects `as *mut _` and `as *const _` conversion" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. + /// + /// ### Why is this bad? + /// This creates a dangling pointer and is better expressed as + /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. + /// + /// ### Example + /// ```no_run + /// let ptr = 4 as *const u32; + /// let aligned = std::mem::align_of::() as *const u32; + /// let mut_ptr: *mut i64 = 8 as *mut _; + /// ``` + /// Use instead: + /// ```no_run + /// let ptr = std::ptr::dangling::(); + /// let aligned = std::ptr::dangling::(); + /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); + /// ``` + #[clippy::version = "1.87.0"] + pub MANUAL_DANGLING_PTR, + style, + "casting small constant literals to pointers to create dangling pointers" +} + pub struct Casts { msrv: Msrv, } @@ -795,6 +822,7 @@ impl_lint_pass!(Casts => [ ZERO_PTR, REF_AS_PTR, AS_POINTER_UNDERSCORE, + MANUAL_DANGLING_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -823,6 +851,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) { + manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + } + if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a9b6b369c4c39..3bf7da43fce37 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -97,6 +97,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, + crate::casts::MANUAL_DANGLING_PTR_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 86f4f190b950a..ab60fefddb447 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } 1,85,0 { UINT_FLOAT_MIDPOINT } - 1,84,0 { CONST_OPTION_AS_SLICE } + 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 51d06ad9b1aa5..a960a65ead2ff 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -30,6 +30,7 @@ pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"] pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"]; pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"]; +pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"]; // Paths in clippy itself pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"]; diff --git a/tests/ui/manual_dangling_ptr.fixed b/tests/ui/manual_dangling_ptr.fixed new file mode 100644 index 0000000000000..b6afe7898906c --- /dev/null +++ b/tests/ui/manual_dangling_ptr.fixed @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = std::ptr::dangling(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling_mut::(); + //~^ manual_dangling_ptr + + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/tests/ui/manual_dangling_ptr.rs b/tests/ui/manual_dangling_ptr.rs new file mode 100644 index 0000000000000..581ad50113e28 --- /dev/null +++ b/tests/ui/manual_dangling_ptr.rs @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = 1 as *const _; + //~^ manual_dangling_ptr + let _ = 2 as *const u32; + //~^ manual_dangling_ptr + let _ = 4 as *mut f32; + //~^ manual_dangling_ptr + + let _ = mem::align_of::() as *const u8; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const u32; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const usize; + //~^ manual_dangling_ptr + + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/tests/ui/manual_dangling_ptr.stderr b/tests/ui/manual_dangling_ptr.stderr new file mode 100644 index 0000000000000..e3bc9b16b0d93 --- /dev/null +++ b/tests/ui/manual_dangling_ptr.stderr @@ -0,0 +1,65 @@ +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:7:24 + | +LL | let _: *const u8 = 1 as *const _; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + | + = note: `-D clippy::manual-dangling-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_dangling_ptr)]` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:9:13 + | +LL | let _ = 2 as *const u32; + | ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:11:13 + | +LL | let _ = 4 as *mut f32; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:14:13 + | +LL | let _ = mem::align_of::() as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:16:13 + | +LL | let _ = mem::align_of::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:18:13 + | +LL | let _ = mem::align_of::() as *const usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: aborting due to 10 previous errors + From 7b1f9d89be8d1f79c75ff667c006d346042e9128 Mon Sep 17 00:00:00 2001 From: Aaron Ang Date: Thu, 20 Feb 2025 23:19:47 -0800 Subject: [PATCH 044/103] Update tests files for `manual_dangling_ptr` lint --- tests/ui/transmute.rs | 2 +- tests/ui/transmute_null_to_fn.rs | 1 + tests/ui/transmute_null_to_fn.stderr | 12 ++++++------ tests/ui/transmuting_null.rs | 1 + tests/ui/transmuting_null.stderr | 6 +++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 3aecde398dc3f..1ec70db9c3c29 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -59,7 +59,7 @@ fn useless() { let _: *const usize = std::mem::transmute(5_isize); //~^ useless_transmute - let _ = 5_isize as *const usize; + let _ = std::ptr::dangling::(); let _: *const usize = std::mem::transmute(1 + 1usize); //~^ useless_transmute diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs index e88f05bb662e2..4712374af934f 100644 --- a/tests/ui/transmute_null_to_fn.rs +++ b/tests/ui/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] #![warn(clippy::transmute_null_to_fn)] #![allow(clippy::zero_ptr, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr index f7d80147445d8..b5b0d4ecc7c03 100644 --- a/tests/ui/transmute_null_to_fn.stderr +++ b/tests/ui/transmute_null_to_fn.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:8:23 + --> tests/ui/transmute_null_to_fn.rs:9:23 | LL | let _: fn() = std::mem::transmute(0 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -9,7 +9,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const ()); = help: to override `-D warnings` add `#[allow(clippy::transmute_null_to_fn)]` error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:11:23 + --> tests/ui/transmute_null_to_fn.rs:12:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -17,7 +17,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:22:23 + --> tests/ui/transmute_null_to_fn.rs:23:23 | LL | let _: fn() = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -25,7 +25,7 @@ LL | let _: fn() = std::mem::transmute(ZPTR); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:32:23 + --> tests/ui/transmute_null_to_fn.rs:33:23 | LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -33,7 +33,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:35:23 + --> tests/ui/transmute_null_to_fn.rs:36:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -41,7 +41,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:38:23 + --> tests/ui/transmute_null_to_fn.rs:39:23 | LL | let _: fn() = std::mem::transmute(ZPTR as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior diff --git a/tests/ui/transmuting_null.rs b/tests/ui/transmuting_null.rs index bcd35bbd4e72a..f3eb5060cd0d3 100644 --- a/tests/ui/transmuting_null.rs +++ b/tests/ui/transmuting_null.rs @@ -3,6 +3,7 @@ #![allow(clippy::zero_ptr)] #![allow(clippy::transmute_ptr_to_ref)] #![allow(clippy::eq_op, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/tests/ui/transmuting_null.stderr b/tests/ui/transmuting_null.stderr index 84e6e374d5253..c68e4102e405b 100644 --- a/tests/ui/transmuting_null.stderr +++ b/tests/ui/transmuting_null.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:10:23 + --> tests/ui/transmuting_null.rs:11:23 | LL | let _: &u64 = std::mem::transmute(0 as *const u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | let _: &u64 = std::mem::transmute(0 as *const u64); = help: to override `-D warnings` add `#[allow(clippy::transmuting_null)]` error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:13:23 + --> tests/ui/transmuting_null.rs:14:23 | LL | let _: &u64 = std::mem::transmute(std::ptr::null::()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:24:23 + --> tests/ui/transmuting_null.rs:25:23 | LL | let _: &u64 = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ From 962329fd9df44abb9dbf89223014a3b1ad50c300 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 24 Mar 2025 08:35:24 +0100 Subject: [PATCH 045/103] Rename `Sugg::maybe_par()` into `Sugg::maybe_paren()` "paren" is used throughout the Clippy codebase as an abbreviation for "parentheses". --- clippy_lints/src/assigning_clones.rs | 6 ++-- clippy_lints/src/bool_to_int_with_if.rs | 2 +- .../src/casts/cast_abs_to_unsigned.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 2 +- .../src/casts/cast_possible_truncation.rs | 2 +- clippy_lints/src/casts/ptr_as_ptr.rs | 2 +- clippy_lints/src/casts/ptr_cast_constness.rs | 2 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 28 +++++++++---------- clippy_lints/src/format.rs | 2 +- clippy_lints/src/from_str_radix_10.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- clippy_lints/src/instant_subtraction.rs | 2 +- clippy_lints/src/len_zero.rs | 4 +-- clippy_lints/src/loops/for_kv_map.rs | 2 +- clippy_lints/src/loops/manual_memcpy.rs | 14 ++++++++-- clippy_lints/src/loops/utils.rs | 6 ++-- clippy_lints/src/manual_assert.rs | 2 +- clippy_lints/src/manual_clamp.rs | 2 +- clippy_lints/src/manual_div_ceil.rs | 2 +- clippy_lints/src/manual_is_ascii_check.rs | 2 +- clippy_lints/src/manual_rotate.rs | 2 +- clippy_lints/src/manual_unwrap_or_default.rs | 2 +- clippy_lints/src/matches/manual_ok_err.rs | 2 +- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- .../src/matches/redundant_pattern_match.rs | 6 ++-- clippy_lints/src/mem_replace.rs | 4 +-- .../methods/from_iter_instead_of_collect.rs | 2 +- clippy_lints/src/methods/manual_str_repeat.rs | 2 +- .../map_with_unused_argument_over_ranges.rs | 2 +- .../src/methods/unnecessary_map_or.rs | 4 +-- clippy_lints/src/needless_bool.rs | 2 +- .../operators/float_equality_without_abs.rs | 2 +- .../src/operators/verbose_bit_mask.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 2 +- clippy_lints/src/partialeq_to_none.rs | 2 +- clippy_lints/src/ptr.rs | 4 +-- clippy_lints/src/ranges.rs | 8 +++--- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/swap.rs | 2 +- .../src/transmute/transmute_float_to_int.rs | 2 +- .../src/transmute/transmute_ptr_to_ptr.rs | 4 +-- .../src/transmute/transmute_ptr_to_ref.rs | 4 +-- clippy_lints/src/useless_conversion.rs | 2 +- clippy_utils/src/sugg.rs | 10 +++---- 46 files changed, 89 insertions(+), 79 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index ab34af7c31745..bf376eea70dcd 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -243,7 +243,7 @@ fn build_sugg<'tcx>( // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app) } - .maybe_par(); + .maybe_paren(); // Determine whether we need to reference the argument to clone_from(). let clone_receiver_type = cx.typeck_results().expr_ty(fn_arg); @@ -284,7 +284,7 @@ fn build_sugg<'tcx>( let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)` // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)` - let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_par(); + let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_paren(); let inner_type = cx.typeck_results().expr_ty(ref_expr); // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it // deref to a mutable reference. @@ -296,7 +296,7 @@ fn build_sugg<'tcx>( } else { // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)` // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)` - Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_par().mut_addr() + Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_paren().mut_addr() }; match call_kind { diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 612712d16843c..876ceef8c32b8 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { s }; - let into_snippet = snippet.clone().maybe_par(); + let into_snippet = snippet.clone().maybe_paren(); let as_snippet = snippet.as_ty(ty); span_lint_and_then( diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 8b3529e84fc6e..164d3540253a0 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -36,7 +36,7 @@ pub(super) fn check( span, format!("casting the result of `{cast_from}::abs()` to {cast_to}"), "replace with", - format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_paren()), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 3ae43732dc03f..0f066fae11844 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -42,7 +42,7 @@ pub(super) fn check( diag.span_suggestion_verbose( expr.span, "use `Into::into` instead", - format!("{}.into()", from_sugg.maybe_par()), + format!("{}.into()", from_sugg.maybe_paren()), applicability, ); }, diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index ca973f4bb1aae..b28ac8dc971da 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -185,7 +185,7 @@ fn offer_suggestion( ) { let cast_to_snip = snippet(cx, cast_to_span, ".."); let suggestion = if cast_to_snip == "_" { - format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par()) + format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_paren()) } else { format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, "..")) }; diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index d57e391b55d54..6f944914b8fd6 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -81,7 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) { ( "try `pointer::cast`, a safer alternative", - format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()), + format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_paren()), ) }; diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index cad9c1df273f0..18e32c5c8a167 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -65,7 +65,7 @@ pub(super) fn check<'tcx>( expr.span, "`as` casting between raw pointers while changing only its constness", format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), + format!("{}.cast_{constness}()", sugg.maybe_paren()), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 0e7f01e44b049..9c3009a86cdc0 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else { unreachable!(); }; - let lhs = Sugg::hir(cx, lhs, "..").maybe_par(); + let lhs = Sugg::hir(cx, lhs, "..").maybe_paren(); let rhs = Sugg::hir(cx, rhs, "..").addr(); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index daa199779e3cb..92b607c87d75f 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -154,7 +154,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su }; } - suggestion.maybe_par() + suggestion.maybe_paren() } fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { @@ -165,7 +165,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar expr.span, "logarithm for bases 2, 10 and e can be computed more accurately", "consider using", - format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_paren()), Applicability::MachineApplicable, ); } @@ -254,13 +254,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: ( SUBOPTIMAL_FLOPS, "square-root of a number can be computed more efficiently and accurately", - format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_paren()), ) } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value { ( IMPRECISE_FLOPS, "cube-root of a number can be computed more accurately", - format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_paren()), ) } else if let Some(exponent) = get_integer_from_float_constant(&value) { ( @@ -268,7 +268,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: "exponentiation with integer powers can be computed more efficiently", format!( "{}.powi({})", - Sugg::hir(cx, receiver, "..").maybe_par(), + Sugg::hir(cx, receiver, "..").maybe_paren(), numeric_literal::format(&exponent.to_string(), None, false) ), ) @@ -330,7 +330,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: "consider using", format!( "{}.mul_add({}, {})", - Sugg::hir(cx, receiver, "..").maybe_par(), + Sugg::hir(cx, receiver, "..").maybe_paren(), maybe_neg_sugg(receiver, expr.hir_id), maybe_neg_sugg(other_addend, other_addend.hir_id), ), @@ -371,7 +371,7 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { { return Some(format!( "{}.hypot({})", - Sugg::hir(cx, lmul_lhs, "..").maybe_par(), + Sugg::hir(cx, lmul_lhs, "..").maybe_paren(), Sugg::hir(cx, rmul_lhs, "..") )); } @@ -403,7 +403,7 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { { return Some(format!( "{}.hypot({})", - Sugg::hir(cx, largs_0, "..").maybe_par(), + Sugg::hir(cx, largs_0, "..").maybe_paren(), Sugg::hir(cx, rargs_0, "..") )); } @@ -449,7 +449,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "(e.pow(x) - 1) can be computed more accurately", "consider using", - format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()), + format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_paren()), Applicability::MachineApplicable, ); } @@ -591,11 +591,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { { let positive_abs_sugg = ( "manual implementation of `abs` method", - format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()), ); let negative_abs_sugg = ( "manual implementation of negation of `abs` method", - format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()), ); let sugg = if is_testing_positive(cx, cond, body) { if if_expr_positive { @@ -672,7 +672,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { "consider using", format!( "{}.log({})", - Sugg::hir(cx, largs_self, "..").maybe_par(), + Sugg::hir(cx, largs_self, "..").maybe_paren(), Sugg::hir(cx, rargs_self, ".."), ), Applicability::MachineApplicable, @@ -703,7 +703,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) { - let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_paren()); if let ExprKind::Lit(literal) = mul_lhs.kind && let ast::LitKind::Float(ref value, float_type) = literal.node && float_type == ast::LitFloatType::Unsuffixed @@ -726,7 +726,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { } else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) { - let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_paren()); if let ExprKind::Lit(literal) = mul_lhs.kind && let ast::LitKind::Float(ref value, float_type) = literal.node && float_type == ast::LitFloatType::Unsuffixed diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 5e3f6b6a13707..94e66769eb265 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { .into_owned() } else { let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "", &mut applicability); - format!("{}.to_string()", sugg.maybe_par()) + format!("{}.to_string()", sugg.maybe_paren()) }; span_useless_format(cx, call_site, sugg, applicability); } diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 7361546153c27..25b087e8484f2 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { }; let sugg = - Sugg::hir_with_applicability(cx, expr, "", &mut Applicability::MachineApplicable).maybe_par(); + Sugg::hir_with_applicability(cx, expr, "", &mut Applicability::MachineApplicable).maybe_paren(); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index fbbd33efd02d6..9e94280fc0746 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { |diag| { let mut app = Applicability::MachineApplicable; let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) - .maybe_par() + .maybe_paren() .to_string(); let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; let method_body = if let Some(first_stmt) = then_block.stmts.first() { diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index cbc3e2ccd5b87..068723cebbfa8 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -239,7 +239,7 @@ fn check_subtraction( // This part of the condition is voluntarily split from the one before to ensure that // if `snippet_opt` fails, it won't try the next conditions. if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) - && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_par) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren) && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { let sugg = format!( diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 4ae1119ab3a27..91f65d0b79ca0 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -123,7 +123,7 @@ fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg expr.span, "manual implementation of `Instant::elapsed`", "try", - format!("{}.elapsed()", sugg.maybe_par()), + format!("{}.elapsed()", sugg.maybe_paren()), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 72e22ae59d8f4..15c44381debbe 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -180,7 +180,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lt.init); - let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par(); + let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, @@ -573,7 +573,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lit1); - let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par(); + let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 185d834becafc..a3b8d725af93d 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), + (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())), ], Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 701567a7d84e7..39af596878178 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -184,7 +184,12 @@ fn build_manual_memcpy_suggestion<'tcx>( { dst_base_str } else { - format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() + format!( + "{dst_base_str}[{}..{}]", + dst_offset.maybe_paren(), + dst_limit.maybe_paren() + ) + .into() }; let method_str = if is_copy(cx, elem_ty) { @@ -196,7 +201,12 @@ fn build_manual_memcpy_suggestion<'tcx>( let src = if is_array_length_equal_to_range(cx, start, end, src.base) { src_base_str } else { - format!("{src_base_str}[{}..{}]", src_offset.maybe_par(), src_limit.maybe_par()).into() + format!( + "{src_base_str}[{}..{}]", + src_offset.maybe_paren(), + src_limit.maybe_paren() + ) + .into() }; format!("{dst}.{method_str}(&{src});") diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index a5185d38e7c33..468c27b508bff 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -263,7 +263,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic if impls_iterator { format!( "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() ) } else { // (&x).into_iter() ==> x.iter() @@ -281,12 +281,12 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{method_name}()", - sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_par(), + sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_paren(), ) }, _ => format!( "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() ), } } diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 83c16d4466d06..8378e15c581c6 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { ExprKind::Unary(UnOp::Not, e) => (e, ""), _ => (cond, "!"), }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_paren(); let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip}){semicolon}"); // we show to the user the suggestion without the comments, but when applying the fix, include the diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 50c8331eebab4..02afe9f0997de 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -181,7 +181,7 @@ fn maybe_emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggest make_assignment, hir_with_ignore_attr, } = suggestion; - let input = Sugg::hir(cx, input, "..").maybe_par(); + let input = Sugg::hir(cx, input, "..").maybe_paren(); let min = Sugg::hir(cx, min, ".."); let max = Sugg::hir(cx, max, ".."); let semicolon = if make_assignment.is_some() { ";" } else { "" }; diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index 9944c4f880481..70db403e10396 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -167,7 +167,7 @@ fn build_suggestion( rhs: &Expr<'_>, applicability: &mut Applicability, ) { - let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_paren(); let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() && matches!( lhs.kind, diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index faf01a276a131..8ab49bd2ea8ea 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -148,7 +148,7 @@ fn check_is_ascii( }; let default_snip = ".."; let mut app = Applicability::MachineApplicable; - let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; if let Some((ty_span, ty)) = ty_sugg { suggestion.push((ty_span, format!("{recv}: {ty}"))); diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 07537fc65c08c..06ee00c2cef3c 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -101,7 +101,7 @@ impl LateLintPass<'_> for ManualRotate { (r_shift_dir, r_amount) }; let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_par(); + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, MANUAL_ROTATE, diff --git a/clippy_lints/src/manual_unwrap_or_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs index 87d2faa225c52..1aa12e72a6d45 100644 --- a/clippy_lints/src/manual_unwrap_or_default.rs +++ b/clippy_lints/src/manual_unwrap_or_default.rs @@ -150,7 +150,7 @@ fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, exp // We now check the `None` arm is calling a method equivalent to `Default::default`. && let body_none = peel_blocks(body_none) && is_default_equivalent(cx, body_none) - && let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par) + && let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_paren) { // Machine applicable only if there are no comments present let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index 576e42a564c2b..02f69b34af1cd 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -132,7 +132,7 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok } else { Applicability::MachineApplicable }; - let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par(); + let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); let sugg = format!("{scrut}.{method}()"); // If the expression being expanded is the `if …` part of an `else if …`, it must be blockified. let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 2bf7ec8ab7dde..5394b7af8ff8e 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -120,7 +120,7 @@ fn lint<'tcx>( let reindented_or_body = reindent_multiline(or_body_snippet, true, Some(indent)); let mut app = Applicability::MachineApplicable; - let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); + let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_paren(); span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 722ea7042dd7f..c4d51a5946e37 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -255,7 +255,7 @@ fn find_method_sugg_for_if_let<'tcx>( }; let sugg = Sugg::hir_with_context(cx, result_expr, ctxt, "_", &mut app) - .maybe_par() + .maybe_paren() .to_string(); diag.span_suggestion(span, "try", format!("{keyword} {sugg}.{good_method}"), app); @@ -279,7 +279,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op _ => op, }; let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par(); + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { @@ -303,7 +303,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } let guard = Sugg::hir(cx, guard, ".."); - let _ = write!(sugg, " && {}", guard.maybe_par()); + let _ = write!(sugg, " && {}", guard.maybe_paren()); } span_lint_and_sugg( diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index a0919947b3fc7..982e0695d7795 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -145,7 +145,7 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &E "consider `Option::take()` instead", format!( "{}.take()", - Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par() + Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_paren() ), applicability, ); @@ -178,7 +178,7 @@ fn check_replace_option_with_some( "consider `Option::replace()` instead", format!( "{}.replace({})", - Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "_", &mut applicability).maybe_par(), + Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "_", &mut applicability).maybe_paren(), snippet_with_applicability(cx, src_arg.span, "_", &mut applicability) ), applicability, diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index f4840785584ef..1f26b44767b25 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp && implements_trait(cx, arg_ty, iter_id, &[]) { // `expr` implements `FromIterator` trait - let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_paren(); let turbofish = extract_turbofish(cx, expr, ty); let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 098721dc046f8..8167e4f960534 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -77,7 +77,7 @@ pub(super) fn check( s @ Cow::Borrowed(_) => s, }, RepeatKind::String => Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app) - .maybe_par() + .maybe_paren() .to_string() .into(), }; diff --git a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 6cf0936c598fa..a2a522a60687d 100644 --- a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -41,7 +41,7 @@ fn extract_count_with_applicability( return Some(format!("{count}")); } let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability) - .maybe_par() + .maybe_paren() .into_string(); if lower_bound == 0 { if range.limits == RangeLimits::Closed { diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index d7bd522ddab94..66f9654e99014 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -109,8 +109,8 @@ pub(super) fn check<'a>( let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { match parent_expr.kind { - ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(), - ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(), + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(), _ => binop, } } else { diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 2eacd6875d6b4..0e648c5cb2bc5 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { || is_receiver_of_method_call(cx, e) || is_as_argument(cx, e) { - snip = snip.maybe_par(); + snip = snip.maybe_paren(); } span_lint_and_sugg( diff --git a/clippy_lints/src/operators/float_equality_without_abs.rs b/clippy_lints/src/operators/float_equality_without_abs.rs index 74e0a6333db0f..047a5a0159cb0 100644 --- a/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/clippy_lints/src/operators/float_equality_without_abs.rs @@ -50,7 +50,7 @@ pub(crate) fn check<'tcx>( // format the suggestion let suggestion = format!( "{}.abs()", - sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_par() + sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_paren() ); // spans the lint span_lint_and_then( diff --git a/clippy_lints/src/operators/verbose_bit_mask.rs b/clippy_lints/src/operators/verbose_bit_mask.rs index a6aba33e431a4..1477378914120 100644 --- a/clippy_lints/src/operators/verbose_bit_mask.rs +++ b/clippy_lints/src/operators/verbose_bit_mask.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( e.span, "bit mask could be simplified with a call to `trailing_zeros`", |diag| { - let sugg = Sugg::hir(cx, left1, "...").maybe_par(); + let sugg = Sugg::hir(cx, left1, "...").maybe_paren(); diag.span_suggestion( e.span, "try", diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 6f302ea196217..f37f3a12d4e9c 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -106,7 +106,7 @@ struct OptionOccurrence { fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", - cond_sugg.maybe_par(), + cond_sugg.maybe_paren(), if as_mut { ".as_mut()" } else if as_ref { diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 6d4216970cc4d..9b9024c810575 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { let sugg = format!( "{}.{}", sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability) - .maybe_par(), + .maybe_paren(), if is_eq { "is_none()" } else { "is_some()" } ); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 55f1ece05593f..597894fe98721 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -287,8 +287,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { is_null_path(cx, l), is_null_path(cx, r), ) { - (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), - (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), + (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(), + (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(), _ => return check_ptr_eq(cx, expr, op.node, l, r), }; diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index cc423eca74fbe..83c36d71a9c7f 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -361,8 +361,8 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { span, "an inclusive range would be more readable", |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_paren(); match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) { Some(true) => { diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect); @@ -398,8 +398,8 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "an exclusive range would be more readable", |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_paren(); diag.span_suggestion( expr.span, "use", diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 1498a49a7a4a9..84597269a58fa 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // avoid clippy::double_parens if !is_in_fn_call_arg { - hint = hint.maybe_par(); + hint = hint.maybe_paren(); } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 7176d533b6164..d1486c2c246e1 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -133,7 +133,7 @@ fn generate_swap_warning<'tcx>( applicability: &mut applicability, } .snippet_index_bindings(&[idx1, idx2, rhs1, rhs2]), - slice.maybe_par(), + slice.maybe_paren(), snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0, snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0, ), diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index f2c757952af38..df2f681a16291 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( } } - sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_par()).into()); + sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_paren()).into()); // cast the result of `to_bits` if `to_ty` is signed sugg = if let ty::Int(int_ty) = to_ty.kind() { diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index fcc763763bd2f..933e25fe98c65 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, "use `pointer::cast` instead", - format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_par()), + format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else if from_pointee_ty == to_pointee_ty @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, format!("use `pointer::{method}` instead"), - format!("{}.{method}()", arg.maybe_par()), + format!("{}.{method}()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else { diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 45ee83c78ab67..e58212fae15cf 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) + format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() } else { @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( } else if *from_ptr_ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) .to_string() diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 57bb2fc27f145..972c1adbd40ae 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -356,7 +356,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) { let mut app = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_par(); + let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( cx, diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 68a1de96a3515..1b35d86146d06 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -326,7 +326,7 @@ impl<'a> Sugg<'a> { /// `self` argument of a method call /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`). #[must_use] - pub fn maybe_par(self) -> Self { + pub fn maybe_paren(self) -> Self { match self { Sugg::NonParen(..) => self, // `(x)` and `(x).y()` both don't need additional parens. @@ -494,7 +494,7 @@ impl Display for ParenHelper { /// operators have the same /// precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_paren()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -1009,12 +1009,12 @@ mod test { } #[test] - fn binop_maybe_par() { + fn binop_maybe_paren() { let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "1".into(), "1".into()); - assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); + assert_eq!("(1 + 1)", sugg.maybe_paren().to_string()); let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into()); - assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); + assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_paren().to_string()); } #[test] fn not_op() { From 315e9aa79fc37e40d37a3ec0a5b46f6b81377306 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 8 Mar 2025 12:13:19 -0500 Subject: [PATCH 046/103] Don't check deprecated configs in `configs_are_tested` test --- clippy_config/src/conf.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 1b9ea464dc3c6..64b5acd19f277 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1090,7 +1090,13 @@ mod tests { fn configs_are_tested() { let mut names: HashSet = crate::get_configuration_metadata() .into_iter() - .map(|meta| meta.name.replace('_', "-")) + .filter_map(|meta| { + if meta.deprecation_reason.is_none() { + Some(meta.name.replace('_', "-")) + } else { + None + } + }) .collect(); let toml_files = WalkDir::new("../tests") From e8c06a71cfe5606a9c4cb8854ee8d981ade9ca1f Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Mon, 24 Mar 2025 22:27:44 +0900 Subject: [PATCH 047/103] remove obsolete "Known Problems" in `ok_expect` --- clippy_lints/src/methods/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1d9296016e25f..44753d963deed 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -478,9 +478,6 @@ declare_clippy_lint! { /// Because you usually call `expect()` on the `Result` /// directly to get a better error message. /// - /// ### Known problems - /// The error type needs to implement `Debug` - /// /// ### Example /// ```no_run /// # let x = Ok::<_, ()>(()); From 809c931804e5f6b338f395d13e2f31724ce1cae2 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 9 Feb 2025 22:26:34 +0100 Subject: [PATCH 048/103] `wildcard_imports`: lint on `pub use` if asked to `warn_on_all_wildcard_imports` should warn on all wildcard imports, including the reexported ones. --- book/src/lint_configuration.md | 3 ++- clippy_config/src/conf.rs | 3 ++- clippy_lints/src/wildcard_imports.rs | 6 +++++- tests/ui-toml/wildcard_imports/wildcard_imports.fixed | 2 +- tests/ui-toml/wildcard_imports/wildcard_imports.rs | 2 +- tests/ui-toml/wildcard_imports/wildcard_imports.stderr | 6 +++--- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 0e264cdcd4a3a..d7bdc00fe10ec 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -1030,7 +1030,8 @@ The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' ## `warn-on-all-wildcard-imports` -Whether to allow certain wildcard imports (prelude, super in tests). +Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests, +or for `pub use` reexports. **Default Value:** `false` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index cac4408fff0e3..90739436c8038 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -737,7 +737,8 @@ define_Conf! { /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' #[lints(verbose_bit_mask)] verbose_bit_mask_threshold: u64 = 1, - /// Whether to allow certain wildcard imports (prelude, super in tests). + /// Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests, + /// or for `pub use` reexports. #[lints(wildcard_imports)] warn_on_all_wildcard_imports: bool = false, /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 405310512dff3..5b3f60ad6ab0b 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -68,6 +68,8 @@ declare_clippy_lint! { /// (including the standard library) provide modules named "prelude" specifically designed /// for wildcard import. /// + /// Wildcard imports reexported through `pub use` are also allowed. + /// /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. /// /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag. @@ -121,7 +123,9 @@ impl LateLintPass<'_> for WildcardImports { } let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id); - if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { + if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) + && !self.warn_on_all + { return; } if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed index af72d6be0e096..20511cbed165e 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -15,7 +15,7 @@ mod my_crate { } } -use utils::{BAR, print}; +pub use utils::{BAR, print}; //~^ ERROR: usage of wildcard import use my_crate::utils::my_util_fn; //~^ ERROR: usage of wildcard import diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/tests/ui-toml/wildcard_imports/wildcard_imports.rs index 91009dd8835f8..8d05910f471ba 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.rs +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -15,7 +15,7 @@ mod my_crate { } } -use utils::*; +pub use utils::*; //~^ ERROR: usage of wildcard import use my_crate::utils::*; //~^ ERROR: usage of wildcard import diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr index 3d3be965aa411..5e624dd6c3cdc 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -1,8 +1,8 @@ error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:5 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:9 | -LL | use utils::*; - | ^^^^^^^^ help: try: `utils::{BAR, print}` +LL | pub use utils::*; + | ^^^^^^^^ help: try: `utils::{BAR, print}` | = note: `-D clippy::wildcard-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` From 621911a67702661b3ce4f2ce482193407d2fc412 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 24 Mar 2025 15:58:40 +0100 Subject: [PATCH 049/103] Fix various typos in lint messages, descriptions and comments --- clippy_lints/src/attrs/repr_attributes.rs | 2 +- clippy_lints/src/copies.rs | 2 +- clippy_lints/src/methods/unnecessary_map_or.rs | 4 ++-- clippy_lints/src/non_std_lazy_statics.rs | 4 ++-- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/zombie_processes.rs | 2 +- clippy_utils/src/str_utils.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 2 +- lintcheck/src/recursive.rs | 2 +- .../non_std_lazy_static_fixable.fixed | 14 +++++++------- .../non_std_lazy_static_fixable.rs | 14 +++++++------- .../non_std_lazy_static_fixable.stderr | 14 +++++++------- .../non_std_lazy_static_unfixable.rs | 12 ++++++------ .../non_std_lazy_static_unfixable.stderr | 12 ++++++------ tests/ui/repr_packed_without_abi.stderr | 4 ++-- 15 files changed, 47 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/attrs/repr_attributes.rs b/clippy_lints/src/attrs/repr_attributes.rs index e5cfbaf952a70..df01c7fde1819 100644 --- a/clippy_lints/src/attrs/repr_attributes.rs +++ b/clippy_lints/src/attrs/repr_attributes.rs @@ -30,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], diag.warn( "unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI", ) - .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") + .help("qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") .span_label(packed_span, "`packed` representation set here"); }, ); diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 03ed9c657b30a..d85005d57ede8 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -256,7 +256,7 @@ fn lint_branches_sharing_code<'tcx>( let suggestion = reindent_multiline(&suggestion, true, indent); let span = span.with_hi(last_block.span.hi()); - // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) let span = span .map_range(cx, |src, range| { (range.start > 4 && src.get(range.start - 4..range.start)? == " ") diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index d7bd522ddab94..274bc5d1bf56a 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -76,7 +76,7 @@ pub(super) fn check<'a>( && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } && switch_to_eager_eval(cx, non_binding_location) - // xor, because if its both then thats a strange edge case and + // xor, because if its both then that's a strange edge case and // we can just ignore it, since by default clippy will error on this && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) && !is_local_used(cx, non_binding_location, hir_id) @@ -92,7 +92,7 @@ pub(super) fn check<'a>( // we may need to add parens around the suggestion // in case the parent expression has additional method calls, // since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)` - // being converted to `Some(5) == Some(5).then(|| 1)` isnt + // being converted to `Some(5) == Some(5).then(|| 1)` isn't // the same thing let inner_non_binding = Sugg::NonParen(Cow::Owned(format!( diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 8305bf345ef19..1df40a674bc46 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -121,7 +121,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { cx, NON_STD_LAZY_STATICS, macro_call.span, - "this macro has been superceded by `std::sync::LazyLock`", + "this macro has been superseded by `std::sync::LazyLock`", ); return; } @@ -240,7 +240,7 @@ impl LazyInfo { cx, NON_STD_LAZY_STATICS, self.ty_span_no_args, - "this type has been superceded by `LazyLock` in the standard library", + "this type has been superseded by `LazyLock` in the standard library", |diag| { diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); }, diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index ee282ee1dfb71..3795c7483da4d 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -65,7 +65,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub SHADOW_REUSE, restriction, - "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`" + "rebinding a name to an expression that reuses the original value, e.g., `let x = x + 1`" } declare_clippy_lint! { diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 7667db469689e..39c1aab8967ad 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -131,7 +131,7 @@ struct WaitFinder<'a, 'tcx> { local_id: HirId, state: VisitorState, early_return: Option, - // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targetted help + // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targeted help // messages missing_wait_branch: Option, } diff --git a/clippy_utils/src/str_utils.rs b/clippy_utils/src/str_utils.rs index 421b25a77fe8b..f0f82c8dddcf5 100644 --- a/clippy_utils/src/str_utils.rs +++ b/clippy_utils/src/str_utils.rs @@ -1,4 +1,4 @@ -/// Dealing with sting indices can be hard, this struct ensures that both the +/// Dealing with string indices can be hard, this struct ensures that both the /// character and byte index are provided for correct indexing. #[derive(Debug, Default, PartialEq, Eq)] pub struct StrIndex { @@ -165,7 +165,7 @@ pub fn camel_case_split(s: &str) -> Vec<&str> { offsets.windows(2).map(|w| &s[w[0]..w[1]]).collect() } -/// Dealing with sting comparison can be complicated, this struct ensures that both the +/// Dealing with string comparison can be complicated, this struct ensures that both the /// character and byte count are provided for correct indexing. #[derive(Debug, Default, PartialEq, Eq)] pub struct StrCount { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 6fdf4c244f8d8..99650d0292faf 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -1352,7 +1352,7 @@ pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_n } } -/// Get's the type of a field by name. +/// Gets the type of a field by name. pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option> { match *ty.kind() { ty::Adt(def, args) if def.is_union() || def.is_struct() => def diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 57073f523648e..6406b2dcb643b 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -64,7 +64,7 @@ fn process_stream( // It's 99% likely that dependencies compiled with recursive mode are on crates.io // and therefore on docs.rs. This links to the sources directly, do avoid invalid - // links due to remaped paths. See rust-lang/docs.rs#2551 for more details. + // links due to remapped paths. See rust-lang/docs.rs#2551 for more details. let base_url = format!( "https://docs.rs/crate/{}/{}/source/src/{{file}}#{{line}}", driver_info.package_name, driver_info.version diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed index f7c56b6fffe81..2b30c8f984ebe 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -9,16 +9,16 @@ use once_cell::sync::Lazy; fn main() {} static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library let x = "bar"; x.to_uppercase() }); static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_QUX: std::sync::LazyLock = { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library if "qux".len() == 3 { std::sync::LazyLock::new(|| "qux".to_uppercase()) } else if "qux".is_ascii() { @@ -39,11 +39,11 @@ mod once_cell_lazy_with_fns { use once_cell::sync::Lazy; static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_replaceable_fns() { let _ = std::sync::LazyLock::force(&LAZY_FOO); diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs index 90bc428137cea..c52338eee83cb 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -9,16 +9,16 @@ use once_cell::sync::Lazy; fn main() {} static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: Lazy = Lazy::new(|| { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library let x = "bar"; x.to_uppercase() }); static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_QUX: Lazy = { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library if "qux".len() == 3 { Lazy::new(|| "qux".to_uppercase()) } else if "qux".is_ascii() { @@ -39,11 +39,11 @@ mod once_cell_lazy_with_fns { use once_cell::sync::Lazy; static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_replaceable_fns() { let _ = Lazy::force(&LAZY_FOO); diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr index 333052ae1c110..bb80cd11c7199 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr @@ -1,4 +1,4 @@ -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:11:18 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -12,7 +12,7 @@ LL - static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); LL + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:13:18 | LL | static LAZY_BAR: Lazy = Lazy::new(|| { @@ -24,7 +24,7 @@ LL - static LAZY_BAR: Lazy = Lazy::new(|| { LL + static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:18:18 | LL | static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; @@ -36,7 +36,7 @@ LL - static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; LL + static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:20:18 | LL | static LAZY_QUX: Lazy = { @@ -54,7 +54,7 @@ LL | } else { LL ~ std::sync::LazyLock::new(|| "qux".to_string()) | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:41:22 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -69,7 +69,7 @@ LL | fn calling_replaceable_fns() { LL ~ let _ = std::sync::LazyLock::force(&LAZY_FOO); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:43:22 | LL | static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); @@ -84,7 +84,7 @@ LL | let _ = Lazy::force(&LAZY_FOO); LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAR); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:45:26 | LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs index 34f8dd1ccb2ea..acc8c04678f50 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs @@ -9,11 +9,11 @@ mod once_cell_lazy { use once_cell::sync::Lazy; static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_irreplaceable_fns() { let _ = Lazy::get(&LAZY_FOO); @@ -31,13 +31,13 @@ mod lazy_static_lazy_static { lazy_static! { static ref LAZY_FOO: String = "foo".to_uppercase(); } - //~^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + //~^^^ ERROR: this macro has been superseded by `std::sync::LazyLock` lazy_static! { static ref LAZY_BAR: String = "bar".to_uppercase(); static ref LAZY_BAZ: String = "baz".to_uppercase(); } - //~^^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` - //~| ERROR: this macro has been superceded by `std::sync::LazyLock` + //~^^^^ ERROR: this macro has been superseded by `std::sync::LazyLock` + //~| ERROR: this macro has been superseded by `std::sync::LazyLock` } fn main() {} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr index 216190ae4ca31..2c35cad6237ab 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr @@ -1,4 +1,4 @@ -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:31:5 | LL | / lazy_static! { @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 | LL | / lazy_static! { @@ -18,7 +18,7 @@ LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); LL | | } | |_____^ -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 | LL | / lazy_static! { @@ -29,7 +29,7 @@ LL | | } | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:11:22 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -41,7 +41,7 @@ LL - static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); LL + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:13:26 | LL | static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); @@ -53,7 +53,7 @@ LL - static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); LL + static mut LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:15:26 | LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/tests/ui/repr_packed_without_abi.stderr b/tests/ui/repr_packed_without_abi.stderr index d1078b3e8e48e..f688e4bc744ae 100644 --- a/tests/ui/repr_packed_without_abi.stderr +++ b/tests/ui/repr_packed_without_abi.stderr @@ -11,7 +11,7 @@ LL | | } | |_^ | = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI - = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` + = help: qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` note: the lint level is defined here --> tests/ui/repr_packed_without_abi.rs:1:9 | @@ -31,7 +31,7 @@ LL | | } | |_^ | = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI - = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` + = help: qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` error: aborting due to 2 previous errors From de8c404ed49e16588d6932b3367b6dd0ef1556a1 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 25 Mar 2025 09:42:06 +0100 Subject: [PATCH 050/103] Fix type error in lint description --- clippy_lints/src/casts/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index dc2a1fa85bf5c..4e7641c7d3b11 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -71,7 +71,7 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let y: i8 = -1; - /// y as u128; // will return 18446744073709551615 + /// y as u64; // will return 18446744073709551615 /// ``` #[clippy::version = "pre 1.29.0"] pub CAST_SIGN_LOSS, From b7cbb4ec8c3ca8434632bd9ff7025649fca1c21e Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 25 Mar 2025 11:16:31 +0100 Subject: [PATCH 051/103] Add myself to reviewer rotation --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 33d3b0728f3d1..4b0ad0fb6bbc0 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -34,4 +34,5 @@ users_on_vacation = [ "@Jarcho", "@blyxyas", "@y21", + "@samueltardieu", ] From e78216c4a14c4590a0efa3deba7f9ed08e1d0b31 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Thu, 6 Mar 2025 13:44:23 +0900 Subject: [PATCH 052/103] suggest `is_some_and` instead of `map_or` in `case_sensitive_file_extension_comparions` --- ...se_sensitive_file_extension_comparisons.rs | 14 +++++-- clippy_lints/src/methods/mod.rs | 2 +- ...sensitive_file_extension_comparisons.fixed | 21 ++++++---- ...se_sensitive_file_extension_comparisons.rs | 7 +++- ...ensitive_file_extension_comparisons.stderr | 40 +++++++++++++------ 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 18568e3661fe5..1c745b7376ea4 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; @@ -16,6 +17,7 @@ pub(super) fn check<'tcx>( call_span: Span, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, + msrv: Msrv, ) { if let ExprKind::MethodCall(path_segment, ..) = recv.kind { if matches!( @@ -58,11 +60,15 @@ pub(super) fn check<'tcx>( let suggestion_source = reindent_multiline( &format!( - "std::path::Path::new({}) + "std::path::Path::new({recv_source}) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", - recv_source, - ext_str.strip_prefix('.').unwrap() + .{}|ext| ext.eq_ignore_ascii_case(\"{}\"))", + if msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND) { + "is_some_and(" + } else { + "map_or(false, " + }, + ext_str.strip_prefix('.').unwrap(), ), true, Some(indent_of(cx, call_span).unwrap_or(0) + 4), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 94d3657d9f123..f17bf6d636622 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4992,7 +4992,7 @@ impl Methods { }, ("ends_with", [arg]) => { if let ExprKind::MethodCall(.., span) = expr.kind { - case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg); + case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg, self.msrv); } path_ends_with_ext::check(cx, recv, arg, expr, self.msrv, &self.allowed_dotfiles); }, diff --git a/tests/ui/case_sensitive_file_extension_comparisons.fixed b/tests/ui/case_sensitive_file_extension_comparisons.fixed index bf7635fdf09bf..0c9d21243546d 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.fixed +++ b/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -1,5 +1,4 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] -#![allow(clippy::unnecessary_map_or)] use std::string::String; @@ -13,7 +12,7 @@ impl TestStruct { fn is_rust_file(filename: &str) -> bool { std::path::Path::new(filename) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) + .is_some_and(|ext| ext.eq_ignore_ascii_case("rs")) //~^ case_sensitive_file_extension_comparisons } @@ -21,18 +20,18 @@ fn main() { // std::string::String and &str should trigger the lint failure with .ext12 let _ = std::path::Path::new(&String::new()) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons // The fixup should preserve the indentation level { let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons } @@ -42,11 +41,11 @@ fn main() { // std::string::String and &str should trigger the lint failure with .EXT12 let _ = std::path::Path::new(&String::new()) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); //~^ case_sensitive_file_extension_comparisons let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); //~^ case_sensitive_file_extension_comparisons // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase @@ -76,3 +75,11 @@ fn main() { let _ = "str".ends_with(".123"); TestStruct {}.ends_with(".123"); } + +#[clippy::msrv = "1.69"] +fn msrv_check() { + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + //~^ case_sensitive_file_extension_comparisons +} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index 0c4070a42d4b0..f8a947aa827b9 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -1,5 +1,4 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] -#![allow(clippy::unnecessary_map_or)] use std::string::String; @@ -64,3 +63,9 @@ fn main() { let _ = "str".ends_with(".123"); TestStruct {}.ends_with(".123"); } + +#[clippy::msrv = "1.69"] +fn msrv_check() { + let _ = String::new().ends_with(".ext12"); + //~^ case_sensitive_file_extension_comparisons +} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr index e035534d26996..93bee8e766719 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -1,5 +1,5 @@ error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:14:5 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:13:5 | LL | filename.ends_with(".rs") | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,11 +11,11 @@ help: use std::path::Path | LL ~ std::path::Path::new(filename) LL + .extension() -LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) +LL + .is_some_and(|ext| ext.eq_ignore_ascii_case("rs")) | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:20:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13 | LL | let _ = String::new().ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,11 +25,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new(&String::new()) LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:22:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:21:13 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,11 +39,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:27:17 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:26:17 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,11 +53,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:35:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:34:13 | LL | let _ = String::new().ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,11 +67,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new(&String::new()) LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:37:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:36:13 | LL | let _ = "str".ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,8 +81,22 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); + | + +error: case-sensitive file extension comparison + --> tests/ui/case_sensitive_file_extension_comparisons.rs:69:13 + | +LL | let _ = String::new().ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); | -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors From 1d890389ffe885ac2b148113c852a115f35fdb01 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 6 Feb 2025 02:41:20 -0500 Subject: [PATCH 053/103] Move `uninlined_format_args` to `style` This lint was downgraded to `pedantic` in part because rust-analyzer was not fully supporting it at the time per #10087. The support has been added over [a year ago](https://github.com/rust-lang/rust-analyzer/issues/11260), so seems like this should be back to style. Another source of the initial frustration was fixed since then as well - this lint does not trigger by default in case only some arguments can be inlined. --- clippy_lints/src/format_args.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 3862ff7921db8..75f0d626f0c4f 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -141,7 +141,7 @@ declare_clippy_lint! { /// format!("{var:.prec$}"); /// ``` /// - /// If allow-mixed-uninlined-format-args is set to false in clippy.toml, + /// If `allow-mixed-uninlined-format-args` is set to `false` in clippy.toml, /// the following code will also trigger the lint: /// ```no_run /// # let var = 42; @@ -159,7 +159,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } From d063e09d102591e9b60d7f4cf0e606c41c11b992 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 15 Feb 2025 17:25:56 -0500 Subject: [PATCH 054/103] Move `uninlined_format_args` to `style` This lint was downgraded to `pedantic` in part because rust-analyzer was not fully supporting it at the time per #10087. The support has been added over [a year ago](https://github.com/rust-lang/rust-analyzer/issues/11260), so seems like this should be back to style. Another source of the initial frustration was fixed since then as well - this lint does not trigger by default in case only some arguments can be inlined. --- .../index_refutable_slice.fixed | 2 +- .../index_refutable_slice.rs | 2 +- tests/ui/author/macro_in_closure.rs | 1 + tests/ui/author/macro_in_loop.rs | 1 + tests/ui/dbg_macro/dbg_macro.fixed | 2 +- tests/ui/dbg_macro/dbg_macro.rs | 2 +- tests/ui/large_futures.fixed | 2 +- tests/ui/large_futures.rs | 2 +- tests/ui/manual_inspect.fixed | 2 +- tests/ui/manual_inspect.rs | 2 +- tests/ui/needless_pass_by_ref_mut.rs | 4 ++-- tests/ui/to_string_in_format_args_incremental.fixed | 2 +- tests/ui/to_string_in_format_args_incremental.rs | 2 +- tests/ui/unnecessary_iter_cloned.fixed | 2 +- tests/ui/unnecessary_iter_cloned.rs | 2 +- tests/ui/unnecessary_operation.fixed | 2 +- tests/ui/unnecessary_operation.rs | 2 +- tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- 19 files changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 36540bf1dcf73..2d5900094ee73 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] - +#![allow(clippy::uninlined_format_args)] fn below_limit() { let slice: Option<&[u32]> = Some(&[1, 2, 3]); if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index da76bb20fd961..94bd99a452fe2 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] - +#![allow(clippy::uninlined_format_args)] fn below_limit() { let slice: Option<&[u32]> = Some(&[1, 2, 3]); if let Some(slice) = slice { diff --git a/tests/ui/author/macro_in_closure.rs b/tests/ui/author/macro_in_closure.rs index 8a02f38fad87b..b6d8e7ecbdd19 100644 --- a/tests/ui/author/macro_in_closure.rs +++ b/tests/ui/author/macro_in_closure.rs @@ -1,4 +1,5 @@ //@ check-pass +#![allow(clippy::uninlined_format_args)] fn main() { #[clippy::author] diff --git a/tests/ui/author/macro_in_loop.rs b/tests/ui/author/macro_in_loop.rs index 84ffe416e839b..f68275fefaaa3 100644 --- a/tests/ui/author/macro_in_loop.rs +++ b/tests/ui/author/macro_in_loop.rs @@ -1,6 +1,7 @@ //@ check-pass #![feature(stmt_expr_attributes)] +#![allow(clippy::uninlined_format_args)] fn main() { #[clippy::author] diff --git a/tests/ui/dbg_macro/dbg_macro.fixed b/tests/ui/dbg_macro/dbg_macro.fixed index fd1a0d8934b3c..8765035e39887 100644 --- a/tests/ui/dbg_macro/dbg_macro.fixed +++ b/tests/ui/dbg_macro/dbg_macro.fixed @@ -1,6 +1,6 @@ #![warn(clippy::dbg_macro)] #![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] - +#![allow(clippy::uninlined_format_args)] fn foo(n: u32) -> u32 { if let Some(n) = n.checked_sub(4) { n } else { n } //~^ dbg_macro diff --git a/tests/ui/dbg_macro/dbg_macro.rs b/tests/ui/dbg_macro/dbg_macro.rs index c96e2c7251c29..301f17db3169f 100644 --- a/tests/ui/dbg_macro/dbg_macro.rs +++ b/tests/ui/dbg_macro/dbg_macro.rs @@ -1,6 +1,6 @@ #![warn(clippy::dbg_macro)] #![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] - +#![allow(clippy::uninlined_format_args)] fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } //~^ dbg_macro diff --git a/tests/ui/large_futures.fixed b/tests/ui/large_futures.fixed index c2159c58de1ec..6b2acaa9ff103 100644 --- a/tests/ui/large_futures.fixed +++ b/tests/ui/large_futures.fixed @@ -1,7 +1,7 @@ #![warn(clippy::large_futures)] #![allow(clippy::never_loop)] #![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn)] +#![allow(clippy::manual_async_fn, clippy::uninlined_format_args)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/tests/ui/large_futures.rs b/tests/ui/large_futures.rs index 567f6344afeac..6bf1fb87727ad 100644 --- a/tests/ui/large_futures.rs +++ b/tests/ui/large_futures.rs @@ -1,7 +1,7 @@ #![warn(clippy::large_futures)] #![allow(clippy::never_loop)] #![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn)] +#![allow(clippy::manual_async_fn, clippy::uninlined_format_args)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/tests/ui/manual_inspect.fixed b/tests/ui/manual_inspect.fixed index 44f15d61f8563..1a722cef43401 100644 --- a/tests/ui/manual_inspect.fixed +++ b/tests/ui/manual_inspect.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_inspect)] -#![allow(clippy::no_effect, clippy::op_ref)] +#![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] fn main() { let _ = Some(0).inspect(|&x| { diff --git a/tests/ui/manual_inspect.rs b/tests/ui/manual_inspect.rs index d34f2abce6ae1..eaf6b9fa0edf6 100644 --- a/tests/ui/manual_inspect.rs +++ b/tests/ui/manual_inspect.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_inspect)] -#![allow(clippy::no_effect, clippy::op_ref)] +#![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] fn main() { let _ = Some(0).map(|x| { diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index f0c5a716ac991..18cca14701f31 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,13 +1,13 @@ #![allow( clippy::if_same_then_else, clippy::no_effect, + clippy::ptr_arg, clippy::redundant_closure_call, - clippy::ptr_arg + clippy::uninlined_format_args )] #![warn(clippy::needless_pass_by_ref_mut)] //@no-rustfix use std::ptr::NonNull; - fn foo(s: &mut Vec, b: &u32, x: &mut u32) { //~^ needless_pass_by_ref_mut diff --git a/tests/ui/to_string_in_format_args_incremental.fixed b/tests/ui/to_string_in_format_args_incremental.fixed index 2a29580e390ce..32efc2b5eba14 100644 --- a/tests/ui/to_string_in_format_args_incremental.fixed +++ b/tests/ui/to_string_in_format_args_incremental.fixed @@ -1,5 +1,5 @@ //@compile-flags: -C incremental=target/debug/test/incr - +#![allow(clippy::uninlined_format_args)] // see https://github.com/rust-lang/rust-clippy/issues/10969 fn main() { diff --git a/tests/ui/to_string_in_format_args_incremental.rs b/tests/ui/to_string_in_format_args_incremental.rs index 18ca82976a40e..197685fb0d7c4 100644 --- a/tests/ui/to_string_in_format_args_incremental.rs +++ b/tests/ui/to_string_in_format_args_incremental.rs @@ -1,5 +1,5 @@ //@compile-flags: -C incremental=target/debug/test/incr - +#![allow(clippy::uninlined_format_args)] // see https://github.com/rust-lang/rust-clippy/issues/10969 fn main() { diff --git a/tests/ui/unnecessary_iter_cloned.fixed b/tests/ui/unnecessary_iter_cloned.fixed index aed2dbe1f1ce4..61f2e3745ad05 100644 --- a/tests/ui/unnecessary_iter_cloned.fixed +++ b/tests/ui/unnecessary_iter_cloned.fixed @@ -1,4 +1,4 @@ -#![allow(unused_assignments)] +#![allow(unused_assignments, clippy::uninlined_format_args)] #![warn(clippy::unnecessary_to_owned)] #[allow(dead_code)] diff --git a/tests/ui/unnecessary_iter_cloned.rs b/tests/ui/unnecessary_iter_cloned.rs index 12fdd150e4233..b90ca00a5fece 100644 --- a/tests/ui/unnecessary_iter_cloned.rs +++ b/tests/ui/unnecessary_iter_cloned.rs @@ -1,4 +1,4 @@ -#![allow(unused_assignments)] +#![allow(unused_assignments, clippy::uninlined_format_args)] #![warn(clippy::unnecessary_to_owned)] #[allow(dead_code)] diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index 05dfb72f48d2d..b632c200b2abc 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -6,7 +6,7 @@ clippy::unnecessary_struct_initialization )] #![warn(clippy::unnecessary_operation)] - +#![allow(clippy::uninlined_format_args)] use std::fmt::Display; use std::ops::Shl; diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 6ef74c3eb1c1e..3e34b46185e34 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -6,7 +6,7 @@ clippy::unnecessary_struct_initialization )] #![warn(clippy::unnecessary_operation)] - +#![allow(clippy::uninlined_format_args)] use std::fmt::Display; use std::ops::Shl; diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 5410033dbd8f4..498dab3eb5070 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -7,7 +7,7 @@ clippy::owned_cow )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] - +#![allow(clippy::uninlined_format_args)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 0619dd4ddec09..b9d613c3a0529 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -7,7 +7,7 @@ clippy::owned_cow )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] - +#![allow(clippy::uninlined_format_args)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; From 8e50cfacdf49ff42432ae2fc6e943f07d316fc21 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 15 Feb 2025 19:54:54 -0500 Subject: [PATCH 055/103] make tests consistent --- .../index_refutable_slice.fixed | 3 +- .../index_refutable_slice.rs | 3 +- .../index_refutable_slice.stderr | 4 +- tests/ui/author/macro_in_closure.rs | 1 + tests/ui/dbg_macro/dbg_macro.fixed | 9 ++- tests/ui/dbg_macro/dbg_macro.rs | 9 ++- tests/ui/dbg_macro/dbg_macro.stderr | 38 +++++------ tests/ui/large_futures.fixed | 9 ++- tests/ui/large_futures.rs | 9 ++- tests/ui/large_futures.stderr | 16 ++--- tests/ui/manual_inspect.fixed | 2 +- tests/ui/manual_inspect.rs | 2 +- tests/ui/needless_pass_by_ref_mut.rs | 5 +- tests/ui/needless_pass_by_ref_mut.stderr | 68 +++++++++---------- ...to_string_in_format_args_incremental.fixed | 2 + .../to_string_in_format_args_incremental.rs | 2 + ...o_string_in_format_args_incremental.stderr | 2 +- tests/ui/unnecessary_operation.fixed | 9 +-- tests/ui/unnecessary_operation.rs | 9 +-- tests/ui/unnecessary_operation.stderr | 40 +++++------ tests/ui/unnecessary_to_owned.rs | 8 +-- 21 files changed, 139 insertions(+), 111 deletions(-) diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 2d5900094ee73..2877871d0bf4c 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,5 +1,6 @@ -#![deny(clippy::index_refutable_slice)] #![allow(clippy::uninlined_format_args)] +#![deny(clippy::index_refutable_slice)] + fn below_limit() { let slice: Option<&[u32]> = Some(&[1, 2, 3]); if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index 94bd99a452fe2..f958b92a102a3 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,5 +1,6 @@ -#![deny(clippy::index_refutable_slice)] #![allow(clippy::uninlined_format_args)] +#![deny(clippy::index_refutable_slice)] + fn below_limit() { let slice: Option<&[u32]> = Some(&[1, 2, 3]); if let Some(slice) = slice { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index 022deb330e6e3..e1a8941e102f5 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,11 +1,11 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:6:17 | LL | if let Some(slice) = slice { | ^^^^^ | note: the lint level is defined here - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:1:9 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:2:9 | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/author/macro_in_closure.rs b/tests/ui/author/macro_in_closure.rs index b6d8e7ecbdd19..373f0148d475a 100644 --- a/tests/ui/author/macro_in_closure.rs +++ b/tests/ui/author/macro_in_closure.rs @@ -1,4 +1,5 @@ //@ check-pass + #![allow(clippy::uninlined_format_args)] fn main() { diff --git a/tests/ui/dbg_macro/dbg_macro.fixed b/tests/ui/dbg_macro/dbg_macro.fixed index 8765035e39887..3b9dee81898ab 100644 --- a/tests/ui/dbg_macro/dbg_macro.fixed +++ b/tests/ui/dbg_macro/dbg_macro.fixed @@ -1,6 +1,11 @@ +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unit_arg, + clippy::unnecessary_operation +)] #![warn(clippy::dbg_macro)] -#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] -#![allow(clippy::uninlined_format_args)] + fn foo(n: u32) -> u32 { if let Some(n) = n.checked_sub(4) { n } else { n } //~^ dbg_macro diff --git a/tests/ui/dbg_macro/dbg_macro.rs b/tests/ui/dbg_macro/dbg_macro.rs index 301f17db3169f..1dbbc6fe98456 100644 --- a/tests/ui/dbg_macro/dbg_macro.rs +++ b/tests/ui/dbg_macro/dbg_macro.rs @@ -1,6 +1,11 @@ +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unit_arg, + clippy::unnecessary_operation +)] #![warn(clippy::dbg_macro)] -#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] -#![allow(clippy::uninlined_format_args)] + fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } //~^ dbg_macro diff --git a/tests/ui/dbg_macro/dbg_macro.stderr b/tests/ui/dbg_macro/dbg_macro.stderr index cd6dce584a2fa..f1412023cc897 100644 --- a/tests/ui/dbg_macro/dbg_macro.stderr +++ b/tests/ui/dbg_macro/dbg_macro.stderr @@ -1,5 +1,5 @@ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:5:22 + --> tests/ui/dbg_macro/dbg_macro.rs:10:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + if let Some(n) = n.checked_sub(4) { n } else { n } | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:11:8 + --> tests/ui/dbg_macro/dbg_macro.rs:16:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + if n <= 1 { | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:14:9 + --> tests/ui/dbg_macro/dbg_macro.rs:19:9 | LL | dbg!(1) | ^^^^^^^ @@ -37,7 +37,7 @@ LL + 1 | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:17:9 + --> tests/ui/dbg_macro/dbg_macro.rs:22:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:23:5 + --> tests/ui/dbg_macro/dbg_macro.rs:28:5 | LL | dbg!(42); | ^^^^^^^^ @@ -61,7 +61,7 @@ LL + 42; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:26:14 + --> tests/ui/dbg_macro/dbg_macro.rs:31:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + foo(3) + factorial(4); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:29:5 + --> tests/ui/dbg_macro/dbg_macro.rs:34:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + (1, 2, 3, 4, 5); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:51:5 + --> tests/ui/dbg_macro/dbg_macro.rs:56:5 | LL | dbg!(); | ^^^^^^ @@ -96,7 +96,7 @@ LL - dbg!(); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:55:13 + --> tests/ui/dbg_macro/dbg_macro.rs:60:13 | LL | let _ = dbg!(); | ^^^^^^ @@ -108,7 +108,7 @@ LL + let _ = (); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:58:9 + --> tests/ui/dbg_macro/dbg_macro.rs:63:9 | LL | bar(dbg!()); | ^^^^^^ @@ -120,7 +120,7 @@ LL + bar(()); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:61:10 + --> tests/ui/dbg_macro/dbg_macro.rs:66:10 | LL | foo!(dbg!()); | ^^^^^^ @@ -132,7 +132,7 @@ LL + foo!(()); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:64:16 + --> tests/ui/dbg_macro/dbg_macro.rs:69:16 | LL | foo2!(foo!(dbg!())); | ^^^^^^ @@ -144,7 +144,7 @@ LL + foo2!(foo!(())); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:46:13 + --> tests/ui/dbg_macro/dbg_macro.rs:51:13 | LL | dbg!(); | ^^^^^^ @@ -159,7 +159,7 @@ LL - dbg!(); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:87:9 + --> tests/ui/dbg_macro/dbg_macro.rs:92:9 | LL | dbg!(2); | ^^^^^^^ @@ -171,7 +171,7 @@ LL + 2; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:94:5 + --> tests/ui/dbg_macro/dbg_macro.rs:99:5 | LL | dbg!(1); | ^^^^^^^ @@ -183,7 +183,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:100:5 + --> tests/ui/dbg_macro/dbg_macro.rs:105:5 | LL | dbg!(1); | ^^^^^^^ @@ -195,7 +195,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:107:9 + --> tests/ui/dbg_macro/dbg_macro.rs:112:9 | LL | dbg!(1); | ^^^^^^^ @@ -207,7 +207,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:114:31 + --> tests/ui/dbg_macro/dbg_macro.rs:119:31 | LL | println!("dbg: {:?}", dbg!(s)); | ^^^^^^^ @@ -219,7 +219,7 @@ LL + println!("dbg: {:?}", s); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:117:22 + --> tests/ui/dbg_macro/dbg_macro.rs:122:22 | LL | print!("{}", dbg!(s)); | ^^^^^^^ diff --git a/tests/ui/large_futures.fixed b/tests/ui/large_futures.fixed index 6b2acaa9ff103..4c7215f0abeb0 100644 --- a/tests/ui/large_futures.fixed +++ b/tests/ui/large_futures.fixed @@ -1,7 +1,10 @@ +#![allow( + clippy::future_not_send, + clippy::manual_async_fn, + clippy::never_loop, + clippy::uninlined_format_args +)] #![warn(clippy::large_futures)] -#![allow(clippy::never_loop)] -#![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn, clippy::uninlined_format_args)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/tests/ui/large_futures.rs b/tests/ui/large_futures.rs index 6bf1fb87727ad..2b5860583f5ec 100644 --- a/tests/ui/large_futures.rs +++ b/tests/ui/large_futures.rs @@ -1,7 +1,10 @@ +#![allow( + clippy::future_not_send, + clippy::manual_async_fn, + clippy::never_loop, + clippy::uninlined_format_args +)] #![warn(clippy::large_futures)] -#![allow(clippy::never_loop)] -#![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn, clippy::uninlined_format_args)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/tests/ui/large_futures.stderr b/tests/ui/large_futures.stderr index fd6ba4e3563de..4280c9e2af284 100644 --- a/tests/ui/large_futures.stderr +++ b/tests/ui/large_futures.stderr @@ -1,5 +1,5 @@ error: large future with a size of 16385 bytes - --> tests/ui/large_futures.rs:10:9 + --> tests/ui/large_futures.rs:13:9 | LL | big_fut([0u8; 1024 * 16]).await; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))` @@ -8,37 +8,37 @@ LL | big_fut([0u8; 1024 * 16]).await; = help: to override `-D warnings` add `#[allow(clippy::large_futures)]` error: large future with a size of 16386 bytes - --> tests/ui/large_futures.rs:13:5 + --> tests/ui/large_futures.rs:16:5 | LL | f.await | ^ help: consider `Box::pin` on it: `Box::pin(f)` error: large future with a size of 16387 bytes - --> tests/ui/large_futures.rs:18:9 + --> tests/ui/large_futures.rs:21:9 | LL | wait().await; | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` error: large future with a size of 16387 bytes - --> tests/ui/large_futures.rs:24:13 + --> tests/ui/large_futures.rs:27:13 | LL | wait().await; | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:32:5 + --> tests/ui/large_futures.rs:35:5 | LL | foo().await; | ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())` error: large future with a size of 49159 bytes - --> tests/ui/large_futures.rs:35:5 + --> tests/ui/large_futures.rs:38:5 | LL | calls_fut(fut).await; | ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))` error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:48:5 + --> tests/ui/large_futures.rs:51:5 | LL | / async { LL | | @@ -61,7 +61,7 @@ LL + }) | error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:61:13 + --> tests/ui/large_futures.rs:64:13 | LL | / async { LL | | diff --git a/tests/ui/manual_inspect.fixed b/tests/ui/manual_inspect.fixed index 1a722cef43401..8d045c48ceb04 100644 --- a/tests/ui/manual_inspect.fixed +++ b/tests/ui/manual_inspect.fixed @@ -1,5 +1,5 @@ -#![warn(clippy::manual_inspect)] #![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] +#![warn(clippy::manual_inspect)] fn main() { let _ = Some(0).inspect(|&x| { diff --git a/tests/ui/manual_inspect.rs b/tests/ui/manual_inspect.rs index eaf6b9fa0edf6..a89d28dab689c 100644 --- a/tests/ui/manual_inspect.rs +++ b/tests/ui/manual_inspect.rs @@ -1,5 +1,5 @@ -#![warn(clippy::manual_inspect)] #![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] +#![warn(clippy::manual_inspect)] fn main() { let _ = Some(0).map(|x| { diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index 18cca14701f31..e9c50ea5dd51c 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,3 +1,5 @@ +//@no-rustfix + #![allow( clippy::if_same_then_else, clippy::no_effect, @@ -6,8 +8,9 @@ clippy::uninlined_format_args )] #![warn(clippy::needless_pass_by_ref_mut)] -//@no-rustfix + use std::ptr::NonNull; + fn foo(s: &mut Vec, b: &u32, x: &mut u32) { //~^ needless_pass_by_ref_mut diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 6637a255b5f51..a718188f4af37 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:11:11 + --> tests/ui/needless_pass_by_ref_mut.rs:14:11 | LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` @@ -8,79 +8,79 @@ LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:37:12 + --> tests/ui/needless_pass_by_ref_mut.rs:40:12 | LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:48:12 + --> tests/ui/needless_pass_by_ref_mut.rs:51:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:51:29 + --> tests/ui/needless_pass_by_ref_mut.rs:54:29 | LL | fn mushroom(&self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:129:16 + --> tests/ui/needless_pass_by_ref_mut.rs:132:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:134:16 + --> tests/ui/needless_pass_by_ref_mut.rs:137:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:139:16 + --> tests/ui/needless_pass_by_ref_mut.rs:142:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:144:16 + --> tests/ui/needless_pass_by_ref_mut.rs:147:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:149:24 + --> tests/ui/needless_pass_by_ref_mut.rs:152:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:154:24 + --> tests/ui/needless_pass_by_ref_mut.rs:157:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:159:32 + --> tests/ui/needless_pass_by_ref_mut.rs:162:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:164:24 + --> tests/ui/needless_pass_by_ref_mut.rs:167:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:164:45 + --> tests/ui/needless_pass_by_ref_mut.rs:167:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:200:16 + --> tests/ui/needless_pass_by_ref_mut.rs:203:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:205:20 + --> tests/ui/needless_pass_by_ref_mut.rs:208:20 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -96,115 +96,115 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:218:39 + --> tests/ui/needless_pass_by_ref_mut.rs:221:39 | LL | async fn inner_async2(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:227:26 + --> tests/ui/needless_pass_by_ref_mut.rs:230:26 | LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:247:30 + --> tests/ui/needless_pass_by_ref_mut.rs:250:30 | LL | async fn call_in_closure1(n: &mut str) { | ^^^^^^^^ help: consider changing to: `&str` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:267:16 + --> tests/ui/needless_pass_by_ref_mut.rs:270:16 | LL | fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:279:22 + --> tests/ui/needless_pass_by_ref_mut.rs:282:22 | LL | async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:334:12 + --> tests/ui/needless_pass_by_ref_mut.rs:337:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:337:18 + --> tests/ui/needless_pass_by_ref_mut.rs:340:18 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:337:45 + --> tests/ui/needless_pass_by_ref_mut.rs:340:45 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:346:46 + --> tests/ui/needless_pass_by_ref_mut.rs:349:46 | LL | async fn foo2(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:363:18 + --> tests/ui/needless_pass_by_ref_mut.rs:366:18 | LL | fn _empty_tup(x: &mut (())) {} | ^^^^^^^^^ help: consider changing to: `&()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:365:19 + --> tests/ui/needless_pass_by_ref_mut.rs:368:19 | LL | fn _single_tup(x: &mut ((i32,))) {} | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:367:18 + --> tests/ui/needless_pass_by_ref_mut.rs:370:18 | LL | fn _multi_tup(x: &mut ((i32, u32))) {} | ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:369:11 + --> tests/ui/needless_pass_by_ref_mut.rs:372:11 | LL | fn _fn(x: &mut (fn())) {} | ^^^^^^^^^^^ help: consider changing to: `&fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:372:23 + --> tests/ui/needless_pass_by_ref_mut.rs:375:23 | LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:374:20 + --> tests/ui/needless_pass_by_ref_mut.rs:377:20 | LL | fn _extern_c_fn(x: &mut extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:376:18 + --> tests/ui/needless_pass_by_ref_mut.rs:379:18 | LL | fn _unsafe_fn(x: &mut unsafe fn()) {} | ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:378:25 + --> tests/ui/needless_pass_by_ref_mut.rs:381:25 | LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:380:20 + --> tests/ui/needless_pass_by_ref_mut.rs:383:20 | LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:382:20 + --> tests/ui/needless_pass_by_ref_mut.rs:385:20 | LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)` diff --git a/tests/ui/to_string_in_format_args_incremental.fixed b/tests/ui/to_string_in_format_args_incremental.fixed index 32efc2b5eba14..98b028c15fd0a 100644 --- a/tests/ui/to_string_in_format_args_incremental.fixed +++ b/tests/ui/to_string_in_format_args_incremental.fixed @@ -1,5 +1,7 @@ //@compile-flags: -C incremental=target/debug/test/incr + #![allow(clippy::uninlined_format_args)] + // see https://github.com/rust-lang/rust-clippy/issues/10969 fn main() { diff --git a/tests/ui/to_string_in_format_args_incremental.rs b/tests/ui/to_string_in_format_args_incremental.rs index 197685fb0d7c4..a5ebe4b032750 100644 --- a/tests/ui/to_string_in_format_args_incremental.rs +++ b/tests/ui/to_string_in_format_args_incremental.rs @@ -1,5 +1,7 @@ //@compile-flags: -C incremental=target/debug/test/incr + #![allow(clippy::uninlined_format_args)] + // see https://github.com/rust-lang/rust-clippy/issues/10969 fn main() { diff --git a/tests/ui/to_string_in_format_args_incremental.stderr b/tests/ui/to_string_in_format_args_incremental.stderr index 535dd21ea5888..62178cc0cfbff 100644 --- a/tests/ui/to_string_in_format_args_incremental.stderr +++ b/tests/ui/to_string_in_format_args_incremental.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `println!` args - --> tests/ui/to_string_in_format_args_incremental.rs:7:21 + --> tests/ui/to_string_in_format_args_incremental.rs:9:21 | LL | println!("{}", s.to_string()); | ^^^^^^^^^^^^ help: remove this diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index b632c200b2abc..645b56fe95e74 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -1,12 +1,13 @@ #![allow( clippy::deref_addrof, - dead_code, - unused, clippy::no_effect, - clippy::unnecessary_struct_initialization + clippy::uninlined_format_args, + clippy::unnecessary_struct_initialization, + dead_code, + unused )] #![warn(clippy::unnecessary_operation)] -#![allow(clippy::uninlined_format_args)] + use std::fmt::Display; use std::ops::Shl; diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 3e34b46185e34..97e90269c5c0c 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -1,12 +1,13 @@ #![allow( clippy::deref_addrof, - dead_code, - unused, clippy::no_effect, - clippy::unnecessary_struct_initialization + clippy::uninlined_format_args, + clippy::unnecessary_struct_initialization, + dead_code, + unused )] #![warn(clippy::unnecessary_operation)] -#![allow(clippy::uninlined_format_args)] + use std::fmt::Display; use std::ops::Shl; diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index eb98af09e7a3d..0fda1dfde1903 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:70:5 + --> tests/ui/unnecessary_operation.rs:71:5 | LL | Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` @@ -8,103 +8,103 @@ LL | Tuple(get_number()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:72:5 + --> tests/ui/unnecessary_operation.rs:73:5 | LL | Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:74:5 + --> tests/ui/unnecessary_operation.rs:75:5 | LL | Struct { ..get_struct() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:76:5 + --> tests/ui/unnecessary_operation.rs:77:5 | LL | Enum::Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:78:5 + --> tests/ui/unnecessary_operation.rs:79:5 | LL | Enum::Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:80:5 + --> tests/ui/unnecessary_operation.rs:81:5 | LL | 5 + get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:82:5 + --> tests/ui/unnecessary_operation.rs:83:5 | LL | *&get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:84:5 + --> tests/ui/unnecessary_operation.rs:85:5 | LL | &get_number(); | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:86:5 + --> tests/ui/unnecessary_operation.rs:87:5 | LL | (5, 6, get_number()); | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:88:5 + --> tests/ui/unnecessary_operation.rs:89:5 | LL | get_number()..; | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:90:5 + --> tests/ui/unnecessary_operation.rs:91:5 | LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:92:5 + --> tests/ui/unnecessary_operation.rs:93:5 | LL | 5..get_number(); | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:94:5 + --> tests/ui/unnecessary_operation.rs:95:5 | LL | [42, get_number()]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:96:5 + --> tests/ui/unnecessary_operation.rs:97:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:98:5 + --> tests/ui/unnecessary_operation.rs:99:5 | LL | (42, get_number()).1; | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:100:5 + --> tests/ui/unnecessary_operation.rs:101:5 | LL | [get_number(); 55]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:102:5 + --> tests/ui/unnecessary_operation.rs:103:5 | LL | [42; 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:104:5 + --> tests/ui/unnecessary_operation.rs:105:5 | LL | / { LL | | @@ -113,7 +113,7 @@ LL | | }; | |______^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:108:5 + --> tests/ui/unnecessary_operation.rs:109:5 | LL | / FooString { LL | | @@ -122,7 +122,7 @@ LL | | }; | |______^ help: statement can be reduced to: `String::from("blah");` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:149:5 + --> tests/ui/unnecessary_operation.rs:150:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index b9d613c3a0529..84ac575ddf521 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,13 +1,13 @@ #![allow( + clippy::manual_async_fn, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, clippy::needless_lifetimes, - clippy::owned_cow + clippy::owned_cow, + clippy::ptr_arg )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] -#![allow(clippy::uninlined_format_args)] + use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; From 1ca79a8797e8c81854a1afc8b1cde40d503c92d6 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Tue, 25 Mar 2025 10:49:14 -0400 Subject: [PATCH 056/103] test fixes --- tests/ui/manual_strip_fixable.fixed | 1 + tests/ui/manual_strip_fixable.rs | 1 + tests/ui/manual_strip_fixable.stderr | 8 +- tests/ui/needless_pass_by_ref_mut.rs | 4 +- tests/ui/needless_pass_by_ref_mut.stderr | 68 +++---- .../ui/unnecessary_os_str_debug_formatting.rs | 1 + ...unnecessary_os_str_debug_formatting.stderr | 12 +- tests/ui/unnecessary_path_debug_formatting.rs | 1 + .../unnecessary_path_debug_formatting.stderr | 18 +- tests/ui/unnecessary_to_owned.fixed | 9 +- tests/ui/unnecessary_to_owned.rs | 3 +- tests/ui/unnecessary_to_owned.stderr | 174 +++++++++--------- 12 files changed, 152 insertions(+), 148 deletions(-) diff --git a/tests/ui/manual_strip_fixable.fixed b/tests/ui/manual_strip_fixable.fixed index 75a3f1645de33..b59e3719d951d 100644 --- a/tests/ui/manual_strip_fixable.fixed +++ b/tests/ui/manual_strip_fixable.fixed @@ -1,4 +1,5 @@ #![warn(clippy::manual_strip)] +#![allow(clippy::uninlined_format_args)] fn main() { let s = "abc"; diff --git a/tests/ui/manual_strip_fixable.rs b/tests/ui/manual_strip_fixable.rs index 5080068449e20..4fb3a9bf007f6 100644 --- a/tests/ui/manual_strip_fixable.rs +++ b/tests/ui/manual_strip_fixable.rs @@ -1,4 +1,5 @@ #![warn(clippy::manual_strip)] +#![allow(clippy::uninlined_format_args)] fn main() { let s = "abc"; diff --git a/tests/ui/manual_strip_fixable.stderr b/tests/ui/manual_strip_fixable.stderr index 1c276e5d8fdfe..da8b0cd08f893 100644 --- a/tests/ui/manual_strip_fixable.stderr +++ b/tests/ui/manual_strip_fixable.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> tests/ui/manual_strip_fixable.rs:7:24 + --> tests/ui/manual_strip_fixable.rs:8:24 | LL | let stripped = &s["ab".len()..]; | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> tests/ui/manual_strip_fixable.rs:6:5 + --> tests/ui/manual_strip_fixable.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,13 +19,13 @@ LL ~ println!("{stripped}{}", stripped); | error: stripping a suffix manually - --> tests/ui/manual_strip_fixable.rs:13:24 + --> tests/ui/manual_strip_fixable.rs:14:24 | LL | let stripped = &s[..s.len() - "bc".len()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> tests/ui/manual_strip_fixable.rs:12:5 + --> tests/ui/manual_strip_fixable.rs:13:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index e9c50ea5dd51c..5d9936fdac4d9 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #![allow( clippy::if_same_then_else, clippy::no_effect, @@ -8,7 +6,7 @@ clippy::uninlined_format_args )] #![warn(clippy::needless_pass_by_ref_mut)] - +//@no-rustfix use std::ptr::NonNull; fn foo(s: &mut Vec, b: &u32, x: &mut u32) { diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index a718188f4af37..94d98f0e9b12d 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:14:11 + --> tests/ui/needless_pass_by_ref_mut.rs:12:11 | LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` @@ -8,79 +8,79 @@ LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:40:12 + --> tests/ui/needless_pass_by_ref_mut.rs:38:12 | LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:51:12 + --> tests/ui/needless_pass_by_ref_mut.rs:49:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:54:29 + --> tests/ui/needless_pass_by_ref_mut.rs:52:29 | LL | fn mushroom(&self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:132:16 + --> tests/ui/needless_pass_by_ref_mut.rs:130:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:137:16 + --> tests/ui/needless_pass_by_ref_mut.rs:135:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:142:16 + --> tests/ui/needless_pass_by_ref_mut.rs:140:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:147:16 + --> tests/ui/needless_pass_by_ref_mut.rs:145:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:152:24 + --> tests/ui/needless_pass_by_ref_mut.rs:150:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:157:24 + --> tests/ui/needless_pass_by_ref_mut.rs:155:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:162:32 + --> tests/ui/needless_pass_by_ref_mut.rs:160:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:167:24 + --> tests/ui/needless_pass_by_ref_mut.rs:165:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:167:45 + --> tests/ui/needless_pass_by_ref_mut.rs:165:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:203:16 + --> tests/ui/needless_pass_by_ref_mut.rs:201:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:208:20 + --> tests/ui/needless_pass_by_ref_mut.rs:206:20 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -96,115 +96,115 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:221:39 + --> tests/ui/needless_pass_by_ref_mut.rs:219:39 | LL | async fn inner_async2(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:230:26 + --> tests/ui/needless_pass_by_ref_mut.rs:228:26 | LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:250:30 + --> tests/ui/needless_pass_by_ref_mut.rs:248:30 | LL | async fn call_in_closure1(n: &mut str) { | ^^^^^^^^ help: consider changing to: `&str` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:270:16 + --> tests/ui/needless_pass_by_ref_mut.rs:268:16 | LL | fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:282:22 + --> tests/ui/needless_pass_by_ref_mut.rs:280:22 | LL | async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:337:12 + --> tests/ui/needless_pass_by_ref_mut.rs:335:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:340:18 + --> tests/ui/needless_pass_by_ref_mut.rs:338:18 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:340:45 + --> tests/ui/needless_pass_by_ref_mut.rs:338:45 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:349:46 + --> tests/ui/needless_pass_by_ref_mut.rs:347:46 | LL | async fn foo2(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:366:18 + --> tests/ui/needless_pass_by_ref_mut.rs:364:18 | LL | fn _empty_tup(x: &mut (())) {} | ^^^^^^^^^ help: consider changing to: `&()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:368:19 + --> tests/ui/needless_pass_by_ref_mut.rs:366:19 | LL | fn _single_tup(x: &mut ((i32,))) {} | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:370:18 + --> tests/ui/needless_pass_by_ref_mut.rs:368:18 | LL | fn _multi_tup(x: &mut ((i32, u32))) {} | ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:372:11 + --> tests/ui/needless_pass_by_ref_mut.rs:370:11 | LL | fn _fn(x: &mut (fn())) {} | ^^^^^^^^^^^ help: consider changing to: `&fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:375:23 + --> tests/ui/needless_pass_by_ref_mut.rs:373:23 | LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:377:20 + --> tests/ui/needless_pass_by_ref_mut.rs:375:20 | LL | fn _extern_c_fn(x: &mut extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:379:18 + --> tests/ui/needless_pass_by_ref_mut.rs:377:18 | LL | fn _unsafe_fn(x: &mut unsafe fn()) {} | ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:381:25 + --> tests/ui/needless_pass_by_ref_mut.rs:379:25 | LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:383:20 + --> tests/ui/needless_pass_by_ref_mut.rs:381:20 | LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:385:20 + --> tests/ui/needless_pass_by_ref_mut.rs:383:20 | LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)` diff --git a/tests/ui/unnecessary_os_str_debug_formatting.rs b/tests/ui/unnecessary_os_str_debug_formatting.rs index 12663ec9a528f..6652efd9ae1d8 100644 --- a/tests/ui/unnecessary_os_str_debug_formatting.rs +++ b/tests/ui/unnecessary_os_str_debug_formatting.rs @@ -1,4 +1,5 @@ #![warn(clippy::unnecessary_debug_formatting)] +#![allow(clippy::uninlined_format_args)] use std::ffi::{OsStr, OsString}; diff --git a/tests/ui/unnecessary_os_str_debug_formatting.stderr b/tests/ui/unnecessary_os_str_debug_formatting.stderr index 001309ab817a1..382e59b046193 100644 --- a/tests/ui/unnecessary_os_str_debug_formatting.stderr +++ b/tests/ui/unnecessary_os_str_debug_formatting.stderr @@ -1,5 +1,5 @@ error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:14:22 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:15:22 | LL | println!("{:?}", os_str); | ^^^^^^ @@ -10,7 +10,7 @@ LL | println!("{:?}", os_str); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_debug_formatting)]` error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:15:22 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:16:22 | LL | println!("{:?}", os_string); | ^^^^^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", os_string); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:17:16 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:18:16 | LL | println!("{os_str:?}"); | ^^^^^^ @@ -28,7 +28,7 @@ LL | println!("{os_str:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:18:16 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:19:16 | LL | println!("{os_string:?}"); | ^^^^^^^^^ @@ -37,7 +37,7 @@ LL | println!("{os_string:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:20:37 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:21:37 | LL | let _: String = format!("{:?}", os_str); | ^^^^^^ @@ -46,7 +46,7 @@ LL | let _: String = format!("{:?}", os_str); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:21:37 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:22:37 | LL | let _: String = format!("{:?}", os_string); | ^^^^^^^^^ diff --git a/tests/ui/unnecessary_path_debug_formatting.rs b/tests/ui/unnecessary_path_debug_formatting.rs index f14f6085c9a14..215e0d5d7802e 100644 --- a/tests/ui/unnecessary_path_debug_formatting.rs +++ b/tests/ui/unnecessary_path_debug_formatting.rs @@ -1,4 +1,5 @@ #![warn(clippy::unnecessary_debug_formatting)] +#![allow(clippy::uninlined_format_args)] use std::ffi::{OsStr, OsString}; use std::ops::Deref; diff --git a/tests/ui/unnecessary_path_debug_formatting.stderr b/tests/ui/unnecessary_path_debug_formatting.stderr index f12fa72c84b35..d244b9ad6716a 100644 --- a/tests/ui/unnecessary_path_debug_formatting.stderr +++ b/tests/ui/unnecessary_path_debug_formatting.stderr @@ -1,5 +1,5 @@ error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:29:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:30:22 | LL | println!("{:?}", os_str); | ^^^^^^ @@ -10,7 +10,7 @@ LL | println!("{:?}", os_str); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_debug_formatting)]` error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:30:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:31:22 | LL | println!("{:?}", os_string); | ^^^^^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", os_string); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:32:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:33:22 | LL | println!("{:?}", path); | ^^^^ @@ -28,7 +28,7 @@ LL | println!("{:?}", path); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:33:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:34:22 | LL | println!("{:?}", path_buf); | ^^^^^^^^ @@ -37,7 +37,7 @@ LL | println!("{:?}", path_buf); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:35:16 + --> tests/ui/unnecessary_path_debug_formatting.rs:36:16 | LL | println!("{path:?}"); | ^^^^ @@ -46,7 +46,7 @@ LL | println!("{path:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:36:16 + --> tests/ui/unnecessary_path_debug_formatting.rs:37:16 | LL | println!("{path_buf:?}"); | ^^^^^^^^ @@ -55,7 +55,7 @@ LL | println!("{path_buf:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:38:37 + --> tests/ui/unnecessary_path_debug_formatting.rs:39:37 | LL | let _: String = format!("{:?}", path); | ^^^^ @@ -64,7 +64,7 @@ LL | let _: String = format!("{:?}", path); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:39:37 + --> tests/ui/unnecessary_path_debug_formatting.rs:40:37 | LL | let _: String = format!("{:?}", path_buf); | ^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _: String = format!("{:?}", path_buf); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:42:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:43:22 | LL | println!("{:?}", &*deref_path); | ^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 498dab3eb5070..b064a8b8f46fb 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,13 +1,14 @@ #![allow( + clippy::manual_async_fn, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, clippy::needless_lifetimes, - clippy::owned_cow + clippy::owned_cow, + clippy::ptr_arg, + clippy::uninlined_format_args )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] -#![allow(clippy::uninlined_format_args)] + use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 84ac575ddf521..7954a4ad4ce77 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -4,7 +4,8 @@ clippy::needless_borrows_for_generic_args, clippy::needless_lifetimes, clippy::owned_cow, - clippy::ptr_arg + clippy::ptr_arg, + clippy::uninlined_format_args )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 8926db34da8c8..6c52be8393010 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:217:64 + --> tests/ui/unnecessary_to_owned.rs:218:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:217:20 + --> tests/ui/unnecessary_to_owned.rs:218:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:219:40 + --> tests/ui/unnecessary_to_owned.rs:220:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:219:21 + --> tests/ui/unnecessary_to_owned.rs:220:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:221:48 + --> tests/ui/unnecessary_to_owned.rs:222:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:221:19 + --> tests/ui/unnecessary_to_owned.rs:222:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:223:35 + --> tests/ui/unnecessary_to_owned.rs:224:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:223:18 + --> tests/ui/unnecessary_to_owned.rs:224:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:225:39 + --> tests/ui/unnecessary_to_owned.rs:226:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:225:20 + --> tests/ui/unnecessary_to_owned.rs:226:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:65:36 + --> tests/ui/unnecessary_to_owned.rs:66:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,391 +70,391 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:67:19 + --> tests/ui/unnecessary_to_owned.rs:68:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> tests/ui/unnecessary_to_owned.rs:70:20 + --> tests/ui/unnecessary_to_owned.rs:71:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:72:38 + --> tests/ui/unnecessary_to_owned.rs:73:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:74:20 + --> tests/ui/unnecessary_to_owned.rs:75:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> tests/ui/unnecessary_to_owned.rs:77:18 + --> tests/ui/unnecessary_to_owned.rs:78:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:79:34 + --> tests/ui/unnecessary_to_owned.rs:80:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:81:18 + --> tests/ui/unnecessary_to_owned.rs:82:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:84:17 + --> tests/ui/unnecessary_to_owned.rs:85:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:86:30 + --> tests/ui/unnecessary_to_owned.rs:87:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:88:17 + --> tests/ui/unnecessary_to_owned.rs:89:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:90:17 + --> tests/ui/unnecessary_to_owned.rs:91:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:93:19 + --> tests/ui/unnecessary_to_owned.rs:94:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:95:36 + --> tests/ui/unnecessary_to_owned.rs:96:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:97:19 + --> tests/ui/unnecessary_to_owned.rs:98:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:99:19 + --> tests/ui/unnecessary_to_owned.rs:100:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:101:19 + --> tests/ui/unnecessary_to_owned.rs:102:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:105:42 + --> tests/ui/unnecessary_to_owned.rs:106:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:109:25 + --> tests/ui/unnecessary_to_owned.rs:110:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:111:26 + --> tests/ui/unnecessary_to_owned.rs:112:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:113:24 + --> tests/ui/unnecessary_to_owned.rs:114:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:115:23 + --> tests/ui/unnecessary_to_owned.rs:116:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:117:25 + --> tests/ui/unnecessary_to_owned.rs:118:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:120:30 + --> tests/ui/unnecessary_to_owned.rs:121:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:122:31 + --> tests/ui/unnecessary_to_owned.rs:123:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:124:29 + --> tests/ui/unnecessary_to_owned.rs:125:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:28 + --> tests/ui/unnecessary_to_owned.rs:127:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:30 + --> tests/ui/unnecessary_to_owned.rs:129:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:131:29 + --> tests/ui/unnecessary_to_owned.rs:132:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:131:43 + --> tests/ui/unnecessary_to_owned.rs:132:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:134:29 + --> tests/ui/unnecessary_to_owned.rs:135:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:134:47 + --> tests/ui/unnecessary_to_owned.rs:135:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:138:26 + --> tests/ui/unnecessary_to_owned.rs:139:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:140:27 + --> tests/ui/unnecessary_to_owned.rs:141:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:142:25 + --> tests/ui/unnecessary_to_owned.rs:143:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:144:24 + --> tests/ui/unnecessary_to_owned.rs:145:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:146:24 + --> tests/ui/unnecessary_to_owned.rs:147:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:148:26 + --> tests/ui/unnecessary_to_owned.rs:149:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:150:26 + --> tests/ui/unnecessary_to_owned.rs:151:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:152:26 + --> tests/ui/unnecessary_to_owned.rs:153:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:155:31 + --> tests/ui/unnecessary_to_owned.rs:156:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:157:32 + --> tests/ui/unnecessary_to_owned.rs:158:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:159:30 + --> tests/ui/unnecessary_to_owned.rs:160:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:161:29 + --> tests/ui/unnecessary_to_owned.rs:162:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:163:29 + --> tests/ui/unnecessary_to_owned.rs:164:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:165:31 + --> tests/ui/unnecessary_to_owned.rs:166:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:167:31 + --> tests/ui/unnecessary_to_owned.rs:168:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:169:31 + --> tests/ui/unnecessary_to_owned.rs:170:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:172:30 + --> tests/ui/unnecessary_to_owned.rs:173:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:172:44 + --> tests/ui/unnecessary_to_owned.rs:173:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:175:30 + --> tests/ui/unnecessary_to_owned.rs:176:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:175:44 + --> tests/ui/unnecessary_to_owned.rs:176:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:178:30 + --> tests/ui/unnecessary_to_owned.rs:179:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:178:44 + --> tests/ui/unnecessary_to_owned.rs:179:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:181:30 + --> tests/ui/unnecessary_to_owned.rs:182:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:181:48 + --> tests/ui/unnecessary_to_owned.rs:182:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:184:30 + --> tests/ui/unnecessary_to_owned.rs:185:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:184:52 + --> tests/ui/unnecessary_to_owned.rs:185:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:187:30 + --> tests/ui/unnecessary_to_owned.rs:188:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:187:48 + --> tests/ui/unnecessary_to_owned.rs:188:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:191:20 + --> tests/ui/unnecessary_to_owned.rs:192:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:194:13 + --> tests/ui/unnecessary_to_owned.rs:195:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:196:13 + --> tests/ui/unnecessary_to_owned.rs:197:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:199:13 + --> tests/ui/unnecessary_to_owned.rs:200:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:201:13 + --> tests/ui/unnecessary_to_owned.rs:202:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:229:26 + --> tests/ui/unnecessary_to_owned.rs:230:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -466,7 +466,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:231:26 + --> tests/ui/unnecessary_to_owned.rs:232:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:233:26 + --> tests/ui/unnecessary_to_owned.rs:234:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:291:14 + --> tests/ui/unnecessary_to_owned.rs:292:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -503,49 +503,49 @@ LL ~ let path = match get_file_path(t) { | error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:357:24 + --> tests/ui/unnecessary_to_owned.rs:358:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:467:12 + --> tests/ui/unnecessary_to_owned.rs:468:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:611:37 + --> tests/ui/unnecessary_to_owned.rs:612:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:622:18 + --> tests/ui/unnecessary_to_owned.rs:623:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:627:14 + --> tests/ui/unnecessary_to_owned.rs:628:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:629:14 + --> tests/ui/unnecessary_to_owned.rs:630:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:635:14 + --> tests/ui/unnecessary_to_owned.rs:636:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:637:14 + --> tests/ui/unnecessary_to_owned.rs:638:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` From aba76d0e78aeb031f941c7a3466eb4d9d13d7a85 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 24 Mar 2025 23:19:18 +0000 Subject: [PATCH 057/103] Allow defining opaques in statics and consts --- clippy_utils/src/ast_utils/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 54261079fcad8..6023ae9cc7b16 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -336,12 +336,14 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { mutability: lm, expr: le, safety: ls, + define_opaque: _, }), Static(box StaticItem { ty: rt, mutability: rm, expr: re, safety: rs, + define_opaque: _, }), ) => lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( @@ -350,12 +352,14 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { generics: lg, ty: lt, expr: le, + define_opaque: _, }), Const(box ConstItem { defaultness: rd, generics: rg, ty: rt, expr: re, + define_opaque: _, }), ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( @@ -490,12 +494,14 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { mutability: lm, expr: le, safety: ls, + define_opaque: _, }), Static(box StaticItem { ty: rt, mutability: rm, expr: re, safety: rs, + define_opaque: _, }), ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs, ( @@ -557,12 +563,14 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { generics: lg, ty: lt, expr: le, + define_opaque: _, }), Const(box ConstItem { defaultness: rd, generics: rg, ty: rt, expr: re, + define_opaque: _, }), ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( From 94233fb0ee1a683d076d933aa2c8f77c8cd9f66c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 1 Mar 2025 17:53:49 +0100 Subject: [PATCH 058/103] Unify `manual_unwrap_or` and `manual_unwrap_or_default` code Both lints share a lot of characteristics but were implemented in unrelated ways. This unifies them, saving around 100 SLOC in the process, and making one more test trigger the lint. Also, this removes useless blocks in suggestions. --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/manual_unwrap_or_default.rs | 212 ------------- clippy_lints/src/matches/manual_unwrap_or.rs | 294 ++++++++++++------- clippy_lints/src/matches/mod.rs | 38 +++ clippy_utils/src/higher.rs | 6 + clippy_utils/src/lib.rs | 1 + tests/ui/manual_unwrap_or.fixed | 30 +- tests/ui/manual_unwrap_or.rs | 15 +- tests/ui/manual_unwrap_or.stderr | 39 ++- tests/ui/manual_unwrap_or_default.fixed | 7 +- tests/ui/manual_unwrap_or_default.rs | 12 +- tests/ui/manual_unwrap_or_default.stderr | 13 +- 13 files changed, 322 insertions(+), 349 deletions(-) delete mode 100644 clippy_lints/src/manual_unwrap_or_default.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a9b6b369c4c39..c2274f0a619cf 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -335,7 +335,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO, crate::manual_string_new::MANUAL_STRING_NEW_INFO, crate::manual_strip::MANUAL_STRIP_INFO, - crate::manual_unwrap_or_default::MANUAL_UNWRAP_OR_DEFAULT_INFO, crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, crate::match_result_ok::MATCH_RESULT_OK_INFO, @@ -345,6 +344,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::matches::MANUAL_MAP_INFO, crate::matches::MANUAL_OK_ERR_INFO, crate::matches::MANUAL_UNWRAP_OR_INFO, + crate::matches::MANUAL_UNWRAP_OR_DEFAULT_INFO, crate::matches::MATCH_AS_REF_INFO, crate::matches::MATCH_BOOL_INFO, crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2d390be5248c6..6f2a4a4c529da 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -226,7 +226,6 @@ mod manual_rotate; mod manual_slice_size_calculation; mod manual_string_new; mod manual_strip; -mod manual_unwrap_or_default; mod map_unit_fn; mod match_result_ok; mod matches; @@ -960,7 +959,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); - store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); diff --git a/clippy_lints/src/manual_unwrap_or_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs deleted file mode 100644 index 1aa12e72a6d45..0000000000000 --- a/clippy_lints/src/manual_unwrap_or_default.rs +++ /dev/null @@ -1,212 +0,0 @@ -use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::GenericArgKind; -use rustc_session::declare_lint_pass; -use rustc_span::sym; - -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, implements_trait}; -use clippy_utils::{is_default_equivalent, is_in_const_context, path_res, peel_blocks, span_contains_comment}; - -declare_clippy_lint! { - /// ### What it does - /// Checks if a `match` or `if let` expression can be simplified using - /// `.unwrap_or_default()`. - /// - /// ### Why is this bad? - /// It can be done in one call with `.unwrap_or_default()`. - /// - /// ### Example - /// ```no_run - /// let x: Option = Some(String::new()); - /// let y: String = match x { - /// Some(v) => v, - /// None => String::new(), - /// }; - /// - /// let x: Option> = Some(Vec::new()); - /// let y: Vec = if let Some(v) = x { - /// v - /// } else { - /// Vec::new() - /// }; - /// ``` - /// Use instead: - /// ```no_run - /// let x: Option = Some(String::new()); - /// let y: String = x.unwrap_or_default(); - /// - /// let x: Option> = Some(Vec::new()); - /// let y: Vec = x.unwrap_or_default(); - /// ``` - #[clippy::version = "1.79.0"] - pub MANUAL_UNWRAP_OR_DEFAULT, - suspicious, - "check if a `match` or `if let` can be simplified with `unwrap_or_default`" -} - -declare_lint_pass!(ManualUnwrapOrDefault => [MANUAL_UNWRAP_OR_DEFAULT]); - -fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { - if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind - && let PatKind::Binding(_, pat_id, _, _) = pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) - || cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id)) - { - Some(pat_id) - } else { - None - } -} - -fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) - { - Some(arm.body) - } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) - { - Some(arm.body) - } else if let PatKind::Wild = arm.pat.kind { - // We consider that the `Some` check will filter it out if it's not right. - Some(arm.body) - } else { - None - } -} - -fn get_some_and_none_bodies<'tcx>( - cx: &LateContext<'tcx>, - arm1: &'tcx Arm<'tcx>, - arm2: &'tcx Arm<'tcx>, -) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { - if let Some(binding_id) = get_some(cx, arm1.pat) - && let Some(body_none) = get_none(cx, arm2) - { - Some(((arm1.body, binding_id), body_none)) - } else if let Some(binding_id) = get_some(cx, arm2.pat) - && let Some(body_none) = get_none(cx, arm1) - { - Some(((arm2.body, binding_id), body_none)) - } else { - None - } -} - -#[allow(clippy::needless_pass_by_value)] -fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, expr: &'tcx Expr<'tcx>) { - // Get expr_name ("if let" or "match" depending on kind of expression), the condition, the body for - // the some arm, the body for the none arm and the binding id of the some arm - let (expr_name, condition, body_some, body_none, binding_id) = match if_let_or_match { - IfLetOrMatch::Match(condition, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) - // Make sure there are no guards to keep things simple - if arm1.guard.is_none() - && arm2.guard.is_none() - // Get the some and none bodies and the binding id of the some arm - && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) => - { - ("match", condition, body_some, body_none, binding_id) - }, - IfLetOrMatch::IfLet(condition, pat, if_expr, Some(else_expr), _) - if let Some(binding_id) = get_some(cx, pat) => - { - ("if let", condition, if_expr, else_expr, binding_id) - }, - _ => { - // All other cases (match with number of arms != 2, if let without else, etc.) - return; - }, - }; - - // We check if the return type of the expression implements Default. - let expr_type = cx.typeck_results().expr_ty(expr); - if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) - && implements_trait(cx, expr_type, default_trait_id, &[]) - // We check if the initial condition implements Default. - && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) - && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() - && implements_trait(cx, condition_ty, default_trait_id, &[]) - // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. - && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind - && let Res::Local(local_id) = path.res - && local_id == binding_id - // We now check the `None` arm is calling a method equivalent to `Default::default`. - && let body_none = peel_blocks(body_none) - && is_default_equivalent(cx, body_none) - && let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_paren) - { - // Machine applicable only if there are no comments present - let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - - // We now check if the condition is a None variant, in which case we need to specify the type - if path_res(cx, condition) - .opt_def_id() - .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) - { - return span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - "replace it with", - format!("{receiver}::<{expr_type}>.unwrap_or_default()"), - applicability, - ); - } - - // We check if the expression type is still uncertain, in which case we ask the user to specify it - if !expr_type_is_certain(cx, condition) { - return span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - format!("ascribe the type {expr_type} and replace your expression with"), - format!("{receiver}.unwrap_or_default()"), - Applicability::Unspecified, - ); - } - - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - "replace it with", - format!("{receiver}.unwrap_or_default()"), - applicability, - ); - } -} - -impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) - && !expr.span.from_expansion() - && !is_in_const_context(cx) - { - handle(cx, if_let_or_match, expr); - } - } -} diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 5394b7af8ff8e..b64ae0b24d818 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,133 +1,219 @@ use clippy_utils::consts::ConstEvalCtxt; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; +use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, ResultErr}; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, Pat, PatExpr, PatExprKind, PatKind}; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_hir::def::Res; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_span::sym; -use super::MANUAL_UNWRAP_OR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::{expr_type_is_certain, get_type_diagnostic_name, implements_trait}; +use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; -pub(super) fn check_match<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - scrutinee: &'tcx Expr<'_>, - arms: &'tcx [Arm<'_>], -) { - let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some((or_arm, unwrap_arm)) = applicable_or_arm(cx, arms) { - check_and_lint(cx, expr, unwrap_arm.pat, scrutinee, unwrap_arm.body, or_arm.body, ty); +use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; + +fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { + if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind + && let PatKind::Binding(_, pat_id, _, _) = pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && let Some(lang_item) = cx.tcx.lang_items().from_def_id(def_id) + && matches!(lang_item, LangItem::OptionSome | LangItem::ResultOk) + { + Some(pat_id) + } else { + None } } -pub(super) fn check_if_let<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - let_pat: &'tcx Pat<'_>, - let_expr: &'tcx Expr<'_>, - then_expr: &'tcx Expr<'_>, - else_expr: &'tcx Expr<'_>, -) { - let ty = cx.typeck_results().expr_ty(let_expr); - let then_ty = cx.typeck_results().expr_ty(then_expr); - // The signature is `fn unwrap_or(self: Option, default: T) -> T`. - // When `expr_adjustments(then_expr).is_empty()`, `T` should equate to `default`'s type. - // Otherwise, type error will occur. - if cx.typeck_results().expr_adjustments(then_expr).is_empty() - && let rustc_middle::ty::Adt(_did, args) = ty.kind() - && let Some(some_ty) = args.first().and_then(|arg| arg.as_type()) - && some_ty != then_ty +fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) { - return; + Some(arm.body) + } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) + { + Some(arm.body) + } else if let PatKind::Wild = arm.pat.kind { + // We consider that the `Some` check will filter it out if it's not right. + Some(arm.body) + } else { + None } - check_and_lint(cx, expr, let_pat, let_expr, then_expr, peel_blocks(else_expr), ty); } -fn check_and_lint<'tcx>( +fn get_some_and_none_bodies<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - let_pat: &'tcx Pat<'_>, - let_expr: &'tcx Expr<'_>, - then_expr: &'tcx Expr<'_>, - else_expr: &'tcx Expr<'_>, - ty: Ty<'tcx>, + arm1: &'tcx Arm<'tcx>, + arm2: &'tcx Arm<'tcx>, +) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { + if let Some(binding_id) = get_some(cx, arm1.pat) + && let Some(body_none) = get_none(cx, arm2) + { + Some(((arm1.body, binding_id), body_none)) + } else if let Some(binding_id) = get_some(cx, arm2.pat) + && let Some(body_none) = get_none(cx, arm1) + { + Some(((arm2.body, binding_id), body_none)) + } else { + None + } +} + +fn handle( + cx: &LateContext<'_>, + expr: &Expr<'_>, + expr_name: &'static str, + condition: &Expr<'_>, + body_some: &Expr<'_>, + body_none: &Expr<'_>, + binding_id: HirId, ) { - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = let_pat.kind - && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) - && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) - && (cx.tcx.lang_items().option_some_variant() == Some(variant_id) - || cx.tcx.lang_items().result_ok_variant() == Some(variant_id)) - && let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind - && path_to_local_id(peel_blocks(then_expr), binding_hir_id) - && cx.typeck_results().expr_adjustments(then_expr).is_empty() - && let Some(ty_name) = find_type_name(cx, ty) - && let Some(or_body_snippet) = else_expr.span.get_source_text(cx) - && let Some(indent) = indent_of(cx, expr.span) - && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() + // Only deal with situations where both alternatives return the same non-adjusted type. + if cx.typeck_results().expr_ty(body_some) != cx.typeck_results().expr_ty(body_none) { + return; + } + + let expr_type = cx.typeck_results().expr_ty(expr); + // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. + if let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind + && let Res::Local(local_id) = path.res + && local_id == binding_id { - lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent); + // Machine applicable only if there are no comments present + let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let receiver = Sugg::hir_with_applicability(cx, condition, "_", &mut applicability).maybe_paren(); + + // We now check the `None` arm is calling a method equivalent to `Default::default`. + if !is_lint_allowed(cx, MANUAL_UNWRAP_OR_DEFAULT, expr.hir_id) + // We check if the return type of the expression implements Default. + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, expr_type, default_trait_id, &[]) + // We check if the initial condition implements Default. + && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) + && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() + && implements_trait(cx, condition_ty, default_trait_id, &[]) + && is_default_equivalent(cx, peel_blocks(body_none)) + { + // We now check if the condition is a None variant, in which case we need to specify the type + if path_res(cx, condition) + .opt_def_id() + .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) + { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}::<{expr_type}>.unwrap_or_default()"), + applicability, + ); + } + + // We check if the expression type is still uncertain, in which case we ask the user to specify it + if !expr_type_is_certain(cx, condition) { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + format!("ascribe the type {expr_type} and replace your expression with"), + format!("{receiver}.unwrap_or_default()"), + Applicability::Unspecified, + ); + } + + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}.unwrap_or_default()"), + applicability, + ); + } else if let Some(ty_name) = find_type_name(cx, cx.typeck_results().expr_ty(condition)) + && cx.typeck_results().expr_adjustments(body_some).is_empty() + && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) + && let Some(indent) = indent_of(cx, expr.span) + && ConstEvalCtxt::new(cx).eval_simple(body_none).is_some() + { + let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent)); + let mut app = Applicability::MachineApplicable; + let suggestion = Sugg::hir_with_context(cx, condition, expr.span.ctxt(), "..", &mut app).maybe_paren(); + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, + expr.span, + format!("this pattern reimplements `{ty_name}::unwrap_or`"), + "replace with", + format!("{suggestion}.unwrap_or({reindented_or_body})",), + app, + ); + } } } fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static str> { - if is_type_diagnostic_item(cx, ty, sym::Option) { - Some("Option") - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - Some("Result") - } else { - None + match get_type_diagnostic_name(cx, ty)? { + sym::Option => Some("Option"), + sym::Result => Some("Result"), + _ => None, } } -fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<(&'a Arm<'a>, &'a Arm<'a>)> { - if arms.len() == 2 - && arms.iter().all(|arm| arm.guard.is_none()) - && let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { - PatKind::Expr(PatExpr { - hir_id, - kind: PatExprKind::Path(qpath), - .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), - PatKind::TupleStruct(ref qpath, [pat], _) => { - matches!(pat.kind, PatKind::Wild) - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) - }, - _ => false, - }) - && let unwrap_arm = &arms[1 - idx] - && !contains_return_break_continue_macro(or_arm.body) +pub fn check_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'tcx>, + arms: &'tcx [Arm<'tcx>], +) { + if let [arm1, arm2] = arms + // Make sure there are no guards to keep things simple + && arm1.guard.is_none() + && arm2.guard.is_none() + // Get the some and none bodies and the binding id of the some arm + && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) { - Some((or_arm, unwrap_arm)) - } else { - None + handle(cx, expr, "match", scrutinee, body_some, body_none, binding_id); } } -fn lint<'tcx>( +pub fn check_if_let<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'tcx>, - scrutinee: &'tcx Expr<'_>, - ty_name: &str, - or_body_snippet: &str, - indent: usize, + expr: &'tcx Expr<'tcx>, + pat: &'tcx Pat<'tcx>, + scrutinee: &'tcx Expr<'tcx>, + then_expr: &'tcx Expr<'tcx>, + else_expr: &'tcx Expr<'tcx>, ) { - let reindented_or_body = reindent_multiline(or_body_snippet, true, Some(indent)); - - let mut app = Applicability::MachineApplicable; - let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_paren(); - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR, - expr.span, - format!("this pattern reimplements `{ty_name}::unwrap_or`"), - "replace with", - format!("{suggestion}.unwrap_or({reindented_or_body})",), - app, - ); + if let Some(binding_id) = get_some(cx, pat) { + handle( + cx, + expr, + "if let", + scrutinee, + peel_blocks(then_expr), + peel_blocks(else_expr), + binding_id, + ); + } } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 2b9173e6f4122..ad299ae8f0f1a 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -722,6 +722,43 @@ declare_clippy_lint! { "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" } +declare_clippy_lint! { + /// ### What it does + /// Checks if a `match` or `if let` expression can be simplified using + /// `.unwrap_or_default()`. + /// + /// ### Why is this bad? + /// It can be done in one call with `.unwrap_or_default()`. + /// + /// ### Example + /// ```no_run + /// let x: Option = Some(String::new()); + /// let y: String = match x { + /// Some(v) => v, + /// None => String::new(), + /// }; + /// + /// let x: Option> = Some(Vec::new()); + /// let y: Vec = if let Some(v) = x { + /// v + /// } else { + /// Vec::new() + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// let x: Option = Some(String::new()); + /// let y: String = x.unwrap_or_default(); + /// + /// let x: Option> = Some(Vec::new()); + /// let y: Vec = x.unwrap_or_default(); + /// ``` + #[clippy::version = "1.79.0"] + pub MANUAL_UNWRAP_OR_DEFAULT, + suspicious, + "check if a `match` or `if let` can be simplified with `unwrap_or_default`" +} + declare_clippy_lint! { /// ### What it does /// Checks for `match vec[idx]` or `match vec[n..m]`. @@ -1040,6 +1077,7 @@ impl_lint_pass!(Matches => [ NEEDLESS_MATCH, COLLAPSIBLE_MATCH, MANUAL_UNWRAP_OR, + MANUAL_UNWRAP_OR_DEFAULT, MATCH_ON_VEC_ITEMS, MATCH_STR_CASE_MISMATCH, SIGNIFICANT_DROP_IN_SCRUTINEE, diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index c4d00002292c9..852378d50e8b4 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -176,6 +176,12 @@ impl<'hir> IfLetOrMatch<'hir> { ), } } + + pub fn scrutinee(&self) -> &'hir Expr<'hir> { + match self { + Self::Match(scrutinee, _, _) | Self::IfLet(scrutinee, _, _, _, _) => scrutinee, + } + } } /// An `if` or `if let` expression diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1307ff79bc5dd..5f39af811cd99 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1027,6 +1027,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), + ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)), _ => false, } } diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 07e4bdd483a8c..e12287a709395 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -18,11 +18,9 @@ fn option_unwrap_or() { // multiline case #[rustfmt::skip] - Some(1).unwrap_or({ - 42 + 42 - + 42 + 42 + 42 - + 42 + 42 + 42 - }); + Some(1).unwrap_or(42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42); // string case Some("Bob").unwrap_or("Alice"); @@ -125,11 +123,9 @@ fn result_unwrap_or() { // multiline case #[rustfmt::skip] - Ok::(1).unwrap_or({ - 42 + 42 - + 42 + 42 + 42 - + 42 + 42 + 42 - }); + Ok::(1).unwrap_or(42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42); // string case Ok::<&str, &str>("Bob").unwrap_or("Alice"); @@ -159,11 +155,7 @@ fn result_unwrap_or() { Ok(s) => s, Err(s) => s, }; - // could lint, but unused_variables takes care of it - match Ok::<&str, &str>("Alice") { - Ok(s) => s, - Err(s) => "Bob", - }; + Ok::<&str, &str>("Alice").unwrap_or("Bob"); Ok::(1).unwrap_or(42); @@ -250,4 +242,12 @@ mod issue_13018 { } } +fn implicit_deref(v: Vec) { + let _ = if let Some(s) = v.first() { s } else { "" }; +} + +fn allowed_manual_unwrap_or_zero() -> u32 { + Some(42).unwrap_or(0) +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index c88b6f95da68e..53cffcab5b56c 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -216,8 +216,8 @@ fn result_unwrap_or() { Ok(s) => s, Err(s) => s, }; - // could lint, but unused_variables takes care of it match Ok::<&str, &str>("Alice") { + //~^ manual_unwrap_or Ok(s) => s, Err(s) => "Bob", }; @@ -316,4 +316,17 @@ mod issue_13018 { } } +fn implicit_deref(v: Vec) { + let _ = if let Some(s) = v.first() { s } else { "" }; +} + +fn allowed_manual_unwrap_or_zero() -> u32 { + if let Some(x) = Some(42) { + //~^ manual_unwrap_or + x + } else { + 0 + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index a5deb55786e96..320e895fb8237 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -44,11 +44,9 @@ LL | | }; | help: replace with | -LL ~ Some(1).unwrap_or({ -LL + 42 + 42 -LL + + 42 + 42 + 42 -LL + + 42 + 42 + 42 -LL ~ }); +LL ~ Some(1).unwrap_or(42 + 42 +LL + + 42 + 42 + 42 +LL ~ + 42 + 42 + 42); | error: this pattern reimplements `Option::unwrap_or` @@ -145,11 +143,9 @@ LL | | }; | help: replace with | -LL ~ Ok::(1).unwrap_or({ -LL + 42 + 42 -LL + + 42 + 42 + 42 -LL + + 42 + 42 + 42 -LL ~ }); +LL ~ Ok::(1).unwrap_or(42 + 42 +LL + + 42 + 42 + 42 +LL ~ + 42 + 42 + 42); | error: this pattern reimplements `Result::unwrap_or` @@ -162,6 +158,16 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` +error: this pattern reimplements `Result::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:219:5 + | +LL | / match Ok::<&str, &str>("Alice") { +LL | | +LL | | Ok(s) => s, +LL | | Err(s) => "Bob", +LL | | }; + | |_____^ help: replace with: `Ok::<&str, &str>("Alice").unwrap_or("Bob")` + error: this pattern reimplements `Result::unwrap_or` --> tests/ui/manual_unwrap_or.rs:225:5 | @@ -184,5 +190,16 @@ LL | | None => 0, LL | | }; | |_________^ help: replace with: `some_macro!().unwrap_or(0)` -error: aborting due to 16 previous errors +error: this pattern reimplements `Option::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:324:5 + | +LL | / if let Some(x) = Some(42) { +LL | | +LL | | x +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: replace with: `Some(42).unwrap_or(0)` + +error: aborting due to 18 previous errors diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 832376fa5af15..f4a78ee761962 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_unwrap_or_default)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::manual_unwrap_or)] +#![allow(clippy::unnecessary_literal_unwrap)] fn main() { let x: Option> = None; @@ -99,3 +99,8 @@ fn issue_12928() { let y = if let Some(Y(a, _)) = x { a } else { 0 }; let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } + +// For symetry with `manual_unwrap_or` test +fn allowed_manual_unwrap_or_zero() -> u32 { + Some(42).unwrap_or_default() +} diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index bedb3f0af0f3e..60b84b621f611 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_unwrap_or_default)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::manual_unwrap_or)] +#![allow(clippy::unnecessary_literal_unwrap)] fn main() { let x: Option> = None; @@ -135,3 +135,13 @@ fn issue_12928() { let y = if let Some(Y(a, _)) = x { a } else { 0 }; let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } + +// For symetry with `manual_unwrap_or` test +fn allowed_manual_unwrap_or_zero() -> u32 { + if let Some(x) = Some(42) { + //~^ manual_unwrap_or_default + x + } else { + 0 + } +} diff --git a/tests/ui/manual_unwrap_or_default.stderr b/tests/ui/manual_unwrap_or_default.stderr index ca9aa159152e3..1e92f20a757e9 100644 --- a/tests/ui/manual_unwrap_or_default.stderr +++ b/tests/ui/manual_unwrap_or_default.stderr @@ -86,5 +86,16 @@ LL | | _ => 0, LL | | }, | |_________^ help: replace it with: `(*b).unwrap_or_default()` -error: aborting due to 8 previous errors +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:141:5 + | +LL | / if let Some(x) = Some(42) { +LL | | +LL | | x +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: replace it with: `Some(42).unwrap_or_default()` + +error: aborting due to 9 previous errors From 6509de3bfb06ba9d951cd4403512b1347f872d69 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 16 Feb 2025 16:27:09 +0100 Subject: [PATCH 059/103] Fix situations identified by `collapsible_if` new hits --- clippy.toml | 2 ++ clippy_lints/src/escape.rs | 9 +++--- clippy_lints/src/item_name_repetitions.rs | 21 +++++++------ clippy_lints/src/mem_replace.rs | 12 ++++---- clippy_lints/src/methods/map_clone.rs | 20 ++++++------- clippy_lints/src/methods/seek_from_current.rs | 10 +++---- .../src/mixed_read_write_in_expression.rs | 30 +++++++++---------- lintcheck/ci-config/clippy.toml | 1 + tests/clippy.toml | 1 + 9 files changed, 51 insertions(+), 55 deletions(-) diff --git a/clippy.toml b/clippy.toml index 7872933552b43..77573105d86a3 100644 --- a/clippy.toml +++ b/clippy.toml @@ -2,6 +2,8 @@ avoid-breaking-exported-api = false check-inconsistent-struct-field-initializers = true +lint-commented-code = true + [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index de0fc2b1bf4bb..831d47ac48779 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -93,12 +93,11 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { // find `self` ty for this trait if relevant if let ItemKind::Trait(_, _, _, _, _, items) = item.kind { for trait_item in items { - if trait_item.id.owner_id.def_id == fn_def_id { + if trait_item.id.owner_id.def_id == fn_def_id // be sure we have `self` parameter in this function - if trait_item.kind == (AssocItemKind::Fn { has_self: true }) { - trait_self_ty = - Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty()); - } + && trait_item.kind == (AssocItemKind::Fn { has_self: true }) + { + trait_self_ty = Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty()); } } } diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 977fd5fce15be..0f800a68cdbf4 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -377,22 +377,21 @@ impl ItemNameRepetitions { "field name starts with the struct's name", ); } - if field_words.len() > item_name_words.len() { + if field_words.len() > item_name_words.len() // lint only if the end is not covered by the start - if field_words + && field_words .iter() .rev() .zip(item_name_words.iter().rev()) .all(|(a, b)| a == b) - { - span_lint_hir( - cx, - STRUCT_FIELD_NAMES, - field.hir_id, - field.span, - "field name ends with the struct's name", - ); - } + { + span_lint_hir( + cx, + STRUCT_FIELD_NAMES, + field.hir_id, + field.span, + "field name ends with the struct's name", + ); } } } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 982e0695d7795..a54d835b538c1 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -304,14 +304,12 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_replace, def_id) - { // Check that second argument is `Option::None` - if !check_replace_option_with_none(cx, src, dest, expr.span) - && !check_replace_option_with_some(cx, src, dest, expr.span, self.msrv) - && !check_replace_with_default(cx, src, dest, expr, self.msrv) - { - check_replace_with_uninit(cx, src, dest, expr.span); - } + && !check_replace_option_with_none(cx, src, dest, expr.span) + && !check_replace_option_with_some(cx, src, dest, expr.span, self.msrv) + && !check_replace_with_default(cx, src, dest, expr, self.msrv) + { + check_replace_with_uninit(cx, src, dest, expr.span); } } } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 128b3695f48b7..50b7578085fa2 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -114,19 +114,17 @@ fn handle_path( ) { if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() && cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id) - { // The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option` // and `Result`. - if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() - && let args = args.as_slice() - && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) - && let ty::Ref(_, ty, Mutability::Not) = ty.kind() - && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() - && lst.iter().all(|l| l.as_type() == Some(*ty)) - && !should_call_clone_as_function(cx, *ty) - { - lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); - } + && let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() + && let args = args.as_slice() + && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) + && let ty::Ref(_, ty, Mutability::Not) = ty.kind() + && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() + && lst.iter().all(|l| l.as_type() == Some(*ty)) + && !should_call_clone_as_function(cx, *ty) + { + lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } } diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs index d318462e58415..28bf2d5aeec5b 100644 --- a/clippy_lints/src/methods/seek_from_current.rs +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -38,13 +38,11 @@ fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) && let ExprKind::Path(ref path) = f.kind && let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id() && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id) - { // check if argument of `SeekFrom::Current` is `0` - if let ExprKind::Lit(lit) = arg.kind - && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node - { - return true; - } + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node + { + return true; } false diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index be728e6c8b74b..274438ac0491e 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -327,22 +327,22 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { return; } - if path_to_local_id(expr, self.var) { + if path_to_local_id(expr, self.var) // Check that this is a read, not a write. - if !is_in_assignment_position(self.cx, expr) { - span_lint_and_then( - self.cx, - MIXED_READ_WRITE_IN_EXPRESSION, - expr.span, - format!("unsequenced read of `{}`", self.cx.tcx.hir_name(self.var)), - |diag| { - diag.span_note( - self.write_expr.span, - "whether read occurs before this write depends on evaluation order", - ); - }, - ); - } + && !is_in_assignment_position(self.cx, expr) + { + span_lint_and_then( + self.cx, + MIXED_READ_WRITE_IN_EXPRESSION, + expr.span, + format!("unsequenced read of `{}`", self.cx.tcx.hir_name(self.var)), + |diag| { + diag.span_note( + self.write_expr.span, + "whether read occurs before this write depends on evaluation order", + ); + }, + ); } match expr.kind { // We're about to descend a closure. Since we don't know when (or diff --git a/lintcheck/ci-config/clippy.toml b/lintcheck/ci-config/clippy.toml index d9eb2ef90dba2..9853465c83f00 100644 --- a/lintcheck/ci-config/clippy.toml +++ b/lintcheck/ci-config/clippy.toml @@ -4,3 +4,4 @@ # to `$PWD/lintcheck/ci-config`. avoid-breaking-exported-api = false +lint-commented-code = false diff --git a/tests/clippy.toml b/tests/clippy.toml index 5eb7ac0354198..91a2e55180b97 100644 --- a/tests/clippy.toml +++ b/tests/clippy.toml @@ -1 +1,2 @@ # default config for tests, overrides clippy.toml at the project root +lint-commented-code = false From a8bfafe92962e6dcd1776ac2d2eb2e85072001df Mon Sep 17 00:00:00 2001 From: todaymoon Date: Wed, 26 Mar 2025 15:36:08 +0800 Subject: [PATCH 060/103] chore: fix some comments Signed-off-by: todaymoon --- clippy_lints/src/operators/identity_op.rs | 2 +- rustc_tools_util/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 0358232282786..e1fd09549a4b8 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -103,7 +103,7 @@ enum Parens { /// /// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently. /// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since -/// the the cast expression will not apply to the same expression. +/// the cast expression will not apply to the same expression. /// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced /// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be /// interpreted as a statement. The same behavior happens for `match`, `loop`, diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 423154a69fa0b..b45edf2345585 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -121,7 +121,7 @@ fn get_output(cmd: &str, args: &[&str]) -> Option { pub fn rerun_if_git_changes() -> Option<()> { // Make sure we get rerun when the git commit changes. // We want to watch two files: HEAD, which tracks which branch we are on, - // and the file for that branch that tracks which commit is is on. + // and the file for that branch that tracks which commit is checked out. // First, find the `HEAD` file. This should work even with worktrees. let git_head_file = PathBuf::from(get_output("git", &["rev-parse", "--git-path", "HEAD"])?); From 1b92712f3f11a7a5c0f1b50343d533844c5fb134 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 22 Mar 2025 21:42:34 +0300 Subject: [PATCH 061/103] expand: Leave traces when expanding `cfg` attributes --- clippy_lints/src/attrs/duplicated_attributes.rs | 3 +-- clippy_lints/src/cfg_not_test.rs | 2 +- clippy_lints/src/methods/is_empty.rs | 2 +- clippy_utils/src/lib.rs | 8 ++++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index 5c486eb90cc2b..4c84e61b1f26c 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -37,7 +37,6 @@ fn check_duplicated_attr( let Some(ident) = attr.ident() else { return }; let name = ident.name; if name == sym::doc - || name == sym::cfg_attr || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason { @@ -47,7 +46,7 @@ fn check_duplicated_attr( return; } if let Some(direct_parent) = parent.last() - && ["cfg", "cfg_attr"].contains(&direct_parent.as_str()) + && direct_parent == sym::cfg_trace.as_str() && [sym::all, sym::not, sym::any].contains(&name) { // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs index 84136a2e6c28f..7590fe96fd214 100644 --- a/clippy_lints/src/cfg_not_test.rs +++ b/clippy_lints/src/cfg_not_test.rs @@ -32,7 +32,7 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]); impl EarlyLintPass for CfgNotTest { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) { - if attr.has_name(rustc_span::sym::cfg) && contains_not_test(attr.meta_item_list().as_deref(), false) { + if attr.has_name(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), false) { span_lint_and_then( cx, CFG_NOT_TEST, diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 7c190e123b72e..4c81b22861b4c 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { cx.tcx .hir_parent_id_iter(id) - .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg))) + .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace))) } /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1307ff79bc5dd..668b0cb69e204 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2629,7 +2629,7 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { if let Res::Def(_, def_id) = path.res { - return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); + return cx.tcx.has_attr(def_id, sym::cfg_trace) || cx.tcx.has_attr(def_id, sym::cfg_attr); } } false @@ -2699,7 +2699,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { /// use [`is_in_cfg_test`] pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { tcx.hir_attrs(id).iter().any(|attr| { - if attr.has_name(sym::cfg) + if attr.has_name(sym::cfg_trace) && let Some(items) = attr.meta_item_list() && let [item] = &*items && item.has_name(sym::test) @@ -2723,11 +2723,11 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - tcx.has_attr(def_id, sym::cfg) + tcx.has_attr(def_id, sym::cfg_trace) || tcx .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)) - .any(|attr| attr.has_name(sym::cfg)) + .any(|attr| attr.has_name(sym::cfg_trace)) } /// Walks up the HIR tree from the given expression in an attempt to find where the value is From 9b1945d9fb57516c585f7aea9dfe638ad3c7a397 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 26 Mar 2025 19:38:09 +0100 Subject: [PATCH 062/103] Prevent including preceeding whitespaces if line contains non blanks This extra condition prevents a problem when removing the '}' in: ```rust ( // There was an opening bracket after the parenthesis, which has been removed // This is a comment }) ``` Removing the whitespaces, including the linefeed, before the '}', would put the closing parenthesis at the end of the `// This is a comment` line, which would make it part of the comment as well. In this case, it is best to keep the span on the '}' alone. --- clippy_utils/src/source.rs | 25 ++++++++++++++++--- .../collapsible_if/collapsible_if.fixed | 4 +-- .../collapsible_if/collapsible_if.stderr | 4 +-- tests/ui/collapsible_if.fixed | 10 ++++++++ tests/ui/collapsible_if.rs | 10 ++++++++ tests/ui/collapsible_if.stderr | 20 ++++++++++++++- tests/ui/manual_inspect.fixed | 11 +++++++- tests/ui/manual_inspect.rs | 9 +++++++ tests/ui/manual_inspect.stderr | 17 +++++++++++-- 9 files changed, 98 insertions(+), 12 deletions(-) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 80066e9702d34..f15906df627f4 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -142,7 +142,19 @@ pub trait SpanRangeExt: SpanRange { map_range(cx.sess().source_map(), self.into_range(), f) } - /// Extends the range to include all preceding whitespace characters. + /// Extends the range to include all preceding whitespace characters, unless there + /// are non-whitespace characters left on the same line after `self`. + /// + /// This extra condition prevents a problem when removing the '}' in: + /// ```ignore + /// ( // There was an opening bracket after the parenthesis, which has been removed + /// // This is a comment + /// }) + /// ``` + /// Removing the whitespaces, including the linefeed, before the '}', would put the + /// closing parenthesis at the end of the `// This is a comment` line, which would + /// make it part of the comment as well. In this case, it is best to keep the span + /// on the '}' alone. fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } @@ -263,10 +275,15 @@ fn map_range( } fn with_leading_whitespace(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp.clone(), |src, range| { - Some(src.get(..range.start)?.trim_end().len()..range.end) + map_range(sm, sp, |src, range| { + let non_blank_after = src.len() - src.get(range.end..)?.trim_start().len(); + if src.get(range.end..non_blank_after)?.contains(['\r', '\n']) { + Some(src.get(..range.start)?.trim_end().len()..range.end) + } else { + Some(range) + } }) - .unwrap_or(sp) + .unwrap() } fn trim_start(sm: &SourceMap, sp: Range) -> Range { diff --git a/tests/ui-toml/collapsible_if/collapsible_if.fixed b/tests/ui-toml/collapsible_if/collapsible_if.fixed index f695f9804d59c..6f5cc47ba6c75 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if.fixed +++ b/tests/ui-toml/collapsible_if/collapsible_if.fixed @@ -13,7 +13,7 @@ fn main() { //~^^^^^^ collapsible_if // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" // Inner comment + if x == "hello" // Inner comment && y == "world" { println!("Hello world!"); } @@ -26,7 +26,7 @@ fn main() { } //~^^^^^^ collapsible_if - if x == "hello" /* Inner comment */ + if x == "hello" /* Inner comment */ && y == "world" { println!("Hello world!"); } diff --git a/tests/ui-toml/collapsible_if/collapsible_if.stderr b/tests/ui-toml/collapsible_if/collapsible_if.stderr index a12c2112f5877..357ce4ad32deb 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if.stderr +++ b/tests/ui-toml/collapsible_if/collapsible_if.stderr @@ -32,7 +32,7 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" // Inner comment +LL ~ if x == "hello" // Inner comment LL ~ && y == "world" { LL | println!("Hello world!"); LL ~ } @@ -70,7 +70,7 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" /* Inner comment */ +LL ~ if x == "hello" /* Inner comment */ LL ~ && y == "world" { LL | println!("Hello world!"); LL ~ } diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index df281651ab14f..e1ceb04f9cb89 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -152,3 +152,13 @@ fn main() { } } } + +#[rustfmt::skip] +fn layout_check() -> u32 { + if true + && true { + } + // This is a comment, do not collapse code to it + ; 3 + //~^^^^^ collapsible_if +} diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index ce979568cc895..0b996dca22e85 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -162,3 +162,13 @@ fn main() { } } } + +#[rustfmt::skip] +fn layout_check() -> u32 { + if true { + if true { + } + // This is a comment, do not collapse code to it + }; 3 + //~^^^^^ collapsible_if +} diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 559db238cb182..532811462393b 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -172,5 +172,23 @@ LL | println!("No comment, linted"); LL ~ } | -error: aborting due to 10 previous errors +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:168:5 + | +LL | / if true { +LL | | if true { +... | +LL | | }; 3 + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | } +LL | // This is a comment, do not collapse code to it +LL ~ ; 3 + | + +error: aborting due to 11 previous errors diff --git a/tests/ui/manual_inspect.fixed b/tests/ui/manual_inspect.fixed index 8d045c48ceb04..ec87fe217aee6 100644 --- a/tests/ui/manual_inspect.fixed +++ b/tests/ui/manual_inspect.fixed @@ -107,7 +107,7 @@ fn main() { let _ = || { let _x = x; }; - return; + return ; } println!("test"); }); @@ -185,3 +185,12 @@ fn main() { }); } } + +#[rustfmt::skip] +fn layout_check() { + if let Some(x) = Some(1).inspect(|&x| { println!("{x}"); //~ manual_inspect + // Do not collapse code into this comment + }) { + println!("{x}"); + } +} diff --git a/tests/ui/manual_inspect.rs b/tests/ui/manual_inspect.rs index a89d28dab689c..e679636201e6a 100644 --- a/tests/ui/manual_inspect.rs +++ b/tests/ui/manual_inspect.rs @@ -197,3 +197,12 @@ fn main() { }); } } + +#[rustfmt::skip] +fn layout_check() { + if let Some(x) = Some(1).map(|x| { println!("{x}"); //~ manual_inspect + // Do not collapse code into this comment + x }) { + println!("{x}"); + } +} diff --git a/tests/ui/manual_inspect.stderr b/tests/ui/manual_inspect.stderr index 510325d2baaa9..eb98f9f5995a3 100644 --- a/tests/ui/manual_inspect.stderr +++ b/tests/ui/manual_inspect.stderr @@ -98,7 +98,7 @@ LL | if x.is_empty() { LL | let _ = || { LL ~ let _x = x; LL | }; -LL ~ return; +LL ~ return ; LL | } LL ~ println!("test"); | @@ -187,5 +187,18 @@ LL | LL ~ println!("{}", x); | -error: aborting due to 13 previous errors +error: using `map` over `inspect` + --> tests/ui/manual_inspect.rs:203:30 + | +LL | if let Some(x) = Some(1).map(|x| { println!("{x}"); + | ^^^ + | +help: try + | +LL ~ if let Some(x) = Some(1).inspect(|&x| { println!("{x}"); +LL | // Do not collapse code into this comment +LL ~ }) { + | + +error: aborting due to 14 previous errors From d9913dd6ad07f113aa83b835da8253bdca4342a9 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 9 Mar 2025 08:36:37 +0100 Subject: [PATCH 063/103] Do not warn about shadowing in a destructuring assigment When lowering a destructuring assignment from AST to HIR, the compiler will reuse the same identifier name (namely `sym::lhs`) for all the fields. The desugaring must be checked for to avoid a false positive of the `shadow_unrelated` lint. --- clippy_lints/src/shadow.rs | 15 ++++++++++++++- tests/ui/shadow.rs | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index ee282ee1dfb71..10e03ac19bd1f 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -8,7 +8,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, LetExpr, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{ + Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, LetExpr, LocalSource, Node, Pat, PatKind, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; @@ -125,6 +127,17 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { return; } + // Desugaring of a destructuring assignment may reuse the same identifier internally. + // Peel `Pat` and `PatField` nodes and check if we reach a desugared `Let` assignment. + if let Some((_, Node::LetStmt(let_stmt))) = cx + .tcx + .hir_parent_iter(pat.hir_id) + .find(|(_, node)| !matches!(node, Node::Pat(_) | Node::PatField(_))) + && let LocalSource::AssignDesugar(_) = let_stmt.source + { + return; + } + let HirId { owner, local_id } = id; // get (or insert) the list of items for this owner and symbol let (ref mut data, scope_owner) = *self.bindings.last_mut().unwrap(); diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 7d503a1cf6c17..05009b2ddd416 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -167,4 +167,19 @@ fn issue13795(value: Issue13795) { //~^ shadow_same } +fn issue14377() { + let a; + let b; + (a, b) = (0, 1); + + struct S { + c: i32, + d: i32, + } + + let c; + let d; + S { c, d } = S { c: 1, d: 2 }; +} + fn main() {} From 9df5177287184465bd9957c6f7d582eaf16a6b2f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 26 Mar 2025 11:20:36 +0100 Subject: [PATCH 064/103] Convert the `collapsible_if` early lint to a late one --- clippy_lints/src/collapsible_if.rs | 86 ++++++++++++++++-------------- clippy_lints/src/lib.rs | 2 +- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index b100e2408de3d..8728f7f2eb592 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,12 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{ - HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability, -}; -use clippy_utils::span_contains_comment; -use rustc_ast::ast; +use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; +use rustc_ast::BinOpKind; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::{Block, Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -89,17 +87,16 @@ impl CollapsibleIf { } } - fn check_collapsible_else_if(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { - if let ast::ExprKind::Block(ref block, _) = else_.kind - && !block_starts_with_comment(cx, block) - && let Some(else_) = expr_block(block) - && else_.attrs.is_empty() + fn check_collapsible_else_if(cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { + if !block_starts_with_comment(cx, else_block) + && let Some(else_) = expr_block(else_block) + && cx.tcx.hir_attrs(else_.hir_id).is_empty() && !else_.span.from_expansion() - && let ast::ExprKind::If(..) = else_.kind + && let ExprKind::If(..) = else_.kind { // Prevent "elseif" // Check that the "else" is followed by whitespace - let up_to_else = then_span.between(block.span); + let up_to_else = then_span.between(else_block.span); let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { @@ -110,29 +107,28 @@ impl CollapsibleIf { span_lint_and_sugg( cx, COLLAPSIBLE_ELSE_IF, - block.span, + else_block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", format!( "{}{}", if requires_space { " " } else { "" }, - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + snippet_block_with_applicability(cx, else_.span, "..", Some(else_block.span), &mut applicability) ), applicability, ); } } - fn check_collapsible_if_if(&self, cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { + fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) { if let Some(inner) = expr_block(then) - && inner.attrs.is_empty() - && let ast::ExprKind::If(check_inner, _, None) = &inner.kind + && cx.tcx.hir_attrs(inner.hir_id).is_empty() + && let ExprKind::If(check_inner, _, None) = &inner.kind // Prevent triggering on `if c { if let a = b { .. } }`. - && !matches!(check_inner.kind, ast::ExprKind::Let(..)) + && !matches!(check_inner.kind, ExprKind::Let(..)) && let ctxt = expr.span.ctxt() && inner.span.ctxt() == ctxt - && let contains_comment = span_contains_comment(cx.sess().source_map(), check.span.to(check_inner.span)) - && (!contains_comment || self.lint_commented_code) + && (self.lint_commented_code || !block_starts_with_comment(cx, then)) { span_lint_and_then( cx, @@ -168,44 +164,54 @@ impl CollapsibleIf { impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); -impl EarlyLintPass for CollapsibleIf { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::If(cond, then, else_) = &expr.kind +impl LateLintPass<'_> for CollapsibleIf { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::If(cond, then, else_) = &expr.kind && !expr.span.from_expansion() { - if let Some(else_) = else_ { + if let Some(else_) = else_ + && let ExprKind::Block(else_, None) = else_.kind + { Self::check_collapsible_else_if(cx, then.span, else_); - } else if !matches!(cond.kind, ast::ExprKind::Let(..)) { + } else if else_.is_none() + && !matches!(cond.kind, ExprKind::Let(..)) + && let ExprKind::Block(then, None) = then.kind + { // Prevent triggering on `if c { if let a = b { .. } }`. - self.check_collapsible_if_if(cx, expr, cond, then); + self.check_collapsible_if_if(cx, expr, cond, block!(then)); } } } } -fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { +fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // We trim all opening braces and whitespaces and then check if the next string is a comment. - let trimmed_block_text = snippet_block(cx, expr.span, "..", None) + let trimmed_block_text = snippet_block(cx, block.span, "..", None) .trim_start_matches(|c: char| c.is_whitespace() || c == '{') .to_owned(); trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -/// If the block contains only one expression, return it. -fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { - if let [stmt] = &*block.stmts - && let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind - { - Some(expr) - } else { - None +/// If `block` is a block with either one expression or a statement containing an expression, +/// return the expression. We don't peel blocks recursively, as extra blocks might be intentional. +fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match block.stmts { + [] => block.expr, + [stmt] => { + if let StmtKind::Semi(expr) = stmt.kind { + Some(expr) + } else { + None + } + }, + _ => None, } } /// If the expression is a `||`, suggest parentheses around it. -fn parens_around(expr: &ast::Expr) -> Vec<(Span, String)> { - if let ast::ExprKind::Binary(op, _, _) = expr.kind - && op.node == ast::BinOpKind::Or +fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { + if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind + && op.node == BinOpKind::Or { vec![ (expr.span.shrink_to_lo(), String::from("(")), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6f2a4a4c529da..d2d8a1c4ff44f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -771,7 +771,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_early_pass(move || Box::new(collapsible_if::CollapsibleIf::new(conf))); + store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); From cd701524701bbc3443a007b52edcee6550abb0be Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 26 Mar 2025 20:23:31 +0100 Subject: [PATCH 065/103] Make `collapsible_if` recognize the `let_chains` feature Until `if let` chains are stabilized, we do not collapse them together or with other `if` expressions unless the `let_chains` feature is enabled. This is the case for example in Clippy sources. --- clippy_lints/src/collapsible_if.rs | 17 +++-- clippy_lints/src/lib.rs | 2 +- .../collapsible_if_let_chains.fixed | 25 ++++++++ .../collapsible_if_let_chains.rs | 28 ++++++++ .../collapsible_if_let_chains.stderr | 64 +++++++++++++++++++ tests/ui/auxiliary/proc_macros.rs | 12 ++-- tests/ui/collapsible_if_let_chains.fixed | 29 +++++++++ tests/ui/collapsible_if_let_chains.rs | 32 ++++++++++ tests/ui/collapsible_if_let_chains.stderr | 58 +++++++++++++++++ 9 files changed, 254 insertions(+), 13 deletions(-) create mode 100644 tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed create mode 100644 tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs create mode 100644 tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr create mode 100644 tests/ui/collapsible_if_let_chains.fixed create mode 100644 tests/ui/collapsible_if_let_chains.rs create mode 100644 tests/ui/collapsible_if_let_chains.stderr diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 8728f7f2eb592..20fae8a6775b9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -5,6 +5,7 @@ use rustc_ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -77,12 +78,14 @@ declare_clippy_lint! { } pub struct CollapsibleIf { + let_chains_enabled: bool, lint_commented_code: bool, } impl CollapsibleIf { - pub fn new(conf: &'static Conf) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { + let_chains_enabled: tcx.features().let_chains(), lint_commented_code: conf.lint_commented_code, } } @@ -124,8 +127,7 @@ impl CollapsibleIf { if let Some(inner) = expr_block(then) && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind - // Prevent triggering on `if c { if let a = b { .. } }`. - && !matches!(check_inner.kind, ExprKind::Let(..)) + && self.eligible_condition(check_inner) && let ctxt = expr.span.ctxt() && inner.span.ctxt() == ctxt && (self.lint_commented_code || !block_starts_with_comment(cx, then)) @@ -160,6 +162,10 @@ impl CollapsibleIf { ); } } + + pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool { + self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..)) + } } impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); @@ -174,11 +180,10 @@ impl LateLintPass<'_> for CollapsibleIf { { Self::check_collapsible_else_if(cx, then.span, else_); } else if else_.is_none() - && !matches!(cond.kind, ExprKind::Let(..)) + && self.eligible_condition(cond) && let ExprKind::Block(then, None) = then.kind { - // Prevent triggering on `if c { if let a = b { .. } }`. - self.check_collapsible_if_if(cx, expr, cond, block!(then)); + self.check_collapsible_if_if(cx, expr, cond, then); } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d2d8a1c4ff44f..ba80e122448d5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -771,7 +771,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))); + store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed new file mode 100644 index 0000000000000..f12273954c6dd --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed @@ -0,0 +1,25 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) + // with comment + && let Some(b) = Some(4) { + let _ = a + b; + } + //~^^^^^^ collapsible_if + + if let Some(a) = Some(3) + // with comment + && a + 1 == 4 { + let _ = a; + } + //~^^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) + // with comment + && let Some(b) = Some(4) { + let _ = b; + } + //~^^^^^^ collapsible_if +} diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs new file mode 100644 index 0000000000000..5a984d7a3cbee --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs @@ -0,0 +1,28 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment + if let Some(b) = Some(4) { + let _ = a + b; + } + } + //~^^^^^^ collapsible_if + + if let Some(a) = Some(3) { + // with comment + if a + 1 == 4 { + let _ = a; + } + } + //~^^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) { + // with comment + if let Some(b) = Some(4) { + let _ = b; + } + } + //~^^^^^^ collapsible_if +} diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr new file mode 100644 index 0000000000000..c22a65a447301 --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr @@ -0,0 +1,64 @@ +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:5:5 + | +LL | / if let Some(a) = Some(3) { +LL | | // with comment +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL | // with comment +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:13:5 + | +LL | / if let Some(a) = Some(3) { +LL | | // with comment +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL | // with comment +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:21:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | // with comment +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL | // with comment +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index 1a2a4ec231143..7a4cc4fa9ee8e 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -131,12 +131,12 @@ fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Resul pub fn make_it_big(input: TokenStream) -> TokenStream { let mut expr_repeat = syn::parse_macro_input!(input as syn::ExprRepeat); let len_span = expr_repeat.len.span(); - if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len { - if let syn::Lit::Int(lit_int) = &expr_lit.lit { - let orig_val = lit_int.base10_parse::().expect("not a valid length parameter"); - let new_val = orig_val.saturating_mul(10); - expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val); - } + if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len + && let syn::Lit::Int(lit_int) = &expr_lit.lit + { + let orig_val = lit_int.base10_parse::().expect("not a valid length parameter"); + let new_val = orig_val.saturating_mul(10); + expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val); } quote::quote!(#expr_repeat).into() } diff --git a/tests/ui/collapsible_if_let_chains.fixed b/tests/ui/collapsible_if_let_chains.fixed new file mode 100644 index 0000000000000..3dd9498a4c9f9 --- /dev/null +++ b/tests/ui/collapsible_if_let_chains.fixed @@ -0,0 +1,29 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + if let Some(a) = Some(3) + && let Some(b) = Some(4) { + let _ = a + b; + } + //~^^^^^ collapsible_if + + if let Some(a) = Some(3) + && a + 1 == 4 { + let _ = a; + } + //~^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) + && let Some(b) = Some(4) { + let _ = b; + } + //~^^^^^ collapsible_if +} diff --git a/tests/ui/collapsible_if_let_chains.rs b/tests/ui/collapsible_if_let_chains.rs new file mode 100644 index 0000000000000..064b9a0be4847 --- /dev/null +++ b/tests/ui/collapsible_if_let_chains.rs @@ -0,0 +1,32 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + if let Some(a) = Some(3) { + if let Some(b) = Some(4) { + let _ = a + b; + } + } + //~^^^^^ collapsible_if + + if let Some(a) = Some(3) { + if a + 1 == 4 { + let _ = a; + } + } + //~^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) { + if let Some(b) = Some(4) { + let _ = b; + } + } + //~^^^^^ collapsible_if +} diff --git a/tests/ui/collapsible_if_let_chains.stderr b/tests/ui/collapsible_if_let_chains.stderr new file mode 100644 index 0000000000000..64a88114c47a3 --- /dev/null +++ b/tests/ui/collapsible_if_let_chains.stderr @@ -0,0 +1,58 @@ +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:12:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:19:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:26:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: aborting due to 3 previous errors + From 79c69112dc1dd5803af4e2682552c1ad2dde14ca Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 26 Mar 2025 20:38:58 +0100 Subject: [PATCH 066/103] Apply `collapsible_if` to Clippy itself Since Clippy uses the `let_chains` feature, there are many occasions to collapse `if` and `if let` statements. --- clippy_dev/src/update_lints.rs | 76 ++--- clippy_dev/src/utils.rs | 8 +- .../src/arbitrary_source_item_ordering.rs | 18 +- .../attrs/blanket_clippy_restriction_lints.rs | 23 +- clippy_lints/src/attrs/deprecated_semver.rs | 8 +- clippy_lints/src/attrs/mod.rs | 41 ++- clippy_lints/src/attrs/useless_attribute.rs | 130 ++++----- clippy_lints/src/await_holding_invalid.rs | 5 +- clippy_lints/src/booleans.rs | 22 +- clippy_lints/src/borrow_deref_ref.rs | 8 +- .../src/casts/cast_possible_truncation.rs | 8 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 19 +- .../src/casts/cast_slice_different_sizes.rs | 65 +++-- clippy_lints/src/casts/unnecessary_cast.rs | 10 +- clippy_lints/src/checked_conversions.rs | 8 +- clippy_lints/src/copies.rs | 8 +- clippy_lints/src/dereference.rs | 105 ++++--- clippy_lints/src/derivable_impls.rs | 24 +- clippy_lints/src/derive.rs | 8 +- clippy_lints/src/doc/markdown.rs | 26 +- clippy_lints/src/doc/mod.rs | 36 ++- clippy_lints/src/drop_forget_ref.rs | 8 +- clippy_lints/src/enum_clike.rs | 8 +- clippy_lints/src/escape.rs | 37 +-- clippy_lints/src/fallible_impl_from.rs | 8 +- clippy_lints/src/floating_point_arithmetic.rs | 144 +++++----- clippy_lints/src/formatting.rs | 41 +-- .../src/functions/too_many_arguments.rs | 18 +- clippy_lints/src/implicit_return.rs | 22 +- clippy_lints/src/indexing_slicing.rs | 40 +-- clippy_lints/src/infinite_iter.rs | 11 +- clippy_lints/src/int_plus_one.rs | 24 +- .../src/invalid_upcast_comparisons.rs | 84 +++--- clippy_lints/src/item_name_repetitions.rs | 95 +++---- clippy_lints/src/len_zero.rs | 18 +- clippy_lints/src/lifetimes.rs | 8 +- clippy_lints/src/literal_representation.rs | 11 +- .../literal_string_with_formatting_args.rs | 9 +- clippy_lints/src/loops/for_kv_map.rs | 74 ++--- clippy_lints/src/loops/manual_memcpy.rs | 123 ++++---- .../src/loops/manual_while_let_some.rs | 16 +- clippy_lints/src/loops/mut_range_bound.rs | 16 +- clippy_lints/src/loops/needless_range_loop.rs | 269 +++++++++--------- clippy_lints/src/loops/never_loop.rs | 8 +- clippy_lints/src/manual_retain.rs | 35 ++- clippy_lints/src/manual_string_new.rs | 15 +- clippy_lints/src/map_unit_fn.rs | 8 +- clippy_lints/src/matches/manual_filter.rs | 20 +- .../src/matches/match_like_matches.rs | 25 +- .../src/matches/match_single_binding.rs | 28 +- .../src/matches/match_str_case_mismatch.rs | 8 +- clippy_lints/src/matches/match_wild_enum.rs | 18 +- .../src/matches/match_wild_err_arm.rs | 11 +- clippy_lints/src/matches/needless_match.rs | 8 +- clippy_lints/src/matches/overlapping_arms.rs | 22 +- .../matches/significant_drop_in_scrutinee.rs | 9 +- clippy_lints/src/matches/wild_in_or_pats.rs | 22 +- .../src/methods/bind_instead_of_map.rs | 8 +- ...se_sensitive_file_extension_comparisons.rs | 10 +- clippy_lints/src/methods/clone_on_copy.rs | 8 +- clippy_lints/src/methods/expect_fun_call.rs | 9 +- clippy_lints/src/methods/is_empty.rs | 16 +- .../src/methods/iterator_step_by_zero.rs | 18 +- .../methods/manual_saturating_arithmetic.rs | 26 +- clippy_lints/src/methods/map_clone.rs | 16 +- clippy_lints/src/methods/mod.rs | 11 +- clippy_lints/src/methods/needless_collect.rs | 26 +- .../src/methods/needless_option_take.rs | 49 ++-- clippy_lints/src/methods/seek_from_current.rs | 29 +- clippy_lints/src/methods/str_splitn.rs | 15 +- clippy_lints/src/methods/suspicious_map.rs | 8 +- .../src/methods/unnecessary_lazy_eval.rs | 94 +++--- .../src/misc_early/builtin_type_shadow.rs | 18 +- .../src/misc_early/redundant_pattern.rs | 30 +- .../misc_early/unneeded_wildcard_pattern.rs | 46 +-- .../src/mismatching_type_param_order.rs | 8 +- clippy_lints/src/missing_const_for_fn.rs | 11 +- clippy_lints/src/missing_fields_in_debug.rs | 7 +- clippy_lints/src/missing_inline.rs | 13 +- .../src/mixed_read_write_in_expression.rs | 16 +- clippy_lints/src/module_style.rs | 32 +-- .../src/multiple_unsafe_ops_per_block.rs | 5 +- clippy_lints/src/mut_key.rs | 8 +- clippy_lints/src/mut_mut.rs | 20 +- clippy_lints/src/mutable_debug_assertion.rs | 11 +- clippy_lints/src/mutex_atomic.rs | 24 +- clippy_lints/src/needless_bool.rs | 8 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 65 ++--- clippy_lints/src/needless_pass_by_value.rs | 71 +++-- clippy_lints/src/needless_update.rs | 21 +- clippy_lints/src/neg_multiply.rs | 16 +- clippy_lints/src/new_without_default.rs | 8 +- clippy_lints/src/no_effect.rs | 31 +- clippy_lints/src/non_zero_suggestions.rs | 7 +- clippy_lints/src/operators/modulo_one.rs | 18 +- .../src/operators/numeric_arithmetic.rs | 16 +- clippy_lints/src/operators/op_ref.rs | 11 +- clippy_lints/src/pass_by_ref_or_value.rs | 32 +-- clippy_lints/src/pathbuf_init_then_push.rs | 19 +- clippy_lints/src/pattern_type_mismatch.rs | 21 +- clippy_lints/src/ptr_offset_with_cast.rs | 24 +- clippy_lints/src/ranges.rs | 32 +-- clippy_lints/src/redundant_clone.rs | 25 +- clippy_lints/src/redundant_slicing.rs | 33 ++- clippy_lints/src/serde_api.rs | 40 +-- clippy_lints/src/shadow.rs | 8 +- .../src/significant_drop_tightening.rs | 8 +- .../src/single_char_lifetime_names.rs | 27 +- .../src/single_component_path_imports.rs | 22 +- clippy_lints/src/strings.rs | 11 +- clippy_lints/src/swap.rs | 11 +- clippy_lints/src/trait_bounds.rs | 23 +- clippy_lints/src/types/mod.rs | 40 +-- clippy_lints/src/unicode.rs | 8 +- clippy_lints/src/unnecessary_wraps.rs | 10 +- clippy_lints/src/unnested_or_patterns.rs | 8 +- clippy_lints/src/unused_io_amount.rs | 17 +- clippy_lints/src/useless_conversion.rs | 56 ++-- .../interning_defined_symbol.rs | 70 ++--- .../src/utils/internal_lints/invalid_paths.rs | 30 +- .../internal_lints/lint_without_lint_pass.rs | 8 +- .../internal_lints/unnecessary_def_path.rs | 8 +- .../unsorted_clippy_utils_paths.rs | 38 ++- clippy_lints/src/zero_sized_map_values.rs | 8 +- clippy_utils/src/diagnostics.rs | 20 +- clippy_utils/src/higher.rs | 11 +- clippy_utils/src/lib.rs | 157 +++++----- clippy_utils/src/source.rs | 18 +- clippy_utils/src/sugg.rs | 5 +- clippy_utils/src/ty/mod.rs | 24 +- clippy_utils/src/usage.rs | 8 +- clippy_utils/src/visitors.rs | 8 +- lintcheck/src/main.rs | 8 +- src/driver.rs | 20 +- tests/compile-test.rs | 14 +- 135 files changed, 1864 insertions(+), 1907 deletions(-) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index b80ee5aac7e76..0be24f322d2de 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -402,53 +402,53 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io } } - if path.exists() { - if let Some(lint) = lints.iter().find(|l| l.name == name) { - if lint.module == name { - // The lint name is the same as the file, we can just delete the entire file - fs::remove_file(path)?; - } else { - // We can't delete the entire file, just remove the declaration - - if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { - // Remove clippy_lints/src/some_mod/some_lint.rs - let mut lint_mod_path = path.to_path_buf(); - lint_mod_path.set_file_name(name); - lint_mod_path.set_extension("rs"); + if path.exists() + && let Some(lint) = lints.iter().find(|l| l.name == name) + { + if lint.module == name { + // The lint name is the same as the file, we can just delete the entire file + fs::remove_file(path)?; + } else { + // We can't delete the entire file, just remove the declaration - let _ = fs::remove_file(lint_mod_path); - } + if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { + // Remove clippy_lints/src/some_mod/some_lint.rs + let mut lint_mod_path = path.to_path_buf(); + lint_mod_path.set_file_name(name); + lint_mod_path.set_extension("rs"); - let mut content = - fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + let _ = fs::remove_file(lint_mod_path); + } - eprintln!( - "warn: you will have to manually remove any code related to `{name}` from `{}`", - path.display() - ); + let mut content = + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); - assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), - "error: `{}` does not contain lint `{}`'s declaration", - path.display(), - lint.name - ); + eprintln!( + "warn: you will have to manually remove any code related to `{name}` from `{}`", + path.display() + ); - // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range.clone(), ""); + assert!( + content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + "error: `{}` does not contain lint `{}`'s declaration", + path.display(), + lint.name + ); - // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {name};"); - content = content.replacen(&mod_decl, "", 1); + // Remove lint declaration (declare_clippy_lint!) + content.replace_range(lint.declaration_range.clone(), ""); - remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); - fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); - } + // Remove the module declaration (mod xyz;) + let mod_decl = format!("\nmod {name};"); + content = content.replacen(&mod_decl, "", 1); - remove_test_assets(name); - remove_lint(name, lints); - return Ok(true); + remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); + fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); } + + remove_test_assets(name); + remove_lint(name, lints); + return Ok(true); } Ok(false) diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index b87fcca13b1ce..206816398f50f 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -30,10 +30,10 @@ pub fn clippy_project_root() -> PathBuf { let current_dir = std::env::current_dir().unwrap(); for path in current_dir.ancestors() { let result = fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result { - if err.kind() == io::ErrorKind::NotFound { - continue; - } + if let Err(err) = &result + && err.kind() == io::ErrorKind::NotFound + { + continue; } let content = result.unwrap(); diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 57cabe437034a..9113c20c795b7 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -263,10 +263,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { continue; } - if let Some(cur_v) = cur_v { - if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span { - Self::lint_member_name(cx, &variant.ident, &cur_v.ident); - } + if let Some(cur_v) = cur_v + && cur_v.ident.name.as_str() > variant.ident.name.as_str() + && cur_v.span != variant.span + { + Self::lint_member_name(cx, &variant.ident, &cur_v.ident); } cur_v = Some(variant); } @@ -278,10 +279,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { continue; } - if let Some(cur_f) = cur_f { - if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span { - Self::lint_member_name(cx, &field.ident, &cur_f.ident); - } + if let Some(cur_f) = cur_f + && cur_f.ident.name.as_str() > field.ident.name.as_str() + && cur_f.span != field.span + { + Self::lint_member_name(cx, &field.ident, &cur_f.ident); } cur_f = Some(field); } diff --git a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index fecf316640636..457692ed5dc53 100644 --- a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -8,17 +8,18 @@ use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { - if let Some(lint_name) = extract_clippy_lint(lint) { - if lint_name.as_str() == "restriction" && name != sym::allow { - span_lint_and_help( - cx, - BLANKET_CLIPPY_RESTRICTION_LINTS, - lint.span(), - "`clippy::restriction` is not meant to be enabled as a group", - None, - "enable the restriction lints you need individually", - ); - } + if let Some(lint_name) = extract_clippy_lint(lint) + && lint_name.as_str() == "restriction" + && name != sym::allow + { + span_lint_and_help( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + lint.span(), + "`clippy::restriction` is not meant to be enabled as a group", + None, + "enable the restriction lints you need individually", + ); } } } diff --git a/clippy_lints/src/attrs/deprecated_semver.rs b/clippy_lints/src/attrs/deprecated_semver.rs index d3153ec6613b5..50943b36809d2 100644 --- a/clippy_lints/src/attrs/deprecated_semver.rs +++ b/clippy_lints/src/attrs/deprecated_semver.rs @@ -6,10 +6,10 @@ use rustc_span::Span; use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { - if let LitKind::Str(is, _) = lit.kind { - if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() { - return; - } + if let LitKind::Str(is, _) = lit.kind + && (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok()) + { + return; } span_lint( cx, diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 6f8a9a6ecbe3d..f7f168cb26792 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -573,28 +573,27 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - if let Some(items) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { - allow_attributes::check(cx, attr); - } - if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) + if let Some(items) = &attr.meta_item_list() + && let Some(ident) = attr.ident() + { + if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes::check(cx, attr); + } + if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes_without_reason::check(cx, ident.name, items, attr); + } + if is_lint_level(ident.name, attr.id) { + blanket_clippy_restriction_lints::check(cx, ident.name, items); + } + if items.is_empty() || !attr.has_name(sym::deprecated) { + return; + } + for item in items { + if let MetaItemInner::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) { - allow_attributes_without_reason::check(cx, ident.name, items, attr); - } - if is_lint_level(ident.name, attr.id) { - blanket_clippy_restriction_lints::check(cx, ident.name, items); - } - if items.is_empty() || !attr.has_name(sym::deprecated) { - return; - } - for item in items { - if let MetaItemInner::MetaItem(mi) = &item - && let MetaItemKind::NameValue(lit) = &mi.kind - && mi.has_name(sym::since) - { - deprecated_semver::check(cx, item.span(), lit); - } + deprecated_semver::check(cx, item.span(), lit); } } } diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index e3e081ce08e9f..064b72b930581 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -14,75 +14,75 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if attr.span.in_external_macro(cx.sess().source_map()) { return; } - if let Some(lint_list) = &attr.meta_item_list() { - if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) { - for lint in lint_list { - match item.kind { - ItemKind::Use(..) => { - let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else { - return; - }; + if let Some(lint_list) = &attr.meta_item_list() + && attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) + { + for lint in lint_list { + match item.kind { + ItemKind::Use(..) => { + let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else { + return; + }; - if namespace.is_none() - && matches!( - name.as_str(), - "ambiguous_glob_reexports" - | "dead_code" - | "deprecated" - | "hidden_glob_reexports" - | "unreachable_pub" - | "unused" - | "unused_braces" - | "unused_import_braces" - | "unused_imports" - ) - { - return; - } + if namespace.is_none() + && matches!( + name.as_str(), + "ambiguous_glob_reexports" + | "dead_code" + | "deprecated" + | "hidden_glob_reexports" + | "unreachable_pub" + | "unused" + | "unused_braces" + | "unused_import_braces" + | "unused_imports" + ) + { + return; + } - if namespace == Some(sym::clippy) - && matches!( - name.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - | "disallowed_types" - | "unused_trait_names" - ) - { - return; - } - }, - ItemKind::ExternCrate(..) => { - if is_word(lint, sym::unused_imports) && skip_unused_imports { - return; - } - if is_word(lint, sym!(unused_extern_crates)) { - return; - } - }, - _ => {}, - } + if namespace == Some(sym::clippy) + && matches!( + name.as_str(), + "wildcard_imports" + | "enum_glob_use" + | "redundant_pub_crate" + | "macro_use_imports" + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" + | "disallowed_types" + | "unused_trait_names" + ) + { + return; + } + }, + ItemKind::ExternCrate(..) => { + if is_word(lint, sym::unused_imports) && skip_unused_imports { + return; + } + if is_word(lint, sym!(unused_extern_crates)) { + return; + } + }, + _ => {}, } - let line_span = first_line_of_span(cx, attr.span); + } + let line_span = first_line_of_span(cx, attr.span); - if let Some(src) = line_span.get_source_text(cx) { - if src.contains("#[") { - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { - diag.span_suggestion( - line_span, - "if you just forgot a `!`, use", - src.replacen("#[", "#![", 1), - Applicability::MaybeIncorrect, - ); - }); - } - } + if let Some(src) = line_span.get_source_text(cx) + && src.contains("#[") + { + #[expect(clippy::collapsible_span_lint_calls)] + span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { + diag.span_suggestion( + line_span, + "if you just forgot a `!`, use", + src.replacen("#[", "#![", 1), + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 92a0c7f9acbcd..d600aec8c9d65 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -192,10 +192,9 @@ impl<'tcx> LateLintPass<'tcx> for AwaitHolding { def_id, .. }) = expr.kind + && let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) { - if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) { - self.check_interior_types(cx, coroutine_layout); - } + self.check_interior_types(cx, coroutine_layout); } } } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 36c1aea263781..249f3b375c0ca 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -242,11 +242,11 @@ struct Hir2Qmm<'a, 'tcx, 'v> { impl<'v> Hir2Qmm<'_, '_, 'v> { fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec) -> Result, String> { for a in a { - if let ExprKind::Binary(binop, lhs, rhs) = &a.kind { - if binop.node == op { - v = self.extract(op, &[lhs, rhs], v)?; - continue; - } + if let ExprKind::Binary(binop, lhs, rhs) = &a.kind + && binop.node == op + { + v = self.extract(op, &[lhs, rhs], v)?; + continue; } v.push(self.run(a)?); } @@ -418,12 +418,12 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio let lhs_snippet = lhs.span.get_source_text(cx)?; let rhs_snippet = rhs.span.get_source_text(cx)?; - if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) { - if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) { - // e.g. `(a as u64) < b`. Without the parens the `<` is - // interpreted as a start of generic arguments for `u64` - return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); - } + if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) + && let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) + { + // e.g. `(a as u64) < b`. Without the parens the `<` is + // interpreted as a start of generic arguments for `u64` + return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); } Some(format!("{lhs_snippet}{op}{rhs_snippet}")) diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index cfe5e424ff750..7cde007a9b66d 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -93,10 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { // has deref trait -> give 2 help // doesn't have deref trait -> give 1 help - if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() { - if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { - return; - } + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() + && !implements_trait(cx, *inner_ty, deref_trait_id, &[]) + { + return; } diag.span_suggestion( diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index b28ac8dc971da..8742f5f1a0e0e 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -64,11 +64,11 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX)) }, ExprKind::MethodCall(method, _, [lo, hi], _) => { - if method.ident.as_str() == "clamp" { + if method.ident.as_str() == "clamp" //FIXME: make this a diagnostic item - if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) { - return lo_bits.max(hi_bits); - } + && let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) + { + return lo_bits.max(hi_bits); } nbits }, diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 57a135abc2e2b..3fca0f8970770 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -19,16 +19,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx.typeck_results().expr_ty(expr), ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind { - if method_path.ident.name.as_str() == "cast" - && let Some(generic_args) = method_path.args - && let [GenericArg::Type(cast_to)] = generic_args.args - // There probably is no obvious reason to do this, just to be consistent with `as` cases. - && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty()) - { - let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } + } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind + && method_path.ident.name.as_str() == "cast" + && let Some(generic_args) = method_path.args + && let [GenericArg::Type(cast_to)] = generic_args.args + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty()) + { + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index c48f253606dcc..a5b295c88b1c7 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -21,42 +21,41 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) start_ty, end_ty, }) = expr_cast_chain_tys(cx, expr) + && let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { - if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { - let from_size = from_layout.size.bytes(); - let to_size = to_layout.size.bytes(); - if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { - span_lint_and_then( - cx, - CAST_SLICE_DIFFERENT_SIZES, - expr.span, - format!( - "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", - start_ty.ty, end_ty.ty, - ), - |diag| { - let ptr_snippet = source::snippet(cx, left_cast.span, ".."); + let from_size = from_layout.size.bytes(); + let to_size = to_layout.size.bytes(); + if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { + span_lint_and_then( + cx, + CAST_SLICE_DIFFERENT_SIZES, + expr.span, + format!( + "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", + start_ty.ty, end_ty.ty, + ), + |diag| { + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); - let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { - Mutability::Mut => ("_mut", "mut"), - Mutability::Not => ("", "const"), - }; - let sugg = format!( - "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", - // get just the ty from the TypeAndMut so that the printed type isn't something like `mut - // T`, extract just the `T` - end_ty.ty - ); + let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { + Mutability::Mut => ("_mut", "mut"), + Mutability::Not => ("", "const"), + }; + let sugg = format!( + "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", + // get just the ty from the TypeAndMut so that the printed type isn't something like `mut + // T`, extract just the `T` + end_ty.ty + ); - diag.span_suggestion( - expr.span, - format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), - sugg, - rustc_errors::Applicability::HasPlaceholders, - ); - }, - ); - } + diag.span_suggestion( + expr.span, + format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + sugg, + rustc_errors::Applicability::HasPlaceholders, + ); + }, + ); } } } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 7885f171461d7..96c5da2cf73ea 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -130,11 +130,11 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = cast_expr.span.get_source_text(cx) { - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { - lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); - return true; - } + if let Some(src) = cast_expr.span.get_source_text(cx) + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) + { + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } }, _ => {}, diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index b36c8662289ca..8ada608049c7b 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -253,11 +253,11 @@ fn get_types_from_cast<'a>( match limit.kind { // `from_type::from(_)` ExprKind::Call(path, _) => { - if let ExprKind::Path(ref path) = path.kind { + if let ExprKind::Path(ref path) = path.kind // `to_type` - if let Some(to_type) = get_implementing_type(path, types, func) { - return Some((from_type, to_type)); - } + && let Some(to_type) = get_implementing_type(path, types, func) + { + return Some((from_type, to_type)); } }, // `to_type::MAX` diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d85005d57ede8..42fbe6438d4ea 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -539,10 +539,10 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .filter(|stmt| !ignore_span.overlaps(stmt.span)) .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); - if let Some(expr) = block.expr { - if res.is_continue() { - res = intravisit::walk_expr(&mut walker, expr); - } + if let Some(expr) = block.expr + && res.is_continue() + { + res = intravisit::walk_expr(&mut walker, expr); } res.is_break() diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 849c60b89b97e..7da5a530eaa3d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1133,61 +1133,60 @@ fn report<'tcx>( impl<'tcx> Dereferencing<'tcx> { fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { - if let Some(outer_pat) = self.ref_locals.get_mut(&local) { - if let Some(pat) = outer_pat { - // Check for auto-deref - if !matches!( - cx.typeck_results().expr_adjustments(e), - [ - Adjustment { - kind: Adjust::Deref(_), - .. - }, - Adjustment { - kind: Adjust::Deref(_), - .. - }, + if let Some(outer_pat) = self.ref_locals.get_mut(&local) + && let Some(pat) = outer_pat + // Check for auto-deref + && !matches!( + cx.typeck_results().expr_adjustments(e), + [ + Adjustment { + kind: Adjust::Deref(_), .. - ] - ) { - match get_parent_expr(cx, e) { - // Field accesses are the same no matter the number of references. - Some(Expr { - kind: ExprKind::Field(..), - .. - }) => (), - Some(&Expr { - span, - kind: ExprKind::Unary(UnOp::Deref, _), - .. - }) if !span.from_expansion() => { - // Remove explicit deref. - let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((span, snip.into())); - }, - Some(parent) if !parent.span.from_expansion() => { - // Double reference might be needed at this point. - if parent.precedence() == ExprPrecedence::Unambiguous { - // Parentheses would be needed here, don't lint. - *outer_pat = None; - } else { - pat.always_deref = false; - let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((e.span, format!("&{snip}"))); - } - }, - _ if !e.span.from_expansion() => { - // Double reference might be needed at this point. - pat.always_deref = false; - let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); - pat.replacements.push((e.span, format!("&{snip}"))); - }, - // Edge case for macros. The span of the identifier will usually match the context of the - // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc - // macros - _ => *outer_pat = None, + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, + .. + ] + ) + { + match get_parent_expr(cx, e) { + // Field accesses are the same no matter the number of references. + Some(Expr { + kind: ExprKind::Field(..), + .. + }) => (), + Some(&Expr { + span, + kind: ExprKind::Unary(UnOp::Deref, _), + .. + }) if !span.from_expansion() => { + // Remove explicit deref. + let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((span, snip.into())); + }, + Some(parent) if !parent.span.from_expansion() => { + // Double reference might be needed at this point. + if parent.precedence() == ExprPrecedence::Unambiguous { + // Parentheses would be needed here, don't lint. + *outer_pat = None; + } else { + pat.always_deref = false; + let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((e.span, format!("&{snip}"))); } - } + }, + _ if !e.span.from_expansion() => { + // Double reference might be needed at this point. + pat.always_deref = false; + let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); + pat.replacements.push((e.span, format!("&{snip}"))); + }, + // Edge case for macros. The span of the identifier will usually match the context of the + // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc + // macros + _ => *outer_pat = None, } } } diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 8d9222e4bf61e..479422f7378ed 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -94,18 +94,18 @@ fn check_struct<'tcx>( ty_args: GenericArgsRef<'_>, typeck_results: &'tcx TypeckResults<'tcx>, ) { - if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args, .. }) = p.segments.last() { - let args = args.map(|a| a.args).unwrap_or(&[]); - - // ty_args contains the generic parameters of the type declaration, while args contains the - // arguments used at instantiation time. If both len are not equal, it means that some - // parameters were not provided (which means that the default values were used); in this - // case we will not risk suggesting too broad a rewrite. We won't either if any argument - // is a type or a const. - if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { - return; - } + if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind + && let Some(PathSegment { args, .. }) = p.segments.last() + { + let args = args.map(|a| a.args).unwrap_or(&[]); + + // ty_args contains the generic parameters of the type declaration, while args contains the + // arguments used at instantiation time. If both len are not equal, it means that some + // parameters were not provided (which means that the default values were used); in this + // case we will not risk suggesting too broad a rewrite. We won't either if any argument + // is a type or a const. + if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { + return; } } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 2ae35b4005579..e5ec17d89a115 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -428,10 +428,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { - if let ExprKind::Block(block, _) = expr.kind { - if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { - return ControlFlow::Break(()); - } + if let ExprKind::Block(block, _) = expr.kind + && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + { + return ControlFlow::Break(()); } walk_expr(self, expr) diff --git a/clippy_lints/src/doc/markdown.rs b/clippy_lints/src/doc/markdown.rs index 8cdaba88e5095..7a1c7c675d2ec 100644 --- a/clippy_lints/src/doc/markdown.rs +++ b/clippy_lints/src/doc/markdown.rs @@ -113,20 +113,20 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b s != "-" && s.contains('-') } - if let Ok(url) = Url::parse(word) { + if let Ok(url) = Url::parse(word) // try to get around the fact that `foo::bar` parses as a valid URL - if !url.cannot_be_a_base() { - span_lint_and_sugg( - cx, - DOC_MARKDOWN, - span, - "you should put bare URLs between `<`/`>` or make a proper Markdown link", - "try", - format!("<{word}>"), - Applicability::MachineApplicable, - ); - return; - } + && !url.cannot_be_a_base() + { + span_lint_and_sugg( + cx, + DOC_MARKDOWN, + span, + "you should put bare URLs between `<`/`>` or make a proper Markdown link", + "try", + format!("<{word}>"), + Applicability::MachineApplicable, + ); + return; } // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 36fd396cc1df8..d0075d01eeaa4 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -880,19 +880,18 @@ fn check_for_code_clusters<'a, Events: Iterator{}", doc[start..end].replace('`', "")); - diag.span_suggestion_verbose( - span, - "wrap the entire group in `` tags", - sugg, - Applicability::MaybeIncorrect, - ); - diag.help("separate code snippets will be shown with a gap"); - }); - } + span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| { + let sugg = format!("{}", doc[start..end].replace('`', "")); + diag.span_suggestion_verbose( + span, + "wrap the entire group in `` tags", + sugg, + Applicability::MaybeIncorrect, + ); + diag.help("separate code snippets will be shown with a gap"); + }); } code_includes_link = false; code_starts_at = None; @@ -1201,16 +1200,15 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { return; } - if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { - if is_panic(self.cx, macro_call.def_id) + if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) + && (is_panic(self.cx, macro_call.def_id) || matches!( self.cx.tcx.item_name(macro_call.def_id).as_str(), "assert" | "assert_eq" | "assert_ne" - ) - { - self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id); - self.panic_span = Some(macro_call.span); - } + )) + { + self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id); + self.panic_span = Some(macro_call.span); } // check for `unwrap` and `expect` for both `Option` and `Result` diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 617982f4da30f..5c360ce6a5f7e 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -144,10 +144,10 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { // .. // } fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool { - if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { - if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) { - return body.hir_id == drop_expr.hir_id; - } + if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) + && let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) + { + return body.hir_id == drop_expr.hir_id; } false } diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index f01b5c840d290..ec81294624efa 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -49,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { - if let ty::Adt(adt, _) = ty.kind() { - if adt.is_enum() { - ty = adt.repr().discr_type().to_ty(cx.tcx); - } + if let ty::Adt(adt, _) = ty.kind() + && adt.is_enum() + { + ty = adt.repr().discr_type().to_ty(cx.tcx); } match ty.kind() { ty::Int(IntTy::Isize) => { diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 831d47ac48779..e1f4026ad611d 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -72,10 +72,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { _: Span, fn_def_id: LocalDefId, ) { - if let Some(header) = fn_kind.header() { - if header.abi != ExternAbi::Rust { - return; - } + if let Some(header) = fn_kind.header() + && header.abi != ExternAbi::Rust + { + return; } let parent_id = cx @@ -141,22 +141,22 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() { - if let PlaceBase::Local(lid) = cmt.place.base { - // FIXME(rust/#120456) - is `swap_remove` correct? - self.set.swap_remove(&lid); - } + if cmt.place.projections.is_empty() + && let PlaceBase::Local(lid) = cmt.place.base + { + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { - if cmt.place.projections.is_empty() { - if let PlaceBase::Local(lid) = cmt.place.base { - // FIXME(rust/#120456) - is `swap_remove` correct? - self.set.swap_remove(&lid); - } + if cmt.place.projections.is_empty() + && let PlaceBase::Local(lid) = cmt.place.base + { + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } @@ -170,10 +170,11 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { // 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 { - if self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) { - return; - } + 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()) { diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index f67d38d932b9a..c868b782f43c3 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -75,10 +75,10 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { - if is_panic(self.lcx, macro_call.def_id) { - self.result.push(expr.span); - } + if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) + && is_panic(self.lcx, macro_call.def_id) + { + self.result.push(expr.span); } // check for `unwrap` diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 92b607c87d75f..d5f0659f8427f 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -228,24 +228,24 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) { - if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { + if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) + && let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { Some("exp2") } else { None - } { - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "exponent for bases 2 and e can be computed more accurately", - "consider using", - format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), - Applicability::MachineApplicable, - ); } + { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "exponent for bases 2 and e can be computed more accurately", + "consider using", + format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), + Applicability::MachineApplicable, + ); } // Check argument @@ -289,55 +289,53 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { - if value == Int(2) { - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind - { - if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() { - return; - } - } - } + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) + && value == Int(2) + && let Some(parent) = get_parent_expr(cx, expr) + { + if let Some(grandparent) = get_parent_expr(cx, parent) + && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind + && method_name.as_str() == "sqrt" + && detect_hypot(cx, receiver).is_some() + { + return; + } + + if let ExprKind::Binary( + Spanned { + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. + }, + lhs, + rhs, + ) = parent.kind + { + let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; - if let ExprKind::Binary( - Spanned { - node: op @ (BinOpKind::Add | BinOpKind::Sub), - .. - }, - lhs, - rhs, - ) = parent.kind - { - let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; - - // Negate expr if original code has subtraction and expr is on the right side - let maybe_neg_sugg = |expr, hir_id| { - let sugg = Sugg::hir(cx, expr, ".."); - if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { - -sugg - } else { - sugg - } - }; - - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - parent.span, - "multiply and add expressions can be calculated more efficiently and accurately", - "consider using", - format!( - "{}.mul_add({}, {})", - Sugg::hir(cx, receiver, "..").maybe_paren(), - maybe_neg_sugg(receiver, expr.hir_id), - maybe_neg_sugg(other_addend, other_addend.hir_id), - ), - Applicability::MachineApplicable, - ); + // Negate expr if original code has subtraction and expr is on the right side + let maybe_neg_sugg = |expr, hir_id| { + let sugg = Sugg::hir(cx, expr, ".."); + if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { + -sugg + } else { + sugg } - } + }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + parent.span, + "multiply and add expressions can be calculated more efficiently and accurately", + "consider using", + format!( + "{}.mul_add({}, {})", + Sugg::hir(cx, receiver, "..").maybe_paren(), + maybe_neg_sugg(receiver, expr.hir_id), + maybe_neg_sugg(other_addend, other_addend.hir_id), + ), + Applicability::MachineApplicable, + ); } } } @@ -483,12 +481,12 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind { - if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind { - if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() { - return; - } - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind + && method_name.as_str() == "sqrt" + && detect_hypot(cx, receiver).is_some() + { + return; } let maybe_neg_sugg = |expr| { @@ -566,15 +564,15 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// If the two expressions are not negations of each other, then it /// returns None. fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> { - if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind { - if eq_expr_value(cx, expr1_negated, expr2) { - return Some((false, expr2)); - } + if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind + && eq_expr_value(cx, expr1_negated, expr2) + { + return Some((false, expr2)); } - if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind { - if eq_expr_value(cx, expr1, expr2_negated) { - return Some((true, expr1)); - } + if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind + && eq_expr_value(cx, expr1, expr2_negated) + { + return Some((true, expr1)); } None } diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index c8fe7ac73cb34..4b482f7b233b8 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -138,27 +138,28 @@ impl EarlyLintPass for Formatting { /// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint. fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { - if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind { - if !lhs.span.from_expansion() && !rhs.span.from_expansion() { - let eq_span = lhs.span.between(rhs.span); - if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind { - if let Some(eq_snippet) = snippet_opt(cx, eq_span) { - let op = op.as_str(); - let eqop_span = lhs.span.between(sub_rhs.span); - if eq_snippet.ends_with('=') { - span_lint_and_note( - cx, - SUSPICIOUS_ASSIGNMENT_FORMATTING, - eqop_span, - format!( - "this looks like you are trying to use `.. {op}= ..`, but you \ + if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + let eq_span = lhs.span.between(rhs.span); + if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind + && let Some(eq_snippet) = snippet_opt(cx, eq_span) + { + let op = op.as_str(); + let eqop_span = lhs.span.between(sub_rhs.span); + if eq_snippet.ends_with('=') { + span_lint_and_note( + cx, + SUSPICIOUS_ASSIGNMENT_FORMATTING, + eqop_span, + format!( + "this looks like you are trying to use `.. {op}= ..`, but you \ really are doing `.. = ({op} ..)`" - ), - None, - format!("to remove this lint, use either `{op}=` or `= {op}`"), - ); - } - } + ), + None, + format!("to remove this lint, use either `{op}=` or `= {op}`"), + ); } } } diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 05dc47f6fe580..48d050aa36aa0 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -47,16 +47,16 @@ pub(super) fn check_fn( } pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) { - if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind // don't lint extern functions decls, it's not their fault - if sig.header.abi == ExternAbi::Rust { - check_arg_number( - cx, - sig.decl, - item.span.with_hi(sig.decl.output.span().hi()), - too_many_arguments_threshold, - ); - } + && sig.header.abi == ExternAbi::Rust + { + check_arg_number( + cx, + sig.decl, + item.span.with_hi(sig.decl.output.span().hi()), + too_many_arguments_threshold, + ); } } diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 5f95464e4d494..ee141ddee15b0 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -153,18 +153,18 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; let _: Option = for_each_expr_without_closures(block, |e| { - if let ExprKind::Break(dest, sub_expr) = e.kind { - if dest.target_id.ok() == Some(expr.hir_id) { - if call_site_span.is_none() && e.span.ctxt() == ctxt { - // At this point sub_expr can be `None` in async functions which either diverge, or return - // the unit type. - if let Some(sub_expr) = sub_expr { - lint_break(cx, e.hir_id, e.span, sub_expr.span); - } - } else { - // the break expression is from a macro call, add a return to the loop - add_return = true; + if let ExprKind::Break(dest, sub_expr) = e.kind + && dest.target_id.ok() == Some(expr.hir_id) + { + if call_site_span.is_none() && e.span.ctxt() == ctxt { + // At this point sub_expr can be `None` in async functions which either diverge, or return + // the unit type. + if let Some(sub_expr) = sub_expr { + lint_break(cx, e.hir_id, e.span, sub_expr.span); } + } else { + // the break expression is from a macro call, add a return to the loop + add_return = true; } } ControlFlow::Continue(()) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 33431385c7de3..be801d43a52bb 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -136,28 +136,28 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { let const_range = to_const_range(cx, range, size); - if let (Some(start), _) = const_range { - if start > size { - span_lint( - cx, - OUT_OF_BOUNDS_INDEXING, - range.start.map_or(expr.span, |start| start.span), - "range is out of bounds", - ); - return; - } + if let (Some(start), _) = const_range + && start > size + { + span_lint( + cx, + OUT_OF_BOUNDS_INDEXING, + range.start.map_or(expr.span, |start| start.span), + "range is out of bounds", + ); + return; } - if let (_, Some(end)) = const_range { - if end > size { - span_lint( - cx, - OUT_OF_BOUNDS_INDEXING, - range.end.map_or(expr.span, |end| end.span), - "range is out of bounds", - ); - return; - } + if let (_, Some(end)) = const_range + && end > size + { + span_lint( + cx, + OUT_OF_BOUNDS_INDEXING, + range.end.map_or(expr.span, |end| end.span), + "range is out of bounds", + ); + return; } if let (Some(_), Some(_)) = const_range { diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 960b9aa032bea..427a1f8255553 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -156,11 +156,12 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name.as_str() == "flat_map" && args.len() == 1 { - if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { - let body = cx.tcx.hir_body(body); - return is_infinite(cx, body.value); - } + if method.ident.name.as_str() == "flat_map" + && args.len() == 1 + && let ExprKind::Closure(&Closure { body, .. }) = args[0].kind + { + let body = cx.tcx.hir_body(body); + return is_infinite(cx, body.value); } Finite }, diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index fc575bff7e63f..67ce57de254d3 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -130,14 +130,14 @@ impl IntPlusOne { BinOpKind::Le => "<", _ => return None, }; - if let Some(snippet) = node.span.get_source_text(cx) { - if let Some(other_side_snippet) = other_side.span.get_source_text(cx) { - let rec = match side { - Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), - Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), - }; - return rec; - } + if let Some(snippet) = node.span.get_source_text(cx) + && let Some(other_side_snippet) = other_side.span.get_source_text(cx) + { + let rec = match side { + Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), + Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), + }; + return rec; } None } @@ -157,10 +157,10 @@ impl IntPlusOne { impl EarlyLintPass for IntPlusOne { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { - if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { - if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec); - } + if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind + && let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) + { + Self::emit_warning(cx, item, rec); } } } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index b42664340d1c8..b0ecc5d52ddb8 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -91,49 +91,49 @@ fn upcast_comparison_bounds_err<'tcx>( rhs: &'tcx Expr<'_>, invert: bool, ) { - if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) { - if rel == Rel::Eq || rel == Rel::Ne { - if norm_rhs_val < lb || norm_rhs_val > ub { - err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); - } - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val < lb - } else { - ub < norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val <= lb - } else { - ub <= norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, true); - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val >= ub - } else { - lb >= norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val > ub - } else { - lb > norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, false); + if let Some((lb, ub)) = lhs_bounds + && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) + { + if rel == Rel::Eq || rel == Rel::Ne { + if norm_rhs_val < lb || norm_rhs_val > ub { + err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); } + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val < lb + } else { + ub < norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val <= lb + } else { + ub <= norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, true); + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val >= ub + } else { + lb >= norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val > ub + } else { + lb > norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, false); } } } diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 0f800a68cdbf4..b1271a264b548 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -444,57 +444,56 @@ impl LateLintPass<'_> for ItemNameRepetitions { let item_name = ident.name.as_str(); let item_camel = to_camel_case(item_name); - if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules { - // constants don't have surrounding modules - if !mod_camel.is_empty() { - if mod_name == &ident.name - && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } + if !item.span.from_expansion() && is_present_in_source(cx, item.span) + && let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules + // constants don't have surrounding modules + && !mod_camel.is_empty() + { + if mod_name == &ident.name + && let ItemKind::Mod(..) = item.kind + && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } - // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() - && cx.tcx.visibility(mod_owner_id.def_id).is_public() - && item_camel.len() > mod_camel.len() - { - let matching = count_match_start(mod_camel, &item_camel); - let rmatching = count_match_end(mod_camel, &item_camel); - let nchars = mod_camel.chars().count(); - - let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); - - if matching.char_count == nchars { - match item_camel.chars().nth(nchars) { - Some(c) if is_word_beginning(c) => span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name starts with its containing module's name", - ), - _ => (), - } - } - if rmatching.char_count == nchars - && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) - { - span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name ends with its containing module's name", - ); - } + // The `module_name_repetitions` lint should only trigger if the item has the module in its + // name. Having the same name is accepted. + if cx.tcx.visibility(item.owner_id).is_public() + && cx.tcx.visibility(mod_owner_id.def_id).is_public() + && item_camel.len() > mod_camel.len() + { + let matching = count_match_start(mod_camel, &item_camel); + let rmatching = count_match_end(mod_camel, &item_camel); + let nchars = mod_camel.chars().count(); + + let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); + + if matching.char_count == nchars { + match item_camel.chars().nth(nchars) { + Some(c) if is_word_beginning(c) => span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name starts with its containing module's name", + ), + _ => (), } } + if rmatching.char_count == nchars + && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name ends with its containing module's name", + ); + } } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 15c44381debbe..5a86045789a87 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -523,10 +523,10 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method - if let Some(name) = get_item_name(cx, method) { - if name.as_str() == "is_empty" { - return; - } + if let Some(name) = get_item_name(cx, method) + && name.as_str() == "is_empty" + { + return; } check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); @@ -588,11 +588,11 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex } fn is_empty_string(expr: &Expr<'_>) -> bool { - if let ExprKind::Lit(lit) = expr.kind { - if let LitKind::Str(lit, _) = lit.node { - let lit = lit.as_str(); - return lit.is_empty(); - } + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(lit, _) = lit.node + { + let lit = lit.as_str(); + return lit.is_empty(); } false } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 3dd2de1fafc72..1769fa53865f0 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -150,10 +150,10 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } = item.kind { check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv); - } else if let ItemKind::Impl(impl_) = item.kind { - if !item.span.from_expansion() { - report_extra_impl_lifetimes(cx, impl_); - } + } else if let ItemKind::Impl(impl_) = item.kind + && !item.span.from_expansion() + { + report_extra_impl_lifetimes(cx, impl_); } } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 805de23408bf5..7cbfa2d097ae5 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -387,12 +387,11 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal { - if let Some(second_size) = groups.next() { - if !groups.all(|i| i == second_size) || first > second_size { - return Err(WarningType::UnusualByteGroupings); - } - } + if (radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal) + && let Some(second_size) = groups.next() + && (!groups.all(|i| i == second_size) || first > second_size) + { + return Err(WarningType::UnusualByteGroupings); } if let Some(second) = groups.next() { diff --git a/clippy_lints/src/literal_string_with_formatting_args.rs b/clippy_lints/src/literal_string_with_formatting_args.rs index 975e6833a35f8..244e7c95122e0 100644 --- a/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/clippy_lints/src/literal_string_with_formatting_args.rs @@ -45,15 +45,14 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { let pat_span = pat.span; - if let PatKind::Tuple(pat, _) = pat.kind { - if pat.len() == 2 { - let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { - ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { - (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), - (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), - _ => return, - }, + if let PatKind::Tuple(pat, _) = pat.kind + && pat.len() == 2 + { + let arg_span = arg.span; + let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { + ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { + (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), + (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), _ => return, - }; - let mutbl = match mutbl { - Mutability::Not => "", - Mutability::Mut => "_mut", - }; - let arg = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, - _ => arg, - }; + }, + _ => return, + }; + let mutbl = match mutbl { + Mutability::Not => "", + Mutability::Mut => "_mut", + }; + let arg = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, + _ => arg, + }; - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { - span_lint_and_then( - cx, - FOR_KV_MAP, - arg_span, - format!("you seem to want to iterate on a map's {kind}s"), - |diag| { - let map = sugg::Sugg::hir(cx, arg, "map"); - diag.multipart_suggestion( - "use the corresponding method", - vec![ - (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())), - ], - Applicability::MachineApplicable, - ); - }, - ); - } + if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + span_lint_and_then( + cx, + FOR_KV_MAP, + arg_span, + format!("you seem to want to iterate on a map's {kind}s"), + |diag| { + let map = sugg::Sugg::hir(cx, arg, "map"); + diag.multipart_suggestion( + "use the corresponding method", + vec![ + (pat_span, snippet(cx, new_pat_span, kind).into_owned()), + (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())), + ], + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 39af596878178..d9c4b526da99e 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -28,37 +28,37 @@ pub(super) fn check<'tcx>( end: Some(end), limits, }) = higher::Range::hir(arg) - { // the var must be a single name - if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - let mut starts = vec![Start { - id: canonical_id, - kind: StartKind::Range, - }]; - - // This is one of few ways to return different iterators - // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 - let mut iter_a = None; - let mut iter_b = None; - - if let ExprKind::Block(block, _) = body.kind { - if let Some(loop_counters) = get_loop_counters(cx, block, expr) { - starts.extend(loop_counters); - } - iter_a = Some(get_assignments(block, &starts)); - } else { - iter_b = Some(get_assignment(body)); + && let PatKind::Binding(_, canonical_id, _, _) = pat.kind + { + let mut starts = vec![Start { + id: canonical_id, + kind: StartKind::Range, + }]; + + // This is one of few ways to return different iterators + // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 + let mut iter_a = None; + let mut iter_b = None; + + if let ExprKind::Block(block, _) = body.kind { + if let Some(loop_counters) = get_loop_counters(cx, block, expr) { + starts.extend(loop_counters); } + iter_a = Some(get_assignments(block, &starts)); + } else { + iter_b = Some(get_assignment(body)); + } - let assignments = iter_a.into_iter().flatten().chain(iter_b); + let assignments = iter_a.into_iter().flatten().chain(iter_b); - let big_sugg = assignments - // The only statements in the for loops can be indexed assignments from - // indexed retrievals (except increments of loop counters). - .map(|o| { - o.and_then(|(lhs, rhs)| { - let rhs = fetch_cloned_expr(rhs); - if let ExprKind::Index(base_left, idx_left, _) = lhs.kind + let big_sugg = assignments + // The only statements in the for loops can be indexed assignments from + // indexed retrievals (except increments of loop counters). + .map(|o| { + o.and_then(|(lhs, rhs)| { + let rhs = fetch_cloned_expr(rhs); + if let ExprKind::Index(base_left, idx_left, _) = lhs.kind && let ExprKind::Index(base_right, idx_right, _) = rhs.kind && let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)) && get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some() @@ -68,42 +68,41 @@ pub(super) fn check<'tcx>( && !local_used_in(cx, canonical_id, base_right) // Source and destination must be different && path_to_local(base_left) != path_to_local(base_right) - { - Some(( - ty, - IndexExpr { - base: base_left, - idx: start_left, - idx_offset: offset_left, - }, - IndexExpr { - base: base_right, - idx: start_right, - idx_offset: offset_right, - }, - )) - } else { - None - } - }) + { + Some(( + ty, + IndexExpr { + base: base_left, + idx: start_left, + idx_offset: offset_left, + }, + IndexExpr { + base: base_right, + idx: start_right, + idx_offset: offset_right, + }, + )) + } else { + None + } }) - .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) - .collect::>>() - .filter(|v| !v.is_empty()) - .map(|v| v.join("\n ")); - - if let Some(big_sugg) = big_sugg { - span_lint_and_sugg( - cx, - MANUAL_MEMCPY, - expr.span, - "it looks like you're manually copying between slices", - "try replacing the loop by", - big_sugg, - Applicability::Unspecified, - ); - return true; - } + }) + .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) + .collect::>>() + .filter(|v| !v.is_empty()) + .map(|v| v.join("\n ")); + + if let Some(big_sugg) = big_sugg { + span_lint_and_sugg( + cx, + MANUAL_MEMCPY, + expr.span, + "it looks like you're manually copying between slices", + "try replacing the loop by", + big_sugg, + Applicability::Unspecified, + ); + return true; } } false diff --git a/clippy_lints/src/loops/manual_while_let_some.rs b/clippy_lints/src/loops/manual_while_let_some.rs index 4473a3343c7c6..9527e258db8ab 100644 --- a/clippy_lints/src/loops/manual_while_let_some.rs +++ b/clippy_lints/src/loops/manual_while_let_some.rs @@ -81,15 +81,15 @@ fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, } fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) { - if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind { - if let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind { - let offending_arg = args - .iter() - .find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span)); + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind + && let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind + { + let offending_arg = args + .iter() + .find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span)); - if let Some(offending_arg) = offending_arg { - report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span); - } + if let Some(offending_arg) = offending_arg { + report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span); } } } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 5afcf51167d47..3130a7f0c1a79 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -82,14 +82,14 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { - if bk == ty::BorrowKind::Mutable { - if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); - } - if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); - } + if bk == ty::BorrowKind::Mutable + && let PlaceBase::Local(id) = cmt.place.base + { + if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { + self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); + } + if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { + self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); } } } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 0f62183eb33d6..7837b18bcd36c 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -31,155 +31,154 @@ pub(super) fn check<'tcx>( ref end, limits, }) = higher::Range::hir(arg) - { // the var must be a single name - if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { - let mut visitor = VarVisitor { - cx, - var: canonical_id, - indexed_mut: FxHashSet::default(), - indexed_indirectly: FxHashMap::default(), - indexed_directly: FxIndexMap::default(), - referenced: FxHashSet::default(), - nonindex: false, - prefer_mutable: false, - }; - walk_expr(&mut visitor, body); - - // linting condition: we only indexed one variable, and indexed it directly - if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { - let (indexed, (indexed_extent, indexed_ty)) = visitor - .indexed_directly - .into_iter() - .next() - .expect("already checked that we have exactly 1 element"); + && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind + { + let mut visitor = VarVisitor { + cx, + var: canonical_id, + indexed_mut: FxHashSet::default(), + indexed_indirectly: FxHashMap::default(), + indexed_directly: FxIndexMap::default(), + referenced: FxHashSet::default(), + nonindex: false, + prefer_mutable: false, + }; + walk_expr(&mut visitor, body); - // ensure that the indexed variable was declared before the loop, see #601 - if let Some(indexed_extent) = indexed_extent { - let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id); - let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); - let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); - if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { - return; - } - } + // linting condition: we only indexed one variable, and indexed it directly + if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { + let (indexed, (indexed_extent, indexed_ty)) = visitor + .indexed_directly + .into_iter() + .next() + .expect("already checked that we have exactly 1 element"); - // don't lint if the container that is indexed does not have .iter() method - let has_iter = has_iter_method(cx, indexed_ty); - if has_iter.is_none() { + // ensure that the indexed variable was declared before the loop, see #601 + if let Some(indexed_extent) = indexed_extent { + let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id); + let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); + let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); + if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { return; } + } - // don't lint if the container that is indexed into is also used without - // indexing - if visitor.referenced.contains(&indexed) { - return; - } + // don't lint if the container that is indexed does not have .iter() method + let has_iter = has_iter_method(cx, indexed_ty); + if has_iter.is_none() { + return; + } - let starts_at_zero = is_integer_const(cx, start, 0); + // don't lint if the container that is indexed into is also used without + // indexing + if visitor.referenced.contains(&indexed) { + return; + } - let skip = if starts_at_zero { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { - return; - } else { - format!(".skip({})", snippet(cx, start.span, "..")) - }; + let starts_at_zero = is_integer_const(cx, start, 0); - let mut end_is_start_plus_val = false; + let skip = if starts_at_zero { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { + return; + } else { + format!(".skip({})", snippet(cx, start.span, "..")) + }; - let take = if let Some(end) = *end { - let mut take_expr = end; + let mut end_is_start_plus_val = false; - if let ExprKind::Binary(ref op, left, right) = end.kind { - if op.node == BinOpKind::Add { - let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); - let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); + let take = if let Some(end) = *end { + let mut take_expr = end; - if start_equal_left { - take_expr = right; - } else if start_equal_right { - take_expr = left; - } + if let ExprKind::Binary(ref op, left, right) = end.kind + && op.node == BinOpKind::Add + { + let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); + let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); - end_is_start_plus_val = start_equal_left | start_equal_right; - } + if start_equal_left { + take_expr = right; + } else if start_equal_right { + take_expr = left; } - if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { - return; - } else { - match limits { - ast::RangeLimits::Closed => { - let take_expr = sugg::Sugg::hir(cx, take_expr, ""); - format!(".take({})", take_expr + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => { - format!(".take({})", snippet(cx, take_expr.span, "..")) - }, - } - } - } else { - String::new() - }; + end_is_start_plus_val = start_equal_left | start_equal_right; + } - let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { - ("mut ", "iter_mut") + if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { + return; } else { - ("", "iter") - }; + match limits { + ast::RangeLimits::Closed => { + let take_expr = sugg::Sugg::hir(cx, take_expr, ""); + format!(".take({})", take_expr + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => { + format!(".take({})", snippet(cx, take_expr.span, "..")) + }, + } + } + } else { + String::new() + }; - let take_is_empty = take.is_empty(); - let mut method_1 = take; - let mut method_2 = skip; + let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { + ("mut ", "iter_mut") + } else { + ("", "iter") + }; - if end_is_start_plus_val { - mem::swap(&mut method_1, &mut method_2); - } + let take_is_empty = take.is_empty(); + let mut method_1 = take; + let mut method_2 = skip; - if visitor.nonindex { - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - arg.span, - format!("the loop variable `{}` is used to index `{indexed}`", ident.name), - |diag| { - diag.multipart_suggestion( - "consider using an iterator and enumerate()", - vec![ - (pat.span, format!("({}, )", ident.name)), - ( - arg.span, - format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), - ), - ], - Applicability::HasPlaceholders, - ); - }, - ); + if end_is_start_plus_val { + mem::swap(&mut method_1, &mut method_2); + } + + if visitor.nonindex { + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + arg.span, + format!("the loop variable `{}` is used to index `{indexed}`", ident.name), + |diag| { + diag.multipart_suggestion( + "consider using an iterator and enumerate()", + vec![ + (pat.span, format!("({}, )", ident.name)), + ( + arg.span, + format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), + ), + ], + Applicability::HasPlaceholders, + ); + }, + ); + } else { + let repl = if starts_at_zero && take_is_empty { + format!("&{ref_mut}{indexed}") } else { - let repl = if starts_at_zero && take_is_empty { - format!("&{ref_mut}{indexed}") - } else { - format!("{indexed}.{method}(){method_1}{method_2}") - }; + format!("{indexed}.{method}(){method_1}{method_2}") + }; - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - arg.span, - format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), - |diag| { - diag.multipart_suggestion( - "consider using an iterator", - vec![(pat.span, "".to_string()), (arg.span, repl)], - Applicability::HasPlaceholders, - ); - }, - ); - } + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + arg.span, + format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), + |diag| { + diag.multipart_suggestion( + "consider using an iterator", + vec![(pat.span, "".to_string()), (arg.span, repl)], + Applicability::HasPlaceholders, + ); + }, + ); } } } @@ -346,10 +345,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } + if let ty::Ref(_, _, mutbl) = *ty.kind() + && mutbl == Mutability::Mut + { + self.prefer_mutable = true; } self.visit_expr(expr); } @@ -361,10 +360,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { iter::once(receiver).chain(args.iter()), ) { self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } + if let ty::Ref(_, _, mutbl) = *ty.kind() + && mutbl == Mutability::Mut + { + self.prefer_mutable = true; } self.visit_expr(expr); } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index c3a2a38b5ec25..69c84bc7038ef 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -244,10 +244,10 @@ fn never_loop_expr<'tcx>( }); combine_seq(first, || { // checks if break targets a block instead of a loop - if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind { - if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) { - *reachable = true; - } + if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind + && let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) + { + *reachable = true; } NeverLoopResult::Diverging }) diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 16dd1ad4e4784..98e8b1f5cf92c 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -92,10 +92,10 @@ fn check_into_iter( && let [filter_params] = filter_body.params { if match_map_type(cx, left_expr) { - if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind { - if let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) { - make_span_lint_and_sugg(cx, parent_expr_span, sugg); - } + if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind + && let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) + { + make_span_lint_and_sugg(cx, parent_expr_span, sugg); } // Cannot lint other cases because `retain` requires two parameters } else { @@ -196,22 +196,21 @@ fn check_to_owned( && let filter_body = cx.tcx.hir_body(closure.body) && let [filter_params] = filter_body.params && msrv.meets(cx, msrvs::STRING_RETAIN) + && let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { - if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { - make_span_lint_and_sugg( - cx, - parent_expr_span, - format!( - "{}.retain(|{}| {})", - snippet(cx, left_expr.span, ".."), - snippet(cx, pat.span, ".."), - snippet(cx, filter_body.value.span, "..") - ), - ); - } - // Be conservative now. Do nothing for the `Binding` case. - // TODO: Ideally, we can rewrite the lambda by stripping one level of reference + make_span_lint_and_sugg( + cx, + parent_expr_span, + format!( + "{}.retain(|{}| {})", + snippet(cx, left_expr.span, ".."), + snippet(cx, pat.span, ".."), + snippet(cx, filter_body.value.span, "..") + ), + ); } + // Be conservative now. Do nothing for the `Binding` case. + // TODO: Ideally, we can rewrite the lambda by stripping one level of reference } fn make_sugg( diff --git a/clippy_lints/src/manual_string_new.rs b/clippy_lints/src/manual_string_new.rs index 5c2a711b5cb23..7ca3b71206671 100644 --- a/clippy_lints/src/manual_string_new.rs +++ b/clippy_lints/src/manual_string_new.rs @@ -113,15 +113,14 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, arg: &Expr<'_>) && is_expr_kind_empty_str(&arg.kind) { warn_then_suggest(cx, span); - } else if let QPath::Resolved(_, path) = qpath { + } else if let QPath::Resolved(_, path) = qpath // From::from(...) or TryFrom::try_from(...) - if let [path_seg1, path_seg2] = path.segments - && is_expr_kind_empty_str(&arg.kind) - && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) - || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)) - { - warn_then_suggest(cx, span); - } + && let [path_seg1, path_seg2] = path.segments + && is_expr_kind_empty_str(&arg.kind) + && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) + || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)) + { + warn_then_suggest(cx, span); } } } diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 56aead85e7c41..b607f8117eb89 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -101,10 +101,10 @@ fn is_unit_type(ty: Ty<'_>) -> bool { fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let ty::FnDef(id, _) = *ty.kind() { - if let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() { - return is_unit_type(fn_type.output()); - } + if let ty::FnDef(id, _) = *ty.kind() + && let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() + { + return is_unit_type(fn_type.output()); } false } diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 4cc43e427ec61..abf723fa6f4ca 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -41,10 +41,10 @@ fn get_cond_expr<'tcx>( fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's // checked by `contains_unsafe_block` - if let ExprKind::Block(block, None) = expr.kind { - if block.stmts.is_empty() { - return block.expr; - } + if let ExprKind::Block(block, None) = expr.kind + && block.stmts.is_empty() + { + return block.expr; } None } @@ -61,13 +61,13 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { // } // Returns true if resolves to `Some(x)`, `false` otherwise fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool { - if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) // there can be not statements in the block as they would be removed when switching to `.filter` - if let ExprKind::Call(callee, [arg]) = inner_expr.kind { - return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) - && path_to_local_id(arg, target); - } + && let ExprKind::Call(callee, [arg]) = inner_expr.kind + { + return ctxt == expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); } false } diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index d29d1ea3e96d9..f14b69d91ce4b 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -76,17 +76,18 @@ where && first_attrs.is_empty() && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty()) { - if let Some(last_pat) = last_pat_opt { - if !is_wild(last_pat) { - return false; - } + if let Some(last_pat) = last_pat_opt + && !is_wild(last_pat) + { + return false; } for arm in iter_without_last.clone() { - if let Some(pat) = arm.1 { - if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { - return false; - } + if let Some(pat) = arm.1 + && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) + && is_some(pat.kind) + { + return false; } } @@ -113,10 +114,10 @@ where // strip potential borrows (#6503), but only if the type is a reference let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { - ex_new = ex_inner; - } + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind + && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() + { + ex_new = ex_inner; } span_lint_and_sugg( cx, diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 864923b27739d..bca41956f1cad 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -178,24 +178,24 @@ fn sugg_with_curlies<'a>( let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); - if let Some(parent_expr) = get_parent_expr(cx, match_expr) { - if let ExprKind::Closure { .. } = parent_expr.kind { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } + if let Some(parent_expr) = get_parent_expr(cx, match_expr) + && let ExprKind::Closure { .. } = parent_expr.kind + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); } // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) { - if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } + if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) + && let ExprKind::Match(..) = arm.body.kind + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); } let assignment_str = assignment.map_or_else(String::new, |span| { diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index df1b83cbb516a..65b93a095b926 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -26,10 +26,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm && let ty::Str = ty.kind() { let mut visitor = MatchExprVisitor { cx }; - if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) { - if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { - lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); - } + if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) + && let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) + { + lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } } } diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 11b588b33554d..24b4a6758004f 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -80,18 +80,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { path }, PatKind::TupleStruct(path, patterns, ..) => { - if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { - missing_variants.retain(|e| e.ctor_def_id() != Some(id)); - } + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() + && arm.guard.is_none() + && patterns.iter().all(|p| !is_refutable(cx, p)) + { + missing_variants.retain(|e| e.ctor_def_id() != Some(id)); } path }, PatKind::Struct(path, patterns, ..) => { - if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { - missing_variants.retain(|e| e.def_id != id); - } + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() + && arm.guard.is_none() + && patterns.iter().all(|p| !is_refutable(cx, p.pat)) + { + missing_variants.retain(|e| e.def_id != id); } path }, diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index d0d2025878e48..8ce8453360f78 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -26,11 +26,12 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' if !matching_wild { // Looking for unused bindings (i.e.: `_e`) for pat in inner { - if let PatKind::Binding(_, id, ident, None) = pat.kind { - if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) { - ident_bind_name = ident.name; - matching_wild = true; - } + if let PatKind::Binding(_, id, ident, None) = pat.kind + && ident.as_str().starts_with('_') + && !is_local_used(cx, arm.body, id) + { + ident_bind_name = ident.name; + matching_wild = true; } } } diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 7e65d586110e5..6c5d7cab2036e 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -67,10 +67,10 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) for arm in arms { let arm_expr = peel_blocks_with_stmt(arm.body); - if let Some(guard_expr) = &arm.guard { - if guard_expr.can_have_side_effects() { - return false; - } + if let Some(guard_expr) = &arm.guard + && guard_expr.can_have_side_effects() + { + return false; } if let PatKind::Wild = arm.pat.kind { diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 4184f8b9e6e8a..d3136c89178e6 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -11,17 +11,17 @@ use super::MATCH_OVERLAPPING_ARM; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() { let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex)); - if !ranges.is_empty() { - if let Some((start, end)) = overlapping(&ranges) { - span_lint_and_note( - cx, - MATCH_OVERLAPPING_ARM, - start.span, - "some ranges overlap", - Some(end.span), - "overlaps with this", - ); - } + if !ranges.is_empty() + && let Some((start, end)) = overlapping(&ranges) + { + span_lint_and_note( + cx, + MATCH_OVERLAPPING_ARM, + start.span, + "some ranges overlap", + Some(end.span), + "overlaps with this", + ); } } } diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 37bac561a6e06..d7dc7604088f7 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -182,17 +182,16 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool { - if let Some(adt) = ty.ty_adt_def() { - if get_attr( + if let Some(adt) = ty.ty_adt_def() + && get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop", ) .count() > 0 - { - return true; - } + { + return true; } if !self.seen_types.insert(ty) { diff --git a/clippy_lints/src/matches/wild_in_or_pats.rs b/clippy_lints/src/matches/wild_in_or_pats.rs index b75d1ab9a7aa3..43102d78bfebd 100644 --- a/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/clippy_lints/src/matches/wild_in_or_pats.rs @@ -15,18 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { return; } for arm in arms { - if let PatKind::Or(fields) = arm.pat.kind { + if let PatKind::Or(fields) = arm.pat.kind // look for multiple fields in this arm that contains at least one Wild pattern - if fields.len() > 1 && fields.iter().any(is_wild) { - span_lint_and_help( - cx, - WILDCARD_IN_OR_PATTERNS, - arm.pat.span, - "wildcard pattern covers any other pattern as it will match anyway", - None, - "consider handling `_` separately", - ); - } + && fields.len() > 1 && fields.iter().any(is_wild) + { + span_lint_and_help( + cx, + WILDCARD_IN_OR_PATTERNS, + arm.pat.span, + "wildcard pattern covers any other pattern as it will match anyway", + None, + "consider handling `_` separately", + ); } } } diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 1e9b29f567f41..f8520c23ea503 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -192,10 +192,10 @@ impl BindInsteadOfMap { } fn is_variant(&self, cx: &LateContext<'_>, res: Res) -> bool { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) { - return cx.tcx.parent(id) == variant_id; - } + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res + && let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) + { + return cx.tcx.parent(id) == variant_id; } false } diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 1c745b7376ea4..d07870d4951e0 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -19,13 +19,13 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, msrv: Msrv, ) { - if let ExprKind::MethodCall(path_segment, ..) = recv.kind { - if matches!( + if let ExprKind::MethodCall(path_segment, ..) = recv.kind + && matches!( path_segment.ident.name.as_str(), "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" - ) { - return; - } + ) + { + return; } if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 1ee27d90d0545..2ecf3eb897988 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -40,10 +40,10 @@ pub(super) fn check( .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); let ty = cx.typeck_results().expr_ty(expr); - if let ty::Ref(_, inner, _) = arg_ty.kind() { - if let ty::Ref(..) = inner.kind() { - return; // don't report clone_on_copy - } + if let ty::Ref(_, inner, _) = arg_ty.kind() + && let ty::Ref(..) = inner.kind() + { + return; // don't report clone_on_copy } if is_copy(cx, ty) { diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index daa6e0e7f940c..f5688e370a478 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -54,10 +54,11 @@ pub(super) fn check<'tcx>( if is_type_lang_item(cx, arg_ty, hir::LangItem::String) { return false; } - if let ty::Ref(_, ty, ..) = arg_ty.kind() { - if ty.is_str() && can_be_static_str(cx, arg) { - return false; - } + if let ty::Ref(_, ty, ..) = arg_ty.kind() + && ty.is_str() + && can_be_static_str(cx, arg) + { + return false; } true } diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 7c190e123b72e..92dc43f9aa4af 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -14,15 +14,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if expr.span.in_external_macro(cx.sess().source_map()) || !receiver.span.eq_ctxt(expr.span) { return; } - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(parent) = get_parent_expr(cx, parent) { - if is_inside_always_const_context(cx.tcx, expr.hir_id) - && let Some(macro_call) = root_macro_call(parent.span) - && is_assert_macro(cx, macro_call.def_id) - { - return; - } - } + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(parent) = get_parent_expr(cx, parent) + && is_inside_always_const_context(cx.tcx, expr.hir_id) + && let Some(macro_call) = root_macro_call(parent.span) + && is_assert_macro(cx, macro_call.def_id) + { + return; } let init_expr = expr_or_init(cx, receiver); if !receiver.span.eq_ctxt(init_expr.span) { diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 9b358235a40df..90d5d9df55eed 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -8,14 +8,14 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) { - if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { - span_lint( - cx, - ITERATOR_STEP_BY_ZERO, - expr.span, - "`Iterator::step_by(0)` will panic at runtime", - ); - } + if is_trait_method(cx, expr, sym::Iterator) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) + { + span_lint( + cx, + ITERATOR_STEP_BY_ZERO, + expr.span, + "`Iterator::step_by(0)` will panic at runtime", + ); } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 13918ed11b87d..18978a1d2bc86 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -106,15 +106,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { }; let check_lit = |expr: &hir::Expr<'_>, check_min: bool| { - if let hir::ExprKind::Lit(lit) = &expr.kind { - if let ast::LitKind::Int(value, _) = lit.node { - if value == maxval { - return Some(MinMax::Max); - } - - if check_min && value == minval { - return Some(MinMax::Min); - } + if let hir::ExprKind::Lit(lit) = &expr.kind + && let ast::LitKind::Int(value, _) = lit.node + { + if value == maxval { + return Some(MinMax::Max); + } + + if check_min && value == minval { + return Some(MinMax::Min); } } @@ -125,10 +125,10 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { return r; } - if ty.is_signed() { - if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind { - return check_lit(val, true); - } + if ty.is_signed() + && let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind + { + return check_lit(val, true); } None diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 50b7578085fa2..333a33f7527d6 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -51,19 +51,19 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ let closure_expr = peel_blocks(closure_body.value); match closure_body.params[0].pat.kind { hir::PatKind::Ref(inner, Mutability::Not) => { - if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind { - if ident_eq(name, closure_expr) { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } + if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind + && ident_eq(name, closure_expr) + { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); } }, hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) => { match closure_expr.kind { hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { - if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } + if ident_eq(name, inner) + && let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() + { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); } }, hir::ExprKind::MethodCall(method, obj, [], _) => { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fad583169b2ea..5b61128b0121c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4667,11 +4667,12 @@ impl_lint_pass!(Methods => [ pub fn method_call<'tcx>( recv: &'tcx Expr<'tcx>, ) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { - if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { - if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { - let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span, call_span)); - } + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind + && !args.iter().any(|e| e.span.from_expansion()) + && !receiver.span.from_expansion() + { + let name = path.ident.name.as_str(); + return Some((name, receiver, args, path.ident.span, call_span)); } None } diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 56ff7e2c61b22..4b3fd452954fa 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -377,20 +377,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if let Some(hir_id) = path_to_local(recv) { - if let Some(index) = self.hir_id_uses_map.remove(&hir_id) { - if self - .illegal_mutable_capture_ids - .intersection(&self.current_mutably_captured_ids) - .next() - .is_none() - { - if let Some(hir_id) = self.current_statement_hir_id { - self.hir_id_uses_map.insert(hir_id, index); - } - } else { - self.uses[index] = None; + if let Some(hir_id) = path_to_local(recv) + && let Some(index) = self.hir_id_uses_map.remove(&hir_id) + { + if self + .illegal_mutable_capture_ids + .intersection(&self.current_mutably_captured_ids) + .next() + .is_none() + { + if let Some(hir_id) = self.current_statement_hir_id { + self.hir_id_uses_map.insert(hir_id, index); } + } else { + self.uses[index] = None; } } } diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 88b9c69f6f949..cd1b97f3c51b5 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -9,26 +9,27 @@ use super::NEEDLESS_OPTION_TAKE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place - if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) { - if let Some(function_name) = source_of_temporary_value(recv) { - span_lint_and_then( - cx, - NEEDLESS_OPTION_TAKE, - expr.span, - "called `Option::take()` on a temporary value", - |diag| { - diag.note(format!( - "`{function_name}` creates a temporary value, so calling take() has no effect" - )); - diag.span_suggestion( - expr.span.with_lo(recv.span.hi()), - "remove", - "", - Applicability::MachineApplicable, - ); - }, - ); - } + if !recv.is_syntactic_place_expr() + && is_expr_option(cx, recv) + && let Some(function_name) = source_of_temporary_value(recv) + { + span_lint_and_then( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "called `Option::take()` on a temporary value", + |diag| { + diag.note(format!( + "`{function_name}` creates a temporary value, so calling take() has no effect" + )); + diag.span_suggestion( + expr.span.with_lo(recv.span.hi()), + "remove", + "", + Applicability::MachineApplicable, + ); + }, + ); } } @@ -44,10 +45,10 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> { match expr.peel_borrows().kind { ExprKind::Call(function, _) => { - if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind { - if !func_path.segments.is_empty() { - return Some(func_path.segments[0].ident.name.as_str()); - } + if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind + && !func_path.segments.is_empty() + { + return Some(func_path.segments[0].ident.name.as_str()); } if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind { return Some(func_path_segment.ident.name.as_str()); diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs index 28bf2d5aeec5b..96180862fd0dc 100644 --- a/clippy_lints/src/methods/seek_from_current.rs +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -15,21 +15,22 @@ use super::SEEK_FROM_CURRENT; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) { - if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); + if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) + && implements_trait(cx, ty, def_id, &[]) + && arg_is_seek_from_current(cx, arg) + { + let mut applicability = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEEK_FROM_CURRENT, - expr.span, - "using `SeekFrom::Current` to start from current position", - "replace with", - format!("{snip}.stream_position()"), - applicability, - ); - } + span_lint_and_sugg( + cx, + SEEK_FROM_CURRENT, + expr.span, + "using `SeekFrom::Current` to start from current position", + "replace with", + format!("{snip}.stream_position()"), + applicability, + ); } } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 4ccefb7ec9d77..d183457da25a2 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -238,15 +238,14 @@ fn indirect_usage<'tcx>( unwrap_kind: Some(unwrap_kind), .. } = iter_usage + && parent_id == local_hir_id { - if parent_id == local_hir_id { - return Some(IndirectUsage { - name: ident.name, - span: stmt.span, - init_expr, - unwrap_kind, - }); - } + return Some(IndirectUsage { + name: ident.name, + span: stmt.span, + init_expr, + unwrap_kind, + }); } } diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 1bd48525f12d8..788014d9bb632 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -13,11 +13,11 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr< && let closure_body = cx.tcx.hir_body(closure.body) && !cx.typeck_results().expr_ty(closure_body.value).is_unit() { - if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) { + if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) // A variable is used mutably inside of the closure. Suppress the lint. - if !map_mutated_vars.is_empty() { - return; - } + && !map_mutated_vars.is_empty() + { + return; } span_lint_and_help( cx, diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 9f4080100da20..e3596cf6a60c9 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -23,56 +23,56 @@ pub(super) fn check<'tcx>( let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); - if is_option || is_result || is_bool { - if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind { - let body = cx.tcx.hir_body(body); - let body_expr = &body.value; + if (is_option || is_result || is_bool) + && let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind + { + let body = cx.tcx.hir_body(body); + let body_expr = &body.value; - if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { - return false; - } + if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { + return false; + } - if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { - let msg = if is_option { - "unnecessary closure used to substitute value for `Option::None`" - } else if is_result { - "unnecessary closure used to substitute value for `Result::Err`" - } else { - "unnecessary closure used with `bool::then`" - }; - let applicability = if body - .params - .iter() - // bindings are checked to be unused above - .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - && matches!( - fn_decl.output, - FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) - ) { - Applicability::MachineApplicable - } else { - // replacing the lambda may break type inference - Applicability::MaybeIncorrect - }; + if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else if is_result { + "unnecessary closure used to substitute value for `Result::Err`" + } else { + "unnecessary closure used with `bool::then`" + }; + let applicability = if body + .params + .iter() + // bindings are checked to be unused above + .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) + && matches!( + fn_decl.output, + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) + ) { + Applicability::MachineApplicable + } else { + // replacing the lambda may break type inference + Applicability::MaybeIncorrect + }; - // This is a duplicate of what's happening in clippy_lints::methods::method_call, - // which isn't ideal, We want to get the method call span, - // but prefer to avoid changing the signature of the function itself. - if let hir::ExprKind::MethodCall(.., span) = expr.kind { - span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { - diag.span_suggestion_verbose( - span, - format!("use `{simplify_using}` instead"), - format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), - applicability, - ); - }); - return true; - } + // This is a duplicate of what's happening in clippy_lints::methods::method_call, + // which isn't ideal, We want to get the method call span, + // but prefer to avoid changing the signature of the function itself. + if let hir::ExprKind::MethodCall(.., span) = expr.kind { + span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { + diag.span_suggestion_verbose( + span, + format!("use `{simplify_using}` instead"), + format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), + applicability, + ); + }); + return true; } } } diff --git a/clippy_lints/src/misc_early/builtin_type_shadow.rs b/clippy_lints/src/misc_early/builtin_type_shadow.rs index 662f7cd8500cf..9ee1e2f3fd17a 100644 --- a/clippy_lints/src/misc_early/builtin_type_shadow.rs +++ b/clippy_lints/src/misc_early/builtin_type_shadow.rs @@ -6,14 +6,14 @@ use rustc_lint::EarlyContext; use super::BUILTIN_TYPE_SHADOW; pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) { - if let GenericParamKind::Type { .. } = param.kind { - if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { - span_lint( - cx, - BUILTIN_TYPE_SHADOW, - param.ident.span, - format!("this generic shadows the built-in type `{}`", prim_ty.name()), - ); - } + if let GenericParamKind::Type { .. } = param.kind + && let Some(prim_ty) = PrimTy::from_name(param.ident.name) + { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + format!("this generic shadows the built-in type `{}`", prim_ty.name()), + ); } } diff --git a/clippy_lints/src/misc_early/redundant_pattern.rs b/clippy_lints/src/misc_early/redundant_pattern.rs index d5b5b2bf2dd1b..3cb51671aaf18 100644 --- a/clippy_lints/src/misc_early/redundant_pattern.rs +++ b/clippy_lints/src/misc_early/redundant_pattern.rs @@ -6,20 +6,20 @@ use rustc_lint::EarlyContext; use super::REDUNDANT_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind { - if let PatKind::Wild = right.kind { - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN, - pat.span, - format!( - "the `{} @ _` pattern can be written as just `{}`", - ident.name, ident.name, - ), - "try", - format!("{}{}", ann.prefix_str(), ident.name), - Applicability::MachineApplicable, - ); - } + if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind + && let PatKind::Wild = right.kind + { + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN, + pat.span, + format!( + "the `{} @ _` pattern can be written as just `{}`", + ident.name, ident.name, + ), + "try", + format!("{}{}", ann.prefix_str(), ident.name), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs index 00f46629f102c..fffaf40c9d141 100644 --- a/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -7,30 +7,30 @@ use rustc_span::Span; use super::UNNEEDED_WILDCARD_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { - if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { - if let Some((left_index, left_pat)) = patterns[..rest_index] - .iter() - .rev() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); - } + if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind + && let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) + { + if let Some((left_index, left_pat)) = patterns[..rest_index] + .iter() + .rev() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); + } - if let Some((right_index, right_pat)) = patterns[rest_index + 1..] - .iter() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint( - cx, - patterns[rest_index].span.shrink_to_hi().to(right_pat.span), - right_index == 0, - ); - } + if let Some((right_index, right_pat)) = patterns[rest_index + 1..] + .iter() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint( + cx, + patterns[rest_index].span.shrink_to_hi().to(right_pat.span), + right_index == 0, + ); } } } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index d52fe7e7d5b9c..394bc4aef1cc7 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -111,10 +111,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { // Checks if impl_param_name is the same as one of type_param_names, // and is in a different position fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool { - if let Some(j) = type_param_names.get(impl_param_name) { - if i != *j { - return true; - } + if let Some(j) = type_param_names.get(impl_param_name) + && i != *j + { + return true; } false } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 38a19dd2999bb..5ac66b4c7e84b 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -139,12 +139,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { // Const fns are not allowed as methods in a trait. { let parent = cx.tcx.hir_get_parent_item(hir_id).def_id; - if parent != CRATE_DEF_ID { - if let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) { - if let hir::ItemKind::Trait(..) = &item.kind { - return; - } - } + if parent != CRATE_DEF_ID + && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) + && let hir::ItemKind::Trait(..) = &item.kind + { + return; } } diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 28dc242742842..05517a5d869c9 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -224,11 +224,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { // NB: can't call cx.typeck_results() as we are not in a body && let typeck_results = cx.tcx.typeck_body(*body_id) && should_lint(cx, typeck_results, block) - { // we intentionally only lint structs, see lint description - if let ItemKind::Struct(_, data, _) = &self_item.kind { - check_struct(cx, typeck_results, block, self_ty, item, data); - } + && let ItemKind::Struct(_, data, _) = &self_item.kind + { + check_struct(cx, typeck_results, block, self_ty, item, data); } } } diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index f49e03ea76528..1f613171b46e8 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -160,12 +160,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; - if let Some(trait_def_id) = trait_def_id { - if trait_def_id.is_local() && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - // If a trait is being implemented for an item, and the - // trait is not exported, we don't need #[inline] - return; - } + if let Some(trait_def_id) = trait_def_id + && trait_def_id.is_local() + && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + { + // If a trait is being implemented for an item, and the + // trait is not exported, we don't need #[inline] + return; } let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 274438ac0491e..fd0170604362c 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -135,10 +135,10 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> { } fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { - if let Some(macro_call) = root_macro_call_first_node(self.cx, e) { - if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { - return; - } + if let Some(macro_call) = root_macro_call_first_node(self.cx, e) + && self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" + { + return; } span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges"); } @@ -372,10 +372,10 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { /// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`. fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::Assign(lhs, ..) = parent.kind { - return lhs.hir_id == expr.hir_id; - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Assign(lhs, ..) = parent.kind + { + return lhs.hir_id == expr.hir_id; } false } diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 676d608eb318c..8065a0a359b49 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -119,22 +119,22 @@ impl EarlyLintPass for ModStyle { } for folder in &folder_segments { - if !mod_folders.contains(folder) { - if let Some((file, path)) = file_map.get(folder) { - span_lint_and_then( - cx, - SELF_NAMED_MODULE_FILES, - Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - format!("`mod.rs` files are required, found `{}`", path.display()), - |diag| { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); - }, - ); - } + if !mod_folders.contains(folder) + && let Some((file, path)) = file_map.get(folder) + { + span_lint_and_then( + cx, + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), + format!("`mod.rs` files are required, found `{}`", path.display()), + |diag| { + let mut correct = path.to_path_buf(); + correct.pop(); + correct.push(folder); + correct.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); + }, + ); } } } diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 2adc27c0b709a..c6c27e22b90e5 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -142,10 +142,9 @@ fn collect_unsafe_exprs<'tcx>( .typeck_results() .type_dependent_def_id(expr.hir_id) .map(|def_id| cx.tcx.fn_sig(def_id)) + && sig.skip_binder().safety().is_unsafe() { - if sig.skip_binder().safety().is_unsafe() { - unsafe_ops.push(("unsafe method call occurs here", expr.span)); - } + unsafe_ops.push(("unsafe method call occurs here", expr.span)); } }, diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 7abc5870d00e0..a45031ce22b91 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -82,10 +82,10 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { - if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { - if trait_ref_of_method(cx, item.owner_id.def_id).is_none() { - self.check_sig(cx, item.owner_id.def_id, sig.decl); - } + if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind + && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + { + self.check_sig(cx, item.owner_id.def_id, sig.decl); } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 3c4ba5141dd9b..d98c70e7f5a85 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -77,16 +77,16 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - if ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { - span_lint_hir( - self.cx, - MUT_MUT, - expr.hir_id, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); - } + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() + && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) + { + span_lint_hir( + self.cx, + MUT_MUT, + expr.hir_id, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); } } } diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 3d943bf5b370e..d5bed54f0be2f 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -97,14 +97,13 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> { return; }, ExprKind::Path(_) => { - if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { - if adj + if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) + && adj .iter() .any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut))) - { - self.found = true; - return; - } + { + self.found = true; + return; } }, // Don't check await desugars diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 49fd29d1dd6dc..fe2157ca533a6 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -91,19 +91,19 @@ declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); impl<'tcx> LateLintPass<'tcx> for Mutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind() { - if is_type_diagnostic_item(cx, ty, sym::Mutex) { - let mutex_param = subst.type_at(0); - if let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ + if let ty::Adt(_, subst) = ty.kind() + && is_type_diagnostic_item(cx, ty, sym::Mutex) + { + let mutex_param = subst.type_at(0); + if let Some(atomic_name) = get_atomic_name(mutex_param) { + let msg = format!( + "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ behavior and not the internal type, consider using `Mutex<()>`" - ); - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - } + ); + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), } } } diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 0e648c5cb2bc5..f768e11a4a2bb 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -426,10 +426,10 @@ fn fetch_bool_block(expr: &Expr<'_>) -> Option { } fn fetch_bool_expr(expr: &Expr<'_>) -> Option { - if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind { - if let LitKind::Bool(value) = lit_ptr.node { - return Some(value); - } + if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind + && let LitKind::Bool(value) = lit_ptr.node + { + return Some(value); } None } diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 576bb27b254c9..3a520c5a7bdd4 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -86,11 +86,11 @@ fn should_skip<'tcx>( return false; } - if let PatKind::Binding(.., name, _) = arg.pat.kind { + if let PatKind::Binding(.., name, _) = arg.pat.kind // If it's a potentially unused variable, we don't check it. - if name.name == kw::Underscore || name.as_str().starts_with('_') { - return true; - } + && (name.name == kw::Underscore || name.as_str().starts_with('_')) + { + return true; } // All spans generated from a proc-macro invocation are the same... @@ -164,13 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { }; // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); @@ -353,10 +353,10 @@ impl MutablyUsedVariablesCtxt<'_> { for (parent, node) in self.tcx.hir_parent_iter(item) { if let Some(fn_sig) = self.tcx.hir_fn_sig_by_hir_id(parent) { return fn_sig.header.is_unsafe(); - } else if let Node::Block(block) = node { - if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { - return true; - } + } else if let Node::Block(block) = node + && matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) + { + return true; } } false @@ -426,10 +426,10 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { // upon! self.add_mutably_used_var(*vid); } - } else if borrow == ty::BorrowKind::Immutable { + } else if borrow == ty::BorrowKind::Immutable // If there is an `async block`, it'll contain a call to a closure which we need to // go into to ensure all "mutate" checks are found. - if let Node::Expr(Expr { + && let Node::Expr(Expr { kind: ExprKind::Call( _, @@ -442,9 +442,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { ), .. }) = self.tcx.hir_node(cmt.hir_id) - { - self.async_closures.insert(*def_id); - } + { + self.async_closures.insert(*def_id); } } @@ -460,10 +459,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && !projections.is_empty() { - if !projections.is_empty() { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } } @@ -477,10 +475,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && self.is_in_unsafe_block(id) { - if self.is_in_unsafe_block(id) { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } self.prev_bind = None; } @@ -499,15 +496,14 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && let FakeReadCause::ForLet(Some(inner)) = cause { - if let FakeReadCause::ForLet(Some(inner)) = cause { - // Seems like we are inside an async function. We need to store the closure `DefId` - // to go through it afterwards. - self.async_closures.insert(inner); - self.add_alias(cmt.hir_id, *vid); - self.prev_move_to_closure.insert(*vid); - self.prev_bind = None; - } + // Seems like we are inside an async function. We need to store the closure `DefId` + // to go through it afterwards. + self.async_closures.insert(inner); + self.add_alias(cmt.hir_id, *vid); + self.prev_move_to_closure.insert(*vid); + self.prev_bind = None; } } @@ -522,10 +518,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && self.is_in_unsafe_block(id) { - if self.is_in_unsafe_block(id) { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } } } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 55ca875edcee6..0c5c624c378ef 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -98,13 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } // Allow `Borrow` or functions to be taken by value @@ -197,20 +197,18 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { { // Dereference suggestion let sugg = |diag: &mut Diag<'_, ()>| { - if let ty::Adt(def, ..) = ty.kind() { - if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { - if type_allowed_to_implement_copy( - cx.tcx, - cx.param_env, - ty, - traits::ObligationCause::dummy_with_span(span), - rustc_hir::Safety::Safe, - ) - .is_ok() - { - diag.span_help(span, "or consider marking this type as `Copy`"); - } - } + if let ty::Adt(def, ..) = ty.kind() + && let Some(span) = cx.tcx.hir().span_if_local(def.did()) + && type_allowed_to_implement_copy( + cx.tcx, + cx.param_env, + ty, + traits::ObligationCause::dummy_with_span(span), + rustc_hir::Safety::Safe, + ) + .is_ok() + { + diag.span_help(span, "or consider marking this type as `Copy`"); } if is_type_diagnostic_item(cx, ty, sym::Vec) @@ -254,29 +252,28 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - if is_type_lang_item(cx, ty, LangItem::String) { - if let Some(clone_spans) = + if is_type_lang_item(cx, ty, LangItem::String) + && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) - { + { + diag.span_suggestion( + input.span, + "consider changing the type to", + "&str", + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { diag.span_suggestion( - input.span, - "consider changing the type to", - "&str", + span, + span.get_source_text(cx) + .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), + suggestion, Applicability::Unspecified, ); - - for (span, suggestion) in clone_spans { - diag.span_suggestion( - span, - span.get_source_text(cx) - .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), - suggestion, - Applicability::Unspecified, - ); - } - - return; } + + return; } diag.span_suggestion_verbose( diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 0cba72bd2c6a9..4a86c3720ca24 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -53,17 +53,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() - && !def.variant(0_usize.into()).is_field_list_non_exhaustive() - { - span_lint( - cx, - NEEDLESS_UPDATE, - base.span, - "struct update has no effect, all the fields in the struct have already been specified", - ); - } + if let ty::Adt(def, _) = ty.kind() + && fields.len() == def.non_enum_variant().fields.len() + && !def.variant(0_usize.into()).is_field_list_non_exhaustive() + { + span_lint( + cx, + NEEDLESS_UPDATE, + base.span, + "struct update has no effect, all the fields in the struct have already been specified", + ); } } } diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 86eb833becc20..07924e67765bc 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -35,14 +35,14 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, left, right) = e.kind { - if BinOpKind::Mul == op.node { - match (&left.kind, &right.kind) { - (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), - _ => {}, - } + if let ExprKind::Binary(ref op, left, right) = e.kind + && BinOpKind::Mul == op.node + { + match (&left.kind, &right.kind) { + (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), + _ => {}, } } } diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index f0ee613791fb9..c4e5498d5ee62 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -99,10 +99,10 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { let mut impls = HirIdSet::default(); cx.tcx.for_each_impl(default_trait_id, |d| { let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() { - if let Some(local_def_id) = ty_def.did().as_local() { - impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); - } + if let Some(ty_def) = ty.ty_adt_def() + && let Some(local_def_id) = ty_def.did().as_local() + { + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); } }); self.impling_types = Some(impls); diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 7187a8f2c11a1..7ab7976d5697a 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -182,23 +182,22 @@ impl NoEffect { ); return true; } - } else if let StmtKind::Let(local) = stmt.kind { - if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) - && !matches!(local.source, LocalSource::AsyncFn) - && let Some(init) = local.init - && local.els.is_none() - && !local.pat.span.from_expansion() - && has_no_effect(cx, init) - && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind - && ident.name.to_ident_string().starts_with('_') - && !in_automatically_derived(cx.tcx, local.hir_id) - { - if let Some(l) = self.local_bindings.last_mut() { - l.push(hir_id); - self.underscore_bindings.insert(hir_id, ident.span); - } - return true; + } else if let StmtKind::Let(local) = stmt.kind + && !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) + && !matches!(local.source, LocalSource::AsyncFn) + && let Some(init) = local.init + && local.els.is_none() + && !local.pat.span.from_expansion() + && has_no_effect(cx, init) + && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind + && ident.name.to_ident_string().starts_with('_') + && !in_automatically_derived(cx.tcx, local.hir_id) + { + if let Some(l) = self.local_bindings.last_mut() { + l.push(hir_id); + self.underscore_bindings.insert(hir_id, ident.span); } + return true; } false } diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index 16c4391c0fbea..635f5678e2a65 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -82,11 +82,10 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit if let ty::Adt(adt_def, _) = receiver_ty.kind() && adt_def.is_struct() && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) + && let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - let arg_snippet = get_arg_snippet(cx, arg, rcv_path); - suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); - } + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); } } } diff --git a/clippy_lints/src/operators/modulo_one.rs b/clippy_lints/src/operators/modulo_one.rs index fc5565e821edd..2e6a071eb1848 100644 --- a/clippy_lints/src/operators/modulo_one.rs +++ b/clippy_lints/src/operators/modulo_one.rs @@ -12,15 +12,15 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); } - if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { - if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { - span_lint( - cx, - MODULO_ONE, - expr.span, - "any number modulo -1 will panic/overflow or result in 0", - ); - } + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() + && is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) + { + span_lint( + cx, + MODULO_ONE, + expr.span, + "any number modulo -1 will panic/overflow or result in 0", + ); } } } diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index c261fd9bd9cb0..d0aa5740713c0 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -75,10 +75,10 @@ impl Context { hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => { let body_span = cx.tcx.hir().span_with_body(body_owner); - if let Some(span) = self.const_span { - if span.contains(body_span) { - return; - } + if let Some(span) = self.const_span + && span.contains(body_span) + { + return; } self.const_span = Some(body_span); }, @@ -90,10 +90,10 @@ impl Context { let body_owner = cx.tcx.hir_body_owner(body.id()); let body_span = cx.tcx.hir().span_with_body(body_owner); - if let Some(span) = self.const_span { - if span.contains(body_span) { - return; - } + if let Some(span) = self.const_span + && span.contains(body_span) + { + return; } self.const_span = None; } diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index 6bbc76cbb2a13..0faa7b9e64665 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -47,12 +47,11 @@ pub(crate) fn check<'tcx>( let rty = cx.typeck_results().expr_ty(r); let lcpy = is_copy(cx, lty); let rcpy = is_copy(cx, rty); - if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { - if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) - || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) - { - return; // Don't lint - } + if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) + && ((are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) + || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))) + { + return; // Don't lint } // either operator autorefs or both args are copyable if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) { diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 0a8e288564875..5d30b66def2c8 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -178,19 +178,18 @@ impl PassByRefOrValue { && size <= self.ref_min_size && let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind { - if let Some(typeck) = cx.maybe_typeck_results() { + if let Some(typeck) = cx.maybe_typeck_results() // Don't lint if a raw pointer is created. // TODO: Limit the check only to raw pointers to the argument (or part of the argument) // which escape the current function. - if typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr()) + && (typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr()) || typeck .adjustments() .items() .flat_map(|(_, a)| a) - .any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer))) - { - continue; - } + .any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer)))) + { + continue; } let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) { "self".into() @@ -282,12 +281,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } let attrs = cx.tcx.hir_attrs(hir_id); for a in attrs { - if let Some(meta_items) = a.meta_item_list() { - if a.has_name(sym::proc_macro_derive) - || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always)) - { - return; - } + if let Some(meta_items) = a.meta_item_list() + && (a.has_name(sym::proc_macro_derive) + || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))) + { + return; } } }, @@ -296,13 +294,13 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } self.check_poly_fn(cx, def_id, decl, Some(span)); diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index b653b459b04c8..35caac855cf61 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -173,16 +173,15 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let Some(mut searcher) = self.searcher.take() { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind - && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) - && name.ident.as_str() == "push" - { - searcher.err_span = searcher.err_span.to(stmt.span); - searcher.arg = Some(*arg_expr); - searcher.display_err(cx); - } + if let Some(mut searcher) = self.searcher.take() + && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind + && path_to_local_id(self_arg, searcher.local_id) + && name.ident.as_str() == "push" + { + searcher.err_span = searcher.err_span.to(stmt.span); + searcher.arg = Some(*arg_expr); + searcher.display_err(cx); } } diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 8f1a1ee76c6a6..4c5b8a315d020 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -177,17 +177,16 @@ fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mut PatKind::Or([p, ..]) => p, _ => p, }; - if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) { - if let [first, ..] = **adjustments { - if let ty::Ref(.., mutability) = *first.kind() { - let level = if p.hir_id == pat.hir_id { - Level::Top - } else { - Level::Lower - }; - result = Some((p.span, mutability, level)); - } - } + if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) + && let [first, ..] = **adjustments + && let ty::Ref(.., mutability) = *first.kind() + { + let level = if p.hir_id == pat.hir_id { + Level::Top + } else { + Level::Lower + }; + result = Some((p.span, mutability, level)); } result.is_none() }); diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 68ae575c9063f..7f74a2fff9f20 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -77,10 +77,10 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { // If the given expression is a cast from a usize, return the lhs of the cast fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind { - if is_expr_ty_usize(cx, cast_lhs_expr) { - return Some(cast_lhs_expr); - } + if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind + && is_expr_ty_usize(cx, cast_lhs_expr) + { + return Some(cast_lhs_expr); } None } @@ -91,14 +91,14 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind { - if is_expr_ty_raw_ptr(cx, arg_0) { - if path_segment.ident.name == sym::offset { - return Some((arg_0, arg_1, Method::Offset)); - } - if path_segment.ident.name.as_str() == "wrapping_offset" { - return Some((arg_0, arg_1, Method::WrappingOffset)); - } + if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind + && is_expr_ty_raw_ptr(cx, arg_0) + { + if path_segment.ident.name == sym::offset { + return Some((arg_0, arg_1, Method::Offset)); + } + if path_segment.ident.name.as_str() == "wrapping_offset" { + return Some((arg_0, arg_1, Method::WrappingOffset)); } } None diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 83c36d71a9c7f..d292ed86ea4c6 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -179,10 +179,10 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, l, r) = expr.kind { - if self.msrv.meets(cx, msrvs::RANGE_CONTAINS) { - check_possible_range_contains(cx, op.node, l, r, expr, expr.span); - } + if let ExprKind::Binary(ref op, l, r) = expr.kind + && self.msrv.meets(cx, msrvs::RANGE_CONTAINS) + { + check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } check_exclusive_range_plus_one(cx, expr); @@ -327,18 +327,18 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> inc: inclusive, }); } - } else if let Some(id) = path_to_local(r) { - if let Some(c) = ConstEvalCtxt::new(cx).eval(l) { - return Some(RangeBounds { - val: c, - expr: l, - id, - name_span: r.span, - val_span: l.span, - ord: ordering.reverse(), - inc: inclusive, - }); - } + } else if let Some(id) = path_to_local(r) + && let Some(c) = ConstEvalCtxt::new(cx).eval(l) + { + return Some(RangeBounds { + val: c, + expr: l, + id, + name_span: r.span, + val_span: l.span, + ord: ordering.reverse(), + inc: inclusive, + }); } } None diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index cfa622aea582f..c9c1a095937b0 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -109,10 +109,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } - if let ty::Adt(def, _) = arg_ty.kind() { - if def.is_manually_drop() { - continue; - } + if let ty::Adt(def, _) = arg_ty.kind() + && def.is_manually_drop() + { + continue; } // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }` @@ -191,11 +191,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { // cloned value is used, and the clone is modified or moved continue; - } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { + } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc // cloned value is mutated, and the clone is alive. - if possible_borrower.local_is_alive_at(ret_local, loc) { - continue; - } + && possible_borrower.local_is_alive_at(ret_local, loc) + { + continue; } clone_usage }; @@ -216,14 +216,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let call_snip = &snip[dot + 1..]; // Machine applicable when `call_snip` looks like `foobar()` - if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { - if call_snip + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) + && call_snip .as_bytes() .iter() .all(|b| b.is_ascii_alphabetic() || *b == b'_') - { - app = Applicability::MachineApplicable; - } + { + app = Applicability::MachineApplicable; } span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 7038b19d27596..1117dea703c2a 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -135,25 +135,24 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { }; diag.span_suggestion(expr.span, help_msg, sugg, app); }); - } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { - if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( + } else if let Some(target_id) = cx.tcx.lang_items().deref_target() + && let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( cx.typing_env(), Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), - ) { - if deref_ty == expr_ty { - let (lint, msg) = DEREF_BY_SLICING_LINT; - span_lint_and_then(cx, lint, expr.span, msg, |diag| { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - } else { - format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - }; - diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); - }); - } - } + ) + && deref_ty == expr_ty + { + let (lint, msg) = DEREF_BY_SLICING_LINT; + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if needs_parens_for_prefix { + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + } else { + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + }; + diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); + }); } } } diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 6a0dfde2d9c9c..a8c6518b592ba 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -32,28 +32,28 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) { - if did == visit_did { - let mut seen_str = None; - let mut seen_string = None; - for item in *items { - match item.ident.as_str() { - "visit_str" => seen_str = Some(item.span), - "visit_string" => seen_string = Some(item.span), - _ => {}, - } - } - if let Some(span) = seen_string { - if seen_str.is_none() { - span_lint( - cx, - SERDE_API_MISUSE, - span, - "you should not implement `visit_string` without also implementing `visit_str`", - ); - } + if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) + && did == visit_did + { + let mut seen_str = None; + let mut seen_string = None; + for item in *items { + match item.ident.as_str() { + "visit_str" => seen_str = Some(item.span), + "visit_string" => seen_string = Some(item.span), + _ => {}, } } + if let Some(span) = seen_string + && seen_str.is_none() + { + span_lint( + cx, + SERDE_API_MISUSE, + span, + "you should not implement `visit_string` without also implementing `visit_str`", + ); + } } } } diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 3795c7483da4d..8194552c53f95 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -167,10 +167,10 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); - if let Some(first_scope) = scope_tree.var_scope(first) { - if let Some(second_scope) = scope_tree.var_scope(second) { - return scope_tree.is_subscope_of(second_scope, first_scope); - } + if let Some(first_scope) = scope_tree.var_scope(first) + && let Some(second_scope) = scope_tree.var_scope(second) + { + return scope_tree.is_subscope_of(second_scope, first_scope); } false diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index e9db7c9d031a9..c705609c8405e 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -181,10 +181,10 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { } } for generic_arg in *b { - if let GenericArgKind::Type(ty) = generic_arg.unpack() { - if self.has_sig_drop_attr(ty) { - return true; - } + if let GenericArgKind::Type(ty) = generic_arg.unpack() + && self.has_sig_drop_attr(ty) + { + return true; } } false diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs index 50a6ee316c8a6..8c34da0d14a4d 100644 --- a/clippy_lints/src/single_char_lifetime_names.rs +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -45,19 +45,20 @@ impl EarlyLintPass for SingleCharLifetimeNames { return; } - if let GenericParamKind::Lifetime = param.kind { - if !param.is_placeholder && param.ident.as_str().len() <= 2 { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - ctx, - SINGLE_CHAR_LIFETIME_NAMES, - param.ident.span, - "single-character lifetime names are likely uninformative", - |diag| { - diag.help("use a more informative name"); - }, - ); - } + if let GenericParamKind::Lifetime = param.kind + && !param.is_placeholder + && param.ident.as_str().len() <= 2 + { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + ctx, + SINGLE_CHAR_LIFETIME_NAMES, + param.ident.span, + "single-character lifetime names are likely uninformative", + |diag| { + diag.help("use a more informative name"); + }, + ); } } } diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index fa08245350429..82f2f30676868 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -204,17 +204,17 @@ impl SingleComponentPathImports { if let UseTreeKind::Nested { items, .. } = &use_tree.kind { for tree in items { let segments = &tree.0.prefix.segments; - if segments.len() == 1 { - if let UseTreeKind::Simple(None) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push(SingleUse { - name, - span: tree.0.span, - item_id: item.id, - can_suggest: false, - }); - } + if segments.len() == 1 + && let UseTreeKind::Simple(None) = tree.0.kind + { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: false, + }); } } } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 27c548bed9f64..dd819510f84ca 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -162,13 +162,12 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { if is_string(cx, left) { if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { let parent = get_parent_expr(cx, e); - if let Some(p) = parent { - if let ExprKind::Assign(target, _, _) = p.kind { + if let Some(p) = parent + && let ExprKind::Assign(target, _, _) = p.kind // avoid duplicate matches - if SpanlessEq::new(cx).eq_expr(target, left) { - return; - } - } + && SpanlessEq::new(cx).eq_expr(target, left) + { + return; } } span_lint( diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index d1486c2c246e1..caab4e66a70c8 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -269,12 +269,11 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< if let ExprKind::Assign(lhs, rhs, _) = expr.kind { return Some((ExprOrIdent::Expr(lhs), rhs)); } - } else if let StmtKind::Let(expr) = stmt.kind { - if let Some(rhs) = expr.init { - if let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind { - return Some((ExprOrIdent::Ident(ident_l), rhs)); - } - } + } else if let StmtKind::Let(expr) = stmt.kind + && let Some(rhs) = expr.init + && let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind + { + return Some((ExprOrIdent::Ident(ident_l), rhs)); } None } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index fa36c9a21f65e..8aac3a5910294 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -151,20 +151,19 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .iter() .filter_map(get_trait_info_from_bound) .for_each(|(trait_item_res, trait_item_segments, span)| { - if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { - if SpanlessEq::new(cx) + if let Some(self_segments) = self_bounds_map.get(&trait_item_res) + && SpanlessEq::new(cx) .paths_by_resolution() .eq_path_segments(self_segments, trait_item_segments) - { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in trait declaration", - None, - "consider removing this trait bound", - ); - } + { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); } }); } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index b6f4c4d7f0a41..3147058b4cda0 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -591,26 +591,26 @@ impl Types { TyKind::Path(ref qpath) if !context.in_body => { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); - if let Some(def_id) = res.opt_def_id() { - if self.is_type_change_allowed(context) { - // All lints that are being checked in this block are guarded by - // the `avoid_breaking_exported_api` configuration. When adding a - // new lint, please also add the name to the configuration documentation - // in `clippy_config::conf` - - let mut triggered = false; - triggered |= box_collection::check(cx, hir_ty, qpath, def_id); - triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); - triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); - triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); - triggered |= option_option::check(cx, hir_ty, qpath, def_id); - triggered |= linked_list::check(cx, hir_ty, def_id); - triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); - triggered |= owned_cow::check(cx, qpath, def_id); - - if triggered { - return; - } + if let Some(def_id) = res.opt_def_id() + && self.is_type_change_allowed(context) + { + // All lints that are being checked in this block are guarded by + // the `avoid_breaking_exported_api` configuration. When adding a + // new lint, please also add the name to the configuration documentation + // in `clippy_config::conf` + + let mut triggered = false; + triggered |= box_collection::check(cx, hir_ty, qpath, def_id); + triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); + triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); + triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); + triggered |= option_option::check(cx, hir_ty, qpath, def_id); + triggered |= linked_list::check(cx, hir_ty, def_id); + triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); + triggered |= owned_cow::check(cx, qpath, def_id); + + if triggered { + return; } } match *qpath { diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index e1fc644e4ceeb..79571b0409d21 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -76,10 +76,10 @@ declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_ impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Lit(lit) = expr.kind { - if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node { - check_str(cx, lit.span, expr.hir_id); - } + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(_, _) | LitKind::Char(_) = lit.node + { + check_str(cx, lit.span, expr.hir_id); } } } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 937e35dea96d9..bcd05cceca9c3 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -93,13 +93,13 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Abort if the method is implementing a trait or of it a trait method. let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } // Get the wrapper and inner types, if can't, abort. diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index f43715d6752e3..535dec7fa977b 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -69,10 +69,10 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if self.msrv.meets(msrvs::OR_PATTERNS) { - if let ast::ExprKind::Let(pat, _, _, _) = &e.kind { - lint_unnested_or_patterns(cx, pat); - } + if self.msrv.meets(msrvs::OR_PATTERNS) + && let ast::ExprKind::Let(pat, _, _, _) = &e.kind + { + lint_unnested_or_patterns(cx, pat); } } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 0687fc319af68..2d88c490b1abe 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -265,15 +265,14 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// If `expr` is an (e).await, return the inner expression "e" that's being /// waited on. Otherwise return None. fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { - if let ExprKind::Call(func, [arg_0]) = expr.kind { - if matches!( - func.kind, - ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) - ) { - return arg_0; - } - } + if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind + && let ExprKind::Call(func, [arg_0]) = expr.kind + && matches!( + func.kind, + ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ) + { + return arg_0; } expr } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 972c1adbd40ae..3a9c997a579d1 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -92,36 +92,36 @@ fn into_iter_bound<'tcx>( let mut into_iter_span = None; for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { - if tr.self_ty().is_param(param_index) { - if tr.def_id() == into_iter_did { - into_iter_span = Some(*span); - } else { - let tr = cx.tcx.erase_regions(tr); - if tr.has_escaping_bound_vars() { - return None; - } - - // Substitute generics in the predicate and replace the IntoIterator type parameter with the - // `.into_iter()` receiver to see if the bound also holds for that type. - let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { - if i == param_index as usize { - GenericArg::from(into_iter_receiver) - } else { - arg - } - })); + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() + && tr.self_ty().is_param(param_index) + { + if tr.def_id() == into_iter_did { + into_iter_span = Some(*span); + } else { + let tr = cx.tcx.erase_regions(tr); + if tr.has_escaping_bound_vars() { + return None; + } - let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); - if !cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .predicate_must_hold_modulo_regions(&obligation) - { - return None; + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg } + })); + + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .predicate_must_hold_modulo_regions(&obligation) + { + return None; } } } diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index e454427adde1b..044894f827932 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -107,48 +107,48 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { Applicability::MachineApplicable, ); } - if let ExprKind::Binary(op, left, right) = expr.kind { - if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { + if let ExprKind::Binary(op, left, right) = expr.kind + && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) + { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { span_lint_and_sugg( cx, UNNECESSARY_SYMBOL_STR, expr.span, - "unnecessary `Symbol` to string conversion", + "unnecessary string allocation", "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), Applicability::MachineApplicable, ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } + } + }, + // nothing found + [(_, None), (_, None)] => {}, } } } diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 252ac5e676822..5e1872ad97521 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -80,22 +80,22 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { .copied(); for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { let lang_item_path = cx.get_def_path(item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(item_def_id) { - if child.ident.name == *item { - return true; - } + if path_syms.starts_with(&lang_item_path) + && let [item] = &path_syms[lang_item_path.len()..] + { + if matches!( + cx.tcx.def_kind(item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(item_def_id) { + if child.ident.name == *item { + return true; } - } else { - for child in cx.tcx.associated_item_def_ids(item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } + } + } else { + for child in cx.tcx.associated_item_def_ids(item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; } } } diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 94a2e598522b2..bee05e52ed26a 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -205,12 +205,10 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { mutbl: Mutability::Not, }, ) = ty.kind + && let TyKind::Path(ref path) = inner.kind + && let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } + return match_def_path(cx, def_id, &paths::LINT); } false diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 76b0a52621be4..3790805df35ac 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -281,10 +281,10 @@ fn path_from_array(exprs: &[Expr<'_>]) -> Option> { exprs .iter() .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } + if let ExprKind::Lit(lit) = &expr.kind + && let LitKind::Str(sym, _) = lit.node + { + return Some((*sym.as_str()).to_owned()); } None diff --git a/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs b/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs index a5c4bf474f7aa..d4c0df1700d39 100644 --- a/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs +++ b/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs @@ -21,28 +21,26 @@ declare_lint_pass!(UnsortedClippyUtilsPaths => [UNSORTED_CLIPPY_UTILS_PATHS]); impl EarlyLintPass for UnsortedClippyUtilsPaths { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(last_name) = last_name { - if *last_name > *name { - span_lint( - cx, - UNSORTED_CLIPPY_UTILS_PATHS, - item.span, - "this constant should be before the previous constant due to lexical \ + if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") + && let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind + && let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") + && let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind + { + let mut last_name: Option<&str> = None; + for item in items { + let name = item.ident.as_str(); + if let Some(last_name) = last_name + && *last_name > *name + { + span_lint( + cx, + UNSORTED_CLIPPY_UTILS_PATHS, + item.span, + "this constant should be before the previous constant due to lexical \ ordering", - ); - } - } - last_name = Some(name); - } - } + ); } + last_name = Some(name); } } } diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index f6948be7f67aa..a97643e0eaca5 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -74,10 +74,10 @@ impl LateLintPass<'_> for ZeroSizedMapValues { fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { let parent_id = cx.tcx.hir_get_parent_item(hir_id); let second_parent_id = cx.tcx.hir_get_parent_item(parent_id.into()).def_id; - if let Node::Item(item) = cx.tcx.hir_node_by_def_id(second_parent_id) { - if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return true; - } + if let Node::Item(item) = cx.tcx.hir_node_by_def_id(second_parent_id) + && let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind + { + return true; } false } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 292792408c642..cd2098a89891d 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -17,16 +17,16 @@ use rustc_span::Span; use std::env; fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { - if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { - if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { - diag.help(format!( - "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", - &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { - // extract just major + minor version and ignore patch versions - format!("rust-{}", n.rsplit_once('.').unwrap().1) - }) - )); - } + if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() + && let Some(lint) = lint.name_lower().strip_prefix("clippy::") + { + diag.help(format!( + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", + &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { + // extract just major + minor version and ignore patch versions + format!("rust-{}", n.rsplit_once('.').unwrap().1) + }) + )); } } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 852378d50e8b4..d4e66ebd8e1fb 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -118,18 +118,17 @@ impl<'hir> IfLet<'hir> { ) = expr.kind { let mut iter = cx.tcx.hir_parent_iter(expr.hir_id); - if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() { - if let Some(( + if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() + && let Some(( _, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }), )) = iter.next() - { - // while loop desugar - return None; - } + { + // while loop desugar + return None; } return Some(Self { let_pat, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5f39af811cd99..e8ec42e0662bd 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -367,10 +367,10 @@ pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks if a method is defined in an impl of a diagnostic item pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return cx.tcx.is_diagnostic_item(diag_item, adt.did()); - } + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) + && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() + { + return cx.tcx.is_diagnostic_item(diag_item, adt.did()); } false } @@ -457,10 +457,10 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { QPath::Resolved(_, path) => match_path(path, segments), QPath::TypeRelative(ty, segment) => match ty.kind { TyKind::Path(ref inner_path) => { - if let [prefix @ .., end] = segments { - if match_qpath(inner_path, prefix) { - return segment.ident.name.as_str() == *end; - } + if let [prefix @ .., end] = segments + && match_qpath(inner_path, prefix) + { + return segment.ident.name.as_str() == *end; } false }, @@ -523,10 +523,10 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { - if let Res::Local(id) = path.res { - return Some(id); - } + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind + && let Res::Local(id) = path.res + { + return Some(id); } None } @@ -893,16 +893,14 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< sym::BinaryHeap, ]; - if let QPath::TypeRelative(_, method) = path { - if method.ident.name == sym::new { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return std_types_symbols.iter().any(|&symbol| { - cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() - }); - } - } - } + if let QPath::TypeRelative(_, method) = path + && method.ident.name == sym::new + && let Some(impl_did) = cx.tcx.impl_of_method(def_id) + && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() + { + return std_types_symbols.iter().any(|&symbol| { + cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() + }); } false } @@ -1204,12 +1202,10 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { .adjustments() .get(child_id) .map_or(&[][..], |x| &**x) - { - if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) = + && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) = *adjust.last().map_or(target, |a| a.target).kind() - { - return CaptureKind::Ref(mutability); - } + { + return CaptureKind::Ref(mutability); } match parent { @@ -1737,10 +1733,10 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool /// Checks whether the given expression is a constant literal of the given value. pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool { // FIXME: use constant folding - if let ExprKind::Lit(spanned) = expr.kind { - if let LitKind::Int(v, _) = spanned.node { - return v == value; - } + if let ExprKind::Lit(spanned) = expr.kind + && let LitKind::Int(v, _) = spanned.node + { + return v == value; } false } @@ -1777,10 +1773,10 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { - if mac_name.as_str() == name { - return Some(new_span); - } + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind + && mac_name.as_str() == name + { + return Some(new_span); } span = new_span; @@ -1806,10 +1802,10 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { - if mac_name.as_str() == name { - return Some(new_span); - } + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind + && mac_name.as_str() == name + { + return Some(new_span); } } @@ -1830,15 +1826,15 @@ pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ExprKind::Call(fun, _) = expr.kind { - if let ExprKind::Path(ref qp) = fun.kind { - let res = cx.qpath_res(qp, fun.hir_id); - return match res { - Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), - _ => false, - }; - } + if let ExprKind::Call(fun, _) = expr.kind + && let ExprKind::Path(ref qp) = fun.kind + { + let res = cx.qpath_res(qp, fun.hir_id); + return match res { + Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), + _ => false, + }; } false } @@ -1911,10 +1907,10 @@ pub fn is_self(slf: &Param<'_>) -> bool { } pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { - if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind { - if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res { - return true; - } + if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind + && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res + { + return true; } false } @@ -2119,10 +2115,10 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, } // final `else {..}` - if !blocks.is_empty() { - if let ExprKind::Block(block, _) = expr.kind { - blocks.push(block); - } + if !blocks.is_empty() + && let ExprKind::Block(block, _) = expr.kind + { + blocks.push(block); } (conds, blocks) @@ -2139,8 +2135,8 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool { /// Peels away all the compiler generated code surrounding the body of an async function, pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind { - if let ExprKind::Block( + if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind + && let ExprKind::Block( Block { stmts: [], expr: @@ -2152,9 +2148,8 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t }, _, ) = tcx.hir_body(body).value.kind - { - return Some(expr); - } + { + return Some(expr); } None } @@ -2628,10 +2623,10 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> } pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - if let Res::Def(_, def_id) = path.res { - return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); - } + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + && let Res::Def(_, def_id) = path.res + { + return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); } false } @@ -2650,18 +2645,16 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym if matches!(tcx.def_kind(id.owner_id), DefKind::Const) && let item = tcx.hir_item(id) && let ItemKind::Const(ident, ty, _generics, _body) = item.kind - { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind // We could also check for the type name `test::TestDescAndFn` - if let Res::Def(DefKind::Struct, _) = path.res { - let has_test_marker = tcx - .hir_attrs(item.hir_id()) - .iter() - .any(|a| a.has_name(sym::rustc_test_marker)); - if has_test_marker { - names.push(ident.name); - } - } + && let Res::Def(DefKind::Struct, _) = path.res + { + let has_test_marker = tcx + .hir_attrs(item.hir_id()) + .iter() + .any(|a| a.has_name(sym::rustc_test_marker)); + if has_test_marker { + names.push(ident.name); } } } @@ -2682,12 +2675,12 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { // Since you can nest functions we need to collect all until we leave // function scope .any(|(_id, node)| { - if let Node::Item(item) = node { - if let ItemKind::Fn { ident, .. } = item.kind { - // Note that we have sorted the item names in the visitor, - // so the binary_search gets the same as `contains`, but faster. - return names.binary_search(&ident.name).is_ok(); - } + if let Node::Item(item) = node + && let ItemKind::Fn { ident, .. } = item.kind + { + // Note that we have sorted the item names in the visitor, + // so the binary_search gets the same as `contains`, but faster. + return names.binary_search(&ident.name).is_ok(); } false }) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 80066e9702d34..56e81dbd59ee7 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -384,10 +384,10 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) { - if snippet.is_empty() { - return false; - } + if let Some(snippet) = snippet_opt(sess, span) + && snippet.is_empty() + { + return false; } true } @@ -408,11 +408,11 @@ pub fn position_before_rarrow(s: &str) -> Option { let mut rpos = rpos; let chars: Vec = s.chars().collect(); while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } + if let Some(c) = chars.get(rpos - 1) + && c.is_whitespace() + { + rpos -= 1; + continue; } break; } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 1b35d86146d06..f1d1936f9fbd2 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -947,11 +947,10 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // some items do not need explicit deref, such as array accesses, // so we mark them as already processed // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3` - if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() { - if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) { + if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() + && matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) { projections_handled = true; } - } }, } }); diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 99650d0292faf..a72414f2aff5a 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -128,10 +128,10 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' // For `impl Trait`, it will register a predicate of `::Assoc = U`, // so we check the term for `U`. ty::ClauseKind::Projection(projection_predicate) => { - if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { - if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { - return true; - } + if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() + && contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) + { + return true; } }, _ => (), @@ -337,20 +337,20 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { - if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { - if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { - return true; - } + if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) + { + return true; } } false }, ty::Dynamic(binder, _, _) => { for predicate in *binder { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) { - return true; - } + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() + && cx.tcx.has_attr(trait_ref.def_id, sym::must_use) + { + return true; } } false diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index a079fd940c009..1b049b6d12c4c 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -126,10 +126,10 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result { - if let Res::Local(id) = path.res { - if self.binding_ids.contains(&id) { - return ControlFlow::Break(()); - } + if let Res::Local(id) = path.res + && self.binding_ids.contains(&id) + { + return ControlFlow::Break(()); } ControlFlow::Continue(()) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 63dd00f2de0fb..fc6e30a980476 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -297,10 +297,10 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { for_each_expr(cx, cx.tcx.hir_body(body).value, |e| { - if let ExprKind::Path(p) = &e.kind { - if cx.qpath_res(p, e.hir_id) == res { - return ControlFlow::Break(()); - } + if let ExprKind::Path(p) = &e.kind + && cx.qpath_res(p, e.hir_id) == res + { + return ControlFlow::Break(()); } ControlFlow::Continue(()) }) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index c8b4a2c9e2073..fe488ef89da1f 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -467,10 +467,10 @@ fn get_perf_data_filename(source_path: &Path) -> String { .for_each(|path| { let file_name = path.file_name(); let file_name = file_name.as_os_str().to_str().unwrap().split('.').next_back().unwrap(); - if let Ok(parsed_file_name) = file_name.parse::() { - if parsed_file_name >= max_number { - max_number = parsed_file_name + 1; - } + if let Ok(parsed_file_name) = file_name.parse::() + && parsed_file_name >= max_number + { + max_number = parsed_file_name + 1; } }); return format!("perf.data.{max_number}"); diff --git a/src/driver.rs b/src/driver.rs index e4092bcd10564..c6b3c06baa419 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -207,12 +207,12 @@ pub fn main() { // Beside checking for existence of `--sysroot` on the command line, we need to // check for the arg files that are prefixed with @ as well to be consistent with rustc for arg in args.iter() { - if let Some(arg_file_path) = arg.strip_prefix('@') { - if let Ok(arg_file) = read_to_string(arg_file_path) { - let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); - if has_arg(&split_arg_file, "--sysroot") { - return true; - } + if let Some(arg_file_path) = arg.strip_prefix('@') + && let Ok(arg_file) = read_to_string(arg_file_path) + { + let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); + if has_arg(&split_arg_file, "--sysroot") { + return true; } } } @@ -221,10 +221,10 @@ pub fn main() { let sys_root_env = std::env::var("SYSROOT").ok(); let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { - if let Some(sys_root) = sys_root_env { - if !has_sysroot_arg(args) { - args.extend(vec!["--sysroot".into(), sys_root]); - } + if let Some(sys_root) = sys_root_env + && !has_sysroot_arg(args) + { + args.extend(vec!["--sysroot".into(), sys_root]); } }; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 956a05288f358..a9ba382daf279 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -86,13 +86,13 @@ fn extern_flags() -> Vec { let name = name.strip_prefix("lib").unwrap_or(name); Some((name, path_str)) }; - if let Some((name, path)) = parse_name_path() { - if TEST_DEPENDENCIES.contains(&name) { - // A dependency may be listed twice if it is available in sysroot, - // and the sysroot dependencies are listed first. As of the writing, - // this only seems to apply to if_chain. - crates.insert(name, path); - } + if let Some((name, path)) = parse_name_path() + && TEST_DEPENDENCIES.contains(&name) + { + // A dependency may be listed twice if it is available in sysroot, + // and the sysroot dependencies are listed first. As of the writing, + // this only seems to apply to if_chain. + crates.insert(name, path); } } let not_found: Vec<&str> = TEST_DEPENDENCIES From 01820a9efe3d7d09d858483f15fcd38b8cf2cab9 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 27 Mar 2025 20:44:03 +0100 Subject: [PATCH 067/103] Do not make incomplete or invalid suggestions The `unnecessary_filter_map` and `unnecessary_find_map` lints were making partial suggestions, proposing to replace the whole expression by only the method name, or a subexpression which contained explicit placeholders. Since even `MaybeIncorrect` suggestions must generate code that compiles, this changes those lints to recommandation lints with no code suggestion. --- .../src/methods/unnecessary_filter_map.rs | 26 +++++---- tests/ui/unnecessary_filter_map.rs | 7 +-- tests/ui/unnecessary_filter_map.stderr | 53 +++++++------------ tests/ui/unnecessary_find_map.rs | 1 - tests/ui/unnecessary_find_map.stderr | 30 +++++------ 5 files changed, 48 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index ca42a9ac04e0b..f920f306bc1ed 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,11 +1,10 @@ use super::utils::clone_or_copy_needed; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; @@ -45,30 +44,32 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { - span_lint_and_sugg( + span_lint( cx, UNNECESSARY_FILTER_MAP, expr.span, - format!("{name} is unnecessary"), - "try removing the filter_map", - String::new(), - Applicability::MaybeIncorrect, + String::from("this call to `.filter_map(..)` is unnecessary"), ); + return; + } + if name == "filter_map" { + "map(..)" + } else { + "map(..).next()" } - if name == "filter_map" { "map" } else { "map(..).next()" } } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) { match cx.typeck_results().expr_ty(body.value).kind() { ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => { - if name == "filter_map" { "filter" } else { "find" } + if name == "filter_map" { "filter(..)" } else { "find(..)" } }, _ => return, } } else { return; }; - span_lint_and_sugg( + span_lint( cx, if name == "filter_map" { UNNECESSARY_FILTER_MAP @@ -76,10 +77,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a UNNECESSARY_FIND_MAP }, expr.span, - format!("this `.{name}` can be written more simply"), - "try instead", - sugg.to_string(), - Applicability::MaybeIncorrect, + format!("this `.{name}(..)` can be written more simply using `.{sugg}`"), ); } } diff --git a/tests/ui/unnecessary_filter_map.rs b/tests/ui/unnecessary_filter_map.rs index c4f1b6bc7e3d7..85582c399ce5f 100644 --- a/tests/ui/unnecessary_filter_map.rs +++ b/tests/ui/unnecessary_filter_map.rs @@ -1,5 +1,4 @@ -//@no-rustfix -#![allow(dead_code)] +#![allow(clippy::redundant_closure)] fn main() { let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); @@ -27,9 +26,7 @@ fn main() { let _ = (0..4).filter_map(Some); let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - //~^ redundant_closure - //~| unnecessary_filter_map - //~| unnecessary_filter_map + //~^ unnecessary_filter_map } fn filter_map_none_changes_item_type() -> impl Iterator { diff --git a/tests/ui/unnecessary_filter_map.stderr b/tests/ui/unnecessary_filter_map.stderr index 6683444b72730..a879633e10f2a 100644 --- a/tests/ui/unnecessary_filter_map.stderr +++ b/tests/ui/unnecessary_filter_map.stderr @@ -1,14 +1,14 @@ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:5:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:4:13 | LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]` -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:8:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:7:13 | LL | let _ = (0..4).filter_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | if x > 1 { ... | LL | | None LL | | }); - | |______^ help: try instead: `filter` + | |______^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:16:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:15:13 | LL | let _ = (0..4).filter_map(|x| match x { | _____________^ @@ -29,40 +29,25 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ help: try instead: `filter` + | |______^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:22:13 +error: this `.filter_map(..)` can be written more simply using `.map(..)` + --> tests/ui/unnecessary_filter_map.rs:21:13 | LL | let _ = (0..4).filter_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: redundant closure - --> tests/ui/unnecessary_filter_map.rs:29:57 +error: this call to `.filter_map(..)` is unnecessary + --> tests/ui/unnecessary_filter_map.rs:28:61 | LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^^^^^^^^ help: replace the closure with the function itself: `Some` - | - = note: `-D clippy::redundant-closure` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` - -error: filter_map is unnecessary - --> tests/ui/unnecessary_filter_map.rs:29:61 - | -LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^ help: try removing the filter_map - -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:29:13 - | -LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + | ^^^^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:169:14 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:166:14 | LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/unnecessary_find_map.rs b/tests/ui/unnecessary_find_map.rs index 8c8a3799f0216..33ba7074d623b 100644 --- a/tests/ui/unnecessary_find_map.rs +++ b/tests/ui/unnecessary_find_map.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![allow(dead_code)] fn main() { diff --git a/tests/ui/unnecessary_find_map.stderr b/tests/ui/unnecessary_find_map.stderr index 94e320773a6fe..3754a3d99538e 100644 --- a/tests/ui/unnecessary_find_map.stderr +++ b/tests/ui/unnecessary_find_map.stderr @@ -1,14 +1,14 @@ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:5:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:4:13 | LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-find-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]` -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:8:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:7:13 | LL | let _ = (0..4).find_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | if x > 1 { ... | LL | | None LL | | }); - | |______^ help: try instead: `find` + | |______^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:16:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:15:13 | LL | let _ = (0..4).find_map(|x| match x { | _____________^ @@ -29,19 +29,19 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ help: try instead: `find` + | |______^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:22:13 +error: this `.find_map(..)` can be written more simply using `.map(..).next()` + --> tests/ui/unnecessary_find_map.rs:21:13 | LL | let _ = (0..4).find_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map(..).next()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:34:14 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:33:14 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors From 3e960c12d8004135a2c864a6d54753df806aeff6 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Wed, 15 Jan 2025 15:01:56 +0000 Subject: [PATCH 068/103] Make missing_const_for_fn operate on non-optimized MIR This has two reasons: First of all we don't need the optimized MIR for evaluating the checks of this lint, as const-eval anyways operates on `mir_for_ctf` which is derived from `mir_drops_elaborated_and_const_checked`. Second of all we might transform MIR in the optimization passes in a way that doesn't work with const-eval, but it is irrelevant since const-eval uses another MIR. Specifically this came up when adding a new check in debug builds (https://github.com/rust-lang/rust/pull/134424), which is added as part of an optimization pass. --- clippy_lints/src/missing_const_for_fn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 5ac66b4c7e84b..67537a251da7f 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -155,9 +155,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let mir = cx.tcx.optimized_mir(def_id); + let mir = cx.tcx.mir_drops_elaborated_and_const_checked(def_id); - if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) + if let Ok(()) = is_min_const_fn(cx, &mir.borrow(), self.msrv) && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = cx.tcx.hir_node_by_def_id(def_id) { From 432a8a7a7cbf057e481e698f4478be1006612b03 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 28 Mar 2025 22:03:26 +0100 Subject: [PATCH 069/103] Properly handle expansion in `single_match` Having a macro call as the scrutinee is supported. However, the proposed suggestion must use the macro call itself, not its expansion. When the scrutinee is a macro call, do not complain about an irrefutable match, as the user may not be aware of the result of the macro. A comparaison will be suggested instead, as if we couldn't see the outcome of the macro. Similarly, do not accept macro calls as arm patterns. --- clippy_lints/src/matches/single_match.rs | 17 +++++----- tests/ui/single_match.fixed | 36 ++++++++++++++++++++ tests/ui/single_match.rs | 42 ++++++++++++++++++++++++ tests/ui/single_match.stderr | 20 ++++++++++- 4 files changed, 106 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 56fbd626eefc4..735ba63eb771e 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,5 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context}; +use clippy_utils::source::{ + SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, +}; use clippy_utils::ty::implements_trait; use clippy_utils::{ is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, @@ -34,8 +36,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { #[rustfmt::skip] pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { if let [arm1, arm2] = arms - && arm1.guard.is_none() - && arm2.guard.is_none() + && !arms.iter().any(|arm| arm.guard.is_some() || arm.pat.span.from_expansion()) && !expr.span.from_expansion() // don't lint for or patterns for now, this makes // the lint noisy in unnecessary situations @@ -106,7 +107,7 @@ fn report_single_pattern( format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); - if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { + if ex.span.eq_ctxt(expr.span) && snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { let msg = "this pattern is irrefutable, `match` is useless"; let (sugg, help) = if is_unit_expr(arm.body) { (String::new(), "`match` expression can be removed") @@ -163,10 +164,10 @@ fn report_single_pattern( let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( "if {} == {}{} {}{els_str}", - snippet(cx, ex.span, ".."), + snippet_with_context(cx, ex.span, ctxt, "..", &mut app).0, // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), - snippet(cx, arm.pat.span, ".."), + snippet_with_applicability(cx, arm.pat.span, "..", &mut app), expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) @@ -174,8 +175,8 @@ fn report_single_pattern( let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( "if let {} = {} {}{els_str}", - snippet(cx, arm.pat.span, ".."), - snippet(cx, ex.span, ".."), + snippet_with_applicability(cx, arm.pat.span, "..", &mut app), + snippet_with_context(cx, ex.span, ctxt, "..", &mut app).0, expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index 0e198ec79344a..db5107600ee6d 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -366,3 +366,39 @@ fn irrefutable_match() { //~^^^^^^^^^ single_match //~| NOTE: you might want to preserve the comments from inside the `match` } + +fn issue_14493() { + macro_rules! mac { + (some) => { + Some(42) + }; + (any) => { + _ + }; + (str) => { + "foo" + }; + } + + if let Some(u) = mac!(some) { println!("{u}") } + //~^^^^ single_match + + // When scrutinee comes from macro, do not tell that arm will always match + // and suggest an equality check instead. + if mac!(str) == "foo" { println!("eq") } + //~^^^^ ERROR: for an equality check + + // Do not lint if any match arm come from expansion + match Some(0) { + mac!(some) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + Some(42) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + mac!(some) => println!("eq"), + _ => println!("neq"), + } +} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index fcac65f8aaf5e..a367b94c4ca6b 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -461,3 +461,45 @@ fn irrefutable_match() { //~^^^^^^^^^ single_match //~| NOTE: you might want to preserve the comments from inside the `match` } + +fn issue_14493() { + macro_rules! mac { + (some) => { + Some(42) + }; + (any) => { + _ + }; + (str) => { + "foo" + }; + } + + match mac!(some) { + Some(u) => println!("{u}"), + _ => (), + } + //~^^^^ single_match + + // When scrutinee comes from macro, do not tell that arm will always match + // and suggest an equality check instead. + match mac!(str) { + "foo" => println!("eq"), + _ => (), + } + //~^^^^ ERROR: for an equality check + + // Do not lint if any match arm come from expansion + match Some(0) { + mac!(some) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + Some(42) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + mac!(some) => println!("eq"), + _ => println!("neq"), + } +} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 2467423b9c17d..1a4edc45c928d 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -321,5 +321,23 @@ LL + println!("{u}"); LL + } | -error: aborting due to 29 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:478:5 + | +LL | / match mac!(some) { +LL | | Some(u) => println!("{u}"), +LL | | _ => (), +LL | | } + | |_____^ help: try: `if let Some(u) = mac!(some) { println!("{u}") }` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match.rs:486:5 + | +LL | / match mac!(str) { +LL | | "foo" => println!("eq"), +LL | | _ => (), +LL | | } + | |_____^ help: try: `if mac!(str) == "foo" { println!("eq") }` + +error: aborting due to 31 previous errors From bc701925d8313a0d3e5092378d66c1df565dc1b9 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 29 Mar 2025 11:18:49 +0100 Subject: [PATCH 070/103] Take advantage of match ergonomics in `clippy_utils::hir_utils` This is a style-only change in order to use match ergonomics by removing unneeded `&` and `ref`. --- clippy_utils/src/hir_utils.rs | 266 +++++++++++++++++----------------- 1 file changed, 133 insertions(+), 133 deletions(-) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 9938e64d24264..4b16896c2986c 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -148,7 +148,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { - (&StmtKind::Let(l), &StmtKind::Let(r)) => { + (StmtKind::Let(l), StmtKind::Let(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { @@ -166,7 +166,7 @@ impl HirEqInterExpr<'_, '_, '_> { && both(l.els.as_ref(), r.els.as_ref(), |l, r| self.eq_block(l, r)) && self.eq_pat(l.pat, r.pat) }, - (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r), + (StmtKind::Expr(l), StmtKind::Expr(r)) | (StmtKind::Semi(l), StmtKind::Semi(r)) => self.eq_expr(l, r), _ => false, } } @@ -260,7 +260,7 @@ impl HirEqInterExpr<'_, '_, '_> { fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { macro_backtrace(expr.span).last().is_some_and(|macro_call| { matches!( - &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), + self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::todo_macro | sym::unimplemented_macro) ) }) @@ -301,58 +301,58 @@ impl HirEqInterExpr<'_, '_, '_> { reduce_exprkind(self.inner.cx, &left.kind), reduce_exprkind(self.inner.cx, &right.kind), ) { - (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => { + (ExprKind::AddrOf(lb, l_mut, le), ExprKind::AddrOf(rb, r_mut, re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, - (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), - (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => { + (ExprKind::Array(l), ExprKind::Array(r)) => self.eq_exprs(l, r), + (ExprKind::Assign(ll, lr, _), ExprKind::Assign(rl, rr, _)) => { self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, - (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => { + (ExprKind::AssignOp(lo, ll, lr), ExprKind::AssignOp(ro, rl, rr)) => { self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, - (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r), - (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => { + (ExprKind::Block(l, _), ExprKind::Block(r, _)) => self.eq_block(l, r), + (ExprKind::Binary(l_op, ll, lr), ExprKind::Binary(r_op, rl, rr)) => { l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) || swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| { l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }) }, - (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => { + (ExprKind::Break(li, le), ExprKind::Break(ri, re)) => { both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name) && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { + (ExprKind::Call(l_fun, l_args), ExprKind::Call(r_fun, r_args)) => { self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, - (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) => { + (ExprKind::Cast(lx, lt), ExprKind::Cast(rx, rt)) => { self.eq_expr(lx, rx) && self.eq_ty(lt, rt) }, - (&ExprKind::Closure(_l), &ExprKind::Closure(_r)) => false, - (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body), - (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { + (ExprKind::Closure(_l), ExprKind::Closure(_r)) => false, + (ExprKind::ConstBlock(lb), ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body), + (ExprKind::Continue(li), ExprKind::Continue(ri)) => { both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name) }, - (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), - (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => { + (ExprKind::DropTemps(le), ExprKind::DropTemps(re)) => self.eq_expr(le, re), + (ExprKind::Field(l_f_exp, l_f_ident), ExprKind::Field(r_f_exp, r_f_ident)) => { l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) }, - (&ExprKind::Index(la, li, _), &ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), - (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { + (ExprKind::Index(la, li, _), ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), + (ExprKind::If(lc, lt, le), ExprKind::If(rc, rt, re)) => { self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Let(l), &ExprKind::Let(r)) => { + (ExprKind::Let(l), ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, - (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { + (ExprKind::Loop(lb, ll, lls, _), ExprKind::Loop(rb, rl, rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll.as_ref(), rl.as_ref(), |l, r| l.ident.name == r.ident.name) }, - (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => { + (ExprKind::Match(le, la, ls), ExprKind::Match(re, ra, rs)) => { (ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_))))) && self.eq_expr(le, re) && over(la, ra, |l, r| { @@ -362,27 +362,27 @@ impl HirEqInterExpr<'_, '_, '_> { }) }, ( - &ExprKind::MethodCall(l_path, l_receiver, l_args, _), - &ExprKind::MethodCall(r_path, r_receiver, r_args, _), + ExprKind::MethodCall(l_path, l_receiver, l_args, _), + ExprKind::MethodCall(r_path, r_receiver, r_args, _), ) => { self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_expr(l_receiver, r_receiver) && self.eq_exprs(l_args, r_args) }, - (&ExprKind::UnsafeBinderCast(lkind, le, None), &ExprKind::UnsafeBinderCast(rkind, re, None)) => + (ExprKind::UnsafeBinderCast(lkind, le, None), ExprKind::UnsafeBinderCast(rkind, re, None)) => lkind == rkind && self.eq_expr(le, re), - (&ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), &ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) => + (ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) => lkind == rkind && self.eq_expr(le, re) && self.eq_ty(lt, rt), - (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => { + (ExprKind::OffsetOf(l_container, l_fields), ExprKind::OffsetOf(r_container, r_fields)) => { self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name) }, (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), - (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { + (ExprKind::Repeat(le, ll), ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_const_arg(ll, rl) }, (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)), - (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { + (ExprKind::Struct(l_path, lf, lo), ExprKind::Struct(r_path, rf, ro)) => { self.eq_qpath(l_path, r_path) && match (lo, ro) { (StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r), @@ -392,58 +392,58 @@ impl HirEqInterExpr<'_, '_, '_> { } && over(lf, rf, |l, r| self.eq_expr_field(l, r)) }, - (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), - (&ExprKind::Use(l_expr, _), &ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr), - (&ExprKind::Type(le, lt), &ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt), - (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re), - (&ExprKind::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re), + (ExprKind::Tup(l_tup), ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), + (ExprKind::Use(l_expr, _), ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr), + (ExprKind::Type(le, lt), ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt), + (ExprKind::Unary(l_op, le), ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re), + (ExprKind::Yield(le, _), ExprKind::Yield(re, _)) => return self.eq_expr(le, re), ( // Else branches for branches above, grouped as per `match_same_arms`. - | &ExprKind::AddrOf(..) - | &ExprKind::Array(..) - | &ExprKind::Assign(..) - | &ExprKind::AssignOp(..) - | &ExprKind::Binary(..) - | &ExprKind::Become(..) - | &ExprKind::Block(..) - | &ExprKind::Break(..) - | &ExprKind::Call(..) - | &ExprKind::Cast(..) - | &ExprKind::ConstBlock(..) - | &ExprKind::Continue(..) - | &ExprKind::DropTemps(..) - | &ExprKind::Field(..) - | &ExprKind::Index(..) - | &ExprKind::If(..) - | &ExprKind::Let(..) - | &ExprKind::Lit(..) - | &ExprKind::Loop(..) - | &ExprKind::Match(..) - | &ExprKind::MethodCall(..) - | &ExprKind::OffsetOf(..) - | &ExprKind::Path(..) - | &ExprKind::Repeat(..) - | &ExprKind::Ret(..) - | &ExprKind::Struct(..) - | &ExprKind::Tup(..) - | &ExprKind::Use(..) - | &ExprKind::Type(..) - | &ExprKind::Unary(..) - | &ExprKind::Yield(..) - | &ExprKind::UnsafeBinderCast(..) + | ExprKind::AddrOf(..) + | ExprKind::Array(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Binary(..) + | ExprKind::Become(..) + | ExprKind::Block(..) + | ExprKind::Break(..) + | ExprKind::Call(..) + | ExprKind::Cast(..) + | ExprKind::ConstBlock(..) + | ExprKind::Continue(..) + | ExprKind::DropTemps(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Lit(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::MethodCall(..) + | ExprKind::OffsetOf(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Ret(..) + | ExprKind::Struct(..) + | ExprKind::Tup(..) + | ExprKind::Use(..) + | ExprKind::Type(..) + | ExprKind::Unary(..) + | ExprKind::Yield(..) + | ExprKind::UnsafeBinderCast(..) // --- Special cases that do not have a positive branch. // `Err` represents an invalid expression, so let's never assume that // an invalid expressions is equal to anything. - | &ExprKind::Err(..) + | ExprKind::Err(..) // For the time being, we always consider that two closures are unequal. // This behavior may change in the future. - | &ExprKind::Closure(..) + | ExprKind::Closure(..) // For the time being, we always consider that two instances of InlineAsm are different. // This behavior may change in the future. - | &ExprKind::InlineAsm(_) + | ExprKind::InlineAsm(_) , _ ) => false, }; @@ -494,11 +494,11 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool { match (&left.kind, &right.kind) { ( - &PatExprKind::Lit { + PatExprKind::Lit { lit: left, negated: left_neg, }, - &PatExprKind::Lit { + PatExprKind::Lit { lit: right, negated: right_neg, }, @@ -512,47 +512,47 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { - (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r), - (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => { + (PatKind::Box(l), PatKind::Box(r)) => self.eq_pat(l, r), + (PatKind::Struct(lp, la, ..), PatKind::Struct(rp, ra, ..)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r)) }, - (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => { + (PatKind::TupleStruct(lp, la, ls), PatKind::TupleStruct(rp, ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, - (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => { + (PatKind::Binding(lb, li, _, lp), PatKind::Binding(rb, ri, _, rp)) => { let eq = lb == rb && both(lp.as_ref(), rp.as_ref(), |l, r| self.eq_pat(l, r)); if eq { - self.locals.insert(li, ri); + self.locals.insert(*li, *ri); } eq }, - (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), - (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), - (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { + (PatKind::Expr(l), PatKind::Expr(r)) => self.eq_pat_expr(l, r), + (PatKind::Tuple(l, ls), PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), + (PatKind::Range(ls, le, li), PatKind::Range(rs, re, ri)) => { both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, - (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), - (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => { + (PatKind::Ref(le, lm), PatKind::Ref(re, rm)) => lm == rm && self.eq_pat(le, re), + (PatKind::Slice(ls, li, le), PatKind::Slice(rs, ri, re)) => { over(ls, rs, |l, r| self.eq_pat(l, r)) && over(le, re, |l, r| self.eq_pat(l, r)) && both(li.as_ref(), ri.as_ref(), |l, r| self.eq_pat(l, r)) }, - (&PatKind::Wild, &PatKind::Wild) => true, + (PatKind::Wild, PatKind::Wild) => true, _ => false, } } fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { match (left, right) { - (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { + (QPath::Resolved(lty, lpath), QPath::Resolved(rty, rpath)) => { both(lty.as_ref(), rty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath) }, - (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => { + (QPath::TypeRelative(lty, lseg), QPath::TypeRelative(rty, rseg)) => { self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) }, - (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, + (QPath::LangItem(llang_item, ..), QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, _ => false, } } @@ -611,15 +611,15 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { - (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), - (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl), + (TyKind::Slice(l_vec), TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), + (TyKind::Array(lt, ll), TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl), (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), - (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), - (&TyKind::Infer(()), &TyKind::Infer(())) => true, + (TyKind::Tup(l), TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), + (TyKind::Infer(()), TyKind::Infer(())) => true, _ => false, } } @@ -853,9 +853,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&e.kind).hash(&mut self.s); - match e.kind { + match &e.kind { ExprKind::AddrOf(kind, m, e) => { - std::mem::discriminant(&kind).hash(&mut self.s); + std::mem::discriminant(kind).hash(&mut self.s); m.hash(&mut self.s); self.hash_expr(e); }, @@ -871,7 +871,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(l); self.hash_expr(r); }, - ExprKind::AssignOp(ref o, l, r) => { + ExprKind::AssignOp(o, l, r) => { std::mem::discriminant(&o.node).hash(&mut self.s); self.hash_expr(l); self.hash_expr(r); @@ -887,11 +887,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(l); self.hash_expr(r); }, - ExprKind::Break(i, ref j) => { + ExprKind::Break(i, j) => { if let Some(i) = i.label { self.hash_name(i.ident.name); } - if let Some(j) = *j { + if let Some(j) = j { self.hash_expr(j); } }, @@ -903,20 +903,20 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); self.hash_ty(ty); }, - ExprKind::Closure(&Closure { + ExprKind::Closure(Closure { capture_clause, body, .. }) => { - std::mem::discriminant(&capture_clause).hash(&mut self.s); + std::mem::discriminant(capture_clause).hash(&mut self.s); // closures inherit TypeckResults - self.hash_expr(self.cx.tcx.hir_body(body).value); + self.hash_expr(self.cx.tcx.hir_body(*body).value); }, - ExprKind::ConstBlock(ref l_id) => { + ExprKind::ConstBlock(l_id) => { self.hash_body(l_id.body); }, ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { self.hash_expr(e); }, - ExprKind::Field(e, ref f) => { + ExprKind::Field(e, f) => { self.hash_expr(e); self.hash_name(f.name); }, @@ -991,23 +991,23 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Lit(l) => { l.node.hash(&mut self.s); }, - ExprKind::Loop(b, ref i, ..) => { + ExprKind::Loop(b, i, ..) => { self.hash_block(b); - if let Some(i) = *i { + if let Some(i) = i { self.hash_name(i.ident.name); } }, - ExprKind::If(cond, then, ref else_opt) => { + ExprKind::If(cond, then, else_opt) => { self.hash_expr(cond); self.hash_expr(then); - if let Some(e) = *else_opt { + if let Some(e) = else_opt { self.hash_expr(e); } }, - ExprKind::Match(e, arms, ref s) => { + ExprKind::Match(e, arms, s) => { self.hash_expr(e); - for arm in arms { + for arm in *arms { self.hash_pat(arm.pat); if let Some(e) = arm.guard { self.hash_expr(e); @@ -1017,38 +1017,38 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { s.hash(&mut self.s); }, - ExprKind::MethodCall(path, receiver, args, ref _fn_span) => { + ExprKind::MethodCall(path, receiver, args, _fn_span) => { self.hash_name(path.ident.name); self.hash_expr(receiver); self.hash_exprs(args); }, ExprKind::OffsetOf(container, fields) => { self.hash_ty(container); - for field in fields { + for field in *fields { self.hash_name(field.name); } }, - ExprKind::Path(ref qpath) => { + ExprKind::Path(qpath) => { self.hash_qpath(qpath); }, ExprKind::Repeat(e, len) => { self.hash_expr(e); self.hash_const_arg(len); }, - ExprKind::Ret(ref e) => { - if let Some(e) = *e { + ExprKind::Ret(e) => { + if let Some(e) = e { self.hash_expr(e); } }, - ExprKind::Struct(path, fields, ref expr) => { + ExprKind::Struct(path, fields, expr) => { self.hash_qpath(path); - for f in fields { + for f in *fields { self.hash_name(f.ident.name); self.hash_expr(f.expr); } - if let StructTailExpr::Base(e) = *expr { + if let StructTailExpr::Base(e) = expr { self.hash_expr(e); } }, @@ -1059,11 +1059,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(expr); }, ExprKind::Unary(lop, le) => { - std::mem::discriminant(&lop).hash(&mut self.s); + std::mem::discriminant(lop).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { - std::mem::discriminant(&kind).hash(&mut self.s); + std::mem::discriminant(kind).hash(&mut self.s); self.hash_expr(expr); if let Some(ty) = ty { self.hash_ty(ty); @@ -1084,7 +1084,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_qpath(&mut self, p: &QPath<'_>) { - match *p { + match p { QPath::Resolved(_, path) => { self.hash_path(path); }, @@ -1092,7 +1092,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(path.ident.name); }, QPath::LangItem(lang_item, ..) => { - std::mem::discriminant(&lang_item).hash(&mut self.s); + std::mem::discriminant(lang_item).hash(&mut self.s); }, } // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); @@ -1123,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); - match pat.kind { + match &pat.kind { PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => { - std::mem::discriminant(&by_ref).hash(&mut self.s); - std::mem::discriminant(&mutability).hash(&mut self.s); + std::mem::discriminant(by_ref).hash(&mut self.s); + std::mem::discriminant(mutability).hash(&mut self.s); if let Some(pat) = pat { self.hash_pat(pat); } @@ -1134,7 +1134,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), PatKind::Expr(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { - for pat in pats { + for pat in *pats { self.hash_pat(pat); } }, @@ -1145,44 +1145,44 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { if let Some(e) = e { self.hash_pat_expr(e); } - std::mem::discriminant(&i).hash(&mut self.s); + std::mem::discriminant(i).hash(&mut self.s); }, PatKind::Ref(pat, mu) => { self.hash_pat(pat); - std::mem::discriminant(&mu).hash(&mut self.s); + std::mem::discriminant(mu).hash(&mut self.s); }, PatKind::Guard(pat, guard) => { self.hash_pat(pat); self.hash_expr(guard); }, PatKind::Slice(l, m, r) => { - for pat in l { + for pat in *l { self.hash_pat(pat); } if let Some(pat) = m { self.hash_pat(pat); } - for pat in r { + for pat in *r { self.hash_pat(pat); } }, - PatKind::Struct(ref qpath, fields, e) => { + PatKind::Struct(qpath, fields, e) => { self.hash_qpath(qpath); - for f in fields { + for f in *fields { self.hash_name(f.ident.name); self.hash_pat(f.pat); } e.hash(&mut self.s); }, PatKind::Tuple(pats, e) => { - for pat in pats { + for pat in *pats { self.hash_pat(pat); } e.hash(&mut self.s); }, - PatKind::TupleStruct(ref qpath, pats, e) => { + PatKind::TupleStruct(qpath, pats, e) => { self.hash_qpath(qpath); - for pat in pats { + for pat in *pats { self.hash_pat(pat); } e.hash(&mut self.s); @@ -1260,7 +1260,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::Slice(ty) => { self.hash_ty(ty); }, - &TyKind::Array(ty, len) => { + TyKind::Array(ty, len) => { self.hash_ty(ty); self.hash_const_arg(len); }, @@ -1333,11 +1333,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) { for arg in arg_list { - match *arg { + match arg { GenericArg::Lifetime(l) => self.hash_lifetime(l), GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()), GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()), - GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()), + GenericArg::Infer(inf) => self.hash_ty(&inf.to_ty()), } } } From 2df6bba5b06dfb073b93199517803a6bd0aa15a0 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Thu, 13 Mar 2025 16:49:34 +0800 Subject: [PATCH 071/103] fix: `unnested_or_patterns` suggests wrongly in `let` --- clippy_lints/src/unnested_or_patterns.rs | 13 ++++++-- tests/ui/unnested_or_patterns.fixed | 13 ++++++++ tests/ui/unnested_or_patterns.rs | 13 ++++++++ tests/ui/unnested_or_patterns.stderr | 38 +++++++++++++++++++++++- 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index f43715d6752e3..913a67b18a66e 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -120,18 +120,25 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { /// Remove all `(p)` patterns in `pat`. fn remove_all_parens(pat: &mut P) { - struct Visitor; + #[derive(Default)] + struct Visitor { + /// If is not in the outer most pattern. This is needed to avoid removing the outermost + /// parens because top-level or-patterns are not allowed in let statements. + is_inner: bool, + } + impl MutVisitor for Visitor { fn visit_pat(&mut self, pat: &mut P) { + let is_inner = mem::replace(&mut self.is_inner, true); walk_pat(self, pat); let inner = match &mut pat.kind { - Paren(i) => mem::replace(&mut i.kind, Wild), + Paren(i) if is_inner => mem::replace(&mut i.kind, Wild), _ => return, }; pat.kind = inner; } } - Visitor.visit_pat(pat); + Visitor::default().visit_pat(pat); } /// Insert parens where necessary according to Rust's precedence rules for patterns. diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 791b2fa131f23..2081772d06b36 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -64,3 +64,16 @@ fn msrv_1_53() { if let [1 | 53] = [0] {} //~^ unnested_or_patterns } + +mod issue9952 { + fn or_in_local() { + let (0 | 1 | _) = 0; + //~^ unnested_or_patterns + + if let (0 | 1 | _) = 0 {} + //~^ unnested_or_patterns + } + + fn or_in_param((x | x | x): i32) {} + //~^ unnested_or_patterns +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index e7e7c7cd2e494..6bf8fce36616c 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -64,3 +64,16 @@ fn msrv_1_53() { if let [1] | [53] = [0] {} //~^ unnested_or_patterns } + +mod issue9952 { + fn or_in_local() { + let (0 | (1 | _)) = 0; + //~^ unnested_or_patterns + + if let (0 | (1 | _)) = 0 {} + //~^ unnested_or_patterns + } + + fn or_in_param((x | (x | x)): i32) {} + //~^ unnested_or_patterns +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index ec5eb983c5a01..c805dc992b1c2 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -204,5 +204,41 @@ LL - if let [1] | [53] = [0] {} LL + if let [1 | 53] = [0] {} | -error: aborting due to 17 previous errors +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:70:13 + | +LL | let (0 | (1 | _)) = 0; + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - let (0 | (1 | _)) = 0; +LL + let (0 | 1 | _) = 0; + | + +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:73:16 + | +LL | if let (0 | (1 | _)) = 0 {} + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - if let (0 | (1 | _)) = 0 {} +LL + if let (0 | 1 | _) = 0 {} + | + +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:77:20 + | +LL | fn or_in_param((x | (x | x)): i32) {} + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - fn or_in_param((x | (x | x)): i32) {} +LL + fn or_in_param((x | x | x): i32) {} + | + +error: aborting due to 20 previous errors From ffaecd008d8c2361d1043a20e06c7090f64193c3 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 29 Mar 2025 17:36:04 +0100 Subject: [PATCH 072/103] manually fulfill lint expectations for all unsafe blocks with metavars --- clippy_lints/src/macro_metavars_in_unsafe.rs | 11 ++++++++++- .../macro_metavars_in_unsafe/default/test.rs | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index 006addb987f5b..0f9d373f0a3d7 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext, sym}; use std::collections::BTreeMap; @@ -249,6 +249,15 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { }) .flatten() .copied() + .inspect(|&unsafe_block| { + if let Level::Expect(id) = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block).0 { + // Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon, + // which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out + // except for the one we emit the warning at, we must manually fulfill the lint + // for all unsafe blocks here. + cx.fulfill_expectation(id); + } + }) .map(|id| { // Remove the syntax context to hide "in this macro invocation" in the diagnostic. // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs index 2465fe45645f1..d3d5b0c103e7f 100644 --- a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs +++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -251,6 +251,16 @@ pub mod issue13219 { } } +#[macro_export] +macro_rules! issue14488 { + ($e:expr) => { + #[expect(clippy::macro_metavars_in_unsafe)] + unsafe { + $e + } + }; +} + fn main() { allow_works!(1); simple!(1); @@ -271,4 +281,10 @@ fn main() { multiple_unsafe_blocks!(1, 1, 1); unsafe_from_root_ctxt!(unsafe { 1 }); nested_macros!(1, 1); + + // These two invocations lead to two expanded unsafe blocks, each with an `#[expect]` on it. + // Only of them gets a warning, which used to result in an unfulfilled expectation for the other + // expanded unsafe block. + issue14488!(1); + issue14488!(2); } From 8f3a0db64fa599238969502ea4991a4be31aec93 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 29 Mar 2025 17:45:21 +0100 Subject: [PATCH 073/103] don't hardcode repository owner in changelog CI --- .github/workflows/clippy_changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clippy_changelog.yml b/.github/workflows/clippy_changelog.yml index a2657bfea490d..1e97154bf8a34 100644 --- a/.github/workflows/clippy_changelog.yml +++ b/.github/workflows/clippy_changelog.yml @@ -24,7 +24,7 @@ jobs: - name: Check Changelog if: ${{ github.event_name == 'pull_request' }} run: | - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \ + body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | \ python -c "import sys, json; print(json.load(sys.stdin)['body'])") output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") if [ -z "$output" ]; then From fb32aaf9ac75b503e0f344dc32ad2bf0f50f6952 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:44:33 +0000 Subject: [PATCH 074/103] new lint: `chars_enumerate_for_byte_indices` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../loops/chars_enumerate_for_byte_indices.rs | 138 ++++++++++++++++++ clippy_lints/src/loops/mod.rs | 45 ++++++ .../ui/chars_enumerate_for_byte_indices.fixed | 59 ++++++++ tests/ui/chars_enumerate_for_byte_indices.rs | 59 ++++++++ .../chars_enumerate_for_byte_indices.stderr | 123 ++++++++++++++++ 7 files changed, 426 insertions(+) create mode 100644 clippy_lints/src/loops/chars_enumerate_for_byte_indices.rs create mode 100644 tests/ui/chars_enumerate_for_byte_indices.fixed create mode 100644 tests/ui/chars_enumerate_for_byte_indices.rs create mode 100644 tests/ui/chars_enumerate_for_byte_indices.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e6f00e857f70..68387498c0d3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5517,6 +5517,7 @@ Released 2018-09-13 [`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts [`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 +[`chars_enumerate_for_byte_indices`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_enumerate_for_byte_indices [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c2274f0a619cf..cd3f40a003dbd 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -287,6 +287,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, + crate::loops::CHARS_ENUMERATE_FOR_BYTE_INDICES_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, diff --git a/clippy_lints/src/loops/chars_enumerate_for_byte_indices.rs b/clippy_lints/src/loops/chars_enumerate_for_byte_indices.rs new file mode 100644 index 0000000000000..40274311d7800 --- /dev/null +++ b/clippy_lints/src/loops/chars_enumerate_for_byte_indices.rs @@ -0,0 +1,138 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{eq_expr_value, higher, path_to_local_id}; +use rustc_errors::{Applicability, MultiSpan}; +use rustc_hir::{Expr, ExprKind, LangItem, Node, Pat, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::CHARS_ENUMERATE_FOR_BYTE_INDICES; + +// The list of `str` methods we want to lint that have a `usize` argument representing a byte index. +// Note: `String` also has methods that work with byte indices, +// but they all take `&mut self` and aren't worth considering since the user couldn't have called +// them while the chars iterator is live anyway. +const BYTE_INDEX_METHODS: &[&str] = &[ + "is_char_boundary", + "floor_char_boundary", + "ceil_char_boundary", + "get", + "index", + "index_mut", + "get_mut", + "get_unchecked", + "get_unchecked_mut", + "slice_unchecked", + "slice_mut_unchecked", + "split_at", + "split_at_mut", + "split_at_checked", + "split_at_mut_checked", +]; + +const CONTINUE: ControlFlow = ControlFlow::Continue(()); + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr<'_>, body: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = iterable.kind + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(iterable.hir_id) + && cx.tcx.is_diagnostic_item(sym::enumerate_method, method_id) + && let ExprKind::MethodCall(_, chars_recv, _, chars_span) = enumerate_recv.kind + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(enumerate_recv.hir_id) + && cx.tcx.is_diagnostic_item(sym::str_chars, method_id) + { + if let PatKind::Tuple([pat, _], _) = pat.kind + && let PatKind::Binding(_, binding_id, ..) = pat.kind + { + // Destructured iterator element `(idx, _)`, look for uses of the binding + for_each_expr(cx, body, |expr| { + if path_to_local_id(expr, binding_id) { + check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); + } + CONTINUE + }); + } else if let PatKind::Binding(_, binding_id, ..) = pat.kind { + // Bound as a tuple, look for `tup.0` + for_each_expr(cx, body, |expr| { + if let ExprKind::Field(e, field) = expr.kind + && path_to_local_id(e, binding_id) + && field.name == sym::integer(0) + { + check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); + } + CONTINUE + }); + } + } +} + +fn check_index_usage<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + pat: &Pat<'_>, + enumerate_span: Span, + chars_span: Span, + chars_recv: &Expr<'_>, +) { + let Some(parent_expr) = index_consumed_at(cx, expr) else { + return; + }; + + let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); + let message = match parent_expr.kind { + ExprKind::MethodCall(segment, recv, ..) + if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str() + && BYTE_INDEX_METHODS.contains(&segment.ident.name.as_str()) + && eq_expr_value(cx, chars_recv, recv) => + { + "passing a character position to a method that expects a byte index" + }, + ExprKind::Index(target, ..) + if is_string_like(cx.typeck_results().expr_ty_adjusted(target).peel_refs()) + && eq_expr_value(cx, chars_recv, target) => + { + "indexing into a string with a character position where a byte index is expected" + }, + _ => return, + }; + + span_lint_hir_and_then( + cx, + CHARS_ENUMERATE_FOR_BYTE_INDICES, + expr.hir_id, + expr.span, + message, + |diag| { + diag.note("a character can take up more than one byte, so they are not interchangeable") + .span_note( + MultiSpan::from_spans(vec![pat.span, enumerate_span]), + "position comes from the enumerate iterator", + ) + .span_suggestion_verbose( + chars_span.to(enumerate_span), + "consider using `.char_indices()` instead", + "char_indices()", + Applicability::MaybeIncorrect, + ); + }, + ); +} + +/// Returns the expression which ultimately consumes the index. +/// This is usually the parent expression, i.e. `.split_at(idx)` for `idx`, +/// but for `.get(..idx)` we want to consider the method call the consuming expression, +/// which requires skipping past the range expression. +fn index_consumed_at<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { + match node { + Node::Expr(expr) if higher::Range::hir(expr).is_some() => {}, + Node::ExprField(_) => {}, + Node::Expr(expr) => return Some(expr), + _ => break, + } + } + None +} diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 4b0bf5a4b3c94..10ba739909f79 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -1,3 +1,4 @@ +mod chars_enumerate_for_byte_indices; mod empty_loop; mod explicit_counter_loop; mod explicit_into_iter_loop; @@ -740,6 +741,48 @@ declare_clippy_lint! { "manually filling a slice with a value" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of a character position yielded by `.chars().enumerate()` in a context where a **byte index** is expected, + /// such as an argument to a specific `str` method or indexing into a `str` or `String`. + /// + /// ### Why is this bad? + /// A character (more specifically, a Unicode scalar value) that is yielded by `str::chars` can take up multiple bytes, + /// so a character position does not necessarily have the same byte index at which the character is stored. + /// Thus, using the character position where a byte index is expected can unexpectedly return wrong values + /// or panic when the string consists of multibyte characters. + /// + /// For example, the character `a` in `äa` is stored at byte index 2 but has the character position 1. + /// Using the character position 1 to index into the string will lead to a panic as it is in the middle of the first character. + /// + /// Instead of `.chars().enumerate()`, the correct iterator to use is `.char_indices()`, which yields byte indices. + /// + /// This pattern is technically fine if the strings are known to only use the ASCII subset, + /// but there is also no downside to just using `.char_indices()` directly. + /// + /// You may also want to read the [chapter on strings in the Rust Book](https://doc.rust-lang.org/book/ch08-02-strings.html) + /// which goes into this in more detail. + /// + /// ### Example + /// ```no_run + /// # let s = "..."; + /// for (idx, c) in s.chars().enumerate() { + /// let _ = s[idx..]; // ⚠️ Panics for strings consisting of multibyte characters + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # let s = "..."; + /// for (idx, c) in s.char_indices() { + /// let _ = s[idx..]; + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub CHARS_ENUMERATE_FOR_BYTE_INDICES, + correctness, + "using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected" +} + pub struct Loops { msrv: Msrv, enforce_iter_loop_reborrow: bool, @@ -777,6 +820,7 @@ impl_lint_pass!(Loops => [ UNUSED_ENUMERATE_INDEX, INFINITE_LOOP, MANUAL_SLICE_FILL, + CHARS_ENUMERATE_FOR_BYTE_INDICES, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -860,6 +904,7 @@ impl Loops { manual_flatten::check(cx, pat, arg, body, span, self.msrv); manual_find::check(cx, pat, arg, body, span, expr); unused_enumerate_index::check(cx, pat, arg, body); + chars_enumerate_for_byte_indices::check(cx, pat, arg, body); } fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { diff --git a/tests/ui/chars_enumerate_for_byte_indices.fixed b/tests/ui/chars_enumerate_for_byte_indices.fixed new file mode 100644 index 0000000000000..7a4f87fa2d484 --- /dev/null +++ b/tests/ui/chars_enumerate_for_byte_indices.fixed @@ -0,0 +1,59 @@ +#![feature(round_char_boundary)] +#![warn(clippy::chars_enumerate_for_byte_indices)] + +trait StrExt { + fn use_index(&self, _: usize); +} +impl StrExt for str { + fn use_index(&self, _: usize) {} +} + +fn bad(prim: &str, string: String) { + for (idx, _) in prim.char_indices() { + let _ = prim[..idx]; + //~^ chars_enumerate_for_byte_indices + prim.split_at(idx); + //~^ chars_enumerate_for_byte_indices + + // This won't panic, but it can still return a wrong substring + let _ = prim[..prim.floor_char_boundary(idx)]; + //~^ chars_enumerate_for_byte_indices + + // can't use #[expect] here because the .fixed file will still have the attribute and create an + // unfulfilled expectation, but make sure lint level attributes work on the use expression: + #[allow(clippy::chars_enumerate_for_byte_indices)] + let _ = prim[..idx]; + } + + for c in prim.char_indices() { + let _ = prim[..c.0]; + //~^ chars_enumerate_for_byte_indices + prim.split_at(c.0); + //~^ chars_enumerate_for_byte_indices + } + + for (idx, _) in string.char_indices() { + let _ = string[..idx]; + //~^ chars_enumerate_for_byte_indices + string.split_at(idx); + //~^ chars_enumerate_for_byte_indices + } +} + +fn good(prim: &str, prim2: &str) { + for (idx, _) in prim.chars().enumerate() { + // Indexing into a different string + let _ = prim2[..idx]; + + // Unknown use + std::hint::black_box(idx); + + // Method call to user defined extension trait + prim.use_index(idx); + + // str method taking a usize that doesn't represent a byte index + prim.splitn(idx, prim2); + } +} + +fn main() {} diff --git a/tests/ui/chars_enumerate_for_byte_indices.rs b/tests/ui/chars_enumerate_for_byte_indices.rs new file mode 100644 index 0000000000000..1e8f555846a44 --- /dev/null +++ b/tests/ui/chars_enumerate_for_byte_indices.rs @@ -0,0 +1,59 @@ +#![feature(round_char_boundary)] +#![warn(clippy::chars_enumerate_for_byte_indices)] + +trait StrExt { + fn use_index(&self, _: usize); +} +impl StrExt for str { + fn use_index(&self, _: usize) {} +} + +fn bad(prim: &str, string: String) { + for (idx, _) in prim.chars().enumerate() { + let _ = prim[..idx]; + //~^ chars_enumerate_for_byte_indices + prim.split_at(idx); + //~^ chars_enumerate_for_byte_indices + + // This won't panic, but it can still return a wrong substring + let _ = prim[..prim.floor_char_boundary(idx)]; + //~^ chars_enumerate_for_byte_indices + + // can't use #[expect] here because the .fixed file will still have the attribute and create an + // unfulfilled expectation, but make sure lint level attributes work on the use expression: + #[allow(clippy::chars_enumerate_for_byte_indices)] + let _ = prim[..idx]; + } + + for c in prim.chars().enumerate() { + let _ = prim[..c.0]; + //~^ chars_enumerate_for_byte_indices + prim.split_at(c.0); + //~^ chars_enumerate_for_byte_indices + } + + for (idx, _) in string.chars().enumerate() { + let _ = string[..idx]; + //~^ chars_enumerate_for_byte_indices + string.split_at(idx); + //~^ chars_enumerate_for_byte_indices + } +} + +fn good(prim: &str, prim2: &str) { + for (idx, _) in prim.chars().enumerate() { + // Indexing into a different string + let _ = prim2[..idx]; + + // Unknown use + std::hint::black_box(idx); + + // Method call to user defined extension trait + prim.use_index(idx); + + // str method taking a usize that doesn't represent a byte index + prim.splitn(idx, prim2); + } +} + +fn main() {} diff --git a/tests/ui/chars_enumerate_for_byte_indices.stderr b/tests/ui/chars_enumerate_for_byte_indices.stderr new file mode 100644 index 0000000000000..0159eb0387b9e --- /dev/null +++ b/tests/ui/chars_enumerate_for_byte_indices.stderr @@ -0,0 +1,123 @@ +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/chars_enumerate_for_byte_indices.rs:13:24 + | +LL | let _ = prim[..idx]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/chars_enumerate_for_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ + = note: `-D clippy::chars-enumerate-for-byte-indices` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::chars_enumerate_for_byte_indices)]` +help: consider using `.char_indices()` instead + | +LL | for (idx, _) in prim.char_indices() { + | ~~~~~~~~~~~~~~ + +error: passing a character position to a method that expects a byte index + --> tests/ui/chars_enumerate_for_byte_indices.rs:15:23 + | +LL | prim.split_at(idx); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/chars_enumerate_for_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL | for (idx, _) in prim.char_indices() { + | ~~~~~~~~~~~~~~ + +error: passing a character position to a method that expects a byte index + --> tests/ui/chars_enumerate_for_byte_indices.rs:19:49 + | +LL | let _ = prim[..prim.floor_char_boundary(idx)]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/chars_enumerate_for_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL | for (idx, _) in prim.char_indices() { + | ~~~~~~~~~~~~~~ + +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/chars_enumerate_for_byte_indices.rs:29:24 + | +LL | let _ = prim[..c.0]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/chars_enumerate_for_byte_indices.rs:28:9 + | +LL | for c in prim.chars().enumerate() { + | ^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL | for c in prim.char_indices() { + | ~~~~~~~~~~~~~~ + +error: passing a character position to a method that expects a byte index + --> tests/ui/chars_enumerate_for_byte_indices.rs:31:23 + | +LL | prim.split_at(c.0); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/chars_enumerate_for_byte_indices.rs:28:9 + | +LL | for c in prim.chars().enumerate() { + | ^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL | for c in prim.char_indices() { + | ~~~~~~~~~~~~~~ + +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/chars_enumerate_for_byte_indices.rs:36:26 + | +LL | let _ = string[..idx]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/chars_enumerate_for_byte_indices.rs:35:10 + | +LL | for (idx, _) in string.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL | for (idx, _) in string.char_indices() { + | ~~~~~~~~~~~~~~ + +error: passing a character position to a method that expects a byte index + --> tests/ui/chars_enumerate_for_byte_indices.rs:38:25 + | +LL | string.split_at(idx); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/chars_enumerate_for_byte_indices.rs:35:10 + | +LL | for (idx, _) in string.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL | for (idx, _) in string.char_indices() { + | ~~~~~~~~~~~~~~ + +error: aborting due to 7 previous errors + From 32cf88450bd1d97a1ec1cfcb67c04173ea92db3c Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 3 Dec 2024 04:24:56 +0100 Subject: [PATCH 075/103] add a note for `str::bytes` --- clippy_lints/src/loops/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 10ba739909f79..9a3fd147fb556 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -758,7 +758,8 @@ declare_clippy_lint! { /// Instead of `.chars().enumerate()`, the correct iterator to use is `.char_indices()`, which yields byte indices. /// /// This pattern is technically fine if the strings are known to only use the ASCII subset, - /// but there is also no downside to just using `.char_indices()` directly. + /// though in those cases it would be better to use `bytes()` directly to make the intent clearer, + /// but there is also no downside to just using `.char_indices()` directly and supporting non-ASCII strings. /// /// You may also want to read the [chapter on strings in the Rust Book](https://doc.rust-lang.org/book/ch08-02-strings.html) /// which goes into this in more detail. From d5d2189c715bba5b12585c52ac8e30ac52c484f9 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 3 Dec 2024 04:33:01 +0100 Subject: [PATCH 076/103] rename lint to `char_indices_as_byte_indices` --- CHANGELOG.md | 2 +- clippy_lints/src/declared_lints.rs | 2 +- ...ces.rs => char_indices_as_byte_indices.rs} | 4 +-- clippy_lints/src/loops/mod.rs | 8 ++--- ...xed => char_indices_as_byte_indices.fixed} | 18 +++++------ ...ces.rs => char_indices_as_byte_indices.rs} | 18 +++++------ ...rr => char_indices_as_byte_indices.stderr} | 32 +++++++++---------- 7 files changed, 42 insertions(+), 42 deletions(-) rename clippy_lints/src/loops/{chars_enumerate_for_byte_indices.rs => char_indices_as_byte_indices.rs} (98%) rename tests/ui/{chars_enumerate_for_byte_indices.fixed => char_indices_as_byte_indices.fixed} (74%) rename tests/ui/{chars_enumerate_for_byte_indices.rs => char_indices_as_byte_indices.rs} (75%) rename tests/ui/{chars_enumerate_for_byte_indices.stderr => char_indices_as_byte_indices.stderr} (79%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68387498c0d3b..ee16c442c0f8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5516,8 +5516,8 @@ Released 2018-09-13 [`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes [`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts [`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test +[`char_indices_as_byte_indices`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_indices_as_byte_indices [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 -[`chars_enumerate_for_byte_indices`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_enumerate_for_byte_indices [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index cd3f40a003dbd..7ee898ec75a94 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -287,7 +287,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, - crate::loops::CHARS_ENUMERATE_FOR_BYTE_INDICES_INFO, + crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, diff --git a/clippy_lints/src/loops/chars_enumerate_for_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs similarity index 98% rename from clippy_lints/src/loops/chars_enumerate_for_byte_indices.rs rename to clippy_lints/src/loops/char_indices_as_byte_indices.rs index 40274311d7800..90e6f71e41ad4 100644 --- a/clippy_lints/src/loops/chars_enumerate_for_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -10,7 +10,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; -use super::CHARS_ENUMERATE_FOR_BYTE_INDICES; +use super::CHAR_INDICES_AS_BYTE_INDICES; // The list of `str` methods we want to lint that have a `usize` argument representing a byte index. // Note: `String` also has methods that work with byte indices, @@ -101,7 +101,7 @@ fn check_index_usage<'tcx>( span_lint_hir_and_then( cx, - CHARS_ENUMERATE_FOR_BYTE_INDICES, + CHAR_INDICES_AS_BYTE_INDICES, expr.hir_id, expr.span, message, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 9a3fd147fb556..2b66827e82eeb 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -1,4 +1,4 @@ -mod chars_enumerate_for_byte_indices; +mod char_indices_as_byte_indices; mod empty_loop; mod explicit_counter_loop; mod explicit_into_iter_loop; @@ -779,7 +779,7 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.83.0"] - pub CHARS_ENUMERATE_FOR_BYTE_INDICES, + pub CHAR_INDICES_AS_BYTE_INDICES, correctness, "using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected" } @@ -821,7 +821,7 @@ impl_lint_pass!(Loops => [ UNUSED_ENUMERATE_INDEX, INFINITE_LOOP, MANUAL_SLICE_FILL, - CHARS_ENUMERATE_FOR_BYTE_INDICES, + CHAR_INDICES_AS_BYTE_INDICES, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -905,7 +905,7 @@ impl Loops { manual_flatten::check(cx, pat, arg, body, span, self.msrv); manual_find::check(cx, pat, arg, body, span, expr); unused_enumerate_index::check(cx, pat, arg, body); - chars_enumerate_for_byte_indices::check(cx, pat, arg, body); + char_indices_as_byte_indices::check(cx, pat, arg, body); } fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { diff --git a/tests/ui/chars_enumerate_for_byte_indices.fixed b/tests/ui/char_indices_as_byte_indices.fixed similarity index 74% rename from tests/ui/chars_enumerate_for_byte_indices.fixed rename to tests/ui/char_indices_as_byte_indices.fixed index 7a4f87fa2d484..a7c6bcd2681a5 100644 --- a/tests/ui/chars_enumerate_for_byte_indices.fixed +++ b/tests/ui/char_indices_as_byte_indices.fixed @@ -1,5 +1,5 @@ #![feature(round_char_boundary)] -#![warn(clippy::chars_enumerate_for_byte_indices)] +#![warn(clippy::char_indices_as_byte_indices)] trait StrExt { fn use_index(&self, _: usize); @@ -11,32 +11,32 @@ impl StrExt for str { fn bad(prim: &str, string: String) { for (idx, _) in prim.char_indices() { let _ = prim[..idx]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices prim.split_at(idx); - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices // This won't panic, but it can still return a wrong substring let _ = prim[..prim.floor_char_boundary(idx)]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices // can't use #[expect] here because the .fixed file will still have the attribute and create an // unfulfilled expectation, but make sure lint level attributes work on the use expression: - #[allow(clippy::chars_enumerate_for_byte_indices)] + #[allow(clippy::char_indices_as_byte_indices)] let _ = prim[..idx]; } for c in prim.char_indices() { let _ = prim[..c.0]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices prim.split_at(c.0); - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices } for (idx, _) in string.char_indices() { let _ = string[..idx]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices string.split_at(idx); - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices } } diff --git a/tests/ui/chars_enumerate_for_byte_indices.rs b/tests/ui/char_indices_as_byte_indices.rs similarity index 75% rename from tests/ui/chars_enumerate_for_byte_indices.rs rename to tests/ui/char_indices_as_byte_indices.rs index 1e8f555846a44..bb0f5df19bc76 100644 --- a/tests/ui/chars_enumerate_for_byte_indices.rs +++ b/tests/ui/char_indices_as_byte_indices.rs @@ -1,5 +1,5 @@ #![feature(round_char_boundary)] -#![warn(clippy::chars_enumerate_for_byte_indices)] +#![warn(clippy::char_indices_as_byte_indices)] trait StrExt { fn use_index(&self, _: usize); @@ -11,32 +11,32 @@ impl StrExt for str { fn bad(prim: &str, string: String) { for (idx, _) in prim.chars().enumerate() { let _ = prim[..idx]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices prim.split_at(idx); - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices // This won't panic, but it can still return a wrong substring let _ = prim[..prim.floor_char_boundary(idx)]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices // can't use #[expect] here because the .fixed file will still have the attribute and create an // unfulfilled expectation, but make sure lint level attributes work on the use expression: - #[allow(clippy::chars_enumerate_for_byte_indices)] + #[allow(clippy::char_indices_as_byte_indices)] let _ = prim[..idx]; } for c in prim.chars().enumerate() { let _ = prim[..c.0]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices prim.split_at(c.0); - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices } for (idx, _) in string.chars().enumerate() { let _ = string[..idx]; - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices string.split_at(idx); - //~^ chars_enumerate_for_byte_indices + //~^ char_indices_as_byte_indices } } diff --git a/tests/ui/chars_enumerate_for_byte_indices.stderr b/tests/ui/char_indices_as_byte_indices.stderr similarity index 79% rename from tests/ui/chars_enumerate_for_byte_indices.stderr rename to tests/ui/char_indices_as_byte_indices.stderr index 0159eb0387b9e..a3c8457839258 100644 --- a/tests/ui/chars_enumerate_for_byte_indices.stderr +++ b/tests/ui/char_indices_as_byte_indices.stderr @@ -1,31 +1,31 @@ error: indexing into a string with a character position where a byte index is expected - --> tests/ui/chars_enumerate_for_byte_indices.rs:13:24 + --> tests/ui/char_indices_as_byte_indices.rs:13:24 | LL | let _ = prim[..idx]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/chars_enumerate_for_byte_indices.rs:12:10 + --> tests/ui/char_indices_as_byte_indices.rs:12:10 | LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ - = note: `-D clippy::chars-enumerate-for-byte-indices` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::chars_enumerate_for_byte_indices)]` + = note: `-D clippy::char-indices-as-byte-indices` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::char_indices_as_byte_indices)]` help: consider using `.char_indices()` instead | LL | for (idx, _) in prim.char_indices() { | ~~~~~~~~~~~~~~ error: passing a character position to a method that expects a byte index - --> tests/ui/chars_enumerate_for_byte_indices.rs:15:23 + --> tests/ui/char_indices_as_byte_indices.rs:15:23 | LL | prim.split_at(idx); | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/chars_enumerate_for_byte_indices.rs:12:10 + --> tests/ui/char_indices_as_byte_indices.rs:12:10 | LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ @@ -35,14 +35,14 @@ LL | for (idx, _) in prim.char_indices() { | ~~~~~~~~~~~~~~ error: passing a character position to a method that expects a byte index - --> tests/ui/chars_enumerate_for_byte_indices.rs:19:49 + --> tests/ui/char_indices_as_byte_indices.rs:19:49 | LL | let _ = prim[..prim.floor_char_boundary(idx)]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/chars_enumerate_for_byte_indices.rs:12:10 + --> tests/ui/char_indices_as_byte_indices.rs:12:10 | LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ @@ -52,14 +52,14 @@ LL | for (idx, _) in prim.char_indices() { | ~~~~~~~~~~~~~~ error: indexing into a string with a character position where a byte index is expected - --> tests/ui/chars_enumerate_for_byte_indices.rs:29:24 + --> tests/ui/char_indices_as_byte_indices.rs:29:24 | LL | let _ = prim[..c.0]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/chars_enumerate_for_byte_indices.rs:28:9 + --> tests/ui/char_indices_as_byte_indices.rs:28:9 | LL | for c in prim.chars().enumerate() { | ^ ^^^^^^^^^^^ @@ -69,14 +69,14 @@ LL | for c in prim.char_indices() { | ~~~~~~~~~~~~~~ error: passing a character position to a method that expects a byte index - --> tests/ui/chars_enumerate_for_byte_indices.rs:31:23 + --> tests/ui/char_indices_as_byte_indices.rs:31:23 | LL | prim.split_at(c.0); | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/chars_enumerate_for_byte_indices.rs:28:9 + --> tests/ui/char_indices_as_byte_indices.rs:28:9 | LL | for c in prim.chars().enumerate() { | ^ ^^^^^^^^^^^ @@ -86,14 +86,14 @@ LL | for c in prim.char_indices() { | ~~~~~~~~~~~~~~ error: indexing into a string with a character position where a byte index is expected - --> tests/ui/chars_enumerate_for_byte_indices.rs:36:26 + --> tests/ui/char_indices_as_byte_indices.rs:36:26 | LL | let _ = string[..idx]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/chars_enumerate_for_byte_indices.rs:35:10 + --> tests/ui/char_indices_as_byte_indices.rs:35:10 | LL | for (idx, _) in string.chars().enumerate() { | ^^^ ^^^^^^^^^^^ @@ -103,14 +103,14 @@ LL | for (idx, _) in string.char_indices() { | ~~~~~~~~~~~~~~ error: passing a character position to a method that expects a byte index - --> tests/ui/chars_enumerate_for_byte_indices.rs:38:25 + --> tests/ui/char_indices_as_byte_indices.rs:38:25 | LL | string.split_at(idx); | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/chars_enumerate_for_byte_indices.rs:35:10 + --> tests/ui/char_indices_as_byte_indices.rs:35:10 | LL | for (idx, _) in string.chars().enumerate() { | ^^^ ^^^^^^^^^^^ From c7390279cafacabaf18c927225751623b9da2cb1 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 30 Mar 2025 13:53:12 +0200 Subject: [PATCH 077/103] add comment and `.clone()` test case --- .../src/loops/char_indices_as_byte_indices.rs | 5 ++- tests/ui/char_indices_as_byte_indices.fixed | 6 ++++ tests/ui/char_indices_as_byte_indices.rs | 6 ++++ tests/ui/char_indices_as_byte_indices.stderr | 35 +++++++++++-------- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 90e6f71e41ad4..8916454ada16e 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -84,6 +84,9 @@ fn check_index_usage<'tcx>( let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); let message = match parent_expr.kind { ExprKind::MethodCall(segment, recv, ..) + // We currently only lint `str` methods (which `String` can deref to), so a `.is_str()` check is sufficient here + // (contrary to the `ExprKind::Index` case which needs to handle both with `is_string_like` because `String` implements + // `Index` directly and no deref to `str` would happen in that case). if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str() && BYTE_INDEX_METHODS.contains(&segment.ident.name.as_str()) && eq_expr_value(cx, chars_recv, recv) => @@ -126,7 +129,7 @@ fn check_index_usage<'tcx>( /// but for `.get(..idx)` we want to consider the method call the consuming expression, /// which requires skipping past the range expression. fn index_consumed_at<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { + for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { match node { Node::Expr(expr) if higher::Range::hir(expr).is_some() => {}, Node::ExprField(_) => {}, diff --git a/tests/ui/char_indices_as_byte_indices.fixed b/tests/ui/char_indices_as_byte_indices.fixed index a7c6bcd2681a5..04c8f6782c51e 100644 --- a/tests/ui/char_indices_as_byte_indices.fixed +++ b/tests/ui/char_indices_as_byte_indices.fixed @@ -54,6 +54,12 @@ fn good(prim: &str, prim2: &str) { // str method taking a usize that doesn't represent a byte index prim.splitn(idx, prim2); } + + let mut string = "äa".to_owned(); + for (idx, _) in string.clone().chars().enumerate() { + // Even though the receiver is the same expression, it should not be treated as the same value. + string.clone().remove(idx); + } } fn main() {} diff --git a/tests/ui/char_indices_as_byte_indices.rs b/tests/ui/char_indices_as_byte_indices.rs index bb0f5df19bc76..773a4fc65f12f 100644 --- a/tests/ui/char_indices_as_byte_indices.rs +++ b/tests/ui/char_indices_as_byte_indices.rs @@ -54,6 +54,12 @@ fn good(prim: &str, prim2: &str) { // str method taking a usize that doesn't represent a byte index prim.splitn(idx, prim2); } + + let mut string = "äa".to_owned(); + for (idx, _) in string.clone().chars().enumerate() { + // Even though the receiver is the same expression, it should not be treated as the same value. + string.clone().remove(idx); + } } fn main() {} diff --git a/tests/ui/char_indices_as_byte_indices.stderr b/tests/ui/char_indices_as_byte_indices.stderr index a3c8457839258..e2b4c1db78cf4 100644 --- a/tests/ui/char_indices_as_byte_indices.stderr +++ b/tests/ui/char_indices_as_byte_indices.stderr @@ -14,8 +14,9 @@ LL | for (idx, _) in prim.chars().enumerate() { = help: to override `-D warnings` add `#[allow(clippy::char_indices_as_byte_indices)]` help: consider using `.char_indices()` instead | -LL | for (idx, _) in prim.char_indices() { - | ~~~~~~~~~~~~~~ +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | error: passing a character position to a method that expects a byte index --> tests/ui/char_indices_as_byte_indices.rs:15:23 @@ -31,8 +32,9 @@ LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ help: consider using `.char_indices()` instead | -LL | for (idx, _) in prim.char_indices() { - | ~~~~~~~~~~~~~~ +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | error: passing a character position to a method that expects a byte index --> tests/ui/char_indices_as_byte_indices.rs:19:49 @@ -48,8 +50,9 @@ LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ help: consider using `.char_indices()` instead | -LL | for (idx, _) in prim.char_indices() { - | ~~~~~~~~~~~~~~ +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | error: indexing into a string with a character position where a byte index is expected --> tests/ui/char_indices_as_byte_indices.rs:29:24 @@ -65,8 +68,9 @@ LL | for c in prim.chars().enumerate() { | ^ ^^^^^^^^^^^ help: consider using `.char_indices()` instead | -LL | for c in prim.char_indices() { - | ~~~~~~~~~~~~~~ +LL - for c in prim.chars().enumerate() { +LL + for c in prim.char_indices() { + | error: passing a character position to a method that expects a byte index --> tests/ui/char_indices_as_byte_indices.rs:31:23 @@ -82,8 +86,9 @@ LL | for c in prim.chars().enumerate() { | ^ ^^^^^^^^^^^ help: consider using `.char_indices()` instead | -LL | for c in prim.char_indices() { - | ~~~~~~~~~~~~~~ +LL - for c in prim.chars().enumerate() { +LL + for c in prim.char_indices() { + | error: indexing into a string with a character position where a byte index is expected --> tests/ui/char_indices_as_byte_indices.rs:36:26 @@ -99,8 +104,9 @@ LL | for (idx, _) in string.chars().enumerate() { | ^^^ ^^^^^^^^^^^ help: consider using `.char_indices()` instead | -LL | for (idx, _) in string.char_indices() { - | ~~~~~~~~~~~~~~ +LL - for (idx, _) in string.chars().enumerate() { +LL + for (idx, _) in string.char_indices() { + | error: passing a character position to a method that expects a byte index --> tests/ui/char_indices_as_byte_indices.rs:38:25 @@ -116,8 +122,9 @@ LL | for (idx, _) in string.chars().enumerate() { | ^^^ ^^^^^^^^^^^ help: consider using `.char_indices()` instead | -LL | for (idx, _) in string.char_indices() { - | ~~~~~~~~~~~~~~ +LL - for (idx, _) in string.chars().enumerate() { +LL + for (idx, _) in string.char_indices() { + | error: aborting due to 7 previous errors From 2eee361a1ae8146d5f499f88690c71a7867828d2 Mon Sep 17 00:00:00 2001 From: WeiTheShinobi Date: Sun, 30 Mar 2025 22:00:01 +0800 Subject: [PATCH 078/103] use `is_automatically_derived()` instead of `has_attr(sym::automatically_derived)` --- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/derive.rs | 6 +++--- clippy_lints/src/missing_fields_in_debug.rs | 2 +- clippy_lints/src/partialeq_ne_impl.rs | 2 +- clippy_lints/src/unconditional_recursion.rs | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 479422f7378ed..10331b3855b84 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { self_ty, .. }) = item.kind - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && !item.span.from_expansion() && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::Default, def_id) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index e5ec17d89a115..a05d57d6836a5 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived); + let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -235,7 +235,7 @@ fn check_hash_peq<'tcx>( { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); + let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); if !hash_is_automatically_derived || peq_is_automatically_derived { return; @@ -278,7 +278,7 @@ fn check_ord_partial_ord<'tcx>( { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); + let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 05517a5d869c9..1932d2d5f9785 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -209,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id) // don't trigger if this impl was derived - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && !item.span.from_expansion() // find `Debug::fmt` function && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt) diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 65671b478ba74..8eaf65e63065e 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { items: impl_items, .. }) = item.kind - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && let Some(eq_trait) = cx.tcx.lang_items().eq_trait() && trait_ref.path.res.def_id() == eq_trait { diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index 11e938b5e4dd5..e07257f4aef07 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -135,7 +135,7 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt }), )) = cx.tcx.hir_parent_iter(hir_id).next() // We exclude `impl` blocks generated from rustc's proc macros. - && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. && let Some(trait_) = impl_.of_trait { @@ -240,7 +240,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local }), )) = cx.tcx.hir_parent_iter(hir_id).next() // We exclude `impl` blocks generated from rustc's proc macros. - && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. && let Some(trait_) = impl_.of_trait && let Some(trait_def_id) = trait_.trait_def_id() @@ -337,7 +337,7 @@ impl UnconditionalRecursion { for (ty, impl_def_ids) in impls.non_blanket_impls() { let Some(self_def_id) = ty.def() else { continue }; for impl_def_id in impl_def_ids { - if !cx.tcx.has_attr(*impl_def_id, sym::automatically_derived) && + if !cx.tcx.is_automatically_derived(*impl_def_id) && let Some(assoc_item) = cx .tcx .associated_items(impl_def_id) From 5f26d0e9703544c751952ba5e13bb55a64f55abf Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 9 Mar 2025 14:55:46 +0100 Subject: [PATCH 079/103] Drop `clippy::invalid_null_ptr_usage` --- clippy_lints/src/declared_lints.rs | 1 - clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/ptr.rs | 73 +--------- tests/ui/crashes/ice-1782.rs | 2 +- tests/ui/invalid_null_ptr_usage.fixed | 66 --------- tests/ui/invalid_null_ptr_usage.rs | 66 --------- tests/ui/invalid_null_ptr_usage.stderr | 136 ------------------ tests/ui/invalid_null_ptr_usage_no_std.fixed | 79 ---------- tests/ui/invalid_null_ptr_usage_no_std.rs | 79 ---------- tests/ui/invalid_null_ptr_usage_no_std.stderr | 136 ------------------ tests/ui/rename.fixed | 1 + tests/ui/rename.rs | 1 + tests/ui/rename.stderr | 30 ++-- 13 files changed, 24 insertions(+), 648 deletions(-) delete mode 100644 tests/ui/invalid_null_ptr_usage.fixed delete mode 100644 tests/ui/invalid_null_ptr_usage.rs delete mode 100644 tests/ui/invalid_null_ptr_usage.stderr delete mode 100644 tests/ui/invalid_null_ptr_usage_no_std.fixed delete mode 100644 tests/ui/invalid_null_ptr_usage_no_std.rs delete mode 100644 tests/ui/invalid_null_ptr_usage_no_std.stderr diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7fa23dad69817..39e4516370709 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -638,7 +638,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::precedence::PRECEDENCE_INFO, crate::precedence::PRECEDENCE_BITS_INFO, crate::ptr::CMP_NULL_INFO, - crate::ptr::INVALID_NULL_PTR_USAGE_INFO, crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, crate::ptr::PTR_EQ_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 0031da406f17f..de66ead4f4204 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -131,6 +131,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::clone_double_ref", "suspicious_double_ref_op"), #[clippy::version = ""] ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = "CURRENT_RUSTC_VERSION"] + ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), #[clippy::version = "1.86.0"] ("clippy::double_neg", "double_negations"), #[clippy::version = ""] diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 55f1ece05593f..50ef56db167c1 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -125,29 +125,6 @@ declare_clippy_lint! { "fns that create mutable refs from immutable ref args" } -declare_clippy_lint! { - /// ### What it does - /// This lint checks for invalid usages of `ptr::null`. - /// - /// ### Why is this bad? - /// This causes undefined behavior. - /// - /// ### Example - /// ```ignore - /// // Undefined behavior - /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } - /// ``` - /// - /// Use instead: - /// ```ignore - /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } - /// ``` - #[clippy::version = "1.53.0"] - pub INVALID_NULL_PTR_USAGE, - correctness, - "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" -} - declare_clippy_lint! { /// ### What it does /// Use `std::ptr::eq` when applicable @@ -177,7 +154,7 @@ declare_clippy_lint! { "use `std::ptr::eq` when comparing raw pointers" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE, PTR_EQ]); +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { @@ -301,54 +278,6 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { format!("{non_null_path_snippet}.is_null()"), Applicability::MachineApplicable, ); - } else { - check_invalid_ptr_usage(cx, expr); - } - } -} - -fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Call(fun, args) = expr.kind - && let ExprKind::Path(ref qpath) = fun.kind - && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() - && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id) - { - // TODO: `ptr_slice_from_raw_parts` and its mutable variant should probably still be linted - // conditionally based on how the return value is used, but not universally like the other - // functions since there are valid uses for null slice pointers. - // - // See: https://github.com/rust-lang/rust-clippy/pull/13452/files#r1773772034 - - // `arg` positions where null would cause U.B. - let arg_indices: &[_] = match name { - sym::ptr_read - | sym::ptr_read_unaligned - | sym::ptr_read_volatile - | sym::ptr_replace - | sym::ptr_write - | sym::ptr_write_bytes - | sym::ptr_write_unaligned - | sym::ptr_write_volatile - | sym::slice_from_raw_parts - | sym::slice_from_raw_parts_mut => &[0], - sym::ptr_copy | sym::ptr_copy_nonoverlapping | sym::ptr_swap | sym::ptr_swap_nonoverlapping => &[0, 1], - _ => return, - }; - - for &arg_idx in arg_indices { - if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) - && let Some(std_or_core) = std_or_core(cx) - { - span_lint_and_sugg( - cx, - INVALID_NULL_PTR_USAGE, - arg.span, - "pointer must be non-null", - "change this to", - format!("{std_or_core}::ptr::NonNull::dangling().as_ptr()"), - Applicability::MachineApplicable, - ); - } } } } diff --git a/tests/ui/crashes/ice-1782.rs b/tests/ui/crashes/ice-1782.rs index fefdc405cce2a..4a1886c08af6a 100644 --- a/tests/ui/crashes/ice-1782.rs +++ b/tests/ui/crashes/ice-1782.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(dead_code, unused_variables)] +#![allow(dead_code, unused_variables, invalid_null_arguments)] #![allow(clippy::unnecessary_cast, clippy::missing_transmute_annotations)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/tests/ui/invalid_null_ptr_usage.fixed b/tests/ui/invalid_null_ptr_usage.fixed deleted file mode 100644 index ce78e89ee829c..0000000000000 --- a/tests/ui/invalid_null_ptr_usage.fixed +++ /dev/null @@ -1,66 +0,0 @@ -fn main() { - unsafe { - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(std::mem::size_of::(), 0); - - let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::replace(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - - std::ptr::swap::(std::ptr::NonNull::dangling().as_ptr(), &mut A); - //~^ invalid_null_ptr_usage - std::ptr::swap::(&mut A, std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - std::ptr::swap_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), &mut A, 0); - //~^ invalid_null_ptr_usage - std::ptr::swap_nonoverlapping::(&mut A, std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::write(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_unaligned(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_volatile(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_bytes::(std::ptr::NonNull::dangling().as_ptr(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage.rs b/tests/ui/invalid_null_ptr_usage.rs deleted file mode 100644 index 361865fbd9601..0000000000000 --- a/tests/ui/invalid_null_ptr_usage.rs +++ /dev/null @@ -1,66 +0,0 @@ -fn main() { - unsafe { - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(std::mem::size_of::(), 0); - - let _a: A = std::ptr::read(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_unaligned(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_volatile(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - - std::ptr::swap::(std::ptr::null_mut(), &mut A); - //~^ invalid_null_ptr_usage - std::ptr::swap::(&mut A, std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); - //~^ invalid_null_ptr_usage - std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::write(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_unaligned(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_volatile(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage.stderr b/tests/ui/invalid_null_ptr_usage.stderr deleted file mode 100644 index 3f9d15b904018..0000000000000 --- a/tests/ui/invalid_null_ptr_usage.stderr +++ /dev/null @@ -1,136 +0,0 @@ -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:3:59 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - | - = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:5:59 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:8:63 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:11:33 - | -LL | std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:13:73 - | -LL | std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:16:48 - | -LL | std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:18:88 - | -LL | std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:24:36 - | -LL | let _a: A = std::ptr::read(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:26:36 - | -LL | let _a: A = std::ptr::read(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:29:46 - | -LL | let _a: A = std::ptr::read_unaligned(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:31:46 - | -LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:34:45 - | -LL | let _a: A = std::ptr::read_volatile(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:36:45 - | -LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:39:39 - | -LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:44:29 - | -LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:46:37 - | -LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:49:44 - | -LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:51:52 - | -LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:54:25 - | -LL | std::ptr::write(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:57:35 - | -LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:60:34 - | -LL | std::ptr::write_volatile(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:63:40 - | -LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: aborting due to 22 previous errors - diff --git a/tests/ui/invalid_null_ptr_usage_no_std.fixed b/tests/ui/invalid_null_ptr_usage_no_std.fixed deleted file mode 100644 index df7ab166187de..0000000000000 --- a/tests/ui/invalid_null_ptr_usage_no_std.fixed +++ /dev/null @@ -1,79 +0,0 @@ -#![no_std] -#![feature(lang_items)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn main() { - unsafe { - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(core::mem::size_of::(), 0); - - let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); - - core::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); - //~^ invalid_null_ptr_usage - core::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - core::ptr::swap_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); - //~^ invalid_null_ptr_usage - core::ptr::swap_nonoverlapping::(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_bytes::(core::ptr::NonNull::dangling().as_ptr(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage_no_std.rs b/tests/ui/invalid_null_ptr_usage_no_std.rs deleted file mode 100644 index 38ddfff055353..0000000000000 --- a/tests/ui/invalid_null_ptr_usage_no_std.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![no_std] -#![feature(lang_items)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn main() { - unsafe { - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy_nonoverlapping::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(core::mem::size_of::(), 0); - - let _a: A = core::ptr::read(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_unaligned(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_volatile(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::replace(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); - - core::ptr::swap::(core::ptr::null_mut(), &mut A); - //~^ invalid_null_ptr_usage - core::ptr::swap::(&mut A, core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - core::ptr::swap_nonoverlapping::(core::ptr::null_mut(), &mut A, 0); - //~^ invalid_null_ptr_usage - core::ptr::swap_nonoverlapping::(&mut A, core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::write(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_unaligned(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_volatile(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_bytes::(core::ptr::null_mut(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/tests/ui/invalid_null_ptr_usage_no_std.stderr b/tests/ui/invalid_null_ptr_usage_no_std.stderr deleted file mode 100644 index b5dd21ce6248f..0000000000000 --- a/tests/ui/invalid_null_ptr_usage_no_std.stderr +++ /dev/null @@ -1,136 +0,0 @@ -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:16:60 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - | - = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:18:60 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:21:64 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:24:34 - | -LL | core::ptr::copy::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:26:75 - | -LL | core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:29:49 - | -LL | core::ptr::copy_nonoverlapping::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:31:90 - | -LL | core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:37:37 - | -LL | let _a: A = core::ptr::read(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:39:37 - | -LL | let _a: A = core::ptr::read(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:42:47 - | -LL | let _a: A = core::ptr::read_unaligned(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:44:47 - | -LL | let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:47:46 - | -LL | let _a: A = core::ptr::read_volatile(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:49:46 - | -LL | let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:52:40 - | -LL | let _a: A = core::ptr::replace(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:57:30 - | -LL | core::ptr::swap::(core::ptr::null_mut(), &mut A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:59:38 - | -LL | core::ptr::swap::(&mut A, core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:62:45 - | -LL | core::ptr::swap_nonoverlapping::(core::ptr::null_mut(), &mut A, 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:64:53 - | -LL | core::ptr::swap_nonoverlapping::(&mut A, core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:67:26 - | -LL | core::ptr::write(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:70:36 - | -LL | core::ptr::write_unaligned(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:73:35 - | -LL | core::ptr::write_volatile(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:76:41 - | -LL | core::ptr::write_bytes::(core::ptr::null_mut(), 42, 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: aborting due to 22 previous errors - diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 501811fa491b3..7964047069689 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -119,6 +119,7 @@ #![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` #![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` #![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 7f4b8062e1b4b..aa7b905b4b818 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -119,6 +119,7 @@ #![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` #![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` #![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index f24eaec3917ad..b3c88167c1115 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -343,71 +343,77 @@ error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_fro LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` -error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` +error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` --> tests/ui/rename.rs:122:9 | +LL | #![warn(clippy::invalid_null_ptr_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` + +error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` + --> tests/ui/rename.rs:123:9 + | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: aborting due to 68 previous errors +error: aborting due to 69 previous errors From 9172556de8db2ea7041073215936ba27b3395622 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 31 Mar 2025 12:39:41 +0000 Subject: [PATCH 080/103] Suggest `./x test src/tools/clippy --bless` when in `rust-lang/rust` --- tests/compile-test.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 956a05288f358..24dc5c545ef73 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -147,7 +147,11 @@ impl TestContext { .map(|filters| filters.split(',').map(str::to_string).collect()) .unwrap_or_default(), target: None, - bless_command: Some("cargo uibless".into()), + bless_command: Some(if IS_RUSTC_TEST_SUITE { + "./x test src/tools/clippy --bless".into() + } else { + "cargo uibless".into() + }), out_dir: target_dir.join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; From ce39784f13022efa0f3b7f23c63150ae51c34d44 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 14 Mar 2025 14:47:03 +0000 Subject: [PATCH 081/103] Move `FindPanicUnwrap` to `missing_headers.rs` --- clippy_lints/src/doc/missing_headers.rs | 78 +++++++++++++++++-- clippy_lints/src/doc/mod.rs | 99 ++----------------------- 2 files changed, 79 insertions(+), 98 deletions(-) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index e75abf28bace8..65d2ff83c259e 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,9 +1,13 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; -use clippy_utils::{is_doc_hidden, return_ty}; -use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; +use clippy_utils::visitors::Visitable; +use clippy_utils::{is_doc_hidden, method_chain_args, return_ty}; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{AnonConst, BodyId, Expr, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty; use rustc_span::{Span, sym}; @@ -13,7 +17,6 @@ pub fn check( sig: FnSig<'_>, headers: DocHeaders, body_id: Option, - panic_info: Option<(Span, bool)>, check_private_items: bool, ) { if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) { @@ -46,13 +49,16 @@ pub fn check( ), _ => (), } - if !headers.panics && panic_info.is_some_and(|el| !el.1) { + if !headers.panics + && let Some(body_id) = body_id + && let Some((panic_span, false)) = FindPanicUnwrap::find_span(cx, body_id) + { span_lint_and_note( cx, MISSING_PANICS_DOC, span, "docs for function which may panic missing `# Panics` section", - panic_info.map(|el| el.0), + Some(panic_span), "first possible panic found here", ); } @@ -89,3 +95,65 @@ pub fn check( } } } + +struct FindPanicUnwrap<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + is_const: bool, + panic_span: Option, + typeck_results: &'tcx ty::TypeckResults<'tcx>, +} + +impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { + pub fn find_span(cx: &'a LateContext<'tcx>, body_id: BodyId) -> Option<(Span, bool)> { + let mut vis = Self { + cx, + is_const: false, + panic_span: None, + typeck_results: cx.tcx.typeck_body(body_id), + }; + cx.tcx.hir_body(body_id).visit(&mut vis); + vis.panic_span.map(|el| (el, vis.is_const)) + } +} + +impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { + type NestedFilter = OnlyBodies; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.panic_span.is_some() { + return; + } + + if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { + if is_panic(self.cx, macro_call.def_id) + || matches!( + self.cx.tcx.item_name(macro_call.def_id).as_str(), + "assert" | "assert_eq" | "assert_ne" + ) + { + self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id); + self.panic_span = Some(macro_call.span); + } + } + + // check for `unwrap` and `expect` for both `Option` and `Result` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) { + let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); + if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option) + || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result) + { + self.panic_span = Some(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + // Panics in const blocks will cause compilation to fail. + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index d0075d01eeaa4..ce0e3cae51c6c 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -3,11 +3,8 @@ use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::Visitable; -use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item}; use pulldown_cmark::Event::{ Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text, @@ -16,18 +13,15 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, It use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; +use rustc_hir::{Attribute, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments, }; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::edition::Edition; -use rustc_span::{Span, sym}; use std::ops::Range; use url::Url; @@ -657,20 +651,16 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { self.check_private_items, ); match item.kind { - ItemKind::Fn { sig, body: body_id, .. } => { + ItemKind::Fn { sig, body, .. } => { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || item.span.in_external_macro(cx.tcx.sess.source_map())) { - let body = cx.tcx.hir_body(body_id); - - let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); missing_headers::check( cx, item.owner_id, sig, headers, - Some(body_id), - panic_info, + Some(body), self.check_private_items, ); } @@ -697,15 +687,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { if let TraitItemKind::Fn(sig, ..) = trait_item.kind && !trait_item.span.in_external_macro(cx.tcx.sess.source_map()) { - missing_headers::check( - cx, - trait_item.owner_id, - sig, - headers, - None, - None, - self.check_private_items, - ); + missing_headers::check(cx, trait_item.owner_id, sig, headers, None, self.check_private_items); } }, Node::ImplItem(impl_item) => { @@ -713,16 +695,12 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { && !impl_item.span.in_external_macro(cx.tcx.sess.source_map()) && !is_trait_impl_item(cx, impl_item.hir_id()) { - let body = cx.tcx.hir_body(body_id); - - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value); missing_headers::check( cx, impl_item.owner_id, sig, headers, Some(body_id), - panic_span, self.check_private_items, ); } @@ -1168,71 +1146,6 @@ fn check_doc<'a, Events: Iterator, Range { - cx: &'a LateContext<'tcx>, - is_const: bool, - panic_span: Option, - typeck_results: &'tcx ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { - pub fn find_span( - cx: &'a LateContext<'tcx>, - typeck_results: &'tcx ty::TypeckResults<'tcx>, - body: impl Visitable<'tcx>, - ) -> Option<(Span, bool)> { - let mut vis = Self { - cx, - is_const: false, - panic_span: None, - typeck_results, - }; - body.visit(&mut vis); - vis.panic_span.map(|el| (el, vis.is_const)) - } -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.panic_span.is_some() { - return; - } - - if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) - && (is_panic(self.cx, macro_call.def_id) - || matches!( - self.cx.tcx.item_name(macro_call.def_id).as_str(), - "assert" | "assert_eq" | "assert_ne" - )) - { - self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id); - self.panic_span = Some(macro_call.span); - } - - // check for `unwrap` and `expect` for both `Option` and `Result` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result) - { - self.panic_span = Some(expr.span); - } - } - - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - // Panics in const blocks will cause compilation to fail. - fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } -} - #[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type fn looks_like_refdef(doc: &str, range: Range) -> Option> { if range.end < range.start { From c89368537cc76c83ecc7ea7282423f9a189e02aa Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 14 Mar 2025 15:02:00 +0000 Subject: [PATCH 082/103] Fix partially const bodies not linting `missing_panics_doc` --- clippy_lints/src/doc/missing_headers.rs | 27 +++++++++--------- tests/ui/missing_panics_doc.rs | 10 +++++++ tests/ui/missing_panics_doc.stderr | 38 ++++++++++++++++--------- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 65d2ff83c259e..b07326c524cc4 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,7 +1,7 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; +use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::visitors::Visitable; use clippy_utils::{is_doc_hidden, method_chain_args, return_ty}; use rustc_hir::intravisit::{self, Visitor}; @@ -51,7 +51,7 @@ pub fn check( } if !headers.panics && let Some(body_id) = body_id - && let Some((panic_span, false)) = FindPanicUnwrap::find_span(cx, body_id) + && let Some(panic_span) = FindPanicUnwrap::find_span(cx, body_id) { span_lint_and_note( cx, @@ -98,21 +98,19 @@ pub fn check( struct FindPanicUnwrap<'a, 'tcx> { cx: &'a LateContext<'tcx>, - is_const: bool, panic_span: Option, typeck_results: &'tcx ty::TypeckResults<'tcx>, } impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { - pub fn find_span(cx: &'a LateContext<'tcx>, body_id: BodyId) -> Option<(Span, bool)> { + pub fn find_span(cx: &'a LateContext<'tcx>, body_id: BodyId) -> Option { let mut vis = Self { cx, - is_const: false, panic_span: None, typeck_results: cx.tcx.typeck_body(body_id), }; cx.tcx.hir_body(body_id).visit(&mut vis); - vis.panic_span.map(|el| (el, vis.is_const)) + vis.panic_span } } @@ -125,13 +123,13 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { } if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { - if is_panic(self.cx, macro_call.def_id) + if (is_panic(self.cx, macro_call.def_id) || matches!( - self.cx.tcx.item_name(macro_call.def_id).as_str(), - "assert" | "assert_eq" | "assert_ne" - ) + self.cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) + )) + && !self.cx.tcx.hir_is_inside_const_context(expr.hir_id) { - self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id); self.panic_span = Some(macro_call.span); } } @@ -139,9 +137,10 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { // check for `unwrap` and `expect` for both `Option` and `Result` if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result) - { + if matches!( + get_type_diagnostic_name(self.cx, receiver_ty), + Some(sym::Option | sym::Result) + ) { self.panic_span = Some(expr.span); } } diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index 95e361c5d5556..95236d33fd8b3 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -151,6 +151,16 @@ pub fn debug_assertions() { debug_assert_ne!(1, 2); } +pub fn partially_const(n: usize) { + //~^ missing_panics_doc + + const { + assert!(N > 5); + } + + assert!(N > n); +} + // all function must be triggered the lint. // `pub` is required, because the lint does not consider unreachable items pub mod issue10240 { diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index a83e2fa367dd3..80ab5b064730b 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -73,76 +73,88 @@ LL | assert_ne!(x, 0); | ^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:157:5 + --> tests/ui/missing_panics_doc.rs:154:1 + | +LL | pub fn partially_const(n: usize) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:161:5 + | +LL | assert!(N > n); + | ^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:167:5 | LL | pub fn option_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:160:9 + --> tests/ui/missing_panics_doc.rs:170:9 | LL | o.unwrap() | ^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:163:5 + --> tests/ui/missing_panics_doc.rs:173:5 | LL | pub fn option_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:166:9 + --> tests/ui/missing_panics_doc.rs:176:9 | LL | o.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:169:5 + --> tests/ui/missing_panics_doc.rs:179:5 | LL | pub fn result_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:172:9 + --> tests/ui/missing_panics_doc.rs:182:9 | LL | res.unwrap() | ^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:175:5 + --> tests/ui/missing_panics_doc.rs:185:5 | LL | pub fn result_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:178:9 + --> tests/ui/missing_panics_doc.rs:188:9 | LL | res.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:181:5 + --> tests/ui/missing_panics_doc.rs:191:5 | LL | pub fn last_unwrap(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:183:10 + --> tests/ui/missing_panics_doc.rs:193:10 | LL | *v.last().unwrap() | ^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:186:5 + --> tests/ui/missing_panics_doc.rs:196:5 | LL | pub fn last_expect(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:188:10 + --> tests/ui/missing_panics_doc.rs:198:10 | LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors From 6ee4f34741877a4070c889f75dffe775f736ab36 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 14 Mar 2025 15:22:33 +0000 Subject: [PATCH 083/103] Abide by `allow`/`expect` in bodies for `missing_panics_doc` --- clippy_lints/src/doc/missing_headers.rs | 6 ++-- clippy_lints/src/doc/mod.rs | 13 +++++++++ tests/ui/missing_panics_doc.rs | 20 +++++++++++++ tests/ui/missing_panics_doc.stderr | 38 ++++++++++++++++--------- 4 files changed, 62 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index b07326c524cc4..dff6fb932b254 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::visitors::Visitable; -use clippy_utils::{is_doc_hidden, method_chain_args, return_ty}; +use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, BodyId, Expr, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; @@ -129,6 +129,7 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) )) && !self.cx.tcx.hir_is_inside_const_context(expr.hir_id) + && !fulfill_or_allowed(self.cx, MISSING_PANICS_DOC, [expr.hir_id]) { self.panic_span = Some(macro_call.span); } @@ -140,7 +141,8 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { if matches!( get_type_diagnostic_name(self.cx, receiver_ty), Some(sym::Option | sym::Result) - ) { + ) && !fulfill_or_allowed(self.cx, MISSING_PANICS_DOC, [expr.hir_id]) + { self.panic_span = Some(expr.span); } } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ce0e3cae51c6c..ab77edf1147cb 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -188,6 +188,19 @@ declare_clippy_lint! { /// } /// } /// ``` + /// + /// Individual panics within a function can be ignored with `#[expect]` or + /// `#[allow]`: + /// + /// ```no_run + /// # use std::num::NonZeroUsize; + /// pub fn will_not_panic(x: usize) { + /// #[expect(clippy::missing_panics_doc, reason = "infallible")] + /// let y = NonZeroUsize::new(1).unwrap(); + /// + /// // If any panics are added in the future the lint will still catch them + /// } + /// ``` #[clippy::version = "1.51.0"] pub MISSING_PANICS_DOC, pedantic, diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index 95236d33fd8b3..ac6431b5452b4 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -161,6 +161,26 @@ pub fn partially_const(n: usize) { assert!(N > n); } +pub fn expect_allow(i: Option) { + #[expect(clippy::missing_panics_doc)] + i.unwrap(); + + #[allow(clippy::missing_panics_doc)] + i.unwrap(); +} + +pub fn expect_allow_with_error(i: Option) { + //~^ missing_panics_doc + + #[expect(clippy::missing_panics_doc)] + i.unwrap(); + + #[allow(clippy::missing_panics_doc)] + i.unwrap(); + + i.unwrap(); +} + // all function must be triggered the lint. // `pub` is required, because the lint does not consider unreachable items pub mod issue10240 { diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index 80ab5b064730b..dbacd15865df4 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -85,76 +85,88 @@ LL | assert!(N > n); | ^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:167:5 + --> tests/ui/missing_panics_doc.rs:172:1 + | +LL | pub fn expect_allow_with_error(i: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:181:5 + | +LL | i.unwrap(); + | ^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:187:5 | LL | pub fn option_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:170:9 + --> tests/ui/missing_panics_doc.rs:190:9 | LL | o.unwrap() | ^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:173:5 + --> tests/ui/missing_panics_doc.rs:193:5 | LL | pub fn option_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:176:9 + --> tests/ui/missing_panics_doc.rs:196:9 | LL | o.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:179:5 + --> tests/ui/missing_panics_doc.rs:199:5 | LL | pub fn result_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:182:9 + --> tests/ui/missing_panics_doc.rs:202:9 | LL | res.unwrap() | ^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:185:5 + --> tests/ui/missing_panics_doc.rs:205:5 | LL | pub fn result_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:188:9 + --> tests/ui/missing_panics_doc.rs:208:9 | LL | res.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:191:5 + --> tests/ui/missing_panics_doc.rs:211:5 | LL | pub fn last_unwrap(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:193:10 + --> tests/ui/missing_panics_doc.rs:213:10 | LL | *v.last().unwrap() | ^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:196:5 + --> tests/ui/missing_panics_doc.rs:216:5 | LL | pub fn last_expect(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:198:10 + --> tests/ui/missing_panics_doc.rs:218:10 | LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors From f894a81654d6fd67137ba0676bfbd295a48cb471 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 22 Mar 2025 16:25:19 +0000 Subject: [PATCH 084/103] Fulfill expectations after first `missing-panics-doc` --- clippy_lints/src/doc/missing_headers.rs | 86 +++++++++---------------- tests/ui/missing_panics_doc.rs | 9 +++ tests/ui/missing_panics_doc.stderr | 38 +++++++---- 3 files changed, 63 insertions(+), 70 deletions(-) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index dff6fb932b254..039937e0207b0 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -2,14 +2,13 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_D use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; -use clippy_utils::visitors::Visitable; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AnonConst, BodyId, Expr, FnSig, OwnerId, Safety}; +use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; -use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty; use rustc_span::{Span, sym}; +use std::ops::ControlFlow; pub fn check( cx: &LateContext<'_>, @@ -51,7 +50,7 @@ pub fn check( } if !headers.panics && let Some(body_id) = body_id - && let Some(panic_span) = FindPanicUnwrap::find_span(cx, body_id) + && let Some(panic_span) = find_panic(cx, body_id) { span_lint_and_note( cx, @@ -96,65 +95,38 @@ pub fn check( } } -struct FindPanicUnwrap<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - panic_span: Option, - typeck_results: &'tcx ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { - pub fn find_span(cx: &'a LateContext<'tcx>, body_id: BodyId) -> Option { - let mut vis = Self { - cx, - panic_span: None, - typeck_results: cx.tcx.typeck_body(body_id), - }; - cx.tcx.hir_body(body_id).visit(&mut vis); - vis.panic_span - } -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { - type NestedFilter = OnlyBodies; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.panic_span.is_some() { - return; - } - - if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { - if (is_panic(self.cx, macro_call.def_id) +fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { + let mut panic_span = None; + let typeck = cx.tcx.typeck_body(body_id); + for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && (is_panic(cx, macro_call.def_id) || matches!( - self.cx.tcx.get_diagnostic_name(macro_call.def_id), + cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) )) - && !self.cx.tcx.hir_is_inside_const_context(expr.hir_id) - && !fulfill_or_allowed(self.cx, MISSING_PANICS_DOC, [expr.hir_id]) - { - self.panic_span = Some(macro_call.span); - } + && !cx.tcx.hir_is_inside_const_context(expr.hir_id) + && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) + && panic_span.is_none() + { + panic_span = Some(macro_call.span); } // check for `unwrap` and `expect` for both `Option` and `Result` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if matches!( - get_type_diagnostic_name(self.cx, receiver_ty), + if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or_else(|| method_chain_args(expr, &["expect"])) + && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() + && matches!( + get_type_diagnostic_name(cx, receiver_ty), Some(sym::Option | sym::Result) - ) && !fulfill_or_allowed(self.cx, MISSING_PANICS_DOC, [expr.hir_id]) - { - self.panic_span = Some(expr.span); - } + ) + && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) + && panic_span.is_none() + { + panic_span = Some(expr.span); } - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - // Panics in const blocks will cause compilation to fail. - fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } + // Visit all nodes to fulfill any `#[expect]`s after the first linted panic + ControlFlow::::Continue(()) + }); + panic_span } diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index ac6431b5452b4..ffdae8504f72e 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -181,6 +181,15 @@ pub fn expect_allow_with_error(i: Option) { i.unwrap(); } +pub fn expect_after_error(x: Option, y: Option) { + //~^ missing_panics_doc + + let x = x.unwrap(); + + #[expect(clippy::missing_panics_doc)] + let y = y.unwrap(); +} + // all function must be triggered the lint. // `pub` is required, because the lint does not consider unreachable items pub mod issue10240 { diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index dbacd15865df4..7f0acf8de9b77 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -97,76 +97,88 @@ LL | i.unwrap(); | ^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:187:5 + --> tests/ui/missing_panics_doc.rs:184:1 + | +LL | pub fn expect_after_error(x: Option, y: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:187:13 + | +LL | let x = x.unwrap(); + | ^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:196:5 | LL | pub fn option_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:190:9 + --> tests/ui/missing_panics_doc.rs:199:9 | LL | o.unwrap() | ^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:193:5 + --> tests/ui/missing_panics_doc.rs:202:5 | LL | pub fn option_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:196:9 + --> tests/ui/missing_panics_doc.rs:205:9 | LL | o.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:199:5 + --> tests/ui/missing_panics_doc.rs:208:5 | LL | pub fn result_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:202:9 + --> tests/ui/missing_panics_doc.rs:211:9 | LL | res.unwrap() | ^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:205:5 + --> tests/ui/missing_panics_doc.rs:214:5 | LL | pub fn result_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:208:9 + --> tests/ui/missing_panics_doc.rs:217:9 | LL | res.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:211:5 + --> tests/ui/missing_panics_doc.rs:220:5 | LL | pub fn last_unwrap(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:213:10 + --> tests/ui/missing_panics_doc.rs:222:10 | LL | *v.last().unwrap() | ^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:216:5 + --> tests/ui/missing_panics_doc.rs:225:5 | LL | pub fn last_expect(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:218:10 + --> tests/ui/missing_panics_doc.rs:227:10 | LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors From 3e837ec25e14b362c26fcb9fdb9f3e2dcdbde2e5 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 31 Mar 2025 15:05:45 +0000 Subject: [PATCH 085/103] Enable triagebot's merge conflict notifications --- triagebot.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 4b0ad0fb6bbc0..737aaec79a0d7 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -9,6 +9,11 @@ allow-unauthenticated = [ # See https://forge.rust-lang.org/triagebot/shortcuts.html [shortcut] +[merge-conflicts] +add = ['S-waiting-on-author'] +remove = ['S-waiting-on-review'] +unless = ['S-blocked', 'S-final-comment-period'] + # Have rustbot inform users about the *No Merge Policy* [no-merges] exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust From a50fb2248a02368888e18ae6e13410c8617313e2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Mar 2025 22:56:13 +1100 Subject: [PATCH 086/103] Avoid `kw::Empty` use for `AuxParamsAttr`. By changing two of the fields to use `Option` instead of `Ident`. As a result, `None` now means "no identifier", which is much clearer than using an empty identifier. --- .../src/significant_drop_tightening.rs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index e9db7c9d031a9..76874cc342066 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -79,10 +79,11 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr { continue; } + let first_bind_ident = apa.first_bind_ident.unwrap(); span_lint_and_then( cx, SIGNIFICANT_DROP_TIGHTENING, - apa.first_bind_ident.span, + first_bind_ident.span, "temporary with significant `Drop` can be early dropped", |diag| { match apa.counter { @@ -91,13 +92,13 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { let indent = " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0)); let init_method = snippet(cx, apa.first_method_span, ".."); let usage_method = snippet(cx, apa.last_method_span, ".."); - let stmt = if apa.last_bind_ident == Ident::empty() { - format!("\n{indent}{init_method}.{usage_method};") - } else { + let stmt = if let Some(last_bind_ident) = apa.last_bind_ident { format!( "\n{indent}let {} = {init_method}.{usage_method};", - snippet(cx, apa.last_bind_ident.span, ".."), + snippet(cx, last_bind_ident.span, ".."), ) + } else { + format!("\n{indent}{init_method}.{usage_method};") }; diag.multipart_suggestion_verbose( @@ -113,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { format!( "\n{}drop({});", " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0)), - apa.first_bind_ident + first_bind_ident ), Applicability::MaybeIncorrect, ); @@ -124,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { apa.first_block_span, format!( "temporary `{}` is currently being dropped at the end of its contained scope", - apa.first_bind_ident + first_bind_ident ), ); }, @@ -283,7 +284,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { let mut apa = AuxParamsAttr { first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, - first_bind_ident: ident, + first_bind_ident: Some(ident), first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -307,7 +308,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { match self.ap.curr_stmt.kind { hir::StmtKind::Let(local) => { if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind { - apa.last_bind_ident = ident; + apa.last_bind_ident = Some(ident); } if let Some(local_init) = local.init && let hir::ExprKind::MethodCall(_, _, _, span) = local_init.kind @@ -373,7 +374,7 @@ struct AuxParamsAttr { first_block_span: Span, /// The binding or variable that references the initial construction of the type marked with /// `#[has_significant_drop]`. - first_bind_ident: Ident, + first_bind_ident: Option, /// Similar to `init_bind_ident` but encompasses the right-hand method call. first_method_span: Span, /// Similar to `init_bind_ident` but encompasses the whole contained statement. @@ -381,7 +382,7 @@ struct AuxParamsAttr { /// The last visited binding or variable span within a block that had any referenced inner type /// marked with `#[has_significant_drop]`. - last_bind_ident: Ident, + last_bind_ident: Option, /// Similar to `last_bind_span` but encompasses the right-hand method call. last_method_span: Span, /// Similar to `last_bind_span` but encompasses the whole contained statement. @@ -395,10 +396,10 @@ impl Default for AuxParamsAttr { has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, first_block_span: DUMMY_SP, - first_bind_ident: Ident::empty(), + first_bind_ident: None, first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, - last_bind_ident: Ident::empty(), + last_bind_ident: None, last_method_span: DUMMY_SP, last_stmt_span: DUMMY_SP, } @@ -413,7 +414,7 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool { +fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Option, lcx: &LateContext<'_>) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res @@ -422,6 +423,7 @@ fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_ let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind && let [first_arg_ps, ..] = arg_path.segments + && let Some(first_bind_ident) = first_bind_ident && &first_arg_ps.ident == first_bind_ident { true From 5101c8e87f6e307c618576f76281e011372a65a4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Mar 2025 09:47:43 +1100 Subject: [PATCH 087/103] Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`. --- clippy_lints/src/crate_in_macro_def.rs | 2 +- clippy_lints/src/doc/needless_doctest_main.rs | 16 ++-- clippy_lints/src/duplicate_mod.rs | 2 +- clippy_lints/src/empty_line_after.rs | 16 ++-- clippy_lints/src/empty_with_brackets.rs | 5 +- .../src/field_scoped_visibility_modifiers.rs | 2 +- clippy_lints/src/multiple_bound_locations.rs | 2 +- clippy_lints/src/partial_pub_fields.rs | 2 +- .../src/single_component_path_imports.rs | 6 +- clippy_utils/src/ast_utils/mod.rs | 79 +++++++++++++++---- 10 files changed, 89 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 7d86bd3e540a1..c2aac7ca090bb 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -53,7 +53,7 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::MacroDef(macro_def) = &item.kind + if let ItemKind::MacroDef(_, macro_def) = &item.kind && item.attrs.iter().any(is_macro_export) && let Some(span) = contains_unhygienic_crate_reference(¯o_def.body.tokens) { diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 3008082c2329d..f6c10da1596b2 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -13,16 +13,16 @@ use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{FileName, Pos, sym}; +use rustc_span::{FileName, Ident, Pos, sym}; use super::Fragments; -fn get_test_spans(item: &Item, test_attr_spans: &mut Vec>) { +fn get_test_spans(item: &Item, ident: Ident, test_attr_spans: &mut Vec>) { test_attr_spans.extend( item.attrs .iter() .find(|attr| attr.has_name(sym::test)) - .map(|attr| attr.span.lo().to_usize()..item.ident.span.hi().to_usize()), + .map(|attr| attr.span.lo().to_usize()..ident.span.hi().to_usize()), ); } @@ -64,10 +64,10 @@ pub fn check( match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => match &item.kind { ItemKind::Fn(box Fn { - sig, body: Some(block), .. - }) if item.ident.name == sym::main => { + ident, sig, body: Some(block), .. + }) if ident.name == sym::main => { if !ignore { - get_test_spans(&item, &mut test_attr_spans); + get_test_spans(&item, *ident, &mut test_attr_spans); } let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. })); let returns_nothing = match &sig.decl.output { @@ -85,10 +85,10 @@ pub fn check( } }, // Another function was found; this case is ignored for needless_doctest_main - ItemKind::Fn(box Fn { .. }) => { + ItemKind::Fn(fn_) => { eligible = false; if !ignore { - get_test_spans(&item, &mut test_attr_spans); + get_test_spans(&item, fn_.ident, &mut test_attr_spans); } }, // Tests with one of these items are ignored diff --git a/clippy_lints/src/duplicate_mod.rs b/clippy_lints/src/duplicate_mod.rs index 1dac7b971f957..243c99a19ce1b 100644 --- a/clippy_lints/src/duplicate_mod.rs +++ b/clippy_lints/src/duplicate_mod.rs @@ -63,7 +63,7 @@ impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]); impl EarlyLintPass for DuplicateMod { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind + if let ItemKind::Mod(_, _, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span) && let Some(local_path) = real.into_local_path() && let Ok(absolute_path) = local_path.canonicalize() diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 80c2b03c41cf4..899b5c5952614 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -9,7 +9,7 @@ use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol}; +use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -375,21 +375,21 @@ impl EmptyLineAfter { &mut self, cx: &EarlyContext<'_>, kind: &ItemKind, - ident: &Ident, + ident: &Option, span: Span, attrs: &[Attribute], id: NodeId, ) { self.items.push(ItemInfo { kind: kind.descr(), - name: ident.name, - span: if span.contains(ident.span) { + name: if let Some(ident) = ident { ident.name } else { sym::dummy }, + span: if let Some(ident) = ident { span.with_hi(ident.span.hi()) } else { span.with_hi(span.lo()) }, mod_items: match kind { - ItemKind::Mod(_, ModKind::Loaded(items, _, _, _)) => items + ItemKind::Mod(_, _, ModKind::Loaded(items, _, _, _)) => items .iter() .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) .map(|i| i.id) @@ -471,7 +471,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.ident, + &item.kind.ident(), item.span, &item.attrs, item.id, @@ -482,7 +482,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.ident, + &item.kind.ident(), item.span, &item.attrs, item.id, @@ -490,6 +490,6 @@ impl EarlyLintPass for EmptyLineAfter { } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind(cx, &item.kind, &item.ident, item.span, &item.attrs, item.id); + self.check_item_kind(cx, &item.kind, &item.kind.ident(), item.span, &item.attrs, item.id); } } diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index 743ec5b9ea7fb..7d87f04fef9d8 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -74,10 +74,9 @@ declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM impl EarlyLintPass for EmptyWithBrackets { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let span_after_ident = item.span.with_lo(item.ident.span.hi()); - - if let ItemKind::Struct(var_data, _) = &item.kind + if let ItemKind::Struct(ident, var_data, _) = &item.kind && has_brackets(var_data) + && let span_after_ident = item.span.with_lo(ident.span.hi()) && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs index ba2b37fbf11a3..aae8291905d37 100644 --- a/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -51,7 +51,7 @@ declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MO impl EarlyLintPass for FieldScopedVisibilityModifiers { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(ref st, _) = item.kind else { + let ItemKind::Struct(_, ref st, _) = item.kind else { return; }; for field in st.fields() { diff --git a/clippy_lints/src/multiple_bound_locations.rs b/clippy_lints/src/multiple_bound_locations.rs index 0e1980a6acb61..4b32ba83b325e 100644 --- a/clippy_lints/src/multiple_bound_locations.rs +++ b/clippy_lints/src/multiple_bound_locations.rs @@ -39,7 +39,7 @@ declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]); impl EarlyLintPass for MultipleBoundLocations { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { - if let FnKind::Fn(_, _, _, Fn { generics, .. }) = kind + if let FnKind::Fn(_, _, Fn { generics, .. }) = kind && !generics.params.is_empty() && !generics.where_clause.predicates.is_empty() { diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs index 267e2067e101a..cda752d003fa0 100644 --- a/clippy_lints/src/partial_pub_fields.rs +++ b/clippy_lints/src/partial_pub_fields.rs @@ -41,7 +41,7 @@ declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); impl EarlyLintPass for PartialPubFields { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(ref st, _) = item.kind else { + let ItemKind::Struct(_, ref st, _) = item.kind else { return; }; diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index fa08245350429..35f80b2acda68 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -174,11 +174,11 @@ impl SingleComponentPathImports { } match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(items, ..)) => { + ItemKind::Mod(_, _, ModKind::Loaded(items, ..)) => { self.check_mod(items); }, - ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { - macros.push(item.ident.name); + ItemKind::MacroDef(ident, MacroDef { macro_rules: true, .. }) => { + macros.push(ident.name); }, ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 6023ae9cc7b16..ff63eb505a5c3 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -321,17 +321,18 @@ pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool { } pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool { - eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) + over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } #[expect(clippy::similar_names, clippy::too_many_lines)] // Just a big match statement pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { - (ExternCrate(l), ExternCrate(r)) => l == r, + (ExternCrate(ls, li), ExternCrate(rs, ri)) => ls == rs && eq_id(*li, *ri), (Use(l), Use(r)) => eq_use_tree(l, r), ( Static(box StaticItem { + ident: li, ty: lt, mutability: lm, expr: le, @@ -339,16 +340,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { define_opaque: _, }), Static(box StaticItem { + ident: ri, ty: rt, mutability: rm, expr: re, safety: rs, define_opaque: _, }), - ) => lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => eq_id(*li, *ri) + && lm == rm + && ls == rs + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()), ( Const(box ConstItem { defaultness: ld, + ident: li, generics: lg, ty: lt, expr: le, @@ -356,16 +363,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { }), Const(box ConstItem { defaultness: rd, + ident: ri, generics: rg, ty: rt, expr: re, define_opaque: _, }), - ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) + && eq_generics(lg, rg) + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()), ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, @@ -374,6 +387,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, @@ -382,12 +396,14 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, - (Mod(lu, lmk), Mod(ru, rmk)) => { - lu == ru + (Mod(ls, li, lmk), Mod(rs, ri, rmk)) => { + ls == rs + && eq_id(*li, *ri) && match (lmk, rmk) { (ModKind::Loaded(litems, linline, _, _), ModKind::Loaded(ritems, rinline, _, _)) => { linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind)) @@ -421,33 +437,40 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, - (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg), - (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => { - eq_variant_data(lv, rv) && eq_generics(lg, rg) + (Enum(li, le, lg), Enum(ri, re, rg)) => { + eq_id(*li, *ri) && over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg) + } + (Struct(li, lv, lg), Struct(ri, rv, rg)) | (Union(li, lv, lg), Union(ri, rv, rg)) => { + eq_id(*li, *ri) && eq_variant_data(lv, rv) && eq_generics(lg, rg) }, ( Trait(box ast::Trait { is_auto: la, safety: lu, + ident: li, generics: lg, bounds: lb, - items: li, + items: lis, }), Trait(box ast::Trait { is_auto: ra, safety: ru, + ident: ri, generics: rg, bounds: rb, - items: ri, + items: ris, }), ) => { la == ra && matches!(lu, Safety::Default) == matches!(ru, Safety::Default) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) - && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) + && over(lis, ris, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, - (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound), + (TraitAlias(li, lg, lb), TraitAlias(ri, rg, rb)) => { + eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) + } ( Impl(box ast::Impl { safety: lu, @@ -480,7 +503,9 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_delim_args(&l.body, &r.body), + (MacroDef(li, ld), MacroDef(ri, rd)) => { + eq_id(*li, *ri) && ld.macro_rules == rd.macro_rules && eq_delim_args(&ld.body, &rd.body) + } _ => false, } } @@ -490,6 +515,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { match (l, r) { ( Static(box StaticItem { + ident: li, ty: lt, mutability: lm, expr: le, @@ -497,17 +523,25 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { define_opaque: _, }), Static(box StaticItem { + ident: ri, ty: rt, mutability: rm, expr: re, safety: rs, define_opaque: _, }), - ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs, + ) => { + eq_id(*li, *ri) + && eq_ty(lt, rt) + && lm == rm + && eq_expr_opt(le.as_ref(), re.as_ref()) + && ls == rs + } ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, @@ -516,6 +550,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, @@ -524,6 +559,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) @@ -560,6 +596,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ( Const(box ConstItem { defaultness: ld, + ident: li, generics: lg, ty: lt, expr: le, @@ -567,16 +604,24 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { }), Const(box ConstItem { defaultness: rd, + ident: ri, generics: rg, ty: rt, expr: re, define_opaque: _, }), - ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => { + eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) + && eq_generics(lg, rg) + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()) + } ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, @@ -585,6 +630,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, @@ -593,6 +639,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) From 3f752b45eb232a2126500e0146d13eb6761dcc74 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Apr 2025 14:49:58 +1100 Subject: [PATCH 088/103] Address review comments. --- clippy_lints/src/empty_line_after.rs | 15 ++++++++------- clippy_utils/src/ast_utils/mod.rs | 14 ++++++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 899b5c5952614..c67dcd3c82b50 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -8,8 +8,7 @@ use rustc_errors::{Applicability, Diag, SuggestionStyle}; use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::kw; -use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, sym}; +use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw}; declare_clippy_lint! { /// ### What it does @@ -375,14 +374,16 @@ impl EmptyLineAfter { &mut self, cx: &EarlyContext<'_>, kind: &ItemKind, - ident: &Option, + ident: Option, span: Span, attrs: &[Attribute], id: NodeId, ) { self.items.push(ItemInfo { kind: kind.descr(), - name: if let Some(ident) = ident { ident.name } else { sym::dummy }, + // FIXME: this `sym::empty` can be leaked, see + // https://github.com/rust-lang/rust/pull/138740#discussion_r2021979899 + name: if let Some(ident) = ident { ident.name } else { kw::Empty }, span: if let Some(ident) = ident { span.with_hi(ident.span.hi()) } else { @@ -471,7 +472,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.kind.ident(), + item.kind.ident(), item.span, &item.attrs, item.id, @@ -482,7 +483,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.kind.ident(), + item.kind.ident(), item.span, &item.attrs, item.id, @@ -490,6 +491,6 @@ impl EarlyLintPass for EmptyLineAfter { } 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); + self.check_item_kind(cx, &item.kind, item.kind.ident(), item.span, &item.attrs, item.id); } } diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index ff63eb505a5c3..eba576392ebce 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -567,20 +567,23 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ( TyAlias(box ast::TyAlias { defaultness: ld, + ident: li, generics: lg, + where_clauses: _, bounds: lb, ty: lt, - .. }), TyAlias(box ast::TyAlias { defaultness: rd, + ident: ri, generics: rg, + where_clauses: _, bounds: rb, ty: rt, - .. }), ) => { eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) @@ -647,20 +650,23 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ( Type(box TyAlias { defaultness: ld, + ident: li, generics: lg, + where_clauses: _, bounds: lb, ty: lt, - .. }), Type(box TyAlias { defaultness: rd, + ident: ri, generics: rg, + where_clauses: _, bounds: rb, ty: rt, - .. }), ) => { eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) From 86e6cb5608457f59c90fd1dc3914fa2a6c4adbf0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 27 Mar 2025 11:48:06 +0000 Subject: [PATCH 089/103] Decouple trait impls of different traits wrt incremental --- clippy_lints/src/derive.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 2ae35b4005579..fae01026487a0 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -324,11 +324,9 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h // there's a Copy impl for any instance of the adt. if !is_copy(cx, ty) { if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).is_some_and(|impls| { - impls.iter().any(|&id| { - matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()) - }) }); if !has_copy_impl { return; From 88c46eaf04a0abfdc68b78ff900dc3043af1b914 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 1 Apr 2025 16:06:28 +0200 Subject: [PATCH 090/103] Install cmake to restore lintcheck run --- .github/workflows/lintcheck.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 70c805903d36e..4490f872b18d4 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -49,6 +49,10 @@ jobs: path: target/debug/lintcheck key: lintcheck-bin-${{ hashfiles('lintcheck/**') }} + # Install cmake to build aws-lc-sys to build tokio-rustls + - name: Install cmake + run: sudo apt-get install -y cmake + - name: Build lintcheck if: steps.cache-lintcheck-bin.outputs.cache-hit != 'true' run: cargo build --manifest-path=lintcheck/Cargo.toml @@ -92,6 +96,10 @@ jobs: path: target/debug/lintcheck key: lintcheck-bin-${{ hashfiles('lintcheck/**') }} + # Install cmake to build aws-lc-sys to build tokio-rustls + - name: Install cmake + run: sudo apt-get install -y cmake + - name: Build lintcheck if: steps.cache-lintcheck-bin.outputs.cache-hit != 'true' run: cargo build --manifest-path=lintcheck/Cargo.toml From 54994b2d4b89a8af8c5a04799e7fa224c7103ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 1 Apr 2025 18:20:41 +0200 Subject: [PATCH 091/103] Fix the primary span of redundant_pub_crate when flagging nameless items --- clippy_lints/src/redundant_pub_crate.rs | 11 ++++------- tests/ui/redundant_pub_crate.fixed | 8 ++++++++ tests/ui/redundant_pub_crate.rs | 8 ++++++++ tests/ui/redundant_pub_crate.stderr | 18 +++++++++++++++++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index f2fdac5a8afaf..7b381fac5f118 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -52,13 +52,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && is_not_macro_export(item) && !item.span.in_external_macro(cx.sess().source_map()) { - // FIXME: `DUMMY_SP` isn't right here, because it causes the - // resulting span to begin at the start of the file. - let span = item.span.with_hi( - item.kind - .ident() - .map_or(rustc_span::DUMMY_SP.hi(), |ident| ident.span.hi()), - ); + let span = item + .kind + .ident() + .map_or(item.span, |ident| item.span.with_hi(ident.span.hi())); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); span_lint_and_then( cx, diff --git a/tests/ui/redundant_pub_crate.fixed b/tests/ui/redundant_pub_crate.fixed index a6450123f4c9d..8a30fedede4a4 100644 --- a/tests/ui/redundant_pub_crate.fixed +++ b/tests/ui/redundant_pub_crate.fixed @@ -131,6 +131,14 @@ mod m4 { } } +mod m5 { + pub mod m5_1 {} + // Test that the primary span isn't butchered for item kinds that don't have an ident. + pub use m5_1::*; //~ redundant_pub_crate + #[rustfmt::skip] + pub use m5_1::{*}; //~ redundant_pub_crate +} + pub use m4::*; mod issue_8732 { diff --git a/tests/ui/redundant_pub_crate.rs b/tests/ui/redundant_pub_crate.rs index 7415d34d50cc7..45ba13a63b2e2 100644 --- a/tests/ui/redundant_pub_crate.rs +++ b/tests/ui/redundant_pub_crate.rs @@ -131,6 +131,14 @@ mod m4 { } } +mod m5 { + pub mod m5_1 {} + // Test that the primary span isn't butchered for item kinds that don't have an ident. + pub(crate) use m5_1::*; //~ redundant_pub_crate + #[rustfmt::skip] + pub(crate) use m5_1::{*}; //~ redundant_pub_crate +} + pub use m4::*; mod issue_8732 { diff --git a/tests/ui/redundant_pub_crate.stderr b/tests/ui/redundant_pub_crate.stderr index 95909ea8b0663..4a47a321028d1 100644 --- a/tests/ui/redundant_pub_crate.stderr +++ b/tests/ui/redundant_pub_crate.stderr @@ -129,5 +129,21 @@ LL | pub(crate) fn g() {} // private due to m4_2 | | | help: consider using: `pub` -error: aborting due to 16 previous errors +error: pub(crate) import inside private module + --> tests/ui/redundant_pub_crate.rs:137:5 + | +LL | pub(crate) use m5_1::*; + | ----------^^^^^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) import inside private module + --> tests/ui/redundant_pub_crate.rs:139:27 + | +LL | pub(crate) use m5_1::{*}; + | ---------- ^ + | | + | help: consider using: `pub` + +error: aborting due to 18 previous errors From d81396b7d0b7e196d729b506b31550f504d54f6f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 1 Apr 2025 19:33:30 +0200 Subject: [PATCH 092/103] Do not build `tokio-rustls` in the CI for the time being A discrepancy between the `cmake` version available on the runners and the one required by the `aws-lc-sys` dependency prevents the crate from buiding. --- .github/workflows/lintcheck.yml | 8 -------- lintcheck/ci_crates.toml | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 4490f872b18d4..70c805903d36e 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -49,10 +49,6 @@ jobs: path: target/debug/lintcheck key: lintcheck-bin-${{ hashfiles('lintcheck/**') }} - # Install cmake to build aws-lc-sys to build tokio-rustls - - name: Install cmake - run: sudo apt-get install -y cmake - - name: Build lintcheck if: steps.cache-lintcheck-bin.outputs.cache-hit != 'true' run: cargo build --manifest-path=lintcheck/Cargo.toml @@ -96,10 +92,6 @@ jobs: path: target/debug/lintcheck key: lintcheck-bin-${{ hashfiles('lintcheck/**') }} - # Install cmake to build aws-lc-sys to build tokio-rustls - - name: Install cmake - run: sudo apt-get install -y cmake - - name: Build lintcheck if: steps.cache-lintcheck-bin.outputs.cache-hit != 'true' run: cargo build --manifest-path=lintcheck/Cargo.toml diff --git a/lintcheck/ci_crates.toml b/lintcheck/ci_crates.toml index 6299823451d01..55d99ebada358 100644 --- a/lintcheck/ci_crates.toml +++ b/lintcheck/ci_crates.toml @@ -122,7 +122,8 @@ winnow = { name = 'winnow', version = '0.6.13' } cpufeatures = { name = 'cpufeatures', version = '0.2.12' } nix = { name = 'nix', version = '0.29.0' } fnv = { name = 'fnv', version = '1.0.7' } -tokio-rustls = { name = 'tokio-rustls', version = '0.26.0' } +# As of 2025-04-01, one dependency doesn't build because of cmake version discrepancy +# tokio-rustls = { name = 'tokio-rustls', version = '0.26.0' } iana-time-zone = { name = 'iana-time-zone', version = '0.1.60' } rustls-webpki = { name = 'rustls-webpki', version = '0.102.5' } crc32fast = { name = 'crc32fast', version = '1.4.2' } From dc7d9ec35c7c729baac22830016f0b1ef862100f Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 11 Mar 2025 20:40:39 -0400 Subject: [PATCH 093/103] Validate paths in `disallowed_*` configurations --- clippy.toml | 3 + clippy_config/src/conf.rs | 90 +++++++++++--- clippy_config/src/lib.rs | 1 + clippy_config/src/types.rs | 114 ++++++++++++++++-- clippy_lints/src/await_holding_invalid.rs | 11 +- clippy_lints/src/disallowed_macros.rs | 10 +- clippy_lints/src/disallowed_methods.rs | 23 ++-- clippy_lints/src/disallowed_types.rs | 34 +++--- .../await_holding_invalid_type.stderr | 9 +- tests/ui-toml/toml_invalid_path/clippy.toml | 14 +++ .../toml_invalid_path/conf_invalid_path.rs | 5 + .../conf_invalid_path.stderr | 23 ++++ 12 files changed, 273 insertions(+), 64 deletions(-) create mode 100644 tests/ui-toml/toml_invalid_path/clippy.toml create mode 100644 tests/ui-toml/toml_invalid_path/conf_invalid_path.rs create mode 100644 tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr diff --git a/clippy.toml b/clippy.toml index 77573105d86a3..0a7724bbe4e61 100644 --- a/clippy.toml +++ b/clippy.toml @@ -7,11 +7,14 @@ lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +allow-invalid = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +allow-invalid = true [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" +allow-invalid = true diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index bd20ee1fda4a1..34c62318aad3d 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -120,12 +120,7 @@ impl ConfError { Self { message: message.into(), suggestion, - span: Span::new( - file.start_pos + BytePos::from_usize(span.start), - file.start_pos + BytePos::from_usize(span.end), - SyntaxContext::root(), - None, - ), + span: span_from_toml_range(file, span), } } } @@ -176,11 +171,61 @@ macro_rules! default_text { }; } +macro_rules! deserialize { + ($map:expr, $ty:ty, $errors:expr, $file:expr) => {{ + let raw_value = $map.next_value::>()?; + let value_span = raw_value.span(); + let value = match <$ty>::deserialize(raw_value.into_inner()) { + Err(e) => { + $errors.push(ConfError::spanned( + $file, + e.to_string().replace('\n', " ").trim(), + None, + value_span, + )); + continue; + }, + Ok(value) => value, + }; + (value, value_span) + }}; + + ($map:expr, $ty:ty, $errors:expr, $file:expr, $replacements_allowed:expr) => {{ + let array = $map.next_value::>>()?; + let mut disallowed_paths_span = Range { + start: usize::MAX, + end: usize::MIN, + }; + let mut disallowed_paths = Vec::new(); + for raw_value in array { + let value_span = raw_value.span(); + let mut disallowed_path = match DisallowedPath::<$replacements_allowed>::deserialize(raw_value.into_inner()) + { + Err(e) => { + $errors.push(ConfError::spanned( + $file, + e.to_string().replace('\n', " ").trim(), + None, + value_span, + )); + continue; + }, + Ok(disallowed_path) => disallowed_path, + }; + disallowed_paths_span = union(&disallowed_paths_span, &value_span); + disallowed_path.set_span(span_from_toml_range($file, value_span)); + disallowed_paths.push(disallowed_path); + } + (disallowed_paths, disallowed_paths_span) + }}; +} + 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])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -219,7 +264,7 @@ macro_rules! define_Conf { let mut errors = Vec::new(); let mut warnings = Vec::new(); - // Declare a local variable for each field field available to a configuration file. + // Declare a local variable for each field available to a configuration file. $(let mut $name = None;)* // could get `Field` here directly, but get `String` first for diagnostics @@ -237,15 +282,8 @@ macro_rules! define_Conf { $(Field::$name => { // 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 raw_value = map.next_value::>()?; - let value_span = raw_value.span(); - let value = match <$ty>::deserialize(raw_value.into_inner()) { - Err(e) => { - errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)); - continue; - } - Ok(value) => value - }; + let (value, value_span) = + deserialize!(map, $ty, errors, self.0 $(, $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())); @@ -286,6 +324,22 @@ macro_rules! define_Conf { }; } +fn union(x: &Range, y: &Range) -> Range { + Range { + start: cmp::min(x.start, y.start), + end: cmp::max(x.end, y.end), + } +} + +fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { + Span::new( + file.start_pos + BytePos::from_usize(span.start), + file.start_pos + BytePos::from_usize(span.end), + SyntaxContext::root(), + None, + ) +} + define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -472,6 +526,7 @@ define_Conf! { )] avoid_breaking_exported_api: bool = true, /// The list of types which may not be held across an await point. + #[disallowed_paths_allow_replacements = false] #[lints(await_holding_invalid_type)] await_holding_invalid_types: Vec = Vec::new(), /// DEPRECATED LINT: BLACKLISTED_NAME. @@ -517,9 +572,11 @@ define_Conf! { #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] cyclomatic_complexity_threshold: u64 = 25, /// The list of disallowed macros, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_macros)] disallowed_macros: Vec = Vec::new(), /// The list of disallowed methods, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_methods)] disallowed_methods: Vec = Vec::new(), /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value @@ -528,6 +585,7 @@ define_Conf! { #[lints(disallowed_names)] disallowed_names: Vec = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), /// The list of disallowed types, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_types)] disallowed_types: Vec = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index 5d6e8b875166c..c227b8900b74a 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -13,6 +13,7 @@ rustc::untranslatable_diagnostic )] +extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_middle; diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 8faac9ecffea4..5949eaca7bc1d 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,5 +1,7 @@ -use clippy_utils::def_path_def_ids; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; +use rustc_hir::PrimTy; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -21,6 +23,17 @@ pub struct DisallowedPath { path: String, reason: Option, replacement: Option, + /// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing + /// definition. + /// + /// This could be useful when conditional compilation is used, or when a clippy.toml file is + /// shared among multiple projects. + allow_invalid: bool, + /// The span of the `DisallowedPath`. + /// + /// Used for diagnostics. + #[serde(skip_serializing)] + span: Span, } impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath { @@ -36,6 +49,8 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, replacement: Option, + #[serde(rename = "allow-invalid")] + allow_invalid: Option, }, } @@ -58,7 +75,7 @@ impl DisallowedPath { &self.path } - pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_, REPLACEMENT_ALLOWED> { + pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) { move |diag| { if let Some(replacement) = &self.replacement { diag.span_suggestion( @@ -72,6 +89,14 @@ impl DisallowedPath { } } } + + pub fn span(&self) -> Span { + self.span + } + + pub fn set_span(&mut self, span: Span) { + self.span = span; + } } impl DisallowedPathEnum { @@ -94,20 +119,87 @@ impl DisallowedPathEnum { Self::Simple(_) => None, } } + + fn allow_invalid(&self) -> bool { + match &self { + Self::WithReason { allow_invalid, .. } => allow_invalid.unwrap_or_default(), + Self::Simple(_) => false, + } + } } /// Creates a map of disallowed items to the reason they were disallowed. +#[allow(clippy::type_complexity)] pub fn create_disallowed_map( tcx: TyCtxt<'_>, - disallowed: &'static [DisallowedPath], -) -> DefIdMap<(&'static str, &'static DisallowedPath)> { - disallowed - .iter() - .map(|x| (x.path(), x.path().split("::").collect::>(), x)) - .flat_map(|(name, path, disallowed_path)| { - def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path))) - }) - .collect() + disallowed_paths: &'static [DisallowedPath], + def_kind_predicate: impl Fn(DefKind) -> bool, + predicate_description: &str, + allow_prim_tys: bool, +) -> ( + DefIdMap<(&'static str, &'static DisallowedPath)>, + FxHashMap)>, +) { + let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath)> = DefIdMap::default(); + let mut prim_tys: FxHashMap)> = + FxHashMap::default(); + for disallowed_path in disallowed_paths { + let path = disallowed_path.path(); + let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::>()); + + let mut found_def_id = None; + let mut found_prim_ty = false; + resolutions.retain(|res| match res { + Res::Def(def_kind, def_id) => { + found_def_id = Some(*def_id); + def_kind_predicate(*def_kind) + }, + Res::PrimTy(_) => { + found_prim_ty = true; + allow_prim_tys + }, + _ => false, + }); + + if resolutions.is_empty() { + let span = disallowed_path.span(); + + if let Some(def_id) = found_def_id { + tcx.sess.dcx().span_warn( + span, + format!( + "expected a {predicate_description}, found {} {}", + tcx.def_descr_article(def_id), + tcx.def_descr(def_id) + ), + ); + } else if found_prim_ty { + tcx.sess.dcx().span_warn( + span, + format!("expected a {predicate_description}, found a primitive type",), + ); + } else if !disallowed_path.allow_invalid { + tcx.sess.dcx().span_warn( + span, + format!("`{path}` does not refer to an existing {predicate_description}"), + ); + } + } + + for res in resolutions { + match res { + Res::Def(_, def_id) => { + def_ids.insert(def_id, (path, disallowed_path)); + }, + Res::PrimTy(ty) => { + prim_tys.insert(ty, (path, disallowed_path)); + }, + _ => unreachable!(), + } + } + } + + (def_ids, prim_tys) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index d600aec8c9d65..52d1d5b4c67a1 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -179,9 +179,14 @@ pub struct AwaitHolding { impl AwaitHolding { pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - Self { - def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types), - } + let (def_ids, _) = create_disallowed_map( + tcx, + &conf.await_holding_invalid_types, + crate::disallowed_types::def_kind_predicate, + "type", + false, + ); + Self { def_ids } } } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 22310bc0798f4..fa33fef230622 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -3,6 +3,7 @@ use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, @@ -71,8 +72,15 @@ pub struct DisallowedMacros { impl DisallowedMacros { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self { + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_macros, + |def_kind| matches!(def_kind, DefKind::Macro(_)), + "macro", + false, + ); Self { - disallowed: create_disallowed_map(tcx, &conf.disallowed_macros), + disallowed, seen: FxHashSet::default(), derive_src: None, earlies, diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 149cf1cf2def1..1382dafa931e4 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -63,9 +63,19 @@ pub struct DisallowedMethods { impl DisallowedMethods { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - Self { - disallowed: create_disallowed_map(tcx, &conf.disallowed_methods), - } + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_methods, + |def_kind| { + matches!( + def_kind, + DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn + ) + }, + "function", + false, + ); + Self { disallowed } } } @@ -74,12 +84,7 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (id, span) = match &expr.kind { - ExprKind::Path(path) - if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = - cx.qpath_res(path, expr.hir_id) => - { - (id, expr.span) - }, + ExprKind::Path(path) if let Res::Def(_, id) = cx.qpath_res(path, expr.hir_id) => (id, expr.span), ExprKind::MethodCall(name, ..) if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => { (id, name.ident.span) }, diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 38903596414cf..2bae82648ac76 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; -use clippy_config::types::DisallowedPath; +use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::Res; +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}; @@ -60,22 +60,7 @@ pub struct DisallowedTypes { impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let mut def_ids = DefIdMap::default(); - let mut prim_tys = FxHashMap::default(); - for disallowed_path in &conf.disallowed_types { - let path: Vec<_> = disallowed_path.path().split("::").collect::>(); - for res in clippy_utils::def_path_res(tcx, &path) { - match res { - Res::Def(_, id) => { - def_ids.insert(id, (disallowed_path.path(), disallowed_path)); - }, - Res::PrimTy(ty) => { - prim_tys.insert(ty, (disallowed_path.path(), disallowed_path)); - }, - _ => {}, - } - } - } + let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true); Self { def_ids, prim_tys } } @@ -95,6 +80,19 @@ impl DisallowedTypes { } } +pub fn def_kind_predicate(def_kind: DefKind) -> bool { + matches!( + def_kind, + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::AssocTy + ) +} + impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { diff --git a/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr index 86e30409af068..d0fce3614a145 100644 --- a/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr @@ -1,11 +1,8 @@ error: error reading Clippy's configuration file: replacement not allowed for this configuration - --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:1:31 + --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:2:5 | -LL | await-holding-invalid-types = [ - | _______________________________^ -LL | | { path = "std::string::String", replacement = "std::net::Ipv4Addr" }, -LL | | ] - | |_^ +LL | { path = "std::string::String", replacement = "std::net::Ipv4Addr" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui-toml/toml_invalid_path/clippy.toml b/tests/ui-toml/toml_invalid_path/clippy.toml new file mode 100644 index 0000000000000..6d0d732a92237 --- /dev/null +++ b/tests/ui-toml/toml_invalid_path/clippy.toml @@ -0,0 +1,14 @@ +[[disallowed-types]] +path = "std::result::Result::Err" + +[[disallowed-macros]] +path = "bool" + +[[disallowed-methods]] +path = "std::process::current_exe" + +# negative test + +[[disallowed-methods]] +path = "std::current_exe" +allow-invalid = true diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs new file mode 100644 index 0000000000000..c152038270348 --- /dev/null +++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs @@ -0,0 +1,5 @@ +//@error-in-other-file: expected a macro, found a primitive type +//@error-in-other-file: `std::process::current_exe` does not refer to an existing function +//@error-in-other-file: expected a type, found a tuple variant + +fn main() {} diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr new file mode 100644 index 0000000000000..82550108eba53 --- /dev/null +++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr @@ -0,0 +1,23 @@ +warning: expected a macro, found a primitive type + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 + | +LL | / [[disallowed-macros]] +LL | | path = "bool" + | |_____________^ + +warning: `std::process::current_exe` does not refer to an existing function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 + | +LL | / [[disallowed-methods]] +LL | | path = "std::process::current_exe" + | |__________________________________^ + +warning: expected a type, found a tuple variant + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 + | +LL | / [[disallowed-types]] +LL | | path = "std::result::Result::Err" + | |_________________________________^ + +warning: 3 warnings emitted + From 130af3fc3ab0d9cbfd249b533aa21e73492076fe Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Apr 2025 07:04:24 +1100 Subject: [PATCH 094/103] Move methods from `Map` to `TyCtxt`, part 5. This eliminates all methods on `Map`. Actually removing `Map` will occur in a follow-up PR. --- clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/derive.rs | 4 ++-- clippy_lints/src/escape.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 8 ++++---- clippy_lints/src/macro_metavars_in_unsafe.rs | 2 +- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/operators/arithmetic_side_effects.rs | 4 ++-- clippy_lints/src/operators/numeric_arithmetic.rs | 4 ++-- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/single_call_fn.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/macros.rs | 3 +-- clippy_utils/src/sugg.rs | 3 +-- 16 files changed, 22 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 7885f171461d7..72953818a708e 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -143,7 +143,7 @@ pub(super) fn check<'tcx>( if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) { if let Some(id) = path_to_local(cast_expr) - && !cx.tcx.hir().span(id).eq_ctxt(cast_expr.span) + && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. // Weird macro wizardry could be involved here. diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 2ae35b4005579..7480b409100c0 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -254,7 +254,7 @@ fn check_hash_peq<'tcx>( |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir().span(hir_id), "`PartialEq` implemented here"); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); } }, ); @@ -298,7 +298,7 @@ fn check_ord_partial_ord<'tcx>( span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir().span(hir_id), "`PartialOrd` implemented here"); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); } }); } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index de0fc2b1bf4bb..e248f08789b2d 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { cx, BOXED_LOCAL, node, - cx.tcx.hir().span(node), + cx.tcx.hir_span(node), "local variable doesn't need to be boxed here", ); } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index d53e139de014b..989997d69f7c7 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -248,7 +248,7 @@ impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { { use_info .index_use - .push((index_value, cx.tcx.hir().span(parent_expr.hir_id))); + .push((index_value, cx.tcx.hir_span(parent_expr.hir_id))); return; } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 5afcf51167d47..a24dd44790e1f 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -85,10 +85,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { if bk == ty::BorrowKind::Mutable { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); } if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); } } } @@ -97,10 +97,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); } if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); } } } diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index 006addb987f5b..df6e85611fb2c 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -253,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // Remove the syntax context to hide "in this macro invocation" in the diagnostic. // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything // related to the callsite. - let span = cx.tcx.hir().span(id); + let span = cx.tcx.hir_span(id); (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None)) }) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 693d1a8dd7643..09ee6f7037c64 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -329,7 +329,7 @@ fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let name = ident.name.as_str(); if name.starts_with('_') && !name.starts_with("__") - && let definition_span = cx.tcx.hir().span(definition_hir_id) + && let definition_span = cx.tcx.hir_span(definition_hir_id) && !definition_span.from_expansion() && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) { diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 576bb27b254c9..b61061d076b74 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -280,7 +280,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { diag.span_suggestion( sp, "consider changing to".to_string(), - format!("&{}", snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"),), + format!("&{}", snippet(cx, cx.tcx.hir_span(inner_ty.ty.hir_id), "_"),), Applicability::Unspecified, ); if cx.effective_visibilities.is_exported(*fn_def_id) { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 55ca875edcee6..978fde33d5170 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Dereference suggestion let sugg = |diag: &mut Diag<'_, ()>| { if let ty::Adt(def, ..) = ty.kind() { - if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { + if let Some(span) = cx.tcx.hir_span_if_local(def.did()) { if type_allowed_to_implement_copy( cx.tcx, cx.param_env, diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 594101427f5a9..08fcd17e55558 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -354,7 +354,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { let body_owner_kind = cx.tcx.hir_body_owner_kind(body_owner_def_id); if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind { - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { @@ -366,7 +366,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { let body_owner = cx.tcx.hir_body_owner(body.id()); - let body_span = cx.tcx.hir().span(body_owner); + let body_span = cx.tcx.hir_span(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index c261fd9bd9cb0..96c9775e292e6 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -73,7 +73,7 @@ impl Context { match cx.tcx.hir_body_owner_kind(body_owner_def_id) { hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => { - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); if let Some(span) = self.const_span { if span.contains(body_span) { @@ -88,7 +88,7 @@ impl Context { pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { let body_owner = cx.tcx.hir_body_owner(body.id()); - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); if let Some(span) = self.const_span { if span.contains(body_span) { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index ee282ee1dfb71..b82ddedd56c0a 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -218,7 +218,7 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) }, }; span_lint_and_then(cx, lint, span, msg, |diag| { - diag.span_note(cx.tcx.hir().span(shadowed), "previous binding is here"); + diag.span_note(cx.tcx.hir_span(shadowed), "previous binding is here"); }); } diff --git a/clippy_lints/src/single_call_fn.rs b/clippy_lints/src/single_call_fn.rs index 1a2fb77acc15a..64891743dc636 100644 --- a/clippy_lints/src/single_call_fn.rs +++ b/clippy_lints/src/single_call_fn.rs @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn { for (&def_id, usage) in &self.def_id_to_usage { if let CallState::Once { call_site } = *usage && let fn_hir_id = cx.tcx.local_def_id_to_hir_id(def_id) - && let fn_span = cx.tcx.hir().span_with_body(fn_hir_id) + && let fn_span = cx.tcx.hir_span_with_body(fn_hir_id) && !self.is_function_allowed(cx, def_id, fn_hir_id, fn_span) { span_lint_hir_and_then( diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 668b0cb69e204..8dc28fa3077e0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2990,7 +2990,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU { adjustments = cx.typeck_results().expr_adjustments(e); } - same_ctxt &= cx.tcx.hir().span(parent_id).ctxt() == ctxt; + same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt; if let Node::Expr(e) = parent { match e.kind { ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => { diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 1a457bc7f2141..dfb30b9c21864 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -178,7 +178,6 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< // get the parent node, possibly skipping over a statement // if the parent is not found, it is sensible to return `Some(root)` - let hir = cx.tcx.hir(); let mut parent_iter = cx.tcx.hir_parent_iter(node.hir_id()); let (parent_id, _) = match parent_iter.next() { None => return Some(ExpnId::root()), @@ -190,7 +189,7 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< }; // get the macro expansion of the parent node - let parent_span = hir.span(parent_id); + let parent_span = cx.tcx.hir_span(parent_id); let Some(parent_macro_call) = macro_backtrace(parent_span).next() else { // the parent node is not in a macro return Some(ExpnId::root()); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 68a1de96a3515..065d48119a24f 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -839,8 +839,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { - let map = self.cx.tcx.hir(); - let span = map.span(cmt.hir_id); + let span = self.cx.tcx.hir_span(cmt.hir_id); let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None); let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability); From 777c7cdce0713762c5b6c278dae017ef0aefd8c8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 1 Apr 2025 10:03:55 +0000 Subject: [PATCH 095/103] Remove a function that has no necessary callers --- clippy_lints/src/new_without_default.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index f0ee613791fb9..1e469c3b63a43 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -97,14 +97,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { { if self.impling_types.is_none() { let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(default_trait_id, |d| { + for &d in cx.tcx.local_trait_impls(default_trait_id) { let ty = cx.tcx.type_of(d).instantiate_identity(); if let Some(ty_def) = ty.ty_adt_def() { if let Some(local_def_id) = ty_def.did().as_local() { impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); } } - }); + } self.impling_types = Some(impls); } From 2d119ce5eb7f041e298ab744b4e5d569461b0119 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 2 Apr 2025 13:22:10 +0000 Subject: [PATCH 096/103] Stop relabelling PRs with merge conflicts --- triagebot.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 737aaec79a0d7..a82fb00c0e9d2 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -10,9 +10,6 @@ allow-unauthenticated = [ [shortcut] [merge-conflicts] -add = ['S-waiting-on-author'] -remove = ['S-waiting-on-review'] -unless = ['S-blocked', 'S-final-comment-period'] # Have rustbot inform users about the *No Merge Policy* [no-merges] From 416acd8cd95c40bb9ed4c100811208a19e9bb773 Mon Sep 17 00:00:00 2001 From: beetrees Date: Thu, 3 Apr 2025 00:53:12 +0100 Subject: [PATCH 097/103] Don't use `f16` and `f128` directly in `clippy_utils` --- clippy_lints/src/neg_multiply.rs | 8 +++++++- clippy_utils/src/consts.rs | 26 ++++++++++++++------------ clippy_utils/src/lib.rs | 2 -- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 07924e67765bc..74c8142787ebc 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -49,10 +49,16 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { } fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { + const F16_ONE: u16 = 1.0_f16.to_bits(); + const F128_ONE: u128 = 1.0_f128.to_bits(); if let ExprKind::Lit(l) = lit.kind && matches!( consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)), - Constant::Int(1) | Constant::F16(1.0) | Constant::F32(1.0) | Constant::F64(1.0) | Constant::F128(1.0) + Constant::Int(1) + | Constant::F16(F16_ONE) + | Constant::F32(1.0) + | Constant::F64(1.0) + | Constant::F128(F128_ONE) ) && cx.typeck_results().expr_ty(exp).is_numeric() { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index dd149c4a29b9f..212aecbf5520d 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -43,14 +43,16 @@ pub enum Constant<'tcx> { Char(char), /// An integer's bit representation. Int(u128), - /// An `f16`. - F16(f16), + /// An `f16` bitcast to a `u16`. + // FIXME(f16_f128): use `f16` once builtins are available on all host tools platforms. + F16(u16), /// An `f32`. F32(f32), /// An `f64`. F64(f64), - /// An `f128`. - F128(f128), + /// An `f128` bitcast to a `u128`. + // FIXME(f16_f128): use `f128` once builtins are available on all host tools platforms. + F128(u128), /// `true` or `false`. Bool(bool), /// An array of constants. @@ -177,7 +179,7 @@ impl Hash for Constant<'_> { }, Self::F16(f) => { // FIXME(f16_f128): once conversions to/from `f128` are available on all platforms, - f.to_bits().hash(state); + f.hash(state); }, Self::F32(f) => { f64::from(f).to_bits().hash(state); @@ -186,7 +188,7 @@ impl Hash for Constant<'_> { f.to_bits().hash(state); }, Self::F128(f) => { - f.to_bits().hash(state); + f.hash(state); }, Self::Bool(b) => { b.hash(state); @@ -292,12 +294,12 @@ impl Constant<'_> { fn parse_f16(s: &str) -> Self { let f: Half = s.parse().unwrap(); - Self::F16(f16::from_bits(f.to_bits().try_into().unwrap())) + Self::F16(f.to_bits().try_into().unwrap()) } fn parse_f128(s: &str) -> Self { let f: Quad = s.parse().unwrap(); - Self::F128(f128::from_bits(f.to_bits())) + Self::F128(f.to_bits()) } } @@ -868,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), - ty::Float(FloatTy::F16) => Some(Constant::F16(f16::from_bits(int.into()))), + ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))), ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))), - ty::Float(FloatTy::F128) => Some(Constant::F128(f128::from_bits(int.into()))), + ty::Float(FloatTy::F128) => Some(Constant::F128(int.into())), ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))), _ => None, }, @@ -892,10 +894,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)), + FloatTy::F16 => Constant::F16(val.to_u16().discard_err()?), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), - FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)), + FloatTy::F128 => Constant::F128(val.to_u128().discard_err()?), }); } Some(Constant::Vec(res)) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e8ec42e0662bd..f4489833d03ab 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,7 +1,5 @@ #![feature(array_chunks)] #![feature(box_patterns)] -#![feature(f128)] -#![feature(f16)] #![feature(if_let_guard)] #![feature(macro_metavar_expr_concat)] #![feature(let_chains)] From 6f0f22cf287ad2a66e478148cc577f41ef222658 Mon Sep 17 00:00:00 2001 From: Samuel Moelius <35515885+smoelius@users.noreply.github.com> Date: Thu, 3 Apr 2025 04:14:23 -0400 Subject: [PATCH 098/103] Fix a typo in derive.rs comment changelog: none --- clippy_lints/src/derive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index a05d57d6836a5..250dedc37dbff 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -481,7 +481,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() } -/// Creates the `ParamEnv` used for the give type's derived `Eq` impl. +/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { // Initial map from generic index to param def. // Vec<(param_def, needs_eq)> From 893a6a381e5e65c712fba24ab23a9b6e3365bc26 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 3 Apr 2025 15:48:20 +0200 Subject: [PATCH 099/103] Update to new rinja version (askama) --- Cargo.toml | 2 +- rinja.toml => askama.toml | 0 tests/compile-test.rs | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename rinja.toml => askama.toml (100%) diff --git a/Cargo.toml b/Cargo.toml index 1a46bcf75025f..1ae5b7d6af07a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } -rinja = { version = "0.3", default-features = false, features = ["config"] } +askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } # UI test dependencies clippy_utils = { path = "clippy_utils" } diff --git a/rinja.toml b/askama.toml similarity index 100% rename from rinja.toml rename to askama.toml diff --git a/tests/compile-test.rs b/tests/compile-test.rs index ea93ced8a8653..0f75914335ba1 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -2,6 +2,8 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] +use askama::Template; +use askama::filters::Safe; use cargo_metadata::Message; use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use clippy_config::ClippyConfiguration; @@ -9,8 +11,6 @@ use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; use pulldown_cmark::{Options, Parser, html}; -use rinja::Template; -use rinja::filters::Safe; use serde::Deserialize; use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; From c44191af484d3dc2c74557723afa04aa668b90fd Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Apr 2025 21:31:56 +0200 Subject: [PATCH 100/103] Bump nightly version -> 2025-04-03 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 7c665b4249776..bbf9d79355665 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-03-20 +nightly-2025-04-03 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fcaeedc9a66b5..eb246030d01f9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-03-20" +channel = "nightly-2025-04-03" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From c97bd7463cd830e4e47a9366da86c0d760a2156a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Apr 2025 21:32:37 +0200 Subject: [PATCH 101/103] Bump Clippy version -> 0.1.88 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ae5b7d6af07a..5a6904d63be90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 934725fccb8ec..93fd2e35d1ba5 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 54347043a13d4..d36b6ae9c953a 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index ba4bb1d177c57..b98e990175033 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" From 944dfc749fe3a33056e8ec0137c004ac616c64fb Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Apr 2025 20:39:54 +0200 Subject: [PATCH 102/103] Changelog for Clippy 1.86 --- CHANGELOG.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9209d11feb34c..83ed2c3c81294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,68 @@ document. ## Unreleased / Beta / In Rust Nightly -[609cd310...master](https://github.com/rust-lang/rust-clippy/compare/609cd310...master) +[3e3715c3...master](https://github.com/rust-lang/rust-clippy/compare/3e3715c3...master) + +## Rust 1.86 + +Current stable, released 2025-04-03 + +[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-12-27T15%3A11%3A38Z..2025-02-06T13%3A57%3A58Z+base%3Amaster) + +### New Lints + +* Added [`unneeded_struct_pattern`] to `style` [#13465](https://github.com/rust-lang/rust-clippy/pull/13465) +* Added [`doc_overindented_list_items`] to `style` [#13711](https://github.com/rust-lang/rust-clippy/pull/13711) +* Added [`manual_ok_err`] to `complexity` [#13740](https://github.com/rust-lang/rust-clippy/pull/13740) +* Added [`non_std_lazy_statics`] to `pedantic` [#13770](https://github.com/rust-lang/rust-clippy/pull/13770) +* Added [`manual_repeat_n`] to `style` [#13858](https://github.com/rust-lang/rust-clippy/pull/13858) +* Added [`manual_option_as_slice`] to `complexity` [#13901](https://github.com/rust-lang/rust-clippy/pull/13901) +* Added [`double_ended_iterator_last`] to `perf` [#13922](https://github.com/rust-lang/rust-clippy/pull/13922) +* Added [`useless_nonzero_new_unchecked`] to `complexity` [#13993](https://github.com/rust-lang/rust-clippy/pull/13993) +* Added [`sliced_string_as_bytes`] to `perf` [#14002](https://github.com/rust-lang/rust-clippy/pull/14002) +* Added [`unnecessary_semicolon`] to `pedantic` [#14032](https://github.com/rust-lang/rust-clippy/pull/14032) +* Added [`return_and_then`] to `restriction` [#14051](https://github.com/rust-lang/rust-clippy/pull/14051) +* Added [`manual_slice_fill`] to `style` [#14082](https://github.com/rust-lang/rust-clippy/pull/14082) +* Added [`precedence_bits`] to `restriction` [#14115](https://github.com/rust-lang/rust-clippy/pull/14115) + +### Moves and Deprecations + +* Moved [`redundant_locals`] to `suspicious` (from `correctness`, now warn-by-default) + [#13747](https://github.com/rust-lang/rust-clippy/pull/13747) +* Moved [`format_push_string`] to `pedantic` (from `restriction`) + [#13894](https://github.com/rust-lang/rust-clippy/pull/13894) +* Moved [`format_collect`] to `pedantic` (from `perf`, now allow-by-default) + [#13894](https://github.com/rust-lang/rust-clippy/pull/13894) +* Moved [`mutex_integer`] to `restriction` (from `nursery`) [#14110](https://github.com/rust-lang/rust-clippy/pull/14110) + +### Enhancements + +* Add `lint-inconsistent-struct-field-initializers` configuration option to [`inconsistent_struct_constructor`] + [#13737](https://github.com/rust-lang/rust-clippy/pull/13737) +* [`len_zero`] now also triggers if deref target implements `is_empty()` + [#13871](https://github.com/rust-lang/rust-clippy/pull/13871) +* [`obfuscated_if_else`] now also triggers for the `.then(..).unwrap_or(..)` pattern + [#14021](https://github.com/rust-lang/rust-clippy/pull/14021) + +### False Positive Fixes + +* [`trailing_empty_array`] no longer triggers in tests [#13844](https://github.com/rust-lang/rust-clippy/pull/13844) +* [`missing_const_for_fn`] no longer triggers in tests [#13945](https://github.com/rust-lang/rust-clippy/pull/13945) +* [`significant_drop_in_scrutinee`]: do not falsely warn for temporaries created by `.await` expansion + [#13985](https://github.com/rust-lang/rust-clippy/pull/13985) + +### ICE Fixes + +* [`borrow_interior_mutable_const`] Fix an ICE that can occur when taking a reference to a tuple/`struct` field of an + interior mutable `const` [#13877](https://github.com/rust-lang/rust-clippy/pull/13877) + +### Others + +* Clippy now uses Rust edition 2024 [#13751](https://github.com/rust-lang/rust-clippy/pull/13751) ## Rust 1.85 -Current stable, released 2025-02-20 +Released 2025-02-20 [View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-11-15T19%3A31%3A08Z..2024-12-26T13%3A59%3A48Z+base%3Amaster) From 57b689809d6b4f6babef8443b497a616f6c6af2d Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Apr 2025 22:37:20 +0200 Subject: [PATCH 103/103] Update Cargo.lock --- Cargo.lock | 63 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96526f7e9e7da..f17899e99e34d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,6 +186,48 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "askama" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4e46abb203e00ef226442d452769233142bbfdd79c3941e84c8e61c4112543" +dependencies = [ + "askama_derive", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54398906821fd32c728135f7b351f0c7494ab95ae421d41b6f5a020e158f28a6" +dependencies = [ + "askama_parser", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash 2.1.1", + "serde", + "serde_derive", + "syn 2.0.100", +] + +[[package]] +name = "askama_parser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow 0.7.4", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -504,9 +546,10 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy" -version = "0.1.87" +version = "0.1.88" dependencies = [ "anstream", + "askama", "cargo_metadata 0.18.1", "clippy_config", "clippy_lints", @@ -520,7 +563,6 @@ dependencies = [ "pulldown-cmark 0.11.3", "quote", "regex", - "rinja", "rustc_tools_util 0.4.2", "serde", "serde_json", @@ -535,7 +577,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.87" +version = "0.1.88" dependencies = [ "clippy_utils", "itertools", @@ -560,7 +602,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.87" +version = "0.1.88" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -583,7 +625,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.87" +version = "0.1.88" dependencies = [ "arrayvec", "itertools", @@ -5418,7 +5460,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] @@ -6429,6 +6471,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr", +] + [[package]] name = "winsplit" version = "0.1.0"