From a4b413d4fd8244e8a54ada9ef6b99160d8f7f01c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 25 Dec 2023 20:53:01 +0000 Subject: [PATCH 001/102] Add asm label support to AST and HIR --- clippy_lints/src/loops/never_loop.rs | 3 +++ clippy_utils/src/hir_utils.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 245a903f99826..65d922f03df3a 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -255,6 +255,9 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, + InlineAsmOperand::Label { block } => { + never_loop_block(cx, block, local_labels, main_loop_id) + } })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index d50332e82da5b..643852c1c54fb 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -835,6 +835,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_body(anon_const.body); }, InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), + InlineAsmOperand::Label { block } => self.hash_block(block), } } }, From dbfbd0e77fd12a46ac81d0ec6fca4012aad0ea2d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 19 Feb 2024 23:30:17 +0100 Subject: [PATCH 002/102] feat: add `const_is_empty` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/is_empty.rs | 40 +++++++++ clippy_lints/src/methods/mod.rs | 35 +++++++- tests/ui/const_is_empty.rs | 52 +++++++++++ tests/ui/const_is_empty.stderr | 124 +++++++++++++++++++++++++++ 6 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/is_empty.rs create mode 100644 tests/ui/const_is_empty.rs create mode 100644 tests/ui/const_is_empty.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 26dce1f926572..9182e61f6d66d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5109,6 +5109,7 @@ Released 2018-09-13 [`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator [`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b6a35bb3e0328..fe9bc77ce556e 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -350,6 +350,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, + crate::methods::CONST_IS_EMPTY_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs new file mode 100644 index 0000000000000..4713c99d33b59 --- /dev/null +++ b/clippy_lints/src/methods/is_empty.rs @@ -0,0 +1,40 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::expr_or_init; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::source_map::Spanned; + +use super::CONST_IS_EMPTY; + +pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) { + return; + } + let init_expr = expr_or_init(cx, receiver); + if let Some(init_is_empty) = is_empty(init_expr) + && init_expr.span.eq_ctxt(receiver.span) + { + span_lint_and_note( + cx, + CONST_IS_EMPTY, + expr.span, + &format!("this expression always evaluates to {init_is_empty:?}"), + Some(init_expr.span), + "because its initialization value is constant", + ); + } +} + +fn is_empty(expr: &'_ rustc_hir::Expr<'_>) -> Option { + if let ExprKind::Lit(Spanned { node, .. }) = expr.kind { + match node { + LitKind::Str(sym, _) => Some(sym.is_empty()), + LitKind::ByteStr(value, _) | LitKind::CStr(value, _) => Some(value.is_empty()), + _ => None, + } + } else { + None + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 820f4c8589065..b6894971acc3e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -36,6 +36,7 @@ mod inefficient_to_string; mod inspect_for_each; mod into_iter_on_ref; mod is_digit_ascii_radix; +mod is_empty; mod iter_cloned_collect; mod iter_count; mod iter_filter; @@ -4041,6 +4042,31 @@ declare_clippy_lint! { "calling `.get().is_some()` or `.get().is_none()` instead of `.contains()` or `.contains_key()`" } +declare_clippy_lint! { + /// ### What it does + /// It identifies calls to `.is_empty()` on constant values. + /// + /// ### Why is this bad? + /// String literals and constant values are known at compile time. Checking if they + /// are empty will always return the same value. This might not be the intention of + /// the expression. + /// + /// ### Example + /// ```no_run + /// let value = ""; + /// if value.is_empty() { + /// println!("the string is empty"); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// println!("the string is empty"); + /// ``` + #[clippy::version = "1.78.0"] + pub CONST_IS_EMPTY, + suspicious, + "is_empty() called on strings known at compile time" +} pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4089,6 +4115,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, COLLAPSIBLE_STR_REPLACE, + CONST_IS_EMPTY, ITER_OVEREAGER_CLONED, CLONED_INSTEAD_OF_COPIED, FLAT_MAP_OPTION, @@ -4442,7 +4469,7 @@ impl Methods { ("as_deref" | "as_deref_mut", []) => { needless_option_as_deref::check(cx, expr, recv, name); }, - ("as_bytes" | "is_empty", []) => { + ("as_bytes", []) => { if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } @@ -4616,6 +4643,12 @@ impl Methods { ("hash", [arg]) => { unit_hash::check(cx, expr, recv, arg); }, + ("is_empty", []) => { + if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { + redundant_as_str::check(cx, expr, recv, as_str_span, span); + } + is_empty::check(cx, expr, recv); + }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false), diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs new file mode 100644 index 0000000000000..d61cdbb042496 --- /dev/null +++ b/tests/ui/const_is_empty.rs @@ -0,0 +1,52 @@ +#![warn(clippy::const_is_empty)] + +fn test_literal() { + if "".is_empty() { + //~^ERROR: this expression always evaluates to true + } + if "foobar".is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn test_byte_literal() { + if b"".is_empty() { + //~^ERROR: this expression always evaluates to true + } + if b"foobar".is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn test_no_mut() { + let mut empty = ""; + if empty.is_empty() { + // No lint because it is mutable + } +} + +fn test_propagated() { + let empty = ""; + let non_empty = "foobar"; + let empty2 = empty; + let non_empty2 = non_empty; + if empty2.is_empty() { + //~^ERROR: this expression always evaluates to true + } + if non_empty2.is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn main() { + let value = "foobar"; + let _ = value.is_empty(); + //~^ ERROR: this expression always evaluates to false + let x = value; + let _ = x.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = "".is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = b"".is_empty(); + //~^ ERROR: this expression always evaluates to true +} diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr new file mode 100644 index 0000000000000..5a89e686242d8 --- /dev/null +++ b/tests/ui/const_is_empty.stderr @@ -0,0 +1,124 @@ +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:4:8 + | +LL | if "".is_empty() { + | ^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:4:8 + | +LL | if "".is_empty() { + | ^^ + = note: `-D clippy::const-is-empty` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]` + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:7:8 + | +LL | if "foobar".is_empty() { + | ^^^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:7:8 + | +LL | if "foobar".is_empty() { + | ^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:13:8 + | +LL | if b"".is_empty() { + | ^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:13:8 + | +LL | if b"".is_empty() { + | ^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:16:8 + | +LL | if b"foobar".is_empty() { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:16:8 + | +LL | if b"foobar".is_empty() { + | ^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:33:8 + | +LL | if empty2.is_empty() { + | ^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:29:17 + | +LL | let empty = ""; + | ^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:36:8 + | +LL | if non_empty2.is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:30:21 + | +LL | let non_empty = "foobar"; + | ^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:43:13 + | +LL | let _ = value.is_empty(); + | ^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:42:17 + | +LL | let value = "foobar"; + | ^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:46:13 + | +LL | let _ = x.is_empty(); + | ^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:42:17 + | +LL | let value = "foobar"; + | ^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:48:13 + | +LL | let _ = "".is_empty(); + | ^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:48:13 + | +LL | let _ = "".is_empty(); + | ^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:50:13 + | +LL | let _ = b"".is_empty(); + | ^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:50:13 + | +LL | let _ = b"".is_empty(); + | ^^^ + +error: aborting due to 10 previous errors + From 89b334d47cf3326af349e2a6a747e707f47bdd30 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 18 Feb 2024 15:39:34 +0100 Subject: [PATCH 003/102] chore: update some tests to allow `const_is_empty` --- tests/ui/bool_assert_comparison.fixed | 2 +- tests/ui/bool_assert_comparison.rs | 2 +- tests/ui/len_zero.fixed | 8 ++++- tests/ui/len_zero.rs | 8 ++++- tests/ui/len_zero.stderr | 46 +++++++++++++-------------- tests/ui/needless_bitwise_bool.fixed | 1 + tests/ui/needless_bitwise_bool.rs | 1 + tests/ui/needless_bitwise_bool.stderr | 2 +- tests/ui/redundant_as_str.fixed | 1 + tests/ui/redundant_as_str.rs | 1 + tests/ui/redundant_as_str.stderr | 4 +-- 11 files changed, 46 insertions(+), 30 deletions(-) diff --git a/tests/ui/bool_assert_comparison.fixed b/tests/ui/bool_assert_comparison.fixed index 63b8e27e1c6c5..b05166a055ee3 100644 --- a/tests/ui/bool_assert_comparison.fixed +++ b/tests/ui/bool_assert_comparison.fixed @@ -1,4 +1,4 @@ -#![allow(unused, clippy::assertions_on_constants)] +#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index 58f81fedb7959..dc51fcf1d36b9 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -1,4 +1,4 @@ -#![allow(unused, clippy::assertions_on_constants)] +#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 745fc7e1a8b36..c16d7a2661615 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -1,5 +1,11 @@ #![warn(clippy::len_zero)] -#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)] +#![allow( + dead_code, + unused, + clippy::needless_if, + clippy::len_without_is_empty, + clippy::const_is_empty +)] extern crate core; use core::ops::Deref; diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 048ad2f4fd34d..5c49a5abf812f 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -1,5 +1,11 @@ #![warn(clippy::len_zero)] -#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)] +#![allow( + dead_code, + unused, + clippy::needless_if, + clippy::len_without_is_empty, + clippy::const_is_empty +)] extern crate core; use core::ops::Deref; diff --git a/tests/ui/len_zero.stderr b/tests/ui/len_zero.stderr index b1f04c94de665..dd07a85d62cac 100644 --- a/tests/ui/len_zero.stderr +++ b/tests/ui/len_zero.stderr @@ -1,5 +1,5 @@ error: length comparison to zero - --> tests/ui/len_zero.rs:82:8 + --> tests/ui/len_zero.rs:88:8 | LL | if x.len() == 0 { | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` @@ -8,13 +8,13 @@ LL | if x.len() == 0 { = help: to override `-D warnings` add `#[allow(clippy::len_zero)]` error: length comparison to zero - --> tests/ui/len_zero.rs:86:8 + --> tests/ui/len_zero.rs:92:8 | LL | if "".len() == 0 {} | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:95:20 + --> tests/ui/len_zero.rs:101:20 | LL | println!("{}", *s1 == ""); | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()` @@ -23,121 +23,121 @@ LL | println!("{}", *s1 == ""); = help: to override `-D warnings` add `#[allow(clippy::comparison_to_empty)]` error: comparison to empty slice - --> tests/ui/len_zero.rs:96:20 + --> tests/ui/len_zero.rs:102:20 | LL | println!("{}", **s2 == ""); | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:97:20 + --> tests/ui/len_zero.rs:103:20 | LL | println!("{}", ***s3 == ""); | ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:98:20 + --> tests/ui/len_zero.rs:104:20 | LL | println!("{}", ****s4 == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:99:20 + --> tests/ui/len_zero.rs:105:20 | LL | println!("{}", *****s5 == ""); | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:100:20 + --> tests/ui/len_zero.rs:106:20 | LL | println!("{}", ******(s6) == ""); | ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:103:20 + --> tests/ui/len_zero.rs:109:20 | LL | println!("{}", &**d2s == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:118:8 + --> tests/ui/len_zero.rs:124:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:121:8 + --> tests/ui/len_zero.rs:127:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:124:8 + --> tests/ui/len_zero.rs:130:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:127:8 + --> tests/ui/len_zero.rs:133:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:130:8 + --> tests/ui/len_zero.rs:136:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:141:8 + --> tests/ui/len_zero.rs:147:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:144:8 + --> tests/ui/len_zero.rs:150:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:147:8 + --> tests/ui/len_zero.rs:153:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:150:8 + --> tests/ui/len_zero.rs:156:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:153:8 + --> tests/ui/len_zero.rs:159:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:167:8 + --> tests/ui/len_zero.rs:173:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:179:6 + --> tests/ui/len_zero.rs:185:6 | LL | (has_is_empty.len() > 0).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:180:6 + --> tests/ui/len_zero.rs:186:6 | LL | (has_is_empty.len() == 0).then(|| println!("Or this!")); | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:184:8 + --> tests/ui/len_zero.rs:190:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` diff --git a/tests/ui/needless_bitwise_bool.fixed b/tests/ui/needless_bitwise_bool.fixed index 201f8a4c19de5..a8176618c1f23 100644 --- a/tests/ui/needless_bitwise_bool.fixed +++ b/tests/ui/needless_bitwise_bool.fixed @@ -1,4 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] +#![allow(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/tests/ui/needless_bitwise_bool.rs b/tests/ui/needless_bitwise_bool.rs index b0e5014b74b74..f190eb2b76e76 100644 --- a/tests/ui/needless_bitwise_bool.rs +++ b/tests/ui/needless_bitwise_bool.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] +#![allow(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/tests/ui/needless_bitwise_bool.stderr b/tests/ui/needless_bitwise_bool.stderr index f29d4492540f9..9f14646c3e5ae 100644 --- a/tests/ui/needless_bitwise_bool.stderr +++ b/tests/ui/needless_bitwise_bool.stderr @@ -1,5 +1,5 @@ error: use of bitwise operator instead of lazy operator between booleans - --> tests/ui/needless_bitwise_bool.rs:22:8 + --> tests/ui/needless_bitwise_bool.rs:23:8 | LL | if y & !x { | ^^^^^^ help: try: `y && !x` diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index 4185b402226c0..708a1cc91506d 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -1,4 +1,5 @@ #![warn(clippy::redundant_as_str)] +#![allow(clippy::const_is_empty)] fn main() { let string = "Hello, world!".to_owned(); diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 7a74d8a55ded7..257af591ceffb 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -1,4 +1,5 @@ #![warn(clippy::redundant_as_str)] +#![allow(clippy::const_is_empty)] fn main() { let string = "Hello, world!".to_owned(); diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr index f086de5fedebb..f5379d701db82 100644 --- a/tests/ui/redundant_as_str.stderr +++ b/tests/ui/redundant_as_str.stderr @@ -1,5 +1,5 @@ error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> tests/ui/redundant_as_str.rs:7:29 + --> tests/ui/redundant_as_str.rs:8:29 | LL | let _redundant = string.as_str().as_bytes(); | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` @@ -8,7 +8,7 @@ LL | let _redundant = string.as_str().as_bytes(); = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> tests/ui/redundant_as_str.rs:8:29 + --> tests/ui/redundant_as_str.rs:9:29 | LL | let _redundant = string.as_str().is_empty(); | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` From 288497093b3bba07d8ab994913135ad6b0cccbc8 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 19 Feb 2024 23:52:09 +0100 Subject: [PATCH 004/102] feat: extend `const_is_empty` with many kinds of constants --- clippy_lints/src/methods/is_empty.rs | 49 +++++---- clippy_utils/src/consts.rs | 96 +++++++++++++++-- tests/ui/const_is_empty.rs | 49 +++++++++ tests/ui/const_is_empty.stderr | 153 ++++++++++++++++----------- 4 files changed, 259 insertions(+), 88 deletions(-) diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 4713c99d33b59..7fe66062251bb 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,40 +1,49 @@ -use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::expr_or_init; -use rustc_ast::LitKind; -use rustc_hir::{Expr, ExprKind}; +use clippy_utils::consts::constant_is_empty; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{find_binding_init, path_to_local}; +use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_span::source_map::Spanned; +use rustc_span::sym; use super::CONST_IS_EMPTY; +/// Expression whose initialization depend on a constant conditioned by a `#[cfg(…)]` directive will +/// not trigger the lint. pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_>) { if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) { return; } let init_expr = expr_or_init(cx, receiver); - if let Some(init_is_empty) = is_empty(init_expr) - && init_expr.span.eq_ctxt(receiver.span) - { - span_lint_and_note( + if !receiver.span.eq_ctxt(init_expr.span) { + return; + } + if let Some(init_is_empty) = constant_is_empty(cx, init_expr) { + span_lint( cx, CONST_IS_EMPTY, expr.span, &format!("this expression always evaluates to {init_is_empty:?}"), - Some(init_expr.span), - "because its initialization value is constant", ); } } -fn is_empty(expr: &'_ rustc_hir::Expr<'_>) -> Option { - if let ExprKind::Lit(Spanned { node, .. }) = expr.kind { - match node { - LitKind::Str(sym, _) => Some(sym.is_empty()), - LitKind::ByteStr(value, _) | LitKind::CStr(value, _) => Some(value.is_empty()), - _ => None, - } - } else { - None +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))) +} + +/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization +/// value depends on a `#[cfg(…)]` directive. +fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { + while let Some(init) = path_to_local(expr) + .and_then(|id| find_binding_init(cx, id)) + .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) + .filter(|init| !is_under_cfg(cx, init.hir_id)) + { + expr = init; } + expr } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 79c691992a85d..774f3f99d46f7 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -10,6 +10,7 @@ use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{alloc_range, Scalar}; +use rustc_middle::mir::ConstValue; use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::symbol::{Ident, Symbol}; @@ -303,6 +304,12 @@ impl ConstantSource { } } +/// Attempts to check whether the expression is a constant representing an empty slice, str, array, +/// etc… +pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option { + ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e) +} + /// Attempts to evaluate the expression as a constant. pub fn constant<'tcx>( lcx: &LateContext<'tcx>, @@ -402,7 +409,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), + ExprKind::Path(ref qpath) => { + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { + let result = mir_to_const(this.lcx, result)?; + this.source = ConstantSource::Constant; + Some(result) + }) + }, ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { @@ -468,6 +481,39 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } + /// Simple constant folding to determine if an expression is an empty slice, str, array, … + pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option { + match e.kind { + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::Path(ref qpath) => { + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { + mir_is_empty(this.lcx, result) + }) + }, + ExprKind::Lit(lit) => { + if is_direct_expn_of(e.span, "cfg").is_some() { + None + } else { + match &lit.node { + LitKind::Str(is, _) => Some(is.is_empty()), + LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.is_empty()), + _ => None, + } + } + }, + ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), + ExprKind::Repeat(..) => { + if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() { + Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0) + } else { + span_bug!(e.span, "typeck error"); + } + }, + _ => None, + } + } + #[expect(clippy::cast_possible_wrap)] fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { use self::Constant::{Bool, Int}; @@ -515,8 +561,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { vec.iter().map(|elem| self.expr(elem)).collect::>() } - /// Lookup a possibly constant expression from an `ExprKind::Path`. - fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option> { + /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. + fn fetch_path_and_apply(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option + where + F: FnOnce(&mut Self, rustc_middle::mir::Const<'tcx>) -> Option, + { let res = self.typeck_results.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { @@ -549,9 +598,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; - let result = mir_to_const(self.lcx, result)?; - self.source = ConstantSource::Constant; - Some(result) + f(self, result) }, _ => None, } @@ -742,7 +789,6 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option> { - use rustc_middle::mir::ConstValue; let mir::Const::Val(val, _) = result else { // We only work on evaluated consts. return None; @@ -788,6 +834,42 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> } } +fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option { + let mir::Const::Val(val, _) = result else { + // We only work on evaluated consts. + return None; + }; + match (val, result.ty().kind()) { + (_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { + ty::Str | ty::Slice(_) => { + if let ConstValue::Indirect { alloc_id, offset } = val { + // Get the length from the slice, using the same formula as + // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. + let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let ptr_size = lcx.tcx.data_layout.pointer_size; + if a.size() < offset + 2 * ptr_size { + // (partially) dangling reference + return None; + } + let len = a + .read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false) + .ok()? + .to_target_usize(&lcx.tcx) + .ok()?; + Some(len == 0) + } else { + None + } + }, + ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + _ => None, + }, + (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + (ConstValue::ZeroSized, _) => Some(true), + _ => None, + } +} + fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, lcx: &LateContext<'tcx>, diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index d61cdbb042496..bd7e3a7c5d5b9 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -38,6 +38,55 @@ fn test_propagated() { } } +const EMPTY_STR: &str = ""; +const NON_EMPTY_STR: &str = "foo"; +const EMPTY_BSTR: &[u8] = b""; +const NON_EMPTY_BSTR: &[u8] = b"foo"; +const EMPTY_U8_SLICE: &[u8] = &[]; +const NON_EMPTY_U8_SLICE: &[u8] = &[1, 2]; +const EMPTY_SLICE: &[u32] = &[]; +const NON_EMPTY_SLICE: &[u32] = &[1, 2]; +const NON_EMPTY_SLICE_REPEAT: &[u32] = &[1; 2]; +const EMPTY_ARRAY: [u32; 0] = []; +const EMPTY_ARRAY_REPEAT: [u32; 0] = [1; 0]; +const NON_EMPTY_ARRAY: [u32; 2] = [1, 2]; +const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2]; +const EMPTY_REF_ARRAY: &[u32; 0] = &[]; +const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3]; + +fn test_from_const() { + let _ = EMPTY_STR.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_STR.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_BSTR.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_BSTR.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = EMPTY_ARRAY_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = EMPTY_U8_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_U8_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_REF_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_REF_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to false +} + fn main() { let value = "foobar"; let _ = value.is_empty(); diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 5a89e686242d8..ef5aa556e6a04 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -4,11 +4,6 @@ error: this expression always evaluates to true LL | if "".is_empty() { | ^^^^^^^^^^^^^ | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:4:8 - | -LL | if "".is_empty() { - | ^^ = note: `-D clippy::const-is-empty` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]` @@ -17,108 +12,144 @@ error: this expression always evaluates to false | LL | if "foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:7:8 - | -LL | if "foobar".is_empty() { - | ^^^^^^^^ error: this expression always evaluates to true --> tests/ui/const_is_empty.rs:13:8 | LL | if b"".is_empty() { | ^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:13:8 - | -LL | if b"".is_empty() { - | ^^^ error: this expression always evaluates to false --> tests/ui/const_is_empty.rs:16:8 | LL | if b"foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:16:8 - | -LL | if b"foobar".is_empty() { - | ^^^^^^^^^ error: this expression always evaluates to true --> tests/ui/const_is_empty.rs:33:8 | LL | if empty2.is_empty() { | ^^^^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:29:17 - | -LL | let empty = ""; - | ^^ error: this expression always evaluates to false --> tests/ui/const_is_empty.rs:36:8 | LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:58:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:30:21 +LL | let _ = EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:60:13 | -LL | let non_empty = "foobar"; - | ^^^^^^^^ +LL | let _ = NON_EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:62:13 + | +LL | let _ = EMPTY_BSTR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:43:13 + --> tests/ui/const_is_empty.rs:64:13 | -LL | let _ = value.is_empty(); - | ^^^^^^^^^^^^^^^^ +LL | let _ = NON_EMPTY_BSTR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:66:13 + | +LL | let _ = EMPTY_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:68:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:42:17 +LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:70:13 | -LL | let value = "foobar"; - | ^^^^^^^^ +LL | let _ = EMPTY_U8_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:46:13 + --> tests/ui/const_is_empty.rs:72:13 | -LL | let _ = x.is_empty(); - | ^^^^^^^^^^^^ +LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:74:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:42:17 +LL | let _ = NON_EMPTY_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:76:13 | -LL | let value = "foobar"; - | ^^^^^^^^ +LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:48:13 + --> tests/ui/const_is_empty.rs:78:13 | -LL | let _ = "".is_empty(); - | ^^^^^^^^^^^^^ +LL | let _ = EMPTY_REF_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:80:13 + | +LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:82:13 + | +LL | let _ = EMPTY_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:84:13 + | +LL | let _ = NON_EMPTY_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:86:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:48:13 +LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:92:13 + | +LL | let _ = value.is_empty(); + | ^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:95:13 + | +LL | let _ = x.is_empty(); + | ^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:97:13 | LL | let _ = "".is_empty(); - | ^^ + | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:50:13 + --> tests/ui/const_is_empty.rs:99:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:50:13 - | -LL | let _ = b"".is_empty(); - | ^^^ -error: aborting due to 10 previous errors +error: aborting due to 25 previous errors From 1159e2c00fdc97dede1dca0464d57750283ac3df Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 20 Feb 2024 00:05:59 +0100 Subject: [PATCH 005/102] feat: add more tests for the `const_is_empty` lint Suggested by @xFredNet and @matthiaskrgr. --- tests/ui/const_is_empty.rs | 68 ++++++++++++++++++++++++++++++++++ tests/ui/const_is_empty.stderr | 58 ++++++++++++++++------------- 2 files changed, 100 insertions(+), 26 deletions(-) diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index bd7e3a7c5d5b9..df7817e4b5fef 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -1,4 +1,6 @@ +#![feature(inline_const)] #![warn(clippy::const_is_empty)] +#![allow(clippy::needless_late_init, unused_must_use)] fn test_literal() { if "".is_empty() { @@ -99,3 +101,69 @@ fn main() { let _ = b"".is_empty(); //~^ ERROR: this expression always evaluates to true } + +fn str_from_arg(var: &str) { + var.is_empty(); + // Do not lint, we know nothiny about var +} + +fn update_str() { + let mut value = "duck"; + value = "penguin"; + + let _ = value.is_empty(); + // Do not lint since value is mutable +} + +fn macros() { + // Content from Macro + let file = include_str!("const_is_empty.rs"); + let _ = file.is_empty(); + // No lint because initializer comes from a macro result + + let var = env!("PATH"); + let _ = var.is_empty(); + // No lint because initializer comes from a macro result +} + +fn conditional_value() { + let value; + + if true { + value = "hey"; + } else { + value = "hej"; + } + + let _ = value.is_empty(); + // Do not lint, current constant folding is too simple to detect this +} + +fn cfg_conditioned() { + #[cfg(test)] + let val = ""; + #[cfg(not(test))] + let val = "foo"; + + let _ = val.is_empty(); + // Do not lint, value depend on a #[cfg(…)] directive +} + +fn not_cfg_conditioned() { + let val = ""; + #[cfg(not(target_os = "inexistent"))] + let _ = val.is_empty(); + //~^ ERROR: this expression always evaluates to true +} + +const fn const_rand() -> &'static str { + "17" +} + +fn const_expressions() { + let _ = const { if true { "1" } else { "2" } }.is_empty(); + // Do not lint, we do not recurse into boolean expressions + + let _ = const_rand().is_empty(); + // Do not lint, we do not recurse into functions +} diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index ef5aa556e6a04..0e09da77bb469 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -1,5 +1,5 @@ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:4:8 + --> tests/ui/const_is_empty.rs:6:8 | LL | if "".is_empty() { | ^^^^^^^^^^^^^ @@ -8,148 +8,154 @@ LL | if "".is_empty() { = help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]` error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:7:8 + --> tests/ui/const_is_empty.rs:9:8 | LL | if "foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:13:8 + --> tests/ui/const_is_empty.rs:15:8 | LL | if b"".is_empty() { | ^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:16:8 + --> tests/ui/const_is_empty.rs:18:8 | LL | if b"foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:33:8 + --> tests/ui/const_is_empty.rs:35:8 | LL | if empty2.is_empty() { | ^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:36:8 + --> tests/ui/const_is_empty.rs:38:8 | LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:58:13 + --> tests/ui/const_is_empty.rs:60:13 | LL | let _ = EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:60:13 + --> tests/ui/const_is_empty.rs:62:13 | LL | let _ = NON_EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:62:13 + --> tests/ui/const_is_empty.rs:64:13 | LL | let _ = EMPTY_BSTR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:64:13 + --> tests/ui/const_is_empty.rs:66:13 | LL | let _ = NON_EMPTY_BSTR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:66:13 + --> tests/ui/const_is_empty.rs:68:13 | LL | let _ = EMPTY_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:68:13 + --> tests/ui/const_is_empty.rs:70:13 | LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:70:13 + --> tests/ui/const_is_empty.rs:72:13 | LL | let _ = EMPTY_U8_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:72:13 + --> tests/ui/const_is_empty.rs:74:13 | LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:74:13 + --> tests/ui/const_is_empty.rs:76:13 | LL | let _ = NON_EMPTY_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:76:13 + --> tests/ui/const_is_empty.rs:78:13 | LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:78:13 + --> tests/ui/const_is_empty.rs:80:13 | LL | let _ = EMPTY_REF_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:80:13 + --> tests/ui/const_is_empty.rs:82:13 | LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:82:13 + --> tests/ui/const_is_empty.rs:84:13 | LL | let _ = EMPTY_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:84:13 + --> tests/ui/const_is_empty.rs:86:13 | LL | let _ = NON_EMPTY_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:86:13 + --> tests/ui/const_is_empty.rs:88:13 | LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:92:13 + --> tests/ui/const_is_empty.rs:94:13 | LL | let _ = value.is_empty(); | ^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:95:13 + --> tests/ui/const_is_empty.rs:97:13 | LL | let _ = x.is_empty(); | ^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:97:13 + --> tests/ui/const_is_empty.rs:99:13 | LL | let _ = "".is_empty(); | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:99:13 + --> tests/ui/const_is_empty.rs:101:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ -error: aborting due to 25 previous errors +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:155:13 + | +LL | let _ = val.is_empty(); + | ^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors From 898ed8825d610d4f441ccb7c84c770261ce45ded Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 26 Feb 2024 09:47:13 +0100 Subject: [PATCH 006/102] feat: make `const_is_empty` lint ignore external constants --- clippy_utils/src/consts.rs | 11 +++++++++++ tests/ui/const_is_empty.rs | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 774f3f99d46f7..a4cb4ee93caaf 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -13,6 +13,7 @@ use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::ConstValue; use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; use rustc_middle::{bug, mir, span_bug}; +use rustc_span::def_id::DefId; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; use rustc_target::abi::Size; @@ -482,11 +483,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } /// Simple constant folding to determine if an expression is an empty slice, str, array, … + /// `None` will be returned if the constness cannot be determined, or if the resolution + /// leaves the local crate. pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr_is_empty(e), ExprKind::Path(ref qpath) => { + if !self + .typeck_results + .qpath_res(qpath, e.hir_id) + .opt_def_id() + .is_some_and(DefId::is_local) + { + return None; + } self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { mir_is_empty(this.lcx, result) }) diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index df7817e4b5fef..ae37a82e4f936 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -167,3 +167,8 @@ fn const_expressions() { let _ = const_rand().is_empty(); // Do not lint, we do not recurse into functions } + +fn constant_from_external_crate() { + let _ = std::env::consts::EXE_EXTENSION.is_empty(); + // Do not lint, `exe_ext` comes from the `std` crate +} From 301d293ef6cc1aeb341b31a5339f7d857a38b930 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 4 Mar 2024 16:38:29 +0000 Subject: [PATCH 007/102] Move `iter_nth` to `style`, add machine applicable suggestion --- clippy_lints/src/methods/iter_nth.rs | 49 +++++++++++++---------- clippy_lints/src/methods/mod.rs | 16 ++++---- tests/ui/iter_nth.fixed | 60 ++++++++++++++++++++++++++++ tests/ui/iter_nth.rs | 3 ++ tests/ui/iter_nth.stderr | 48 ++++++++++++++++++---- 5 files changed, 139 insertions(+), 37 deletions(-) create mode 100644 tests/ui/iter_nth.fixed diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 121043104058f..5b0b70b4b9659 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,10 +1,10 @@ -use super::utils::derefs_to_slice; -use crate::methods::iter_nth_zero; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::get_type_diagnostic_name; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use rustc_span::Span; use super::ITER_NTH; @@ -12,28 +12,33 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_recv: &'tcx hir::Expr<'tcx>, - nth_recv: &hir::Expr<'_>, - nth_arg: &hir::Expr<'_>, - is_mut: bool, -) { - let mut_str = if is_mut { "_mut" } else { "" }; - let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() { - "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::Vec) { - "`Vec`" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::VecDeque) { - "`VecDeque`" - } else { - iter_nth_zero::check(cx, expr, nth_recv, nth_arg); - return; // caller is not a type that we want to lint + iter_method: &str, + iter_span: Span, + nth_span: Span, +) -> bool { + let caller_type = match get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(iter_recv).peel_refs()) { + Some(sym::Vec) => "`Vec`", + Some(sym::VecDeque) => "`VecDeque`", + _ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice", + // caller is not a type that we want to lint + _ => return false, }; - span_lint_and_help( + span_lint_and_then( cx, ITER_NTH, expr.span, - &format!("called `.iter{mut_str}().nth()` on a {caller_type}"), - None, - &format!("calling `.get{mut_str}()` is both faster and more readable"), + &format!("called `.{iter_method}().nth()` on a {caller_type}"), + |diag| { + let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" }; + diag.span_suggestion_verbose( + iter_span.to(nth_span), + format!("`{get_method}` is equivalent but more concise"), + get_method, + Applicability::MachineApplicable, + ); + }, ); + + true } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8a24ccea3a1eb..5445391c03503 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1235,12 +1235,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `.iter().nth()` (and the related - /// `.iter_mut().nth()`) on standard library types with *O*(1) element access. + /// Checks for usage of `.iter().nth()`/`.iter_mut().nth()` on standard library types that have + /// equivalent `.get()`/`.get_mut()` methods. /// /// ### Why is this bad? - /// `.get()` and `.get_mut()` are more efficient and more - /// readable. + /// `.get()` and `.get_mut()` are equivalent but more concise. /// /// ### Example /// ```no_run @@ -1256,7 +1255,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub ITER_NTH, - perf, + style, "using `.iter().nth()` on a standard library type with O(1) element access" } @@ -4723,8 +4722,11 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some((iter_method @ ("iter" | "iter_mut"), iter_recv, [], iter_span, _)) => { + if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) { + iter_nth_zero::check(cx, expr, recv, n_arg); + } + }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), diff --git a/tests/ui/iter_nth.fixed b/tests/ui/iter_nth.fixed new file mode 100644 index 0000000000000..aff3731a88372 --- /dev/null +++ b/tests/ui/iter_nth.fixed @@ -0,0 +1,60 @@ +//@aux-build:option_helpers.rs + +#![warn(clippy::iter_nth)] +#![allow(clippy::useless_vec)] + +#[macro_use] +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::VecDeque; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +/// Checks implementation of `ITER_NTH` lint. +fn iter_nth() { + let mut some_vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); + + { + // Make sure we lint `.iter()` for relevant types. + let bad_vec = some_vec.get(3); + let bad_slice = &some_vec[..].get(3); + let bad_boxed_slice = boxed_slice.get(3); + let bad_vec_deque = some_vec_deque.get(3); + } + + { + // Make sure we lint `.iter_mut()` for relevant types. + let bad_vec = some_vec.get_mut(3); + } + { + let bad_slice = &some_vec[..].get_mut(3); + } + { + let bad_vec_deque = some_vec_deque.get_mut(3); + } + + let vec_ref = &Vec::::new(); + vec_ref.get(3); + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + let ok = false_positive.iter().nth(3); + let ok_mut = false_positive.iter_mut().nth(3); +} + +fn main() {} diff --git a/tests/ui/iter_nth.rs b/tests/ui/iter_nth.rs index 7c567bb81d88a..89d68044dddac 100644 --- a/tests/ui/iter_nth.rs +++ b/tests/ui/iter_nth.rs @@ -48,6 +48,9 @@ fn iter_nth() { let bad_vec_deque = some_vec_deque.iter_mut().nth(3); } + let vec_ref = &Vec::::new(); + vec_ref.iter().nth(3); + // Make sure we don't lint for non-relevant types. let false_positive = HasIter; let ok = false_positive.iter().nth(3); diff --git a/tests/ui/iter_nth.stderr b/tests/ui/iter_nth.stderr index c5dd0c99727be..178463f534754 100644 --- a/tests/ui/iter_nth.stderr +++ b/tests/ui/iter_nth.stderr @@ -4,9 +4,12 @@ error: called `.iter().nth()` on a `Vec` LL | let bad_vec = some_vec.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable = note: `-D clippy::iter-nth` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_nth)]` +help: `get` is equivalent but more concise + | +LL | let bad_vec = some_vec.get(3); + | ~~~ error: called `.iter().nth()` on a slice --> tests/ui/iter_nth.rs:35:26 @@ -14,7 +17,10 @@ error: called `.iter().nth()` on a slice LL | let bad_slice = &some_vec[..].iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_slice = &some_vec[..].get(3); + | ~~~ error: called `.iter().nth()` on a slice --> tests/ui/iter_nth.rs:36:31 @@ -22,7 +28,10 @@ error: called `.iter().nth()` on a slice LL | let bad_boxed_slice = boxed_slice.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_boxed_slice = boxed_slice.get(3); + | ~~~ error: called `.iter().nth()` on a `VecDeque` --> tests/ui/iter_nth.rs:37:29 @@ -30,7 +39,10 @@ error: called `.iter().nth()` on a `VecDeque` LL | let bad_vec_deque = some_vec_deque.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_vec_deque = some_vec_deque.get(3); + | ~~~ error: called `.iter_mut().nth()` on a `Vec` --> tests/ui/iter_nth.rs:42:23 @@ -38,7 +50,10 @@ error: called `.iter_mut().nth()` on a `Vec` LL | let bad_vec = some_vec.iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_vec = some_vec.get_mut(3); + | ~~~~~~~ error: called `.iter_mut().nth()` on a slice --> tests/ui/iter_nth.rs:45:26 @@ -46,7 +61,10 @@ error: called `.iter_mut().nth()` on a slice LL | let bad_slice = &some_vec[..].iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_slice = &some_vec[..].get_mut(3); + | ~~~~~~~ error: called `.iter_mut().nth()` on a `VecDeque` --> tests/ui/iter_nth.rs:48:29 @@ -54,7 +72,21 @@ error: called `.iter_mut().nth()` on a `VecDeque` LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_vec_deque = some_vec_deque.get_mut(3); + | ~~~~~~~ + +error: called `.iter().nth()` on a `Vec` + --> tests/ui/iter_nth.rs:52:5 + | +LL | vec_ref.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: `get` is equivalent but more concise + | +LL | vec_ref.get(3); + | ~~~ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors From 865ac89dbd26780e9a39815516c07c6e6148a4bb Mon Sep 17 00:00:00 2001 From: beetrees Date: Mon, 15 May 2023 18:37:47 +0000 Subject: [PATCH 008/102] Use `rustc_driver::args::raw_args()` in Clippy --- src/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index 8fc66644632cd..9e42abbc9aa9e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -190,7 +190,7 @@ pub fn main() { }); exit(rustc_driver::catch_with_exit_code(move || { - let mut orig_args: Vec = env::args().collect(); + let mut orig_args = rustc_driver::args::raw_args(&early_dcx)?; let has_sysroot_arg = |args: &mut [String]| -> bool { if arg_value(args, "--sysroot", |_| true).is_some() { From a9858da1ec9c70afe26a014ed79959685dd43a28 Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Thu, 7 Mar 2024 23:41:48 +0900 Subject: [PATCH 009/102] Fix dependency specifications --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5d1d0ce2c42fb..64571d4cc80d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ path = "src/driver.rs" clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } rustc_tools_util = "0.3.0" -tempfile = { version = "3.2", optional = true } +tempfile = { version = "3.3", optional = true } termize = "0.1" color-print = "0.3.4" anstream = "0.6.0" @@ -32,7 +32,7 @@ anstream = "0.6.0" [dev-dependencies] ui_test = "0.22.2" tester = "0.9" -regex = "1.5" +regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" # This is used by the `collect-metadata` alias. @@ -42,8 +42,8 @@ itertools = "0.12" # UI test dependencies clippy_utils = { path = "clippy_utils" } if_chain = "1.0" -quote = "1.0" -serde = { version = "1.0.125", features = ["derive"] } +quote = "1.0.25" +serde = { version = "1.0.145", features = ["derive"] } syn = { version = "2.0", features = ["full"] } futures = "0.3" parking_lot = "0.12" From 7e83df40685907ea917813ec5b13fea360bdc070 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 7 Mar 2024 17:19:29 +0100 Subject: [PATCH 010/102] Merge commit '93f0a9a91f58c9b2153868f458402155fb6265bb' into clippy-subtree-update --- .github/driver.sh | 4 +- .github/workflows/clippy.yml | 11 +- .github/workflows/clippy_bors.yml | 32 +- CHANGELOG.md | 112 +- book/src/development/trait_checking.md | 50 + book/src/development/type_checking.md | 16 + book/src/lint_configuration.md | 884 ++++++------ clippy_config/src/conf.rs | 6 +- clippy_config/src/lib.rs | 2 +- clippy_config/src/metadata.rs | 4 +- clippy_config/src/msrvs.rs | 1 + clippy_lints/src/assigning_clones.rs | 322 +++++ clippy_lints/src/attrs.rs | 1210 ----------------- .../attrs/allow_attributes_without_reason.rs | 37 + .../attrs/blanket_clippy_restriction_lints.rs | 44 + clippy_lints/src/attrs/deprecated_cfg_attr.rs | 87 ++ clippy_lints/src/attrs/deprecated_semver.rs | 20 + clippy_lints/src/attrs/empty_line_after.rs | 52 + clippy_lints/src/attrs/inline_always.rs | 29 + clippy_lints/src/attrs/maybe_misused_cfg.rs | 51 + .../src/attrs/mismatched_target_os.rs | 90 ++ .../src/attrs/mixed_attributes_style.rs | 30 + clippy_lints/src/attrs/mod.rs | 588 ++++++++ clippy_lints/src/attrs/non_minimal_cfg.rs | 49 + .../src/attrs/should_panic_without_expect.rs | 54 + .../src/attrs/unnecessary_clippy_cfg.rs | 70 + clippy_lints/src/attrs/useless_attribute.rs | 73 + clippy_lints/src/attrs/utils.rs | 87 ++ clippy_lints/src/booleans.rs | 26 +- clippy_lints/src/casts/unnecessary_cast.rs | 13 +- clippy_lints/src/declared_lints.rs | 2 + clippy_lints/src/derive.rs | 6 +- clippy_lints/src/doc/markdown.rs | 17 +- clippy_lints/src/doc/mod.rs | 18 +- clippy_lints/src/entry.rs | 55 +- clippy_lints/src/let_underscore.rs | 5 +- clippy_lints/src/lib.rs | 2 + clippy_lints/src/loops/manual_memcpy.rs | 6 +- .../matches/significant_drop_in_scrutinee.rs | 7 +- clippy_lints/src/methods/mod.rs | 2 + clippy_lints/src/operators/identity_op.rs | 41 +- .../src/operators/misrefactored_assign_op.rs | 5 +- clippy_lints/src/question_mark.rs | 205 +-- clippy_lints/src/redundant_closure_call.rs | 9 + clippy_lints/src/redundant_field_names.rs | 34 +- clippy_lints/src/std_instead_of_core.rs | 6 +- ...ead_local_initializer_can_be_made_const.rs | 67 +- clippy_lints/src/transmute/mod.rs | 2 +- .../transmutes_expressible_as_ptr_casts.rs | 2 + clippy_lints/src/types/vec_box.rs | 7 +- clippy_utils/src/paths.rs | 1 - clippy_utils/src/sugg.rs | 2 +- rust-toolchain | 2 +- src/main.rs | 9 +- tests/compile-test.rs | 1 + tests/missing-test-files.rs | 2 +- tests/ui-internal/custom_ice_message.rs | 1 + tests/ui-internal/custom_ice_message.stderr | 7 +- tests/ui-internal/disallow_span_lint.rs | 15 +- tests/ui-internal/disallow_span_lint.stderr | 2 +- .../undocumented_unsafe_blocks.default.stderr | 74 +- ...undocumented_unsafe_blocks.disabled.stderr | 94 +- .../undocumented_unsafe_blocks.rs | 7 +- tests/ui/assigning_clones.fixed | 222 +++ tests/ui/assigning_clones.rs | 222 +++ tests/ui/assigning_clones.stderr | 107 ++ .../ui/crashes/unreachable-array-or-slice.rs | 2 +- tests/ui/derive_partial_eq_without_eq.fixed | 26 + tests/ui/derive_partial_eq_without_eq.rs | 26 + tests/ui/derive_partial_eq_without_eq.stderr | 14 +- tests/ui/doc/doc-fixable.fixed | 5 + tests/ui/doc/doc-fixable.rs | 5 + tests/ui/doc/doc-fixable.stderr | 13 +- tests/ui/doc/issue_9473.fixed | 9 + tests/ui/doc/issue_9473.rs | 9 + tests/ui/doc/issue_9473.stderr | 15 + tests/ui/else_if_without_else.rs | 2 + tests/ui/else_if_without_else.stderr | 4 +- tests/ui/empty_docs.rs | 2 + tests/ui/empty_docs.stderr | 18 +- tests/ui/entry.fixed | 11 + tests/ui/entry.rs | 11 + tests/ui/explicit_iter_loop.fixed | 2 +- tests/ui/explicit_iter_loop.rs | 2 +- tests/ui/field_reassign_with_default.rs | 1 + tests/ui/field_reassign_with_default.stderr | 44 +- tests/ui/identity_op.fixed | 2 +- tests/ui/identity_op.rs | 2 +- tests/ui/indexing_slicing_index.rs | 2 + tests/ui/indexing_slicing_index.stderr | 32 +- tests/ui/let_underscore_untyped.rs | 2 + tests/ui/manual_let_else.rs | 10 + tests/ui/manual_let_else.stderr | 68 +- .../ui/manual_memcpy/without_loop_counters.rs | 58 +- .../without_loop_counters.stderr | 52 +- tests/ui/manual_retain.fixed | 2 + tests/ui/manual_retain.rs | 2 + tests/ui/manual_retain.stderr | 76 +- tests/ui/many_single_char_names.rs | 2 + tests/ui/many_single_char_names.stderr | 10 +- tests/ui/min_rust_version_invalid_attr.rs | 2 + tests/ui/min_rust_version_invalid_attr.stderr | 12 +- tests/ui/mixed_attributes_style.rs | 39 + tests/ui/mixed_attributes_style.stderr | 30 + tests/ui/mut_mut.rs | 2 + tests/ui/mut_mut.stderr | 18 +- tests/ui/no_effect_replace.rs | 2 + tests/ui/no_effect_replace.stderr | 16 +- tests/ui/non_canonical_clone_impl.fixed | 1 + tests/ui/non_canonical_clone_impl.rs | 1 + tests/ui/non_canonical_clone_impl.stderr | 8 +- tests/ui/nonminimal_bool.rs | 6 + tests/ui/nonminimal_bool.stderr | 58 +- tests/ui/nonminimal_bool_methods.fixed | 2 + tests/ui/nonminimal_bool_methods.rs | 2 + tests/ui/nonminimal_bool_methods.stderr | 26 +- tests/ui/option_option.rs | 2 + tests/ui/option_option.stderr | 26 +- tests/ui/ptr_as_ptr.fixed | 1 + tests/ui/ptr_as_ptr.rs | 1 + tests/ui/ptr_as_ptr.stderr | 66 +- tests/ui/question_mark.fixed | 10 + tests/ui/question_mark.rs | 10 + tests/ui/redundant_closure_call_fixable.fixed | 17 + tests/ui/redundant_closure_call_fixable.rs | 17 + tests/ui/redundant_field_names.fixed | 15 +- tests/ui/redundant_field_names.rs | 15 +- tests/ui/redundant_field_names.stderr | 15 +- tests/ui/renamed_builtin_attr.fixed | 2 + tests/ui/renamed_builtin_attr.rs | 2 + tests/ui/renamed_builtin_attr.stderr | 2 +- tests/ui/single_match.fixed | 2 + tests/ui/single_match.rs | 2 + tests/ui/single_match.stderr | 36 +- tests/ui/single_match_else.fixed | 2 + tests/ui/single_match_else.rs | 2 + tests/ui/single_match_else.stderr | 18 +- tests/ui/std_instead_of_core.fixed | 11 +- tests/ui/std_instead_of_core.rs | 9 + tests/ui/std_instead_of_core.stderr | 36 +- tests/ui/suspicious_operation_groupings.fixed | 2 + tests/ui/suspicious_operation_groupings.rs | 2 + .../ui/suspicious_operation_groupings.stderr | 52 +- ..._local_initializer_can_be_made_const.fixed | 14 + ...ead_local_initializer_can_be_made_const.rs | 14 + ...local_initializer_can_be_made_const.stderr | 14 +- .../transmutes_expressible_as_ptr_casts.fixed | 7 + .../ui/transmutes_expressible_as_ptr_casts.rs | 7 + tests/ui/type_complexity.rs | 2 + tests/ui/type_complexity.stderr | 30 +- tests/ui/unknown_attribute.rs | 2 + tests/ui/unknown_attribute.stderr | 2 +- tests/ui/unnecessary_cast.fixed | 6 + tests/ui/unnecessary_cast.rs | 6 + tests/ui/unnecessary_cast.stderr | 8 +- 155 files changed, 4143 insertions(+), 2430 deletions(-) create mode 100644 clippy_lints/src/assigning_clones.rs delete mode 100644 clippy_lints/src/attrs.rs create mode 100644 clippy_lints/src/attrs/allow_attributes_without_reason.rs create mode 100644 clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs create mode 100644 clippy_lints/src/attrs/deprecated_cfg_attr.rs create mode 100644 clippy_lints/src/attrs/deprecated_semver.rs create mode 100644 clippy_lints/src/attrs/empty_line_after.rs create mode 100644 clippy_lints/src/attrs/inline_always.rs create mode 100644 clippy_lints/src/attrs/maybe_misused_cfg.rs create mode 100644 clippy_lints/src/attrs/mismatched_target_os.rs create mode 100644 clippy_lints/src/attrs/mixed_attributes_style.rs create mode 100644 clippy_lints/src/attrs/mod.rs create mode 100644 clippy_lints/src/attrs/non_minimal_cfg.rs create mode 100644 clippy_lints/src/attrs/should_panic_without_expect.rs create mode 100644 clippy_lints/src/attrs/unnecessary_clippy_cfg.rs create mode 100644 clippy_lints/src/attrs/useless_attribute.rs create mode 100644 clippy_lints/src/attrs/utils.rs create mode 100644 tests/ui/assigning_clones.fixed create mode 100644 tests/ui/assigning_clones.rs create mode 100644 tests/ui/assigning_clones.stderr create mode 100644 tests/ui/doc/issue_9473.fixed create mode 100644 tests/ui/doc/issue_9473.rs create mode 100644 tests/ui/doc/issue_9473.stderr create mode 100644 tests/ui/mixed_attributes_style.rs create mode 100644 tests/ui/mixed_attributes_style.stderr diff --git a/.github/driver.sh b/.github/driver.sh index 11fd6b5c79ed8..2eafdd0fbc87b 100755 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -50,11 +50,11 @@ diff -u normalized.stderr tests/ui/double_neg.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same SYSROOT=$(rustc --print sysroot) -diff -u <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) +diff -u <(./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) echo "fn main() {}" >target/driver_test.rs # we can't run 2 rustcs on the same file at the same time -CLIPPY=$(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc) +CLIPPY=$(./target/debug/clippy-driver ./target/driver_test.rs --rustc) RUSTC=$(rustc ./target/driver_test.rs) diff -u <($CLIPPY) <($RUSTC) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 5ba960db66dde..603f91a910b18 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -44,11 +44,6 @@ jobs: run: rustup show active-toolchain # Run - - name: Set LD_LIBRARY_PATH (Linux) - run: | - SYSROOT=$(rustc --print sysroot) - echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - - name: Build run: cargo build --tests --features deny-warnings,internal @@ -72,6 +67,6 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: bash .github/driver.sh - env: - OS: ${{ runner.os }} + run: | + TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') + rustup run $TOOLCHAIN bash .github/driver.sh diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 012797e5ca72a..0bc28c1f9d996 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -59,7 +59,7 @@ jobs: host: i686-unknown-linux-gnu - os: windows-latest host: x86_64-pc-windows-msvc - - os: macos-latest + - os: macos-13 host: x86_64-apple-darwin runs-on: ${{ matrix.os }} @@ -87,23 +87,6 @@ jobs: rustup show active-toolchain # Run - - name: Set LD_LIBRARY_PATH (Linux) - if: runner.os == 'Linux' - run: | - SYSROOT=$(rustc --print sysroot) - echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - - name: Link rustc dylib (MacOS) - if: runner.os == 'macOS' - run: | - SYSROOT=$(rustc --print sysroot) - sudo mkdir -p /usr/local/lib - sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \; - - name: Set PATH (Windows) - if: runner.os == 'Windows' - run: | - SYSROOT=$(rustc --print sysroot) - echo "$SYSROOT/bin" >> $GITHUB_PATH - - name: Build run: cargo build --tests --features deny-warnings,internal @@ -136,7 +119,9 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: bash .github/driver.sh + run: | + TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') + rustup run $TOOLCHAIN bash .github/driver.sh env: OS: ${{ runner.os }} @@ -236,11 +221,6 @@ jobs: - name: Install toolchain run: rustup show active-toolchain - - name: Set LD_LIBRARY_PATH - run: | - SYSROOT=$(rustc --print sysroot) - echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - # Download - name: Download target dir uses: actions/download-artifact@v3 @@ -254,8 +234,8 @@ jobs: # Run - name: Test ${{ matrix.integration }} run: | - RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \ - $CARGO_TARGET_DIR/debug/integration --show-output + TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') + rustup run $TOOLCHAIN $CARGO_TARGET_DIR/debug/integration --show-output env: INTEGRATION: ${{ matrix.integration }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f0b01742deb05..d3b2c0a7bf6ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5046,6 +5046,7 @@ Released 2018-09-13 [`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`assigning_clones`]: https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock @@ -5423,6 +5424,7 @@ Released 2018-09-13 [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop [`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes +[`mixed_attributes_style`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_attributes_style [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression [`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files @@ -5816,74 +5818,74 @@ Released 2018-09-13 [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset +[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates +[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments +[`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes +[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement +[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero +[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests +[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests +[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args +[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings +[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests +[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception +[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests +[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles +[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates +[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars +[`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts +[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports [`arithmetic-side-effects-allowed`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed [`arithmetic-side-effects-allowed-binary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-binary [`arithmetic-side-effects-allowed-unary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-unary +[`array-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#array-size-threshold [`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api -[`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv +[`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-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 -[`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold +[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros +[`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods [`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names -[`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline -[`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline +[`disallowed-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-types [`doc-valid-idents`]: https://doc.rust-lang.org/clippy/lint_configuration.html#doc-valid-idents -[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold -[`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold -[`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold -[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack +[`enable-raw-pointer-heuristic-for-send`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enable-raw-pointer-heuristic-for-send +[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow +[`enforced-import-renames`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforced-import-renames [`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold -[`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold [`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold -[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold +[`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold +[`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 [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold -[`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit +[`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 +[`max-include-file-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-include-file-size +[`max-struct-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-struct-bools +[`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length +[`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds +[`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 +[`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 -[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold -[`array-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#array-size-threshold +[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior +[`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline +[`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline +[`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold [`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold -[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold -[`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds -[`max-struct-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-struct-bools -[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools -[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports -[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros -[`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods -[`disallowed-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-types -[`unreadable-literal-lint-fractions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unreadable-literal-lint-fractions -[`upper-case-acronyms-aggressive`]: https://doc.rust-lang.org/clippy/lint_configuration.html#upper-case-acronyms-aggressive -[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else -[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish [`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces -[`enforced-import-renames`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforced-import-renames -[`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts -[`enable-raw-pointer-heuristic-for-send`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enable-raw-pointer-heuristic-for-send -[`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length -[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types -[`max-include-file-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-include-file-size -[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests -[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests -[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests -[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests -[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold -[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability -[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args +[`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold [`suppress-restriction-lint-in-const`]: https://doc.rust-lang.org/clippy/lint_configuration.html#suppress-restriction-lint-in-const -[`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items -[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold +[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack +[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold +[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold +[`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit +[`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold [`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size -[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception -[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars -[`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold -[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement -[`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes -[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings -[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments -[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates -[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles -[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates -[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow -[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items -[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior -[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero -[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports +[`unreadable-literal-lint-fractions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unreadable-literal-lint-fractions +[`upper-case-acronyms-aggressive`]: https://doc.rust-lang.org/clippy/lint_configuration.html#upper-case-acronyms-aggressive +[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold +[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold +[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index fb263922cf88e..b7d229ccc1930 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -94,6 +94,53 @@ impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { } ``` +## Creating Types Programmatically + +Traits are often generic over a type parameter, e.g. `Borrow` is generic +over `T`. Rust allows us to implement a trait for a specific type. For example, +we can implement `Borrow<[u8]>` for a hypothetical type `Foo`. Let's suppose +that we would like to find whether our type actually implements `Borrow<[u8]>`. + +To do so, we can use the same `implements_trait` function as above, and supply +a type parameter that represents `[u8]`. Since `[u8]` is a specialization of +`[T]`, we can use the [`Ty::new_slice`][new_slice] method to create a type +that represents `[T]` and supply `u8` as a type parameter. +To create a `ty::Ty` programmatically, we rely on `Ty::new_*` methods. These +methods create a `TyKind` and then wrap it in a `Ty` struct. This means we +have access to all the primitive types, such as `Ty::new_char`, +`Ty::new_bool`, `Ty::new_int`, etc. We can also create more complex types, +such as slices, tuples, and references out of these basic building blocks. + +For trait checking, it is not enough to create the types, we need to convert +them into [GenericArg]. In rustc, a generic is an entity that the compiler +understands and has three kinds, type, const and lifetime. By calling +`.into()` on a constructed [Ty], we wrap the type into a generic which can +then be used by the query system to decide whether the specialized trait +is implemented. + +The following code demonstrates how to do this: + +```rust + +use rustc_middle::ty::Ty; +use clippy_utils::ty::implements_trait; +use rustc_span::symbol::sym; + +let ty = todo!("Get the `Foo` type to check for a trait implementation"); +let borrow_id = cx.tcx.get_diagnostic_item(sym::Borrow).unwrap(); // avoid unwrap in real code +let slice_of_bytes_t = Ty::new_slice(cx.tcx, cx.tcx.types.u8); +let generic_param = slice_of_bytes_t.into(); +if implements_trait(cx, ty, borrow_id, &[generic_param]) { + todo!("Rest of lint implementation") +} +``` + +In essence, the [Ty] struct allows us to create types programmatically in a +representation that can be used by the compiler and the query engine. We then +use the `rustc_middle::Ty` of the type we are interested in, and query the +compiler to see if it indeed implements the trait we are interested in. + + [DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html [diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html [lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html @@ -102,4 +149,7 @@ impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { [symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html [symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html +[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html [rust]: https://github.com/rust-lang/rust +[new_slice]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_slice +[GenericArg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html diff --git a/book/src/development/type_checking.md b/book/src/development/type_checking.md index dc29ab5d08dad..136b3fd027057 100644 --- a/book/src/development/type_checking.md +++ b/book/src/development/type_checking.md @@ -123,6 +123,22 @@ the [`TypeckResults::node_type()`][node_type] method inside of bodies. > **Warning**: Don't use `hir_ty_to_ty` inside of bodies, because this can cause ICEs. +## Creating Types programmatically + +A common usecase for creating types programmatically is when we want to check if a type implements a trait (see +[Trait Checking](trait_checking.md)). + +Here's an example of how to create a `Ty` for a slice of `u8`, i.e. `[u8]` + +```rust +use rustc_middle::ty::Ty; +// assume we have access to a LateContext +let ty = Ty::new_slice(cx.tcx, Ty::new_u8()); +``` + +In general, we rely on `Ty::new_*` methods. These methods define the basic building-blocks that the +type-system and trait-system use to define and understand the written code. + ## Useful Links Below are some useful links to further explore the concepts covered diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 214a60d3bfdf7..a985346b3c05f 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -10,455 +10,505 @@ and lints affected. --- -## `arithmetic-side-effects-allowed` -Suppress checking of the passed type names in all types of operations. - -If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. +## `absolute-paths-allowed-crates` +Which crates to allow absolute paths from -#### Example +**Default Value:** `[]` -```toml -arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] -``` +--- +**Affected lints:** +* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) -#### Noteworthy -A type, say `SomeType`, listed in this configuration has the same behavior of -`["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. +## `absolute-paths-max-segments` +The maximum number of segments a path can have before being linted, anything above this will +be linted. -**Default Value:** `[]` +**Default Value:** `2` --- **Affected lints:** -* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) +* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) -## `arithmetic-side-effects-allowed-binary` -Suppress checking of the passed type pair names in binary operations like addition or -multiplication. +## `accept-comment-above-attributes` +Whether to accept a safety comment to be placed above the attributes for the `unsafe` block -Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless -of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. +**Default Value:** `true` -Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as -`["AnotherType", "SomeType"]`. +--- +**Affected lints:** +* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks) -#### Example -```toml -arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] -``` +## `accept-comment-above-statement` +Whether to accept a safety comment to be placed above the statement containing the `unsafe` block -**Default Value:** `[]` +**Default Value:** `true` --- **Affected lints:** -* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) +* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks) -## `arithmetic-side-effects-allowed-unary` -Suppress checking of the passed type names in unary operations like "negation" (`-`). +## `allow-comparison-to-zero` +Don't lint when comparing the result of a modulo operation to zero. -#### Example +**Default Value:** `true` -```toml -arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] -``` +--- +**Affected lints:** +* [`modulo_arithmetic`](https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic) -**Default Value:** `[]` + +## `allow-dbg-in-tests` +Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` + +**Default Value:** `false` --- **Affected lints:** -* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) +* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) -## `avoid-breaking-exported-api` -Suppress lints whenever the suggested change would cause breakage for other crates. +## `allow-expect-in-tests` +Whether `expect` should be allowed in test functions or `#[cfg(test)]` -**Default Value:** `true` +**Default Value:** `false` --- **Affected lints:** -* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) -* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) -* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) -* [`unnecessary_wraps`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps) -* [`unused_self`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self) -* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) -* [`wrong_self_convention`](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention) -* [`box_collection`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection) -* [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) -* [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) -* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) -* [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) -* [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) -* [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) -* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns) -* [`single_call_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn) +* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) -## `msrv` -The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` +## `allow-mixed-uninlined-format-args` +Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + +**Default Value:** `true` --- **Affected lints:** -* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) -* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) -* [`cloned_instead_of_copied`](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied) -* [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) -* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) -* [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) -* [`filter_map_next`](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next) -* [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) -* [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) -* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) -* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) -* [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) -* [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) -* [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) -* [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) -* [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) -* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) -* [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) -* [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) -* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) -* [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) -* [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) -* [`deprecated_cfg_attr`](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr) -* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) -* [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) -* [`borrow_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) -* [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) -* [`err_expect`](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect) -* [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) -* [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) -* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) -* [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) -* [`collapsible_str_replace`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) -* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) -* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) -* [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) -* [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) -* [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) -* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) -* [`derivable_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) -* [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) -* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) -* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) -* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) -* [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) -* [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold) -* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) -* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) -* [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals) -## `cognitive-complexity-threshold` -The maximum cognitive complexity a function can have +## `allow-one-hash-in-raw-strings` +Whether to allow `r#""#` when `r""` can be used -**Default Value:** `25` +**Default Value:** `false` --- **Affected lints:** -* [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) +* [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) -## `excessive-nesting-threshold` -The maximum amount of nesting a block can reside in +## `allow-print-in-tests` +Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` -**Default Value:** `0` +**Default Value:** `false` --- **Affected lints:** -* [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting) +* [`print_stderr`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr) +* [`print_stdout`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout) -## `disallowed-names` -The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value -`".."` can be used as part of the list to indicate that the configured values should be appended to the -default configuration of Clippy. By default, any configuration will replace the default value. +## `allow-private-module-inception` +Whether to allow module inception if it's not public. -**Default Value:** `["foo", "baz", "quux"]` +**Default Value:** `false` --- **Affected lints:** -* [`disallowed_names`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names) +* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception) -## `semicolon-inside-block-ignore-singleline` -Whether to lint only if it's multiline. +## `allow-unwrap-in-tests` +Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` **Default Value:** `false` --- **Affected lints:** -* [`semicolon_inside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block) +* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) -## `semicolon-outside-block-ignore-multiline` -Whether to lint only if it's singleline. +## `allowed-dotfiles` +Additional dotfiles (files or directories starting with a dot) to allow -**Default Value:** `false` +**Default Value:** `[]` --- **Affected lints:** -* [`semicolon_outside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block) +* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext) -## `doc-valid-idents` -The list of words this lint should not consider as identifiers needing ticks. The value -`".."` can be used as part of the list to indicate, that the configured values should be appended to the -default configuration of Clippy. By default, any configuration will replace the default value. For example: -* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. -* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. +## `allowed-duplicate-crates` +A list of crate names to allow duplicates of -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `[]` --- **Affected lints:** -* [`doc_markdown`](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown) +* [`multiple_crate_versions`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions) -## `too-many-arguments-threshold` -The maximum number of argument a function or method can have +## `allowed-idents-below-min-chars` +Allowed names below the minimum allowed characters. The value `".."` can be used as part of +the list to indicate, that the configured values should be appended to the default +configuration of Clippy. By default, any configuration will replace the default value. -**Default Value:** `7` +**Default Value:** `["j", "z", "i", "y", "n", "x", "w"]` --- **Affected lints:** -* [`too_many_arguments`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments) +* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) -## `type-complexity-threshold` -The maximum complexity a type can have +## `allowed-scripts` +The list of unicode scripts allowed to be used in the scope. -**Default Value:** `250` +**Default Value:** `["Latin"]` --- **Affected lints:** -* [`type_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity) +* [`disallowed_script_idents`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents) -## `single-char-binding-names-threshold` -The maximum number of single char bindings a scope may have +## `allowed-wildcard-imports` +List of path segments allowed to have wildcard imports. -**Default Value:** `4` +#### Example + +```toml +allowed-wildcard-imports = [ "utils", "common" ] +``` + +#### Noteworthy + +1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. +2. Paths with any segment that containing the word 'prelude' +are already allowed by default. + +**Default Value:** `[]` --- **Affected lints:** -* [`many_single_char_names`](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) +* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) -## `too-large-for-stack` -The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap +## `arithmetic-side-effects-allowed` +Suppress checking of the passed type names in all types of operations. -**Default Value:** `200` +If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + +#### Example + +```toml +arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] +``` + +#### Noteworthy + +A type, say `SomeType`, listed in this configuration has the same behavior of +`["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. + +**Default Value:** `[]` --- **Affected lints:** -* [`boxed_local`](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local) -* [`useless_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec) +* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) -## `enum-variant-name-threshold` -The minimum number of enum variants for the lints about variant names to trigger +## `arithmetic-side-effects-allowed-binary` +Suppress checking of the passed type pair names in binary operations like addition or +multiplication. -**Default Value:** `3` +Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless +of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. + +Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as +`["AnotherType", "SomeType"]`. + +#### Example + +```toml +arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] +``` + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +## `arithmetic-side-effects-allowed-unary` +Suppress checking of the passed type names in unary operations like "negation" (`-`). + +#### Example + +```toml +arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] +``` + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +## `array-size-threshold` +The maximum allowed size for arrays on the stack + +**Default Value:** `512000` --- **Affected lints:** +* [`large_const_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays) +* [`large_stack_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays) + + +## `avoid-breaking-exported-api` +Suppress lints whenever the suggested change would cause breakage for other crates. + +**Default Value:** `true` + +--- +**Affected lints:** +* [`box_collection`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection) * [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) +* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) +* [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) +* [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) +* [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) +* [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) +* [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) +* [`single_call_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn) +* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) +* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns) +* [`unnecessary_wraps`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps) +* [`unused_self`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self) +* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) +* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) +* [`wrong_self_convention`](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention) -## `struct-field-name-threshold` -The minimum number of struct fields for the lints about field names to trigger +## `await-holding-invalid-types` -**Default Value:** `3` + +**Default Value:** `[]` --- **Affected lints:** -* [`struct_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_field_names) +* [`await_holding_invalid_type`](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type) -## `enum-variant-size-threshold` -The maximum size of an enum's variant to avoid box suggestion +## `cargo-ignore-publish` +For internal testing only, ignores the current `publish` settings in the Cargo manifest. -**Default Value:** `200` +**Default Value:** `false` --- **Affected lints:** -* [`large_enum_variant`](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant) +* [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata) -## `verbose-bit-mask-threshold` -The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' +## `check-private-items` +Whether to also run the listed lints on private items. -**Default Value:** `1` +**Default Value:** `false` --- **Affected lints:** -* [`verbose_bit_mask`](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask) +* [`missing_errors_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc) +* [`missing_panics_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc) +* [`missing_safety_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc) +* [`unnecessary_safety_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc) -## `literal-representation-threshold` -The lower bound for linting decimal literals +## `cognitive-complexity-threshold` +The maximum cognitive complexity a function can have -**Default Value:** `16384` +**Default Value:** `25` --- **Affected lints:** -* [`decimal_literal_representation`](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation) +* [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) -## `trivial-copy-size-limit` -The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by -reference. By default there is no limit +## `disallowed-macros` +The list of disallowed macros, written as fully qualified paths. + +**Default Value:** `[]` --- **Affected lints:** -* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) +* [`disallowed_macros`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros) -## `pass-by-value-size-limit` -The minimum size (in bytes) to consider a type for passing by reference instead of by value. +## `disallowed-methods` +The list of disallowed methods, written as fully qualified paths. -**Default Value:** `256` +**Default Value:** `[]` --- **Affected lints:** -* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) +* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) -## `too-many-lines-threshold` -The maximum number of lines a function or method can have +## `disallowed-names` +The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value +`".."` can be used as part of the list to indicate that the configured values should be appended to the +default configuration of Clippy. By default, any configuration will replace the default value. -**Default Value:** `100` +**Default Value:** `["foo", "baz", "quux"]` --- **Affected lints:** -* [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) +* [`disallowed_names`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names) -## `array-size-threshold` -The maximum allowed size for arrays on the stack +## `disallowed-types` +The list of disallowed types, written as fully qualified paths. -**Default Value:** `512000` +**Default Value:** `[]` --- **Affected lints:** -* [`large_stack_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays) -* [`large_const_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays) +* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) -## `stack-size-threshold` -The maximum allowed stack size for functions in bytes +## `doc-valid-idents` +The list of words this lint should not consider as identifiers needing ticks. The value +`".."` can be used as part of the list to indicate, that the configured values should be appended to the +default configuration of Clippy. By default, any configuration will replace the default value. For example: +* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. +* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `512000` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** -* [`large_stack_frames`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames) +* [`doc_markdown`](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown) -## `vec-box-size-threshold` -The size of the boxed type in bytes, where boxing in a `Vec` is allowed +## `enable-raw-pointer-heuristic-for-send` +Whether to apply the raw pointer heuristic to determine if a type is `Send`. -**Default Value:** `4096` +**Default Value:** `true` --- **Affected lints:** -* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) +* [`non_send_fields_in_send_ty`](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty) -## `max-trait-bounds` -The maximum number of bounds a trait can have to be linted +## `enforce-iter-loop-reborrow` +Whether to recommend using implicit into iter for reborrowed values. -**Default Value:** `3` +#### Example +```no_run +let mut vec = vec![1, 2, 3]; +let rmvec = &mut vec; +for _ in rmvec.iter() {} +for _ in rmvec.iter_mut() {} +``` + +Use instead: +```no_run +let mut vec = vec![1, 2, 3]; +let rmvec = &mut vec; +for _ in &*rmvec {} +for _ in &mut *rmvec {} +``` + +**Default Value:** `false` --- **Affected lints:** -* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) +* [`explicit_iter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop) -## `max-struct-bools` -The maximum number of bool fields a struct can have +## `enforced-import-renames` +The list of imports to always rename, a fully qualified path followed by the rename. -**Default Value:** `3` +**Default Value:** `[]` --- **Affected lints:** -* [`struct_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools) +* [`missing_enforced_import_renames`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames) -## `max-fn-params-bools` -The maximum number of bool parameters a function can have +## `enum-variant-name-threshold` +The minimum number of enum variants for the lints about variant names to trigger **Default Value:** `3` --- **Affected lints:** -* [`fn_params_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools) +* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) -## `warn-on-all-wildcard-imports` -Whether to allow certain wildcard imports (prelude, super in tests). +## `enum-variant-size-threshold` +The maximum size of an enum's variant to avoid box suggestion -**Default Value:** `false` +**Default Value:** `200` --- **Affected lints:** -* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) +* [`large_enum_variant`](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant) -## `disallowed-macros` -The list of disallowed macros, written as fully qualified paths. +## `excessive-nesting-threshold` +The maximum amount of nesting a block can reside in -**Default Value:** `[]` +**Default Value:** `0` --- **Affected lints:** -* [`disallowed_macros`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros) +* [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting) -## `disallowed-methods` -The list of disallowed methods, written as fully qualified paths. +## `future-size-threshold` +The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint -**Default Value:** `[]` +**Default Value:** `16384` --- **Affected lints:** -* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) +* [`large_futures`](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures) -## `disallowed-types` -The list of disallowed types, written as fully qualified paths. +## `ignore-interior-mutability` +A list of paths to types that should be treated like `Arc`, i.e. ignored but +for the generic parameters for determining interior mutability -**Default Value:** `[]` +**Default Value:** `["bytes::Bytes"]` --- **Affected lints:** -* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) +* [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond) +* [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) -## `unreadable-literal-lint-fractions` -Should the fraction of a decimal be linted to include separators. +## `large-error-threshold` +The maximum size of the `Err`-variant in a `Result` returned from a function -**Default Value:** `true` +**Default Value:** `128` --- **Affected lints:** -* [`unreadable_literal`](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal) +* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) -## `upper-case-acronyms-aggressive` -Enables verbose mode. Triggers if there is more than one uppercase char next to each other +## `literal-representation-threshold` +The lower bound for linting decimal literals -**Default Value:** `false` +**Default Value:** `16384` --- **Affected lints:** -* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) +* [`decimal_literal_representation`](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation) ## `matches-for-let-else` @@ -472,163 +522,219 @@ be filtering for common types. * [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) -## `cargo-ignore-publish` -For internal testing only, ignores the current `publish` settings in the Cargo manifest. +## `max-fn-params-bools` +The maximum number of bool parameters a function can have -**Default Value:** `false` +**Default Value:** `3` --- **Affected lints:** -* [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata) - +* [`fn_params_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools) -## `standard-macro-braces` -Enforce the named macros always use the braces specified. -A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro -could be used with a full path two `MacroMatcher`s have to be added one with the full path -`crate_name::macro_name` and one with just the macro name. +## `max-include-file-size` +The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes -**Default Value:** `[]` +**Default Value:** `1000000` --- **Affected lints:** -* [`nonstandard_macro_braces`](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces) +* [`large_include_file`](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file) -## `enforced-import-renames` -The list of imports to always rename, a fully qualified path followed by the rename. +## `max-struct-bools` +The maximum number of bool fields a struct can have -**Default Value:** `[]` +**Default Value:** `3` --- **Affected lints:** -* [`missing_enforced_import_renames`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames) +* [`struct_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools) -## `allowed-scripts` -The list of unicode scripts allowed to be used in the scope. +## `max-suggested-slice-pattern-length` +When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in +the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. +For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. -**Default Value:** `["Latin"]` +**Default Value:** `3` --- **Affected lints:** -* [`disallowed_script_idents`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents) +* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) -## `enable-raw-pointer-heuristic-for-send` -Whether to apply the raw pointer heuristic to determine if a type is `Send`. +## `max-trait-bounds` +The maximum number of bounds a trait can have to be linted -**Default Value:** `true` +**Default Value:** `3` --- **Affected lints:** -* [`non_send_fields_in_send_ty`](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty) +* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) -## `max-suggested-slice-pattern-length` -When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in -the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. -For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. +## `min-ident-chars-threshold` +Minimum chars an ident can have, anything below or equal to this will be linted. -**Default Value:** `3` +**Default Value:** `1` --- **Affected lints:** -* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) -## `await-holding-invalid-types` +## `missing-docs-in-crate-items` +Whether to **only** check for missing documentation in items visible within the current +crate. For example, `pub(crate)` items. +**Default Value:** `false` -**Default Value:** `[]` +--- +**Affected lints:** +* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) + + +## `msrv` +The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` --- **Affected lints:** -* [`await_holding_invalid_type`](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type) +* [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) +* [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) +* [`borrow_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) +* [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) +* [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) +* [`cloned_instead_of_copied`](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied) +* [`collapsible_str_replace`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) +* [`deprecated_cfg_attr`](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr) +* [`derivable_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) +* [`err_expect`](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect) +* [`filter_map_next`](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next) +* [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) +* [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) +* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) +* [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) +* [`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_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) +* [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) +* [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) +* [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) +* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) +* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) +* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) +* [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) +* [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold) +* [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) +* [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) +* [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) +* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) +* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) +* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [`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) +* [`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) +* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) +* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) +* [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) +* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) +* [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) +* [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) +* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) -## `max-include-file-size` -The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes +## `pass-by-value-size-limit` +The minimum size (in bytes) to consider a type for passing by reference instead of by value. -**Default Value:** `1000000` +**Default Value:** `256` --- **Affected lints:** -* [`large_include_file`](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file) +* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) -## `allow-expect-in-tests` -Whether `expect` should be allowed in test functions or `#[cfg(test)]` +## `pub-underscore-fields-behavior` +Lint "public" fields in a struct that are prefixed with an underscore based on their +exported visibility, or whether they are marked as "pub". -**Default Value:** `false` +**Default Value:** `"PubliclyExported"` --- **Affected lints:** -* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) +* [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) -## `allow-unwrap-in-tests` -Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` +## `semicolon-inside-block-ignore-singleline` +Whether to lint only if it's multiline. **Default Value:** `false` --- **Affected lints:** -* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) +* [`semicolon_inside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block) -## `allow-dbg-in-tests` -Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` +## `semicolon-outside-block-ignore-multiline` +Whether to lint only if it's singleline. **Default Value:** `false` --- **Affected lints:** -* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) +* [`semicolon_outside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block) -## `allow-print-in-tests` -Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` +## `single-char-binding-names-threshold` +The maximum number of single char bindings a scope may have -**Default Value:** `false` +**Default Value:** `4` --- **Affected lints:** -* [`print_stdout`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout) -* [`print_stderr`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr) +* [`many_single_char_names`](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) -## `large-error-threshold` -The maximum size of the `Err`-variant in a `Result` returned from a function +## `stack-size-threshold` +The maximum allowed stack size for functions in bytes -**Default Value:** `128` +**Default Value:** `512000` --- **Affected lints:** -* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) +* [`large_stack_frames`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames) -## `ignore-interior-mutability` -A list of paths to types that should be treated like `Arc`, i.e. ignored but -for the generic parameters for determining interior mutability +## `standard-macro-braces` +Enforce the named macros always use the braces specified. -**Default Value:** `["bytes::Bytes"]` +A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro +could be used with a full path two `MacroMatcher`s have to be added one with the full path +`crate_name::macro_name` and one with just the macro name. + +**Default Value:** `[]` --- **Affected lints:** -* [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) -* [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond) +* [`nonstandard_macro_braces`](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces) -## `allow-mixed-uninlined-format-args` -Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` +## `struct-field-name-threshold` +The minimum number of struct fields for the lints about field names to trigger -**Default Value:** `true` +**Default Value:** `3` --- **Affected lints:** -* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) +* [`struct_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_field_names) ## `suppress-restriction-lint-in-const` @@ -645,217 +751,111 @@ if no suggestion can be made. * [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) -## `missing-docs-in-crate-items` -Whether to **only** check for missing documentation in items visible within the current -crate. For example, `pub(crate)` items. - -**Default Value:** `false` - ---- -**Affected lints:** -* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) - - -## `future-size-threshold` -The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint +## `too-large-for-stack` +The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap -**Default Value:** `16384` +**Default Value:** `200` --- **Affected lints:** -* [`large_futures`](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures) +* [`boxed_local`](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local) +* [`useless_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec) -## `unnecessary-box-size` -The byte size a `T` in `Box` can have, below which it triggers the `clippy::unnecessary_box` lint +## `too-many-arguments-threshold` +The maximum number of argument a function or method can have -**Default Value:** `128` +**Default Value:** `7` --- **Affected lints:** -* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns) +* [`too_many_arguments`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments) -## `allow-private-module-inception` -Whether to allow module inception if it's not public. +## `too-many-lines-threshold` +The maximum number of lines a function or method can have -**Default Value:** `false` +**Default Value:** `100` --- **Affected lints:** -* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception) - +* [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) -## `allowed-idents-below-min-chars` -Allowed names below the minimum allowed characters. The value `".."` can be used as part of -the list to indicate, that the configured values should be appended to the default -configuration of Clippy. By default, any configuration will replace the default value. -**Default Value:** `["j", "z", "i", "y", "n", "x", "w"]` +## `trivial-copy-size-limit` +The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by +reference. By default there is no limit --- **Affected lints:** -* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) +* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) -## `min-ident-chars-threshold` -Minimum chars an ident can have, anything below or equal to this will be linted. +## `type-complexity-threshold` +The maximum complexity a type can have -**Default Value:** `1` +**Default Value:** `250` --- **Affected lints:** -* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) +* [`type_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity) -## `accept-comment-above-statement` -Whether to accept a safety comment to be placed above the statement containing the `unsafe` block +## `unnecessary-box-size` +The byte size a `T` in `Box` can have, below which it triggers the `clippy::unnecessary_box` lint -**Default Value:** `true` +**Default Value:** `128` --- **Affected lints:** -* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks) +* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns) -## `accept-comment-above-attributes` -Whether to accept a safety comment to be placed above the attributes for the `unsafe` block +## `unreadable-literal-lint-fractions` +Should the fraction of a decimal be linted to include separators. **Default Value:** `true` --- **Affected lints:** -* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks) +* [`unreadable_literal`](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal) -## `allow-one-hash-in-raw-strings` -Whether to allow `r#""#` when `r""` can be used +## `upper-case-acronyms-aggressive` +Enables verbose mode. Triggers if there is more than one uppercase char next to each other **Default Value:** `false` --- **Affected lints:** -* [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) - - -## `absolute-paths-max-segments` -The maximum number of segments a path can have before being linted, anything above this will -be linted. - -**Default Value:** `2` - ---- -**Affected lints:** -* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) - - -## `absolute-paths-allowed-crates` -Which crates to allow absolute paths from - -**Default Value:** `[]` - ---- -**Affected lints:** -* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) - - -## `allowed-dotfiles` -Additional dotfiles (files or directories starting with a dot) to allow - -**Default Value:** `[]` - ---- -**Affected lints:** -* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext) +* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) -## `allowed-duplicate-crates` -A list of crate names to allow duplicates of +## `vec-box-size-threshold` +The size of the boxed type in bytes, where boxing in a `Vec` is allowed -**Default Value:** `[]` +**Default Value:** `4096` --- **Affected lints:** -* [`multiple_crate_versions`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions) - - -## `enforce-iter-loop-reborrow` -Whether to recommend using implicit into iter for reborrowed values. +* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) -#### Example -```no_run -let mut vec = vec![1, 2, 3]; -let rmvec = &mut vec; -for _ in rmvec.iter() {} -for _ in rmvec.iter_mut() {} -``` -Use instead: -```no_run -let mut vec = vec![1, 2, 3]; -let rmvec = &mut vec; -for _ in &*rmvec {} -for _ in &mut *rmvec {} -``` +## `verbose-bit-mask-threshold` +The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' -**Default Value:** `false` +**Default Value:** `1` --- **Affected lints:** -* [`explicit_iter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop) +* [`verbose_bit_mask`](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask) -## `check-private-items` -Whether to also run the listed lints on private items. +## `warn-on-all-wildcard-imports` +Whether to allow certain wildcard imports (prelude, super in tests). **Default Value:** `false` ---- -**Affected lints:** -* [`missing_safety_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc) -* [`unnecessary_safety_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc) -* [`missing_panics_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc) -* [`missing_errors_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc) - - -## `pub-underscore-fields-behavior` -Lint "public" fields in a struct that are prefixed with an underscore based on their -exported visibility, or whether they are marked as "pub". - -**Default Value:** `"PubliclyExported"` - ---- -**Affected lints:** -* [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) - - -## `allow-comparison-to-zero` -Don't lint when comparing the result of a modulo operation to zero. - -**Default Value:** `true` - ---- -**Affected lints:** -* [`modulo_arithmetic`](https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic) - - -## `allowed-wildcard-imports` -List of path segments allowed to have wildcard imports. - -#### Example - -```toml -allowed-wildcard-imports = [ "utils", "common" ] -``` - -#### Noteworthy - -1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. -2. Paths with any segment that containing the word 'prelude' -are already allowed by default. - -**Default Value:** `[]` - --- **Affected lints:** * [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index b781259ad9695..673b6328b3901 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -193,7 +193,7 @@ macro_rules! define_Conf { } pub fn get_configuration_metadata() -> Vec { - vec![ + let mut sorted = vec![ $( { let deprecation_reason = wrap_option!($($dep)?); @@ -206,7 +206,9 @@ macro_rules! define_Conf { ) }, )+ - ] + ]; + sorted.sort_by(|a, b| a.name.cmp(&b.name)); + sorted } }; } diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index 3d5002f1780ae..01e2aa6e0a602 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -5,7 +5,7 @@ clippy::must_use_candidate, clippy::missing_panics_doc, rustc::diagnostic_outside_of_impl, - rustc::untranslatable_diagnostic, + rustc::untranslatable_diagnostic )] extern crate rustc_ast; diff --git a/clippy_config/src/metadata.rs b/clippy_config/src/metadata.rs index 3ba2796e18d3c..400887185e8cf 100644 --- a/clippy_config/src/metadata.rs +++ b/clippy_config/src/metadata.rs @@ -26,9 +26,11 @@ impl ClippyConfiguration { doc_comment: &'static str, deprecation_reason: Option<&'static str>, ) -> Self { - let (lints, doc) = parse_config_field_doc(doc_comment) + let (mut lints, doc) = parse_config_field_doc(doc_comment) .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); + lints.sort(); + Self { name: to_kebab(name), lints, diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index f4389db627d8b..a8a32f7ed208b 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -24,6 +24,7 @@ msrv_aliases! { 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } + 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs new file mode 100644 index 0000000000000..b1c552c7a8dd2 --- /dev/null +++ b/clippy_lints/src/assigning_clones.rs @@ -0,0 +1,322 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::HirNode; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_trait_method, path_to_local}; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, Expr, ExprKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Instance, Mutability}; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::DefId; +use rustc_span::symbol::sym; +use rustc_span::ExpnKind; + +declare_clippy_lint! { + /// ### What it does + /// Checks for code like `foo = bar.clone();` + /// + /// ### Why is this bad? + /// Custom `Clone::clone_from()` or `ToOwned::clone_into` implementations allow the objects + /// to share resources and therefore avoid allocations. + /// + /// ### Example + /// ```rust + /// struct Thing; + /// + /// impl Clone for Thing { + /// fn clone(&self) -> Self { todo!() } + /// fn clone_from(&mut self, other: &Self) { todo!() } + /// } + /// + /// pub fn assign_to_ref(a: &mut Thing, b: Thing) { + /// *a = b.clone(); + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct Thing; + /// impl Clone for Thing { + /// fn clone(&self) -> Self { todo!() } + /// fn clone_from(&mut self, other: &Self) { todo!() } + /// } + /// + /// pub fn assign_to_ref(a: &mut Thing, b: Thing) { + /// a.clone_from(&b); + /// } + /// ``` + #[clippy::version = "1.77.0"] + pub ASSIGNING_CLONES, + perf, + "assigning the result of cloning may be inefficient" +} +declare_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); + +impl<'tcx> LateLintPass<'tcx> for AssigningClones { + fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) { + // Do not fire the lint in macros + let expn_data = assign_expr.span().ctxt().outer_expn_data(); + match expn_data.kind { + ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return, + ExpnKind::Root => {}, + } + + let ExprKind::Assign(lhs, rhs, _span) = assign_expr.kind else { + return; + }; + + let Some(call) = extract_call(cx, rhs) else { + return; + }; + + if is_ok_to_suggest(cx, lhs, &call) { + suggest(cx, assign_expr, lhs, &call); + } + } +} + +// Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`. +fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + let fn_def_id = clippy_utils::fn_def_id(cx, expr)?; + + // Fast paths to only check method calls without arguments or function calls with a single argument + let (target, kind, resolved_method) = match expr.kind { + ExprKind::MethodCall(path, receiver, [], _span) => { + let args = cx.typeck_results().node_args(expr.hir_id); + + // If we could not resolve the method, don't apply the lint + let Ok(Some(resolved_method)) = Instance::resolve(cx.tcx, cx.param_env, fn_def_id, args) else { + return None; + }; + if is_trait_method(cx, expr, sym::Clone) && path.ident.name == sym::clone { + (TargetTrait::Clone, CallKind::MethodCall { receiver }, resolved_method) + } else if is_trait_method(cx, expr, sym::ToOwned) && path.ident.name.as_str() == "to_owned" { + (TargetTrait::ToOwned, CallKind::MethodCall { receiver }, resolved_method) + } else { + return None; + } + }, + ExprKind::Call(function, [arg]) => { + let kind = cx.typeck_results().node_type(function.hir_id).kind(); + + // If we could not resolve the method, don't apply the lint + let Ok(Some(resolved_method)) = (match kind { + ty::FnDef(_, args) => Instance::resolve(cx.tcx, cx.param_env, fn_def_id, args), + _ => Ok(None), + }) else { + return None; + }; + if cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) { + ( + TargetTrait::ToOwned, + CallKind::FunctionCall { self_arg: arg }, + resolved_method, + ) + } else if let Some(trait_did) = cx.tcx.trait_of_item(fn_def_id) + && cx.tcx.is_diagnostic_item(sym::Clone, trait_did) + { + ( + TargetTrait::Clone, + CallKind::FunctionCall { self_arg: arg }, + resolved_method, + ) + } else { + return None; + } + }, + _ => return None, + }; + + Some(CallCandidate { + target, + kind, + method_def_id: resolved_method.def_id(), + }) +} + +// Return true if we find that the called method has a custom implementation and isn't derived or +// provided by default by the corresponding trait. +fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) -> bool { + // If the left-hand side is a local variable, it might be uninitialized at this point. + // In that case we do not want to suggest the lint. + if let Some(local) = path_to_local(lhs) { + // TODO: This check currently bails if the local variable has no initializer. + // That is overly conservative - the lint should fire even if there was no initializer, + // but the variable has been initialized before `lhs` was evaluated. + if let Some(Node::Local(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) + && local.init.is_none() + { + return false; + } + } + + let Some(impl_block) = cx.tcx.impl_of_method(call.method_def_id) else { + return false; + }; + + // If the method implementation comes from #[derive(Clone)], then don't suggest the lint. + // Automatically generated Clone impls do not currently override `clone_from`. + // See e.g. https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 for more details. + if cx.tcx.is_builtin_derived(impl_block) { + return false; + } + + // Find the function for which we want to check that it is implemented. + let provided_fn = match call.target { + TargetTrait::Clone => cx.tcx.get_diagnostic_item(sym::Clone).and_then(|clone| { + cx.tcx + .provided_trait_methods(clone) + .find(|item| item.name == sym::clone_from) + }), + TargetTrait::ToOwned => cx.tcx.get_diagnostic_item(sym::ToOwned).and_then(|to_owned| { + cx.tcx + .provided_trait_methods(to_owned) + .find(|item| item.name.as_str() == "clone_into") + }), + }; + let Some(provided_fn) = provided_fn else { + return false; + }; + + // Now take a look if the impl block defines an implementation for the method that we're interested + // in. If not, then we're using a default implementation, which is not interesting, so we will + // not suggest the lint. + let implemented_fns = cx.tcx.impl_item_implementor_ids(impl_block); + implemented_fns.contains_key(&provided_fn.def_id) +} + +fn suggest<'tcx>( + cx: &LateContext<'tcx>, + assign_expr: &hir::Expr<'tcx>, + lhs: &hir::Expr<'tcx>, + call: &CallCandidate<'tcx>, +) { + span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| { + let mut applicability = Applicability::MachineApplicable; + + diag.span_suggestion( + assign_expr.span, + call.suggestion_msg(), + call.suggested_replacement(cx, lhs, &mut applicability), + applicability, + ); + }); +} + +#[derive(Copy, Clone, Debug)] +enum CallKind<'tcx> { + MethodCall { receiver: &'tcx Expr<'tcx> }, + FunctionCall { self_arg: &'tcx Expr<'tcx> }, +} + +#[derive(Copy, Clone, Debug)] +enum TargetTrait { + Clone, + ToOwned, +} + +#[derive(Debug)] +struct CallCandidate<'tcx> { + target: TargetTrait, + kind: CallKind<'tcx>, + // DefId of the called method from an impl block that implements the target trait + method_def_id: DefId, +} + +impl<'tcx> CallCandidate<'tcx> { + #[inline] + fn message(&self) -> &'static str { + match self.target { + TargetTrait::Clone => "assigning the result of `Clone::clone()` may be inefficient", + TargetTrait::ToOwned => "assigning the result of `ToOwned::to_owned()` may be inefficient", + } + } + + #[inline] + fn suggestion_msg(&self) -> &'static str { + match self.target { + TargetTrait::Clone => "use `clone_from()`", + TargetTrait::ToOwned => "use `clone_into()`", + } + } + + fn suggested_replacement( + &self, + cx: &LateContext<'tcx>, + lhs: &hir::Expr<'tcx>, + applicability: &mut Applicability, + ) -> String { + match self.target { + TargetTrait::Clone => { + match self.kind { + CallKind::MethodCall { receiver } => { + let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", applicability) + } else { + // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", applicability) + } + .maybe_par(); + + // Determine whether we need to reference the argument to clone_from(). + let clone_receiver_type = cx.typeck_results().expr_ty(receiver); + let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver); + let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + if clone_receiver_type != clone_receiver_adj_type { + // The receiver may have been a value type, so we need to add an `&` to + // be sure the argument to clone_from will be a reference. + arg_sugg = arg_sugg.addr(); + }; + + format!("{receiver_sugg}.clone_from({arg_sugg})") + }, + CallKind::FunctionCall { self_arg, .. } => { + let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", applicability) + } else { + // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr() + }; + // The RHS had to be exactly correct before the call, there is no auto-deref for function calls. + let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + + format!("Clone::clone_from({self_sugg}, {rhs_sugg})") + }, + } + }, + TargetTrait::ToOwned => { + 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, "_", applicability).maybe_par(); + 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. + if matches!(inner_type.kind(), ty::Ref(_, _, Mutability::Mut)) { + sugg + } else { + sugg.mut_addr() + } + } 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, "_", applicability) + .maybe_par() + .mut_addr() + }; + + match self.kind { + CallKind::MethodCall { receiver } => { + let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + format!("{receiver_sugg}.clone_into({rhs_sugg})") + }, + CallKind::FunctionCall { self_arg, .. } => { + let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})") + }, + } + }, + } + } +} diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs deleted file mode 100644 index f2937d51340b8..0000000000000 --- a/clippy_lints/src/attrs.rs +++ /dev/null @@ -1,1210 +0,0 @@ -//! checks for attributes - -use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::{ - span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, -}; -use clippy_utils::is_from_proc_macro; -use clippy_utils::macros::{is_panic, macro_backtrace}; -use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use rustc_ast::token::{Token, TokenKind}; -use rustc_ast::tokenstream::TokenTree; -use rustc_ast::{ - AttrArgs, AttrArgsEq, AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem, -}; -use rustc_errors::Applicability; -use rustc_hir::{ - Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, -}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, impl_lint_pass}; -use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span, DUMMY_SP}; -use semver::Version; - -static UNIX_SYSTEMS: &[&str] = &[ - "android", - "dragonfly", - "emscripten", - "freebsd", - "fuchsia", - "haiku", - "illumos", - "ios", - "l4re", - "linux", - "macos", - "netbsd", - "openbsd", - "redox", - "solaris", - "vxworks", -]; - -// NOTE: windows is excluded from the list because it's also a valid target family. -static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"]; - -declare_clippy_lint! { - /// ### What it does - /// Checks for items annotated with `#[inline(always)]`, - /// unless the annotated function is empty or simply panics. - /// - /// ### Why is this bad? - /// While there are valid uses of this annotation (and once - /// you know when to use it, by all means `allow` this lint), it's a common - /// newbie-mistake to pepper one's code with it. - /// - /// As a rule of thumb, before slapping `#[inline(always)]` on a function, - /// measure if that additional function call really affects your runtime profile - /// sufficiently to make up for the increase in compile time. - /// - /// ### Known problems - /// False positives, big time. This lint is meant to be - /// deactivated by everyone doing serious performance work. This means having - /// done the measurement. - /// - /// ### Example - /// ```ignore - /// #[inline(always)] - /// fn not_quite_hot_code(..) { ... } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub INLINE_ALWAYS, - pedantic, - "use of `#[inline(always)]`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `extern crate` and `use` items annotated with - /// lint attributes. - /// - /// This lint permits lint attributes for lints emitted on the items themself. - /// For `use` items these lints are: - /// * deprecated - /// * unreachable_pub - /// * unused_imports - /// * clippy::enum_glob_use - /// * clippy::macro_use_imports - /// * clippy::wildcard_imports - /// - /// For `extern crate` items these lints are: - /// * `unused_imports` on items with `#[macro_use]` - /// - /// ### Why is this bad? - /// Lint attributes have no effect on crate imports. Most - /// likely a `!` was forgotten. - /// - /// ### Example - /// ```ignore - /// #[deny(dead_code)] - /// extern crate foo; - /// #[forbid(dead_code)] - /// use foo::bar; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// #[allow(unused_imports)] - /// use foo::baz; - /// #[allow(unused_imports)] - /// #[macro_use] - /// extern crate baz; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub USELESS_ATTRIBUTE, - correctness, - "use of lint attributes on `extern crate` items" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[deprecated]` annotations with a `since` - /// field that is not a valid semantic version. Also allows "TBD" to signal - /// future deprecation. - /// - /// ### Why is this bad? - /// For checking the version of the deprecation, it must be - /// a valid semver. Failing that, the contained information is useless. - /// - /// ### Example - /// ```no_run - /// #[deprecated(since = "forever")] - /// fn something_else() { /* ... */ } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub DEPRECATED_SEMVER, - correctness, - "use of `#[deprecated(since = \"x\")]` where x is not semver" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for empty lines after outer attributes - /// - /// ### Why is this bad? - /// Most likely the attribute was meant to be an inner attribute using a '!'. - /// If it was meant to be an outer attribute, then the following item - /// should not be separated by empty lines. - /// - /// ### Known problems - /// Can cause false positives. - /// - /// From the clippy side it's difficult to detect empty lines between an attributes and the - /// following item because empty lines and comments are not part of the AST. The parsing - /// currently works for basic cases but is not perfect. - /// - /// ### Example - /// ```no_run - /// #[allow(dead_code)] - /// - /// fn not_quite_good_code() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// // Good (as inner attribute) - /// #![allow(dead_code)] - /// - /// fn this_is_fine() { } - /// - /// // or - /// - /// // Good (as outer attribute) - /// #[allow(dead_code)] - /// fn this_is_fine_too() { } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub EMPTY_LINE_AFTER_OUTER_ATTR, - nursery, - "empty line after outer attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for empty lines after documentation comments. - /// - /// ### Why is this bad? - /// The documentation comment was most likely meant to be an inner attribute or regular comment. - /// If it was intended to be a documentation comment, then the empty line should be removed to - /// be more idiomatic. - /// - /// ### Known problems - /// Only detects empty lines immediately following the documentation. If the doc comment is followed - /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr` - /// in combination with this lint to detect both cases. - /// - /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`). - /// - /// ### Example - /// ```no_run - /// /// Some doc comment with a blank line after it. - /// - /// fn not_quite_good_code() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// /// Good (no blank line) - /// fn this_is_fine() { } - /// ``` - /// - /// ```no_run - /// // Good (convert to a regular comment) - /// - /// fn this_is_fine_too() { } - /// ``` - /// - /// ```no_run - /// //! Good (convert to a comment on an inner attribute) - /// - /// fn this_is_fine_as_well() { } - /// ``` - #[clippy::version = "1.70.0"] - pub EMPTY_LINE_AFTER_DOC_COMMENTS, - nursery, - "empty line after documentation comments" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. - /// - /// ### Why is this bad? - /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust. - /// These lints should only be enabled on a lint-by-lint basis and with careful consideration. - /// - /// ### Example - /// ```no_run - /// #![deny(clippy::restriction)] - /// ``` - /// - /// Use instead: - /// ```no_run - /// #![deny(clippy::as_conversions)] - /// ``` - #[clippy::version = "1.47.0"] - pub BLANKET_CLIPPY_RESTRICTION_LINTS, - suspicious, - "enabling the complete restriction group" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it - /// with `#[rustfmt::skip]`. - /// - /// ### Why is this bad? - /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) - /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes. - /// - /// ### Known problems - /// This lint doesn't detect crate level inner attributes, because they get - /// processed before the PreExpansionPass lints get executed. See - /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) - /// - /// ### Example - /// ```no_run - /// #[cfg_attr(rustfmt, rustfmt_skip)] - /// fn main() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[rustfmt::skip] - /// fn main() { } - /// ``` - #[clippy::version = "1.32.0"] - pub DEPRECATED_CFG_ATTR, - complexity, - "usage of `cfg_attr(rustfmt)` instead of tool attributes" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cfg attributes having operating systems used in target family position. - /// - /// ### Why is this bad? - /// The configuration option will not be recognised and the related item will not be included - /// by the conditional compilation engine. - /// - /// ### Example - /// ```no_run - /// #[cfg(linux)] - /// fn conditional() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// # mod hidden { - /// #[cfg(target_os = "linux")] - /// fn conditional() { } - /// # } - /// - /// // or - /// - /// #[cfg(unix)] - /// fn conditional() { } - /// ``` - /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. - #[clippy::version = "1.45.0"] - pub MISMATCHED_TARGET_OS, - correctness, - "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for attributes that allow lints without a reason. - /// - /// (This requires the `lint_reasons` feature) - /// - /// ### Why is this bad? - /// Allowing a lint should always have a reason. This reason should be documented to - /// ensure that others understand the reasoning - /// - /// ### Example - /// ```no_run - /// #![feature(lint_reasons)] - /// - /// #![allow(clippy::some_lint)] - /// ``` - /// - /// Use instead: - /// ```no_run - /// #![feature(lint_reasons)] - /// - /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] - /// ``` - #[clippy::version = "1.61.0"] - pub ALLOW_ATTRIBUTES_WITHOUT_REASON, - restriction, - "ensures that all `allow` and `expect` attributes have a reason" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[should_panic]` attributes without specifying the expected panic message. - /// - /// ### Why is this bad? - /// The expected panic message should be specified to ensure that the test is actually - /// panicking with the expected message, and not another unrelated panic. - /// - /// ### Example - /// ```no_run - /// fn random() -> i32 { 0 } - /// - /// #[should_panic] - /// #[test] - /// fn my_test() { - /// let _ = 1 / random(); - /// } - /// ``` - /// - /// Use instead: - /// ```no_run - /// fn random() -> i32 { 0 } - /// - /// #[should_panic = "attempt to divide by zero"] - /// #[test] - /// fn my_test() { - /// let _ = 1 / random(); - /// } - /// ``` - #[clippy::version = "1.74.0"] - pub SHOULD_PANIC_WITHOUT_EXPECT, - pedantic, - "ensures that all `should_panic` attributes specify its expected panic message" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `any` and `all` combinators in `cfg` with only one condition. - /// - /// ### Why is this bad? - /// If there is only one condition, no need to wrap it into `any` or `all` combinators. - /// - /// ### Example - /// ```no_run - /// #[cfg(any(unix))] - /// pub struct Bar; - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[cfg(unix)] - /// pub struct Bar; - /// ``` - #[clippy::version = "1.71.0"] - pub NON_MINIMAL_CFG, - style, - "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[cfg(features = "...")]` and suggests to replace it with - /// `#[cfg(feature = "...")]`. - /// - /// It also checks if `cfg(test)` was misspelled. - /// - /// ### Why is this bad? - /// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It - /// may cause conditional compilation not work quietly. - /// - /// ### Example - /// ```no_run - /// #[cfg(features = "some-feature")] - /// fn conditional() { } - /// #[cfg(tests)] - /// mod tests { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[cfg(feature = "some-feature")] - /// fn conditional() { } - /// #[cfg(test)] - /// mod tests { } - /// ``` - #[clippy::version = "1.69.0"] - pub MAYBE_MISUSED_CFG, - suspicious, - "prevent from misusing the wrong attr name" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for - /// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with - /// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`. - /// - /// ### Why is this bad? - /// This feature has been deprecated for years and shouldn't be used anymore. - /// - /// ### Example - /// ```no_run - /// #[cfg(feature = "cargo-clippy")] - /// struct Bar; - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[cfg(clippy)] - /// struct Bar; - /// ``` - #[clippy::version = "1.78.0"] - pub DEPRECATED_CLIPPY_CFG_ATTR, - suspicious, - "usage of `cfg(feature = \"cargo-clippy\")` instead of `cfg(clippy)`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]` - /// and suggests to replace it with `#[allow(clippy::lint)]`. - /// - /// ### Why is this bad? - /// There is no reason to put clippy attributes behind a clippy `cfg` as they are not - /// run by anything else than clippy. - /// - /// ### Example - /// ```no_run - /// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))] - /// ``` - /// - /// Use instead: - /// ```no_run - /// #![allow(clippy::deprecated_cfg_attr)] - /// ``` - #[clippy::version = "1.78.0"] - pub UNNECESSARY_CLIPPY_CFG, - suspicious, - "usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`" -} - -declare_lint_pass!(Attributes => [ - ALLOW_ATTRIBUTES_WITHOUT_REASON, - INLINE_ALWAYS, - DEPRECATED_SEMVER, - USELESS_ATTRIBUTE, - BLANKET_CLIPPY_RESTRICTION_LINTS, - SHOULD_PANIC_WITHOUT_EXPECT, -]); - -impl<'tcx> LateLintPass<'tcx> for Attributes { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - for (name, level) in &cx.sess().opts.lint_opts { - if name == "clippy::restriction" && *level > Level::Allow { - span_lint_and_then( - cx, - BLANKET_CLIPPY_RESTRICTION_LINTS, - DUMMY_SP, - "`clippy::restriction` is not meant to be enabled as a group", - |diag| { - diag.note(format!( - "because of the command line `--{} clippy::restriction`", - level.as_str() - )); - diag.help("enable the restriction lints you need individually"); - }, - ); - } - } - } - - fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { - if let Some(items) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - if is_lint_level(ident.name) { - check_clippy_lint_names(cx, ident.name, items); - } - if matches!(ident.name, sym::allow | sym::expect) { - check_lint_reason(cx, ident.name, items, attr); - } - if items.is_empty() || !attr.has_name(sym::deprecated) { - return; - } - for item in items { - if let NestedMetaItem::MetaItem(mi) = &item - && let MetaItemKind::NameValue(lit) = &mi.kind - && mi.has_name(sym::since) - { - check_deprecated_since(cx, item.span(), lit); - } - } - } - } - if attr.has_name(sym::should_panic) { - check_should_panic_reason(cx, attr); - } - } - - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if is_relevant_item(cx, item) { - check_attrs(cx, item.span, item.ident.name, attrs); - } - match item.kind { - ItemKind::ExternCrate(..) | ItemKind::Use(..) => { - let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use)); - - for attr in attrs { - if in_external_macro(cx.sess(), attr.span) { - return; - } - if let Some(lint_list) = &attr.meta_item_list() { - if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) { - for lint in lint_list { - match item.kind { - ItemKind::Use(..) => { - if is_word(lint, sym::unused_imports) - || is_word(lint, sym::deprecated) - || is_word(lint, sym!(unreachable_pub)) - || is_word(lint, sym!(unused)) - || is_word(lint, sym!(unused_import_braces)) - || extract_clippy_lint(lint).map_or(false, |s| { - matches!( - s.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - ) - }) - { - 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); - - if let Some(mut sugg) = snippet_opt(cx, line_span) { - if sugg.contains("#[") { - span_lint_and_then( - cx, - USELESS_ATTRIBUTE, - line_span, - "useless lint attribute", - |diag| { - sugg = sugg.replacen("#[", "#![", 1); - diag.span_suggestion( - line_span, - "if you just forgot a `!`, use", - sugg, - Applicability::MaybeIncorrect, - ); - }, - ); - } - } - } - } - } - }, - _ => {}, - } - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - if is_relevant_impl(cx, item) { - check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - if is_relevant_trait(cx, item) { - check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); - } - } -} - -/// Returns the lint name if it is clippy lint. -fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { - if let Some(meta_item) = lint.meta_item() - && meta_item.path.segments.len() > 1 - && let tool_name = meta_item.path.segments[0].ident - && tool_name.name == sym::clippy - { - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - return Some(lint_name); - } - None -} - -fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) { - if let AttrKind::Normal(normal_attr) = &attr.kind { - if let AttrArgs::Eq(_, AttrArgsEq::Hir(_)) = &normal_attr.item.args { - // `#[should_panic = ".."]` found, good - return; - } - - if let AttrArgs::Delimited(args) = &normal_attr.item.args - && let mut tt_iter = args.tokens.trees() - && let Some(TokenTree::Token( - Token { - kind: TokenKind::Ident(sym::expected, _), - .. - }, - _, - )) = tt_iter.next() - && let Some(TokenTree::Token( - Token { - kind: TokenKind::Eq, .. - }, - _, - )) = tt_iter.next() - && let Some(TokenTree::Token( - Token { - kind: TokenKind::Literal(_), - .. - }, - _, - )) = tt_iter.next() - { - // `#[should_panic(expected = "..")]` found, good - return; - } - - span_lint_and_sugg( - cx, - SHOULD_PANIC_WITHOUT_EXPECT, - attr.span, - "#[should_panic] attribute without a reason", - "consider specifying the expected panic", - "#[should_panic(expected = /* panic message */)]".into(), - Applicability::HasPlaceholders, - ); - } -} - -fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) { - 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", - ); - } - } - } -} - -fn check_lint_reason<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) { - // Check for the feature - if !cx.tcx.features().lint_reasons { - return; - } - - // Check if the reason is present - if let Some(item) = items.last().and_then(NestedMetaItem::meta_item) - && let MetaItemKind::NameValue(_) = &item.kind - && item.path == sym::reason - { - return; - } - - // Check if the attribute is in an external macro and therefore out of the developer's control - if in_external_macro(cx.sess(), attr.span) || is_from_proc_macro(cx, &attr) { - return; - } - - span_lint_and_help( - cx, - ALLOW_ATTRIBUTES_WITHOUT_REASON, - attr.span, - &format!("`{}` attribute without specifying a reason", name.as_str()), - None, - "try adding a reason at the end with `, reason = \"..\"`", - ); -} - -fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if let ItemKind::Fn(_, _, eid) = item.kind { - is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value) - } else { - true - } -} - -fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool { - match item.kind { - ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value), - _ => false, - } -} - -fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { - match item.kind { - TraitItemKind::Fn(_, TraitFn::Required(_)) => true, - TraitItemKind::Fn(_, TraitFn::Provided(eid)) => { - is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value) - }, - _ => false, - } -} - -fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool { - block.stmts.first().map_or( - block - .expr - .as_ref() - .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)), - |stmt| match &stmt.kind { - StmtKind::Local(_) => true, - StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), - StmtKind::Item(_) => false, - }, - ) -} - -fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { - if macro_backtrace(expr.span).last().map_or(false, |macro_call| { - is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable - }) { - return false; - } - match &expr.kind { - ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), - ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), - ExprKind::Ret(None) | ExprKind::Break(_, None) => false, - _ => true, - } -} - -fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { - if span.from_expansion() { - return; - } - - for attr in attrs { - if let Some(values) = attr.meta_item_list() { - if values.len() != 1 || !attr.has_name(sym::inline) { - continue; - } - if is_word(&values[0], sym::always) { - span_lint( - cx, - INLINE_ALWAYS, - attr.span, - &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), - ); - } - } - } -} - -fn check_deprecated_since(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) { - if let LitKind::Str(is, _) = lit.kind { - if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() { - return; - } - } - span_lint( - cx, - DEPRECATED_SEMVER, - span, - "the since field must contain a semver-compliant version", - ); -} - -fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { - if let NestedMetaItem::MetaItem(mi) = &nmi { - mi.is_word() && mi.has_name(expected) - } else { - false - } -} - -pub struct EarlyAttributes { - pub msrv: Msrv, -} - -impl_lint_pass!(EarlyAttributes => [ - DEPRECATED_CFG_ATTR, - MISMATCHED_TARGET_OS, - EMPTY_LINE_AFTER_OUTER_ATTR, - EMPTY_LINE_AFTER_DOC_COMMENTS, - NON_MINIMAL_CFG, - MAYBE_MISUSED_CFG, - DEPRECATED_CLIPPY_CFG_ATTR, - UNNECESSARY_CLIPPY_CFG, -]); - -impl EarlyLintPass for EarlyAttributes { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - check_empty_line_after_outer_attr(cx, item); - } - - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - check_deprecated_cfg_attr(cx, attr, &self.msrv); - check_deprecated_cfg(cx, attr); - check_mismatched_target_os(cx, attr); - check_minimal_cfg_condition(cx, attr); - check_misused_cfg(cx, attr); - } - - extract_msrv_attr!(EarlyContext); -} - -/// Check for empty lines after outer attributes. -/// -/// Attributes and documentation comments are both considered outer attributes -/// by the AST. However, the average user likely considers them to be different. -/// Checking for empty lines after each of these attributes is split into two different -/// lints but can share the same logic. -fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - let mut iter = item.attrs.iter().peekable(); - while let Some(attr) = iter.next() { - if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..))) - && attr.style == AttrStyle::Outer - && is_present_in_source(cx, attr.span) - { - let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); - let end_of_attr_to_next_attr_or_item = Span::new( - attr.span.hi(), - iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()), - item.span.ctxt(), - item.span.parent(), - ); - - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) { - let lines = snippet.split('\n').collect::>(); - let lines = without_block_comments(lines); - - if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - let (lint_msg, lint_type) = match attr.kind { - AttrKind::DocComment(..) => ( - "found an empty line after a doc comment. \ - Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?", - EMPTY_LINE_AFTER_DOC_COMMENTS, - ), - AttrKind::Normal(..) => ( - "found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - EMPTY_LINE_AFTER_OUTER_ATTR, - ), - }; - - span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg); - } - } - } - } -} - -fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) { - if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") { - span_lint_and_sugg( - cx, - DEPRECATED_CLIPPY_CFG_ATTR, - item.span, - "`feature = \"cargo-clippy\"` was replaced by `clippy`", - "replace with", - "clippy".to_string(), - Applicability::MachineApplicable, - ); - } -} - -fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) { - if let Some(ident) = attr.ident() { - if ["any", "all", "not"].contains(&ident.name.as_str()) { - let Some(list) = attr.meta_item_list() else { return }; - for item in list.iter().filter_map(|item| item.meta_item()) { - check_deprecated_cfg_recursively(cx, item); - } - } else { - check_cargo_clippy_attr(cx, attr); - } - } -} - -fn check_deprecated_cfg(cx: &EarlyContext<'_>, attr: &Attribute) { - if attr.has_name(sym::cfg) - && let Some(list) = attr.meta_item_list() - { - for item in list.iter().filter_map(|item| item.meta_item()) { - check_deprecated_cfg_recursively(cx, item); - } - } -} - -fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { - // check cfg_attr - if attr.has_name(sym::cfg_attr) - && let Some(items) = attr.meta_item_list() - && items.len() == 2 - && let Some(feature_item) = items[0].meta_item() - { - // check for `rustfmt` - if feature_item.has_name(sym::rustfmt) - && msrv.meets(msrvs::TOOL_ATTRIBUTES) - // check for `rustfmt_skip` and `rustfmt::skip` - && let Some(skip_item) = &items[1].meta_item() - && (skip_item.has_name(sym!(rustfmt_skip)) - || skip_item - .path - .segments - .last() - .expect("empty path in attribute") - .ident - .name - == sym::skip) - // Only lint outer attributes, because custom inner attributes are unstable - // Tracking issue: https://github.com/rust-lang/rust/issues/54726 - && attr.style == AttrStyle::Outer - { - span_lint_and_sugg( - cx, - DEPRECATED_CFG_ATTR, - attr.span, - "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", - "use", - "#[rustfmt::skip]".to_string(), - Applicability::MachineApplicable, - ); - } else { - check_deprecated_cfg_recursively(cx, feature_item); - if let Some(behind_cfg_attr) = items[1].meta_item() { - check_clippy_cfg_attr(cx, feature_item, behind_cfg_attr, attr); - } - } - } -} - -fn check_clippy_cfg_attr( - cx: &EarlyContext<'_>, - cfg_attr: &rustc_ast::MetaItem, - behind_cfg_attr: &rustc_ast::MetaItem, - attr: &Attribute, -) { - if cfg_attr.has_name(sym::clippy) - && let Some(ident) = behind_cfg_attr.ident() - && Level::from_symbol(ident.name, Some(attr.id)).is_some() - && let Some(items) = behind_cfg_attr.meta_item_list() - { - let nb_items = items.len(); - let mut clippy_lints = Vec::with_capacity(items.len()); - for item in items { - if let Some(meta_item) = item.meta_item() - && let [part1, _] = meta_item.path.segments.as_slice() - && part1.ident.name == sym::clippy - { - clippy_lints.push(item.span()); - } - } - if clippy_lints.is_empty() { - return; - } - if nb_items == clippy_lints.len() { - if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) { - span_lint_and_sugg( - cx, - UNNECESSARY_CLIPPY_CFG, - attr.span, - "no need to put clippy lints behind a `clippy` cfg", - "replace with", - format!( - "#{}[{}]", - if attr.style == AttrStyle::Inner { "!" } else { "" }, - snippet - ), - Applicability::MachineApplicable, - ); - } - } else { - let snippet = clippy_lints - .iter() - .filter_map(|sp| snippet_opt(cx, *sp)) - .collect::>() - .join(","); - span_lint_and_note( - cx, - UNNECESSARY_CLIPPY_CFG, - clippy_lints, - "no need to put clippy lints behind a `clippy` cfg", - None, - &format!( - "write instead: `#{}[{}({})]`", - if attr.style == AttrStyle::Inner { "!" } else { "" }, - ident.name, - snippet - ), - ); - } - } -} - -fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - if !meta.has_name(sym::any) && !meta.has_name(sym::all) { - continue; - } - if let MetaItemKind::List(list) = &meta.kind { - check_nested_cfg(cx, list); - if list.len() == 1 { - span_lint_and_then( - cx, - NON_MINIMAL_CFG, - meta.span, - "unneeded sub `cfg` when there is only one condition", - |diag| { - if let Some(snippet) = snippet_opt(cx, list[0].span()) { - diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect); - } - }, - ); - } else if list.is_empty() && meta.has_name(sym::all) { - span_lint_and_then( - cx, - NON_MINIMAL_CFG, - meta.span, - "unneeded sub `cfg` when there is no condition", - |_| {}, - ); - } - } - } - } -} - -fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - if let Some(ident) = meta.ident() - && ident.name.as_str() == "features" - && let Some(val) = meta.value_str() - { - span_lint_and_sugg( - cx, - MAYBE_MISUSED_CFG, - meta.span, - "'feature' may be misspelled as 'features'", - "did you mean", - format!("feature = \"{val}\""), - Applicability::MaybeIncorrect, - ); - } - if let MetaItemKind::List(list) = &meta.kind { - check_nested_misused_cfg(cx, list); - // If this is not a list, then we check for `cfg(test)`. - } else if let Some(ident) = meta.ident() - && matches!(ident.name.as_str(), "tests" | "Test") - { - span_lint_and_sugg( - cx, - MAYBE_MISUSED_CFG, - meta.span, - &format!("'test' may be misspelled as '{}'", ident.name.as_str()), - "did you mean", - "test".to_string(), - Applicability::MaybeIncorrect, - ); - } - } - } -} - -fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) { - if attr.has_name(sym::cfg) - && let Some(items) = attr.meta_item_list() - { - check_nested_cfg(cx, &items); - } -} - -fn check_misused_cfg(cx: &EarlyContext<'_>, attr: &Attribute) { - if attr.has_name(sym::cfg) - && let Some(items) = attr.meta_item_list() - { - check_nested_misused_cfg(cx, &items); - } -} - -fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { - fn find_os(name: &str) -> Option<&'static str> { - UNIX_SYSTEMS - .iter() - .chain(NON_UNIX_SYSTEMS.iter()) - .find(|&&os| os == name) - .copied() - } - - fn is_unix(name: &str) -> bool { - UNIX_SYSTEMS.iter().any(|&os| os == name) - } - - fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { - let mut mismatched = Vec::new(); - - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - match &meta.kind { - MetaItemKind::List(list) => { - mismatched.extend(find_mismatched_target_os(list)); - }, - MetaItemKind::Word => { - if let Some(ident) = meta.ident() - && let Some(os) = find_os(ident.name.as_str()) - { - mismatched.push((os, ident.span)); - } - }, - MetaItemKind::NameValue(..) => {}, - } - } - } - - mismatched - } - - if attr.has_name(sym::cfg) - && let Some(list) = attr.meta_item_list() - && let mismatched = find_mismatched_target_os(&list) - && !mismatched.is_empty() - { - let mess = "operating system used in target family position"; - - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { - // Avoid showing the unix suggestion multiple times in case - // we have more than one mismatch for unix-like systems - let mut unix_suggested = false; - - for (os, span) in mismatched { - let sugg = format!("target_os = \"{os}\""); - diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); - - if !unix_suggested && is_unix(os) { - diag.help("did you mean `unix`?"); - unix_suggested = true; - } - } - }); - } -} - -fn is_lint_level(symbol: Symbol) -> bool { - matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid) -} diff --git a/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/clippy_lints/src/attrs/allow_attributes_without_reason.rs new file mode 100644 index 0000000000000..df00f23e37e87 --- /dev/null +++ b/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -0,0 +1,37 @@ +use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; +use rustc_ast::{MetaItemKind, NestedMetaItem}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::sym; +use rustc_span::symbol::Symbol; + +pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) { + // Check for the feature + if !cx.tcx.features().lint_reasons { + return; + } + + // Check if the reason is present + if let Some(item) = items.last().and_then(NestedMetaItem::meta_item) + && let MetaItemKind::NameValue(_) = &item.kind + && item.path == sym::reason + { + return; + } + + // Check if the attribute is in an external macro and therefore out of the developer's control + if in_external_macro(cx.sess(), attr.span) || is_from_proc_macro(cx, &attr) { + return; + } + + span_lint_and_help( + cx, + ALLOW_ATTRIBUTES_WITHOUT_REASON, + attr.span, + &format!("`{}` attribute without specifying a reason", name.as_str()), + None, + "try adding a reason at the end with `, reason = \"..\"`", + ); +} diff --git a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs new file mode 100644 index 0000000000000..9b08fd6d654a5 --- /dev/null +++ b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -0,0 +1,44 @@ +use super::utils::extract_clippy_lint; +use super::BLANKET_CLIPPY_RESTRICTION_LINTS; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use rustc_ast::NestedMetaItem; +use rustc_lint::{LateContext, Level, LintContext}; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, DUMMY_SP}; + +pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) { + 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", + ); + } + } + } +} + +pub(super) fn check_command_line(cx: &LateContext<'_>) { + for (name, level) in &cx.sess().opts.lint_opts { + if name == "clippy::restriction" && *level > Level::Allow { + span_lint_and_then( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + DUMMY_SP, + "`clippy::restriction` is not meant to be enabled as a group", + |diag| { + diag.note(format!( + "because of the command line `--{} clippy::restriction`", + level.as_str() + )); + diag.help("enable the restriction lints you need individually"); + }, + ); + } + } +} diff --git a/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/clippy_lints/src/attrs/deprecated_cfg_attr.rs new file mode 100644 index 0000000000000..e872ab6b4b5f5 --- /dev/null +++ b/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -0,0 +1,87 @@ +use super::{unnecessary_clippy_cfg, Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR}; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::AttrStyle; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; +use rustc_span::sym; + +pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { + // check cfg_attr + if attr.has_name(sym::cfg_attr) + && let Some(items) = attr.meta_item_list() + && items.len() == 2 + && let Some(feature_item) = items[0].meta_item() + { + // check for `rustfmt` + if feature_item.has_name(sym::rustfmt) + && msrv.meets(msrvs::TOOL_ATTRIBUTES) + // check for `rustfmt_skip` and `rustfmt::skip` + && let Some(skip_item) = &items[1].meta_item() + && (skip_item.has_name(sym!(rustfmt_skip)) + || skip_item + .path + .segments + .last() + .expect("empty path in attribute") + .ident + .name + == sym::skip) + // Only lint outer attributes, because custom inner attributes are unstable + // Tracking issue: https://github.com/rust-lang/rust/issues/54726 + && attr.style == AttrStyle::Outer + { + span_lint_and_sugg( + cx, + DEPRECATED_CFG_ATTR, + attr.span, + "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", + "use", + "#[rustfmt::skip]".to_string(), + Applicability::MachineApplicable, + ); + } else { + check_deprecated_cfg_recursively(cx, feature_item); + if let Some(behind_cfg_attr) = items[1].meta_item() { + unnecessary_clippy_cfg::check(cx, feature_item, behind_cfg_attr, attr); + } + } + } +} + +pub(super) fn check_clippy(cx: &EarlyContext<'_>, attr: &Attribute) { + if attr.has_name(sym::cfg) + && let Some(list) = attr.meta_item_list() + { + for item in list.iter().filter_map(|item| item.meta_item()) { + check_deprecated_cfg_recursively(cx, item); + } + } +} + +fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) { + if let Some(ident) = attr.ident() { + if ["any", "all", "not"].contains(&ident.name.as_str()) { + let Some(list) = attr.meta_item_list() else { return }; + for item in list.iter().filter_map(|item| item.meta_item()) { + check_deprecated_cfg_recursively(cx, item); + } + } else { + check_cargo_clippy_attr(cx, attr); + } + } +} + +fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) { + if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") { + span_lint_and_sugg( + cx, + DEPRECATED_CLIPPY_CFG_ATTR, + item.span, + "`feature = \"cargo-clippy\"` was replaced by `clippy`", + "replace with", + "clippy".to_string(), + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/attrs/deprecated_semver.rs b/clippy_lints/src/attrs/deprecated_semver.rs new file mode 100644 index 0000000000000..1898c145c76a9 --- /dev/null +++ b/clippy_lints/src/attrs/deprecated_semver.rs @@ -0,0 +1,20 @@ +use super::DEPRECATED_SEMVER; +use clippy_utils::diagnostics::span_lint; +use rustc_ast::{LitKind, MetaItemLit}; +use rustc_lint::LateContext; +use rustc_span::Span; +use semver::Version; + +pub(super) fn check(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) { + if let LitKind::Str(is, _) = lit.kind { + if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() { + return; + } + } + span_lint( + cx, + DEPRECATED_SEMVER, + span, + "the since field must contain a semver-compliant version", + ); +} diff --git a/clippy_lints/src/attrs/empty_line_after.rs b/clippy_lints/src/attrs/empty_line_after.rs new file mode 100644 index 0000000000000..ca43e76ac5780 --- /dev/null +++ b/clippy_lints/src/attrs/empty_line_after.rs @@ -0,0 +1,52 @@ +use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments}; +use rustc_ast::{AttrKind, AttrStyle}; +use rustc_lint::EarlyContext; +use rustc_span::Span; + +/// Check for empty lines after outer attributes. +/// +/// Attributes and documentation comments are both considered outer attributes +/// by the AST. However, the average user likely considers them to be different. +/// Checking for empty lines after each of these attributes is split into two different +/// lints but can share the same logic. +pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { + let mut iter = item.attrs.iter().peekable(); + while let Some(attr) = iter.next() { + if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..))) + && attr.style == AttrStyle::Outer + && is_present_in_source(cx, attr.span) + { + let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); + let end_of_attr_to_next_attr_or_item = Span::new( + attr.span.hi(), + iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()), + item.span.ctxt(), + item.span.parent(), + ); + + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) { + let lines = snippet.split('\n').collect::>(); + let lines = without_block_comments(lines); + + if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { + let (lint_msg, lint_type) = match attr.kind { + AttrKind::DocComment(..) => ( + "found an empty line after a doc comment. \ + Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?", + EMPTY_LINE_AFTER_DOC_COMMENTS, + ), + AttrKind::Normal(..) => ( + "found an empty line after an outer attribute. \ + Perhaps you forgot to add a `!` to make it an inner attribute?", + EMPTY_LINE_AFTER_OUTER_ATTR, + ), + }; + + span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg); + } + } + } + } +} diff --git a/clippy_lints/src/attrs/inline_always.rs b/clippy_lints/src/attrs/inline_always.rs new file mode 100644 index 0000000000000..cfcd2cc6a00b7 --- /dev/null +++ b/clippy_lints/src/attrs/inline_always.rs @@ -0,0 +1,29 @@ +use super::utils::is_word; +use super::INLINE_ALWAYS; +use clippy_utils::diagnostics::span_lint; +use rustc_ast::Attribute; +use rustc_lint::LateContext; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; + +pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { + if span.from_expansion() { + return; + } + + for attr in attrs { + if let Some(values) = attr.meta_item_list() { + if values.len() != 1 || !attr.has_name(sym::inline) { + continue; + } + if is_word(&values[0], sym::always) { + span_lint( + cx, + INLINE_ALWAYS, + attr.span, + &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), + ); + } + } + } +} diff --git a/clippy_lints/src/attrs/maybe_misused_cfg.rs b/clippy_lints/src/attrs/maybe_misused_cfg.rs new file mode 100644 index 0000000000000..5a70866eda587 --- /dev/null +++ b/clippy_lints/src/attrs/maybe_misused_cfg.rs @@ -0,0 +1,51 @@ +use super::{Attribute, MAYBE_MISUSED_CFG}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::{MetaItemKind, NestedMetaItem}; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; +use rustc_span::sym; + +pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { + if attr.has_name(sym::cfg) + && let Some(items) = attr.meta_item_list() + { + check_nested_misused_cfg(cx, &items); + } +} + +fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { + for item in items { + if let NestedMetaItem::MetaItem(meta) = item { + if let Some(ident) = meta.ident() + && ident.name.as_str() == "features" + && let Some(val) = meta.value_str() + { + span_lint_and_sugg( + cx, + MAYBE_MISUSED_CFG, + meta.span, + "'feature' may be misspelled as 'features'", + "did you mean", + format!("feature = \"{val}\""), + Applicability::MaybeIncorrect, + ); + } + if let MetaItemKind::List(list) = &meta.kind { + check_nested_misused_cfg(cx, list); + // If this is not a list, then we check for `cfg(test)`. + } else if let Some(ident) = meta.ident() + && matches!(ident.name.as_str(), "tests" | "Test") + { + span_lint_and_sugg( + cx, + MAYBE_MISUSED_CFG, + meta.span, + &format!("'test' may be misspelled as '{}'", ident.name.as_str()), + "did you mean", + "test".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } +} diff --git a/clippy_lints/src/attrs/mismatched_target_os.rs b/clippy_lints/src/attrs/mismatched_target_os.rs new file mode 100644 index 0000000000000..b1cc0a763c5e9 --- /dev/null +++ b/clippy_lints/src/attrs/mismatched_target_os.rs @@ -0,0 +1,90 @@ +use super::{Attribute, MISMATCHED_TARGET_OS}; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::{MetaItemKind, NestedMetaItem}; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; +use rustc_span::{sym, Span}; + +static UNIX_SYSTEMS: &[&str] = &[ + "android", + "dragonfly", + "emscripten", + "freebsd", + "fuchsia", + "haiku", + "illumos", + "ios", + "l4re", + "linux", + "macos", + "netbsd", + "openbsd", + "redox", + "solaris", + "vxworks", +]; + +// NOTE: windows is excluded from the list because it's also a valid target family. +static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"]; + +pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { + fn find_os(name: &str) -> Option<&'static str> { + UNIX_SYSTEMS + .iter() + .chain(NON_UNIX_SYSTEMS.iter()) + .find(|&&os| os == name) + .copied() + } + + fn is_unix(name: &str) -> bool { + UNIX_SYSTEMS.iter().any(|&os| os == name) + } + + fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { + let mut mismatched = Vec::new(); + + for item in items { + if let NestedMetaItem::MetaItem(meta) = item { + match &meta.kind { + MetaItemKind::List(list) => { + mismatched.extend(find_mismatched_target_os(list)); + }, + MetaItemKind::Word => { + if let Some(ident) = meta.ident() + && let Some(os) = find_os(ident.name.as_str()) + { + mismatched.push((os, ident.span)); + } + }, + MetaItemKind::NameValue(..) => {}, + } + } + } + + mismatched + } + + if attr.has_name(sym::cfg) + && let Some(list) = attr.meta_item_list() + && let mismatched = find_mismatched_target_os(&list) + && !mismatched.is_empty() + { + let mess = "operating system used in target family position"; + + span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { + // Avoid showing the unix suggestion multiple times in case + // we have more than one mismatch for unix-like systems + let mut unix_suggested = false; + + for (os, span) in mismatched { + let sugg = format!("target_os = \"{os}\""); + diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); + + if !unix_suggested && is_unix(os) { + diag.help("did you mean `unix`?"); + unix_suggested = true; + } + } + }); + } +} diff --git a/clippy_lints/src/attrs/mixed_attributes_style.rs b/clippy_lints/src/attrs/mixed_attributes_style.rs new file mode 100644 index 0000000000000..c2e21cfd33006 --- /dev/null +++ b/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -0,0 +1,30 @@ +use super::MIXED_ATTRIBUTES_STYLE; +use clippy_utils::diagnostics::span_lint; +use rustc_ast::AttrStyle; +use rustc_lint::EarlyContext; + +pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { + let mut has_outer = false; + let mut has_inner = false; + + for attr in &item.attrs { + if attr.span.from_expansion() { + continue; + } + match attr.style { + AttrStyle::Inner => has_inner = true, + AttrStyle::Outer => has_outer = true, + } + } + if !has_outer || !has_inner { + return; + } + let mut attrs_iter = item.attrs.iter().filter(|attr| !attr.span.from_expansion()); + let span = attrs_iter.next().unwrap().span; + span_lint( + cx, + MIXED_ATTRIBUTES_STYLE, + span.with_hi(attrs_iter.last().unwrap().span.hi()), + "item has both inner and outer attributes", + ); +} diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs new file mode 100644 index 0000000000000..c4c65d3248a7d --- /dev/null +++ b/clippy_lints/src/attrs/mod.rs @@ -0,0 +1,588 @@ +//! checks for attributes + +mod allow_attributes_without_reason; +mod blanket_clippy_restriction_lints; +mod deprecated_cfg_attr; +mod deprecated_semver; +mod empty_line_after; +mod inline_always; +mod maybe_misused_cfg; +mod mismatched_target_os; +mod mixed_attributes_style; +mod non_minimal_cfg; +mod should_panic_without_expect; +mod unnecessary_clippy_cfg; +mod useless_attribute; +mod utils; + +use clippy_config::msrvs::Msrv; +use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; +use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, impl_lint_pass}; +use rustc_span::sym; +use utils::{is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for items annotated with `#[inline(always)]`, + /// unless the annotated function is empty or simply panics. + /// + /// ### Why is this bad? + /// While there are valid uses of this annotation (and once + /// you know when to use it, by all means `allow` this lint), it's a common + /// newbie-mistake to pepper one's code with it. + /// + /// As a rule of thumb, before slapping `#[inline(always)]` on a function, + /// measure if that additional function call really affects your runtime profile + /// sufficiently to make up for the increase in compile time. + /// + /// ### Known problems + /// False positives, big time. This lint is meant to be + /// deactivated by everyone doing serious performance work. This means having + /// done the measurement. + /// + /// ### Example + /// ```ignore + /// #[inline(always)] + /// fn not_quite_hot_code(..) { ... } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub INLINE_ALWAYS, + pedantic, + "use of `#[inline(always)]`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `extern crate` and `use` items annotated with + /// lint attributes. + /// + /// This lint permits lint attributes for lints emitted on the items themself. + /// For `use` items these lints are: + /// * deprecated + /// * unreachable_pub + /// * unused_imports + /// * clippy::enum_glob_use + /// * clippy::macro_use_imports + /// * clippy::wildcard_imports + /// + /// For `extern crate` items these lints are: + /// * `unused_imports` on items with `#[macro_use]` + /// + /// ### Why is this bad? + /// Lint attributes have no effect on crate imports. Most + /// likely a `!` was forgotten. + /// + /// ### Example + /// ```ignore + /// #[deny(dead_code)] + /// extern crate foo; + /// #[forbid(dead_code)] + /// use foo::bar; + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// #[allow(unused_imports)] + /// use foo::baz; + /// #[allow(unused_imports)] + /// #[macro_use] + /// extern crate baz; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub USELESS_ATTRIBUTE, + correctness, + "use of lint attributes on `extern crate` items" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[deprecated]` annotations with a `since` + /// field that is not a valid semantic version. Also allows "TBD" to signal + /// future deprecation. + /// + /// ### Why is this bad? + /// For checking the version of the deprecation, it must be + /// a valid semver. Failing that, the contained information is useless. + /// + /// ### Example + /// ```no_run + /// #[deprecated(since = "forever")] + /// fn something_else() { /* ... */ } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub DEPRECATED_SEMVER, + correctness, + "use of `#[deprecated(since = \"x\")]` where x is not semver" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty lines after outer attributes + /// + /// ### Why is this bad? + /// Most likely the attribute was meant to be an inner attribute using a '!'. + /// If it was meant to be an outer attribute, then the following item + /// should not be separated by empty lines. + /// + /// ### Known problems + /// Can cause false positives. + /// + /// From the clippy side it's difficult to detect empty lines between an attributes and the + /// following item because empty lines and comments are not part of the AST. The parsing + /// currently works for basic cases but is not perfect. + /// + /// ### Example + /// ```no_run + /// #[allow(dead_code)] + /// + /// fn not_quite_good_code() { } + /// ``` + /// + /// Use instead: + /// ```no_run + /// // Good (as inner attribute) + /// #![allow(dead_code)] + /// + /// fn this_is_fine() { } + /// + /// // or + /// + /// // Good (as outer attribute) + /// #[allow(dead_code)] + /// fn this_is_fine_too() { } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub EMPTY_LINE_AFTER_OUTER_ATTR, + nursery, + "empty line after outer attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty lines after documentation comments. + /// + /// ### Why is this bad? + /// The documentation comment was most likely meant to be an inner attribute or regular comment. + /// If it was intended to be a documentation comment, then the empty line should be removed to + /// be more idiomatic. + /// + /// ### Known problems + /// Only detects empty lines immediately following the documentation. If the doc comment is followed + /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr` + /// in combination with this lint to detect both cases. + /// + /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`). + /// + /// ### Example + /// ```no_run + /// /// Some doc comment with a blank line after it. + /// + /// fn not_quite_good_code() { } + /// ``` + /// + /// Use instead: + /// ```no_run + /// /// Good (no blank line) + /// fn this_is_fine() { } + /// ``` + /// + /// ```no_run + /// // Good (convert to a regular comment) + /// + /// fn this_is_fine_too() { } + /// ``` + /// + /// ```no_run + /// //! Good (convert to a comment on an inner attribute) + /// + /// fn this_is_fine_as_well() { } + /// ``` + #[clippy::version = "1.70.0"] + pub EMPTY_LINE_AFTER_DOC_COMMENTS, + nursery, + "empty line after documentation comments" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. + /// + /// ### Why is this bad? + /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust. + /// These lints should only be enabled on a lint-by-lint basis and with careful consideration. + /// + /// ### Example + /// ```no_run + /// #![deny(clippy::restriction)] + /// ``` + /// + /// Use instead: + /// ```no_run + /// #![deny(clippy::as_conversions)] + /// ``` + #[clippy::version = "1.47.0"] + pub BLANKET_CLIPPY_RESTRICTION_LINTS, + suspicious, + "enabling the complete restriction group" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it + /// with `#[rustfmt::skip]`. + /// + /// ### Why is this bad? + /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) + /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes. + /// + /// ### Known problems + /// This lint doesn't detect crate level inner attributes, because they get + /// processed before the PreExpansionPass lints get executed. See + /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) + /// + /// ### Example + /// ```no_run + /// #[cfg_attr(rustfmt, rustfmt_skip)] + /// fn main() { } + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[rustfmt::skip] + /// fn main() { } + /// ``` + #[clippy::version = "1.32.0"] + pub DEPRECATED_CFG_ATTR, + complexity, + "usage of `cfg_attr(rustfmt)` instead of tool attributes" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cfg attributes having operating systems used in target family position. + /// + /// ### Why is this bad? + /// The configuration option will not be recognised and the related item will not be included + /// by the conditional compilation engine. + /// + /// ### Example + /// ```no_run + /// #[cfg(linux)] + /// fn conditional() { } + /// ``` + /// + /// Use instead: + /// ```no_run + /// # mod hidden { + /// #[cfg(target_os = "linux")] + /// fn conditional() { } + /// # } + /// + /// // or + /// + /// #[cfg(unix)] + /// fn conditional() { } + /// ``` + /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. + #[clippy::version = "1.45.0"] + pub MISMATCHED_TARGET_OS, + correctness, + "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for attributes that allow lints without a reason. + /// + /// (This requires the `lint_reasons` feature) + /// + /// ### Why is this bad? + /// Allowing a lint should always have a reason. This reason should be documented to + /// ensure that others understand the reasoning + /// + /// ### Example + /// ```no_run + /// #![feature(lint_reasons)] + /// + /// #![allow(clippy::some_lint)] + /// ``` + /// + /// Use instead: + /// ```no_run + /// #![feature(lint_reasons)] + /// + /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] + /// ``` + #[clippy::version = "1.61.0"] + pub ALLOW_ATTRIBUTES_WITHOUT_REASON, + restriction, + "ensures that all `allow` and `expect` attributes have a reason" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[should_panic]` attributes without specifying the expected panic message. + /// + /// ### Why is this bad? + /// The expected panic message should be specified to ensure that the test is actually + /// panicking with the expected message, and not another unrelated panic. + /// + /// ### Example + /// ```no_run + /// fn random() -> i32 { 0 } + /// + /// #[should_panic] + /// #[test] + /// fn my_test() { + /// let _ = 1 / random(); + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn random() -> i32 { 0 } + /// + /// #[should_panic = "attempt to divide by zero"] + /// #[test] + /// fn my_test() { + /// let _ = 1 / random(); + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub SHOULD_PANIC_WITHOUT_EXPECT, + pedantic, + "ensures that all `should_panic` attributes specify its expected panic message" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `any` and `all` combinators in `cfg` with only one condition. + /// + /// ### Why is this bad? + /// If there is only one condition, no need to wrap it into `any` or `all` combinators. + /// + /// ### Example + /// ```no_run + /// #[cfg(any(unix))] + /// pub struct Bar; + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[cfg(unix)] + /// pub struct Bar; + /// ``` + #[clippy::version = "1.71.0"] + pub NON_MINIMAL_CFG, + style, + "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[cfg(features = "...")]` and suggests to replace it with + /// `#[cfg(feature = "...")]`. + /// + /// It also checks if `cfg(test)` was misspelled. + /// + /// ### Why is this bad? + /// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It + /// may cause conditional compilation not work quietly. + /// + /// ### Example + /// ```no_run + /// #[cfg(features = "some-feature")] + /// fn conditional() { } + /// #[cfg(tests)] + /// mod tests { } + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[cfg(feature = "some-feature")] + /// fn conditional() { } + /// #[cfg(test)] + /// mod tests { } + /// ``` + #[clippy::version = "1.69.0"] + pub MAYBE_MISUSED_CFG, + suspicious, + "prevent from misusing the wrong attr name" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for + /// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with + /// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`. + /// + /// ### Why is this bad? + /// This feature has been deprecated for years and shouldn't be used anymore. + /// + /// ### Example + /// ```no_run + /// #[cfg(feature = "cargo-clippy")] + /// struct Bar; + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[cfg(clippy)] + /// struct Bar; + /// ``` + #[clippy::version = "1.78.0"] + pub DEPRECATED_CLIPPY_CFG_ATTR, + suspicious, + "usage of `cfg(feature = \"cargo-clippy\")` instead of `cfg(clippy)`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]` + /// and suggests to replace it with `#[allow(clippy::lint)]`. + /// + /// ### Why is this bad? + /// There is no reason to put clippy attributes behind a clippy `cfg` as they are not + /// run by anything else than clippy. + /// + /// ### Example + /// ```no_run + /// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))] + /// ``` + /// + /// Use instead: + /// ```no_run + /// #![allow(clippy::deprecated_cfg_attr)] + /// ``` + #[clippy::version = "1.78.0"] + pub UNNECESSARY_CLIPPY_CFG, + suspicious, + "usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks that an item has only one kind of attributes. + /// + /// ### Why is this bad? + /// Having both kinds of attributes makes it more complicated to read code. + /// + /// ### Example + /// ```no_run + /// #[cfg(linux)] + /// pub fn foo() { + /// #![cfg(windows)] + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[cfg(linux)] + /// #[cfg(windows)] + /// pub fn foo() { + /// } + /// ``` + #[clippy::version = "1.78.0"] + pub MIXED_ATTRIBUTES_STYLE, + suspicious, + "item has both inner and outer attributes" +} + +declare_lint_pass!(Attributes => [ + ALLOW_ATTRIBUTES_WITHOUT_REASON, + INLINE_ALWAYS, + DEPRECATED_SEMVER, + USELESS_ATTRIBUTE, + BLANKET_CLIPPY_RESTRICTION_LINTS, + SHOULD_PANIC_WITHOUT_EXPECT, +]); + +impl<'tcx> LateLintPass<'tcx> for Attributes { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + blanket_clippy_restriction_lints::check_command_line(cx); + } + + fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { + if let Some(items) = &attr.meta_item_list() { + if let Some(ident) = attr.ident() { + if is_lint_level(ident.name, attr.id) { + blanket_clippy_restriction_lints::check(cx, ident.name, items); + } + if matches!(ident.name, sym::allow | sym::expect) { + allow_attributes_without_reason::check(cx, ident.name, items, attr); + } + if items.is_empty() || !attr.has_name(sym::deprecated) { + return; + } + for item in items { + if let NestedMetaItem::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) + { + deprecated_semver::check(cx, item.span(), lit); + } + } + } + } + if attr.has_name(sym::should_panic) { + should_panic_without_expect::check(cx, attr); + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + if is_relevant_item(cx, item) { + inline_always::check(cx, item.span, item.ident.name, attrs); + } + match item.kind { + ItemKind::ExternCrate(..) | ItemKind::Use(..) => useless_attribute::check(cx, item, attrs), + _ => {}, + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if is_relevant_impl(cx, item) { + inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if is_relevant_trait(cx, item) { + inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); + } + } +} + +pub struct EarlyAttributes { + pub msrv: Msrv, +} + +impl_lint_pass!(EarlyAttributes => [ + DEPRECATED_CFG_ATTR, + MISMATCHED_TARGET_OS, + EMPTY_LINE_AFTER_OUTER_ATTR, + EMPTY_LINE_AFTER_DOC_COMMENTS, + NON_MINIMAL_CFG, + MAYBE_MISUSED_CFG, + DEPRECATED_CLIPPY_CFG_ATTR, + UNNECESSARY_CLIPPY_CFG, + MIXED_ATTRIBUTES_STYLE, +]); + +impl EarlyLintPass for EarlyAttributes { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { + empty_line_after::check(cx, item); + mixed_attributes_style::check(cx, item); + } + + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + deprecated_cfg_attr::check(cx, attr, &self.msrv); + deprecated_cfg_attr::check_clippy(cx, attr); + mismatched_target_os::check(cx, attr); + non_minimal_cfg::check(cx, attr); + maybe_misused_cfg::check(cx, attr); + } + + extract_msrv_attr!(EarlyContext); +} diff --git a/clippy_lints/src/attrs/non_minimal_cfg.rs b/clippy_lints/src/attrs/non_minimal_cfg.rs new file mode 100644 index 0000000000000..3fde70615853d --- /dev/null +++ b/clippy_lints/src/attrs/non_minimal_cfg.rs @@ -0,0 +1,49 @@ +use super::{Attribute, NON_MINIMAL_CFG}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use rustc_ast::{MetaItemKind, NestedMetaItem}; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; +use rustc_span::sym; + +pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { + if attr.has_name(sym::cfg) + && let Some(items) = attr.meta_item_list() + { + check_nested_cfg(cx, &items); + } +} + +fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { + for item in items { + if let NestedMetaItem::MetaItem(meta) = item { + if !meta.has_name(sym::any) && !meta.has_name(sym::all) { + continue; + } + if let MetaItemKind::List(list) = &meta.kind { + check_nested_cfg(cx, list); + if list.len() == 1 { + span_lint_and_then( + cx, + NON_MINIMAL_CFG, + meta.span, + "unneeded sub `cfg` when there is only one condition", + |diag| { + if let Some(snippet) = snippet_opt(cx, list[0].span()) { + diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect); + } + }, + ); + } else if list.is_empty() && meta.has_name(sym::all) { + span_lint_and_then( + cx, + NON_MINIMAL_CFG, + meta.span, + "unneeded sub `cfg` when there is no condition", + |_| {}, + ); + } + } + } + } +} diff --git a/clippy_lints/src/attrs/should_panic_without_expect.rs b/clippy_lints/src/attrs/should_panic_without_expect.rs new file mode 100644 index 0000000000000..2d45cbbf621f4 --- /dev/null +++ b/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -0,0 +1,54 @@ +use super::{Attribute, SHOULD_PANIC_WITHOUT_EXPECT}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::token::{Token, TokenKind}; +use rustc_ast::tokenstream::TokenTree; +use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind}; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_span::sym; + +pub(super) fn check(cx: &LateContext<'_>, attr: &Attribute) { + if let AttrKind::Normal(normal_attr) = &attr.kind { + if let AttrArgs::Eq(_, AttrArgsEq::Hir(_)) = &normal_attr.item.args { + // `#[should_panic = ".."]` found, good + return; + } + + if let AttrArgs::Delimited(args) = &normal_attr.item.args + && let mut tt_iter = args.tokens.trees() + && let Some(TokenTree::Token( + Token { + kind: TokenKind::Ident(sym::expected, _), + .. + }, + _, + )) = tt_iter.next() + && let Some(TokenTree::Token( + Token { + kind: TokenKind::Eq, .. + }, + _, + )) = tt_iter.next() + && let Some(TokenTree::Token( + Token { + kind: TokenKind::Literal(_), + .. + }, + _, + )) = tt_iter.next() + { + // `#[should_panic(expected = "..")]` found, good + return; + } + + span_lint_and_sugg( + cx, + SHOULD_PANIC_WITHOUT_EXPECT, + attr.span, + "#[should_panic] attribute without a reason", + "consider specifying the expected panic", + "#[should_panic(expected = /* panic message */)]".into(), + Applicability::HasPlaceholders, + ); + } +} diff --git a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs new file mode 100644 index 0000000000000..05da69636c62f --- /dev/null +++ b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -0,0 +1,70 @@ +use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; +use rustc_ast::AttrStyle; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, Level}; +use rustc_span::sym; + +pub(super) fn check( + cx: &EarlyContext<'_>, + cfg_attr: &rustc_ast::MetaItem, + behind_cfg_attr: &rustc_ast::MetaItem, + attr: &Attribute, +) { + if cfg_attr.has_name(sym::clippy) + && let Some(ident) = behind_cfg_attr.ident() + && Level::from_symbol(ident.name, Some(attr.id)).is_some() + && let Some(items) = behind_cfg_attr.meta_item_list() + { + let nb_items = items.len(); + let mut clippy_lints = Vec::with_capacity(items.len()); + for item in items { + if let Some(meta_item) = item.meta_item() + && let [part1, _] = meta_item.path.segments.as_slice() + && part1.ident.name == sym::clippy + { + clippy_lints.push(item.span()); + } + } + if clippy_lints.is_empty() { + return; + } + if nb_items == clippy_lints.len() { + if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) { + span_lint_and_sugg( + cx, + UNNECESSARY_CLIPPY_CFG, + attr.span, + "no need to put clippy lints behind a `clippy` cfg", + "replace with", + format!( + "#{}[{}]", + if attr.style == AttrStyle::Inner { "!" } else { "" }, + snippet + ), + Applicability::MachineApplicable, + ); + } + } else { + let snippet = clippy_lints + .iter() + .filter_map(|sp| snippet_opt(cx, *sp)) + .collect::>() + .join(","); + span_lint_and_note( + cx, + UNNECESSARY_CLIPPY_CFG, + clippy_lints, + "no need to put clippy lints behind a `clippy` cfg", + None, + &format!( + "write instead: `#{}[{}({})]`", + if attr.style == AttrStyle::Inner { "!" } else { "" }, + ident.name, + snippet + ), + ); + } + } +} diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs new file mode 100644 index 0000000000000..7575f502a7c29 --- /dev/null +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -0,0 +1,73 @@ +use super::utils::{extract_clippy_lint, is_lint_level, is_word}; +use super::{Attribute, USELESS_ATTRIBUTE}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{first_line_of_span, snippet_opt}; +use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::sym; + +pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) { + let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use)); + + for attr in attrs { + if in_external_macro(cx.sess(), attr.span) { + return; + } + if let Some(lint_list) = &attr.meta_item_list() { + if attr.ident().map_or(false, |ident| is_lint_level(ident.name, attr.id)) { + for lint in lint_list { + match item.kind { + ItemKind::Use(..) => { + if is_word(lint, sym::unused_imports) + || is_word(lint, sym::deprecated) + || is_word(lint, sym!(unreachable_pub)) + || is_word(lint, sym!(unused)) + || is_word(lint, sym!(unused_import_braces)) + || extract_clippy_lint(lint).map_or(false, |s| { + matches!( + s.as_str(), + "wildcard_imports" + | "enum_glob_use" + | "redundant_pub_crate" + | "macro_use_imports" + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" + ) + }) + { + 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); + + if let Some(mut sugg) = snippet_opt(cx, line_span) { + if sugg.contains("#[") { + span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { + sugg = sugg.replacen("#[", "#![", 1); + diag.span_suggestion( + line_span, + "if you just forgot a `!`, use", + sugg, + Applicability::MaybeIncorrect, + ); + }); + } + } + } + } + } +} diff --git a/clippy_lints/src/attrs/utils.rs b/clippy_lints/src/attrs/utils.rs new file mode 100644 index 0000000000000..9b36cc00444f3 --- /dev/null +++ b/clippy_lints/src/attrs/utils.rs @@ -0,0 +1,87 @@ +use clippy_utils::macros::{is_panic, macro_backtrace}; +use rustc_ast::{AttrId, NestedMetaItem}; +use rustc_hir::{ + Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, +}; +use rustc_lint::{LateContext, Level}; +use rustc_middle::ty; +use rustc_span::sym; +use rustc_span::symbol::Symbol; + +pub(super) fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { + if let NestedMetaItem::MetaItem(mi) = &nmi { + mi.is_word() && mi.has_name(expected) + } else { + false + } +} + +pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool { + Level::from_symbol(symbol, Some(attr_id)).is_some() +} + +pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if let ItemKind::Fn(_, _, eid) = item.kind { + is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value) + } else { + true + } +} + +pub(super) fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool { + match item.kind { + ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value), + _ => false, + } +} + +pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { + match item.kind { + TraitItemKind::Fn(_, TraitFn::Required(_)) => true, + TraitItemKind::Fn(_, TraitFn::Provided(eid)) => { + is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value) + }, + _ => false, + } +} + +fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool { + block.stmts.first().map_or( + block + .expr + .as_ref() + .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)), + |stmt| match &stmt.kind { + StmtKind::Local(_) => true, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), + StmtKind::Item(_) => false, + }, + ) +} + +fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { + if macro_backtrace(expr.span).last().map_or(false, |macro_call| { + is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable + }) { + return false; + } + match &expr.kind { + ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), + ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), + ExprKind::Ret(None) | ExprKind::Break(_, None) => false, + _ => true, + } +} + +/// Returns the lint name if it is clippy lint. +pub(super) fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { + if let Some(meta_item) = lint.meta_item() + && meta_item.path.segments.len() > 1 + && let tool_name = meta_item.path.segments[0].ident + && tool_name.name == sym::clippy + { + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + return Some(lint_name); + } + None +} diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 0d66f2d644d98..a474356608fbe 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -88,7 +88,6 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match expr.kind { - ExprKind::Unary(UnOp::Not, sub) => check_inverted_condition(cx, expr.span, sub), // This check the case where an element in a boolean comparison is inverted, like: // // ``` @@ -119,27 +118,6 @@ fn bin_op_eq_str(op: BinOpKind) -> Option<&'static str> { } } -fn check_inverted_condition(cx: &LateContext<'_>, expr_span: Span, sub_expr: &Expr<'_>) { - if !expr_span.from_expansion() - && let ExprKind::Binary(op, left, right) = sub_expr.kind - && let Some(left) = snippet_opt(cx, left.span) - && let Some(right) = snippet_opt(cx, right.span) - { - let Some(op) = inverted_bin_op_eq_str(op.node) else { - return; - }; - span_lint_and_sugg( - cx, - NONMINIMAL_BOOL, - expr_span, - "this boolean expression can be simplified", - "try", - format!("{left} {op} {right}",), - Applicability::MachineApplicable, - ); - } -} - fn check_inverted_bool_in_condition( cx: &LateContext<'_>, expr_span: Span, @@ -148,8 +126,8 @@ fn check_inverted_bool_in_condition( right: &Expr<'_>, ) { if expr_span.from_expansion() - && (!cx.typeck_results().node_types()[left.hir_id].is_bool() - || !cx.typeck_results().node_types()[right.hir_id].is_bool()) + || !cx.typeck_results().node_types()[left.hir_id].is_bool() + || !cx.typeck_results().node_types()[right.hir_id].is_bool() { return; } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index cc513d46bf4ec..08341ff32f358 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -151,13 +151,24 @@ pub(super) fn check<'tcx>( return false; } + // If the whole cast expression is a unary expression (`(*x as T)`) or an addressof + // expression (`(&x as T)`), then not surrounding the suggestion into a block risks us + // changing the precedence of operators if the cast expression is followed by an operation + // with higher precedence than the unary operator (`(*x as T).foo()` would become + // `*x.foo()`, which changes what the `*` applies on). + // The same is true if the expression encompassing the cast expression is a unary + // expression or an addressof expression. + let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)) + || get_parent_expr(cx, expr) + .map_or(false, |e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))); + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) { + if needs_block { format!("{{ {cast_str} }}") } else { cast_str diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index fb3ae2457e3c4..2b324f5f96e81 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -47,6 +47,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO, crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO, crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO, + crate::assigning_clones::ASSIGNING_CLONES_INFO, crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO, crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO, crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, @@ -58,6 +59,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MAYBE_MISUSED_CFG_INFO, crate::attrs::MISMATCHED_TARGET_OS_INFO, + crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO, crate::attrs::UNNECESSARY_CLIPPY_CFG_INFO, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 6144ec7b3caca..f0f2c7d6658f1 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -451,8 +451,8 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) && !has_non_exhaustive_attr(cx.tcx, *adt) + && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]) // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() @@ -471,6 +471,10 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r } } +fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { + 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. fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> { // Initial map from generic index to param def. diff --git a/clippy_lints/src/doc/markdown.rs b/clippy_lints/src/doc/markdown.rs index a58219c294660..d2f147565917f 100644 --- a/clippy_lints/src/doc/markdown.rs +++ b/clippy_lints/src/doc/markdown.rs @@ -8,7 +8,7 @@ use url::Url; use crate::doc::DOC_MARKDOWN; -pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet, text: &str, span: Span) { +pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet, text: &str, span: Span, code_level: isize) { for orig_word in text.split(|c: char| c.is_whitespace() || c == '\'') { // Trim punctuation as in `some comment (see foo::bar).` // ^^ @@ -46,11 +46,11 @@ pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet, text: &str, span.parent(), ); - check_word(cx, word, span); + check_word(cx, word, span, code_level); } } -fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { +fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize) { /// Checks if a string is upper-camel-case, i.e., starts with an uppercase and /// contains at least two uppercase letters (`Clippy` is ok) and one lower-case /// letter (`NASA` is ok). @@ -60,7 +60,14 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { return false; } - let s = s.strip_suffix('s').unwrap_or(s); + let s = if let Some(prefix) = s.strip_suffix("es") + && prefix.chars().all(|c| c.is_ascii_uppercase()) + && matches!(prefix.chars().last(), Some('S' | 'X')) + { + prefix + } else { + s.strip_suffix('s').unwrap_or(s) + }; s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 @@ -90,7 +97,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { } // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) - if has_underscore(word) && has_hyphen(word) { + if code_level > 0 || (has_underscore(word) && has_hyphen(word)) { return; } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 9af34c3a7bf0b..003d26b7b89d9 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -599,10 +599,19 @@ fn check_doc<'a, Events: Iterator, Range, Range)> = Vec::new(); + let mut text_to_check: Vec<(CowStr<'_>, Range, isize)> = Vec::new(); let mut paragraph_range = 0..0; + let mut code_level = 0; + for (event, range) in events { match event { + Html(tag) => { + if tag.starts_with(" { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { @@ -652,16 +661,15 @@ fn check_doc<'a, Events: Iterator, Range (), // We don't care about other tags - Html(_html) => (), // HTML is weird, just ignore it SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { paragraph_range.end = range.end; @@ -694,7 +702,7 @@ fn check_doc<'a, Events: Iterator, Range { + /// Whether the check for `contains_key` was negated. negated: bool, + /// The map on which the check is performed. map: &'tcx Expr<'tcx>, + /// The key that is checked to be contained. key: &'tcx Expr<'tcx>, + /// The context of the whole condition expression. call_ctxt: SyntaxContext, } + +/// Inspect the given expression and return details about the `contains_key` check. +/// +/// If the given expression is not a `contains_key` check against a `BTreeMap` or a `HashMap`, +/// return `None`. fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { let mut negated = false; let expr = peel_hir_expr_while(expr, |e| match e.kind { @@ -229,6 +248,7 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio }, _ => None, }); + match expr.kind { ExprKind::MethodCall( _, @@ -261,11 +281,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio } } +/// Details on an expression inserting a key into a map. +/// +/// For instance, on the following: +/// ```ignore +/// self.the_map.insert("the_key", 3 + 4); +/// ``` +/// +/// - `map` will be the `self.the_map` expression +/// - `key` will be the `"the_key"` expression +/// - `value` will be the `3 + 4` expression struct InsertExpr<'tcx> { + /// The map into which the insertion is performed. map: &'tcx Expr<'tcx>, + /// The key at which to insert. key: &'tcx Expr<'tcx>, + /// The value to insert. value: &'tcx Expr<'tcx>, } + +/// Inspect the given expression and return details about the `insert` call. +/// +/// If the given expression is not an `insert` call into a `BTreeMap` or a `HashMap`, return `None`. fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; @@ -298,7 +335,7 @@ struct Insertion<'tcx> { value: &'tcx Expr<'tcx>, } -/// This visitor needs to do a multiple things: +/// This visitor needs to do multiple things: /// * Find all usages of the map. An insertion can only be made before any other usages of the map. /// * Determine if there's an insertion using the same key. There's no need for the entry api /// otherwise. @@ -346,7 +383,7 @@ impl<'tcx> InsertSearcher<'_, 'tcx> { res } - /// Visits an expression which is not itself in a tail position, but other sibling expressions + /// Visit an expression which is not itself in a tail position, but other sibling expressions /// may be. e.g. if conditions fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) { let in_tail_pos = self.in_tail_pos; @@ -354,6 +391,19 @@ impl<'tcx> InsertSearcher<'_, 'tcx> { self.visit_expr(e); self.in_tail_pos = in_tail_pos; } + + /// Visit the key and value expression of an insert expression. + /// There may not be uses of the map in either of those two either. + fn visit_insert_expr_arguments(&mut self, e: &InsertExpr<'tcx>) { + let in_tail_pos = self.in_tail_pos; + let allow_insert_closure = self.allow_insert_closure; + let is_single_insert = self.is_single_insert; + walk_expr(self, e.key); + walk_expr(self, e.value); + self.in_tail_pos = in_tail_pos; + self.allow_insert_closure = allow_insert_closure; + self.is_single_insert = is_single_insert; + } } impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { @@ -425,6 +475,7 @@ 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) => { + 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) diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 606c2ed72becc..0ea53c3928027 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; -use rustc_hir::{Local, PatKind}; +use rustc_hir::{Local, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{GenericArgKind, IsSuggestable}; @@ -139,7 +139,8 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { - if !in_external_macro(cx.tcx.sess, local.span) + if matches!(local.source, LocalSource::Normal) + && !in_external_macro(cx.tcx.sess, local.span) && let PatKind::Wild = local.pat.kind && let Some(init) = local.init { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 76e759683145e..b930175c4d89a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -80,6 +80,7 @@ mod as_conversions; mod asm_syntax; mod assertions_on_constants; mod assertions_on_result_states; +mod assigning_clones; mod async_yields_async; mod attrs; mod await_holding_invalid; @@ -1118,6 +1119,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); + store.register_late_pass(|_| Box::new(assigning_clones::AssigningClones)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 58f713d81871f..18f799e875a07 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; +use clippy_utils::usage::local_used_in; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use rustc_ast::ast; use rustc_errors::Applicability; @@ -63,8 +64,9 @@ pub(super) fn check<'tcx>( && get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some() && let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts) && let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts) - - // Source and destination must be different + && !local_used_in(cx, canonical_id, base_left) + && !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(( diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 69f86836775df..b770ad0ddb5cc 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -37,12 +37,7 @@ pub(super) fn check<'tcx>( } } -fn set_diagnostic<'tcx>( - diag: &mut Diag<'_, ()>, - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - found: FoundSigDrop, -) { +fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { if found.lint_suggestion == LintSuggestion::MoveAndClone { // If our suggestion is to move and clone, then we want to leave it to the user to // decide how to address this lint, since it may be that cloning is inappropriate. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index adb696ad5096c..8a24ccea3a1eb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3875,6 +3875,7 @@ declare_clippy_lint! { } declare_clippy_lint! { + /// ### What it does /// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where f is a function or closure that returns the `bool` type. /// /// ### Why is this bad? @@ -3981,6 +3982,7 @@ declare_clippy_lint! { } declare_clippy_lint! { + /// ### What it does /// Checks for the manual creation of C strings (a string with a `NUL` byte at the end), either /// through one of the `CStr` constructor functions, or more plainly by calling `.as_ptr()` /// on a (byte) string literal with a hardcoded `\0` byte at the end. diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index f671517c13461..0879dcd9bcdf6 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -42,7 +42,7 @@ pub(crate) fn check<'tcx>( match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check_op( + let _ = check_op( cx, left, 0, @@ -50,8 +50,7 @@ pub(crate) fn check<'tcx>( peeled_right_span, needs_parenthesis(cx, expr, right), right_is_coerced_to_value, - ); - check_op( + ) || check_op( cx, right, 0, @@ -62,7 +61,7 @@ pub(crate) fn check<'tcx>( ); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check_op( + let _ = check_op( cx, right, 0, @@ -73,7 +72,7 @@ pub(crate) fn check<'tcx>( ); }, BinOpKind::Mul => { - check_op( + let _ = check_op( cx, left, 1, @@ -81,8 +80,18 @@ pub(crate) fn check<'tcx>( peeled_right_span, needs_parenthesis(cx, expr, right), right_is_coerced_to_value, + ) || check_op( + cx, + right, + 1, + expr.span, + peeled_left_span, + Parens::Unneeded, + left_is_coerced_to_value, ); - check_op( + }, + BinOpKind::Div => { + let _ = check_op( cx, right, 1, @@ -92,17 +101,8 @@ pub(crate) fn check<'tcx>( left_is_coerced_to_value, ); }, - BinOpKind::Div => check_op( - cx, - right, - 1, - expr.span, - peeled_left_span, - Parens::Unneeded, - left_is_coerced_to_value, - ), BinOpKind::BitAnd => { - check_op( + let _ = check_op( cx, left, -1, @@ -110,8 +110,7 @@ pub(crate) fn check<'tcx>( peeled_right_span, needs_parenthesis(cx, expr, right), right_is_coerced_to_value, - ); - check_op( + ) || check_op( cx, right, -1, @@ -201,12 +200,12 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } } -fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) { +fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), - _ => return, + _ => return false, }; if match m { 0 => v == 0, @@ -215,8 +214,10 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa _ => unreachable!(), } { span_ineffective_operation(cx, span, arg, parens, is_erased); + return true; } } + false } fn span_ineffective_operation( diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index fecc5a8578ed2..311cbd050a10c 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -21,9 +21,8 @@ pub(super) fn check<'tcx>( // lhs op= l op r if eq_expr_value(cx, lhs, l) { lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, r); - } - // lhs op= l commutative_op r - if is_commutative(op) && eq_expr_value(cx, lhs, r) { + } else if is_commutative(op) && eq_expr_value(cx, lhs, r) { + // lhs op= l commutative_op r lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, l); } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 1df2d5e460232..cf7f730140ceb 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -203,108 +203,106 @@ fn expr_return_none_or_err( } } -impl QuestionMark { - fn inside_try_block(&self) -> bool { - self.try_block_depth_stack.last() > Some(&0) - } - - /// Checks if the given expression on the given context matches the following structure: - /// - /// ```ignore - /// if option.is_none() { - /// return None; - /// } - /// ``` - /// - /// ```ignore - /// if result.is_err() { - /// return result; - /// } - /// ``` - /// - /// If it matches, it will suggest to use the question mark operator instead - fn check_is_none_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if !self.inside_try_block() - && let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) - && !is_else_clause(cx.tcx, expr) - && let ExprKind::MethodCall(segment, caller, ..) = &cond.kind - && let caller_ty = cx.typeck_results().expr_ty(caller) - && let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then) - && (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block)) - { - let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); - let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) - && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); - let sugg = if let Some(else_inner) = r#else { - if eq_expr_value(cx, caller, peel_blocks(else_inner)) { - format!("Some({receiver_str}?)") - } else { - return; - } +/// Checks if the given expression on the given context matches the following structure: +/// +/// ```ignore +/// if option.is_none() { +/// return None; +/// } +/// ``` +/// +/// ```ignore +/// if result.is_err() { +/// return result; +/// } +/// ``` +/// +/// If it matches, it will suggest to use the question mark operator instead +fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) + && !is_else_clause(cx.tcx, expr) + && let ExprKind::MethodCall(segment, caller, ..) = &cond.kind + && let caller_ty = cx.typeck_results().expr_ty(caller) + && let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then) + && (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block)) + { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); + let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); + let sugg = if let Some(else_inner) = r#else { + if eq_expr_value(cx, caller, peel_blocks(else_inner)) { + format!("Some({receiver_str}?)") } else { - format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) - }; + return; + } + } else { + format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) + }; - span_lint_and_sugg( - cx, - QUESTION_MARK, - expr.span, - "this block may be rewritten with the `?` operator", - "replace it with", - sugg, - applicability, - ); - } + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + "replace it with", + sugg, + applicability, + ); } +} - fn check_if_let_some_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if !self.inside_try_block() - && let Some(higher::IfLet { - let_pat, - let_expr, - if_then, - if_else, - .. - }) = higher::IfLet::hir(cx, expr) - && !is_else_clause(cx.tcx, expr) - && let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind - && ddpos.as_opt_usize().is_none() - && let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind - && let caller_ty = cx.typeck_results().expr_ty(let_expr) - && let if_block = IfBlockType::IfLet( - cx.qpath_res(path1, let_pat.hir_id), - caller_ty, - ident.name, - let_expr, - if_then, - if_else, - ) - && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) - || is_early_return(sym::Result, cx, &if_block)) - && if_else - .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) - .filter(|e| *e) - .is_none() - { - let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); - let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); - let sugg = format!( - "{receiver_str}{}?{}", - if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, - if requires_semi { ";" } else { "" } - ); - span_lint_and_sugg( - cx, - QUESTION_MARK, - expr.span, - "this block may be rewritten with the `?` operator", - "replace it with", - sugg, - applicability, - ); - } +fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let Some(higher::IfLet { + let_pat, + let_expr, + if_then, + if_else, + .. + }) = higher::IfLet::hir(cx, expr) + && !is_else_clause(cx.tcx, expr) + && let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind + && ddpos.as_opt_usize().is_none() + && let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind + && let caller_ty = cx.typeck_results().expr_ty(let_expr) + && let if_block = IfBlockType::IfLet( + cx.qpath_res(path1, let_pat.hir_id), + caller_ty, + ident.name, + let_expr, + if_then, + if_else, + ) + && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) + || is_early_return(sym::Result, cx, &if_block)) + && if_else + .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) + .filter(|e| *e) + .is_none() + { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); + let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); + let sugg = format!( + "{receiver_str}{}?{}", + if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, + if requires_semi { ";" } else { "" } + ); + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + "replace it with", + sugg, + applicability, + ); + } +} + +impl QuestionMark { + fn inside_try_block(&self) -> bool { + self.try_block_depth_stack.last() > Some(&0) } } @@ -324,15 +322,18 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !in_constant(cx, stmt.hir_id) { + if !self.inside_try_block() && !in_constant(cx, stmt.hir_id) { check_let_some_else_return_none(cx, stmt); } self.check_manual_let_else(cx, stmt); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !in_constant(cx, expr.hir_id) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) { - self.check_is_none_or_err_and_early_return(cx, expr); - self.check_if_let_some_or_err_and_early_return(cx, expr); + if !self.inside_try_block() + && !in_constant(cx, expr.hir_id) + && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + { + 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_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index e3f0ce2a92206..f61527cc0a9fe 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -159,6 +159,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // ^^ we only want to lint for this call (but we walk up the calls to consider both calls). // without this check, we'd end up linting twice. && !matches!(recv.kind, hir::ExprKind::Call(..)) + // Check if `recv` comes from a macro expansion. If it does, make sure that it's an expansion that is + // the same as the one the call is in. + // For instance, let's assume `x!()` returns a closure: + // B ---v + // x!()() + // ^- A + // The call happens in the expansion `A`, while the closure originates from the expansion `B`. + // We don't want to suggest replacing `x!()()` with `x!()`. + && recv.span.ctxt().outer_expn() == expr.span.ctxt().outer_expn() && let (full_expr, call_depth) = get_parent_call_exprs(cx, expr) && let Some((body, fn_decl, coroutine_kind, params)) = find_innermost_closure(cx, recv, call_depth) // outside macros we lint properly. Inside macros, we lint only ||() style closures. diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index fb000cd7184ec..17b031d5f015c 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -59,24 +59,22 @@ impl EarlyLintPass for RedundantFieldNames { } if let ExprKind::Struct(ref se) = expr.kind { for field in &se.fields { - if field.is_shorthand { - continue; - } - if let ExprKind::Path(None, path) = &field.expr.kind { - if path.segments.len() == 1 - && path.segments[0].ident == field.ident - && path.segments[0].args.is_none() - { - span_lint_and_sugg( - cx, - REDUNDANT_FIELD_NAMES, - field.span, - "redundant field names in struct initialization", - "replace it with", - field.ident.to_string(), - Applicability::MachineApplicable, - ); - } + if !field.is_shorthand + && let ExprKind::Path(None, path) = &field.expr.kind + && let [segment] = path.segments.as_slice() + && segment.args.is_none() + && segment.ident == field.ident + && field.span.eq_ctxt(field.ident.span) + { + span_lint_and_sugg( + cx, + REDUNDANT_FIELD_NAMES, + field.span, + "redundant field names in struct initialization", + "replace it with", + field.ident.to_string(), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 38fd54a0f1e0c..c0e4f3c368a8d 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -109,7 +109,6 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { - self.prev_span = path.span; return; }, }, @@ -117,13 +116,12 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { (ALLOC_INSTEAD_OF_CORE, "alloc", "core") } else { - self.prev_span = path.span; return; } }, _ => return, }; - if path.span != self.prev_span { + if first_segment.ident.span != self.prev_span { span_lint_and_sugg( cx, lint, @@ -133,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { replace_with.to_string(), Applicability::MachineApplicable, ); - self.prev_span = path.span; + self.prev_span = first_segment.ident.span; } } } diff --git a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs index 9fee4c062007f..1af3733ebfa4b 100644 --- a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs +++ b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs @@ -1,12 +1,11 @@ -use clippy_config::msrvs::Msrv; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::source::snippet; +use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{intravisit, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym::thread_local_macro; @@ -57,6 +56,31 @@ impl ThreadLocalInitializerCanBeMadeConst { impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST]); +#[inline] +fn is_thread_local_initializer( + cx: &LateContext<'_>, + fn_kind: rustc_hir::intravisit::FnKind<'_>, + span: rustc_span::Span, +) -> Option { + let macro_def_id = span.source_callee()?.macro_def_id?; + Some( + cx.tcx.is_diagnostic_item(thread_local_macro, macro_def_id) + && matches!(fn_kind, intravisit::FnKind::ItemFn(..)), + ) +} + +#[inline] +fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool { + // Building MIR for `fn`s with unsatisfiable preds results in ICE. + if !fn_has_unsatisfiable_preds(cx, defid) + && let mir = cx.tcx.optimized_mir(defid) + && let Ok(()) = is_min_const_fn(cx.tcx, mir, msrv) + { + return true; + } + false +} + impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { fn check_fn( &mut self, @@ -65,31 +89,32 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { _: &'tcx rustc_hir::FnDecl<'tcx>, body: &'tcx rustc_hir::Body<'tcx>, span: rustc_span::Span, - defid: rustc_span::def_id::LocalDefId, + local_defid: rustc_span::def_id::LocalDefId, ) { - if in_external_macro(cx.sess(), span) - && let Some(callee) = span.source_callee() - && let Some(macro_def_id) = callee.macro_def_id - && cx.tcx.is_diagnostic_item(thread_local_macro, macro_def_id) - && let intravisit::FnKind::ItemFn(..) = fn_kind - // Building MIR for `fn`s with unsatisfiable preds results in ICE. - && !fn_has_unsatisfiable_preds(cx, defid.to_def_id()) - && let mir = cx.tcx.optimized_mir(defid.to_def_id()) - && let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) - // this is the `__init` function emitted by the `thread_local!` macro - // when the `const` keyword is not used. We avoid checking the `__init` directly - // as that is not a public API. - // we know that the function is const-qualifiable, so now we need only to get the - // initializer expression to span-lint it. + let defid = local_defid.to_def_id(); + if self.msrv.meets(msrvs::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST) + && is_thread_local_initializer(cx, fn_kind, span).unwrap_or(false) + // Some implementations of `thread_local!` include an initializer fn. + // In the case of a const initializer, the init fn is also const, + // so we can skip the lint in that case. This occurs only on some + // backends due to conditional compilation: + // https://doc.rust-lang.org/src/std/sys/common/thread_local/mod.rs.html + // for details on this issue, see: + // https://github.com/rust-lang/rust-clippy/pull/12276 + && !cx.tcx.is_const_fn(defid) + && initializer_can_be_made_const(cx, defid, &self.msrv) + // we know that the function is const-qualifiable, so now + // we need only to get the initializer expression to span-lint it. && let ExprKind::Block(block, _) = body.value.kind - && let Some(ret_expr) = block.expr + && let Some(unpeeled) = block.expr + && let ret_expr = peel_blocks(unpeeled) && let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }") && initializer_snippet != "thread_local! { ... }" { span_lint_and_sugg( cx, THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST, - ret_expr.span, + unpeeled.span, "initializer for `thread_local` value can be made `const`", "replace with", format!("const {{ {initializer_snippet} }}"), diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 06de7a1103166..e47b14bf63b21 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -592,7 +592,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | (eager_transmute::check(cx, e, arg, from_ty, to_ty)); if !linted { - transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); + transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg, const_context); } } } diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index bbecc39a8130c..043c9c8860192 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -18,10 +18,12 @@ pub(super) fn check<'tcx>( from_ty_adjusted: bool, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, + const_context: bool, ) -> bool { use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; let mut app = Applicability::MachineApplicable; let mut sugg = match check_cast(cx, e, from_ty, to_ty) { + Some(FnPtrAddrCast | PtrAddrCast) if const_context => return false, Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => { Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) .as_ty(to_ty.to_string()) diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index a285f771f1b1c..7926738d68f1a 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths::ALLOCATOR_GLOBAL; +use clippy_utils::last_path_segment; use clippy_utils::source::snippet; -use clippy_utils::{last_path_segment, match_def_path}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, GenericArg, QPath, TyKind}; +use rustc_hir::{self as hir, GenericArg, LangItem, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -50,7 +49,7 @@ pub(super) fn check<'tcx>( (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { if let TyKind::Path(path) = inner.kind && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { - match_def_path(cx, did, &ALLOCATOR_GLOBAL) + cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) } else { false } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 2f728b62754e1..987f28192a85d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -115,4 +115,3 @@ pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"]; pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; #[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so pub const BOOL_THEN: [&str; 4] = ["core", "bool", "", "then"]; -pub const ALLOCATOR_GLOBAL: [&str; 3] = ["alloc", "alloc", "Global"]; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e74621a0fbbaa..5090d0bd98b16 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -995,7 +995,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // no adjustment needed here, as field projections are handled by the compiler ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() { ty::Adt(..) | ty::Tuple(_) => { - replacement_str = ident_str_with_proj.clone(); + replacement_str.clone_from(&ident_str_with_proj); projections_handled = true; }, _ => (), diff --git a/rust-toolchain b/rust-toolchain index fb038b617dfe6..070b62887d5de 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-02-22" +channel = "nightly-2024-03-07" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/main.rs b/src/main.rs index dffa854177b95..30beaae34d2fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -174,8 +174,13 @@ To allow or deny a lint from the command line you can use cargo clipp You can use tool lints to allow or deny lints from your code, e.g.: #[allow(clippy::needless_lifetimes)] -" - ) + +Manifest Options: + --manifest-path <> Path to Cargo.toml + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date + --offline Run without accessing the network +") } #[cfg(test)] mod tests { diff --git a/tests/compile-test.rs b/tests/compile-test.rs index a088896b6b007..a0c8bf9334c4e 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -138,6 +138,7 @@ fn base_config(test_dir: &str) -> (Config, Args) { "-Aunused", "-Ainternal_features", "-Zui-testing", + "-Zdeduplicate-diagnostics=no", "-Dwarnings", &format!("-Ldependency={}", deps_path.display()), ] diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 0d35a22cd9a4e..141c11ddb47f9 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -54,7 +54,7 @@ fn explore_directory(dir: &Path) -> Vec { let file_prefix = path.file_prefix().unwrap().to_str().unwrap().to_string(); if let Some(ext) = path.extension() { match ext.to_str().unwrap() { - "rs" | "toml" => current_file = file_prefix.clone(), + "rs" | "toml" => current_file.clone_from(&file_prefix), "stderr" | "stdout" => { if file_prefix != current_file { missing_files.push(path.to_str().unwrap().to_string()); diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 9b0db660c9975..400bfd5d9751d 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -5,6 +5,7 @@ //@normalize-stderr-test: "'rustc'" -> "''" //@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc running on " //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" +//@normalize-stderr-test: "this compiler `.*` is outdated" -> "this compiler is outdated" #![deny(clippy::internal)] #![allow(clippy::missing_clippy_version_attribute)] diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index d8b158816c9f8..b84f4e87e073f 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -4,11 +4,14 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: the compiler unexpectedly panicked. this is a bug. -note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml +note: it seems that this compiler is outdated, a newer nightly should have been released in the mean time + | + = note: please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists + = note: if the problem still persists, we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml note: rustc running on -note: compiler flags: -Z ui-testing +note: compiler flags: -Z ui-testing -Z deduplicate-diagnostics=no note: Clippy version: foo diff --git a/tests/ui-internal/disallow_span_lint.rs b/tests/ui-internal/disallow_span_lint.rs index c5029bbd9a425..5a2a868ed3ec6 100644 --- a/tests/ui-internal/disallow_span_lint.rs +++ b/tests/ui-internal/disallow_span_lint.rs @@ -10,22 +10,11 @@ use rustc_hir::hir_id::HirId; use rustc_lint::{Lint, LintContext}; use rustc_middle::ty::TyCtxt; -pub fn a( - cx: impl LintContext, - lint: &'static Lint, - span: impl Into, - msg: impl Into) -{ +pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into, msg: impl Into) { cx.span_lint(lint, span, msg, |_| {}); } -pub fn b( - tcx: TyCtxt<'_>, - lint: &'static Lint, - hir_id: HirId, - span: impl Into, - msg: impl Into, -) { +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, msg, |_| {}); } diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index 1a1ad26290c29..ae5d6843406a7 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -8,7 +8,7 @@ LL | cx.span_lint(lint, span, msg, |_| {}); = 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:24:5 + --> tests/ui-internal/disallow_span_lint.rs:18:5 | LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 87f84d8f7dd41..37d6905573706 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 @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:266:19 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:271:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:270:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:275:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = 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:274:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = 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:274:29 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = 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:274:48 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = 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:278:18 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = 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:278:37 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = 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:282:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = 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:287:19 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:292:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = 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:293:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:298:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = 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:297:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:302:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = 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:301:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:306:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = 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:311:8 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:316:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = 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:317:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:322:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:325:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:330:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = 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:329:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:334:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = 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:339:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:344:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = 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:343:20 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:348:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:350:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:355:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:357:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:362:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:378:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:383:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:411:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:416:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:417:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:422:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:463:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:468:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:467:19 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:472:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:471:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:475:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:480:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:474:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:479:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:481:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:483:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:488:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:492:1 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:497:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = 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:505:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:510:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -291,13 +291,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:504:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:509:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:506:12 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:511:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -305,7 +305,7 @@ LL | if unsafe { true } { = 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:509:23 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:514:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ 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 5ffe73f5a2f16..400fde997e9fc 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 @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:266:19 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:271:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:270:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:275:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = 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:274:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = 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:274:29 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = 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:274:48 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = 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:278:18 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = 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:278:37 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = 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:282:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = 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:287:19 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:292:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = 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:293:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:298:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = 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:297:14 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:302:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = 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:301:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:306:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = 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:311:8 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:316:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = 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:317:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:322:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:325:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:330:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = 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:329:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:334:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = 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:339:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:344:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = 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:343:20 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:348:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:350:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:355:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:357:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:362:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:378:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:383:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:411:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:416:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:417:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:422:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:463:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:468:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:467:19 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:472:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:471:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:475:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:480:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:474:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:479:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:481:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:483:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:488:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:492:1 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:497:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = 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:502:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:9 | LL | unsafe {}; | ^^^^^^^^^ @@ -287,7 +287,7 @@ LL | unsafe {}; = 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:505:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:510:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -299,13 +299,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:504:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:509:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:506:12 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:511:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL | if unsafe { true } { = 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:509:23 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:514:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ @@ -321,7 +321,7 @@ LL | let bar = unsafe {}; = 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:527:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:532:9 | LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -329,7 +329,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; = 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:531:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:536:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() = 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:535:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:540:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -345,7 +345,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() = 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:541:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:546:5 | LL | unsafe {} | ^^^^^^^^^ @@ -353,7 +353,7 @@ LL | unsafe {} = 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:545:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:550:5 | LL | unsafe { | ^^^^^^^^ @@ -361,7 +361,7 @@ LL | unsafe { = 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:552:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:557:9 | LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -369,7 +369,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; = 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:557:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:562:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -377,7 +377,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() = 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:563:9 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:568:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -385,7 +385,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() = 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:568:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:573:5 | LL | unsafe {} | ^^^^^^^^^ 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 8997073c8a555..e5ef9d35fb6b8 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -4,7 +4,12 @@ //@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] -#![allow(deref_nullptr, non_local_definitions, clippy::let_unit_value, clippy::missing_safety_doc)] +#![allow( + deref_nullptr, + non_local_definitions, + clippy::let_unit_value, + clippy::missing_safety_doc +)] #![feature(lint_reasons)] extern crate proc_macro_unsafe; diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed new file mode 100644 index 0000000000000..c66e0c1f6028c --- /dev/null +++ b/tests/ui/assigning_clones.fixed @@ -0,0 +1,222 @@ +#![allow(unused)] +#![allow(clippy::redundant_clone)] +#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 +#![allow(clippy::needless_late_init)] +#![allow(clippy::box_collection)] +#![warn(clippy::assigning_clones)] + +use std::borrow::ToOwned; +use std::ops::{Add, Deref, DerefMut}; + +// Clone +pub struct HasCloneFrom; + +impl Clone for HasCloneFrom { + fn clone(&self) -> Self { + Self + } + fn clone_from(&mut self, source: &Self) { + *self = HasCloneFrom; + } +} + +fn clone_method_rhs_val(mut_thing: &mut HasCloneFrom, value_thing: HasCloneFrom) { + mut_thing.clone_from(&value_thing); +} + +fn clone_method_rhs_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing.clone_from(ref_thing); +} + +fn clone_method_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing.clone_from(ref_thing); +} + +fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_function_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(&mut mut_thing, ref_thing); +} + +fn clone_function_through_trait(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_function_through_type(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_function_fully_qualified(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_method_lhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be kept as necessary for a receiver + (mut_thing + &mut HasCloneFrom).clone_from(ref_thing); +} + +fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be removed since they are not needed in a function argument + mut_thing.clone_from(ref_thing + ref_thing); +} + +fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { + let mut a = HasCloneFrom; + for _ in 1..10 { + a.clone_from(&b); + } + a +} + +fn assign_to_late_init_mut_var(b: HasCloneFrom) { + let mut a; + a = HasCloneFrom; + a = b.clone(); +} + +fn assign_to_uninit_var(b: HasCloneFrom) { + let a; + a = b.clone(); +} + +fn assign_to_uninit_mut_var(b: HasCloneFrom) { + let mut a; + a = b.clone(); +} + +#[derive(Clone)] +pub struct HasDeriveClone; + +fn ignore_derive_clone(a: &mut HasDeriveClone, b: &HasDeriveClone) { + // Should not be linted, since the Clone impl is derived + *a = b.clone(); +} + +pub struct HasCloneImpl; + +impl Clone for HasCloneImpl { + fn clone(&self) -> Self { + Self + } +} + +fn ignore_missing_clone_from(a: &mut HasCloneImpl, b: &HasCloneImpl) { + // Should not be linted, since the Clone impl doesn't override clone_from + *a = b.clone(); +} + +struct FakeClone; + +impl FakeClone { + /// This looks just like `Clone::clone` + fn clone(&self) -> Self { + FakeClone + } +} + +fn ignore_fake_clone() { + let mut a = FakeClone; + let b = FakeClone; + // Should not be linted, since the Clone impl doesn't come from std + a = b.clone(); +} + +fn ignore_generic_clone(a: &mut T, b: &T) { + // Should not be linted, since we don't know the actual clone impl + *a = b.clone(); +} + +macro_rules! clone_inside { + ($a:expr, $b: expr) => { + $a = $b.clone(); + }; +} + +fn clone_inside_macro() { + let mut a = String::new(); + let b = String::new(); + clone_inside!(a, b); +} + +// ToOwned +fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { + ref_str.clone_into(mut_string); +} + +fn owned_method_val(mut mut_string: String, ref_str: &str) { + ref_str.clone_into(&mut mut_string); +} + +struct HasDeref { + a: String, +} + +impl Deref for HasDeref { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.a + } +} + +impl DerefMut for HasDeref { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.a + } +} + +fn owned_method_box(mut_box_string: &mut Box, ref_str: &str) { + ref_str.clone_into(&mut (*mut_box_string)); +} + +fn owned_method_deref(mut_box_string: &mut HasDeref, ref_str: &str) { + ref_str.clone_into(&mut (*mut_box_string)); +} + +fn owned_function_mut_ref(mut_thing: &mut String, ref_str: &str) { + ToOwned::clone_into(ref_str, mut_thing); +} + +fn owned_function_val(mut mut_thing: String, ref_str: &str) { + ToOwned::clone_into(ref_str, &mut mut_thing); +} + +struct FakeToOwned; +impl FakeToOwned { + /// This looks just like `ToOwned::to_owned` + fn to_owned(&self) -> Self { + FakeToOwned + } +} + +fn fake_to_owned() { + let mut a = FakeToOwned; + let b = FakeToOwned; + // Should not be linted, since the ToOwned impl doesn't come from std + a = b.to_owned(); +} + +fn main() {} + +/// Trait implementation to allow producing a `Thing` with a low-precedence expression. +impl Add for HasCloneFrom { + type Output = Self; + fn add(self, _: HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&Thing` with a low-precedence expression. +impl<'a> Add for &'a HasCloneFrom { + type Output = Self; + fn add(self, _: &'a HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&mut Thing` with a low-precedence expression. +impl<'a> Add for &'a mut HasCloneFrom { + type Output = Self; + fn add(self, _: &'a mut HasCloneFrom) -> Self { + self + } +} diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs new file mode 100644 index 0000000000000..b9f994d3e034c --- /dev/null +++ b/tests/ui/assigning_clones.rs @@ -0,0 +1,222 @@ +#![allow(unused)] +#![allow(clippy::redundant_clone)] +#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 +#![allow(clippy::needless_late_init)] +#![allow(clippy::box_collection)] +#![warn(clippy::assigning_clones)] + +use std::borrow::ToOwned; +use std::ops::{Add, Deref, DerefMut}; + +// Clone +pub struct HasCloneFrom; + +impl Clone for HasCloneFrom { + fn clone(&self) -> Self { + Self + } + fn clone_from(&mut self, source: &Self) { + *self = HasCloneFrom; + } +} + +fn clone_method_rhs_val(mut_thing: &mut HasCloneFrom, value_thing: HasCloneFrom) { + *mut_thing = value_thing.clone(); +} + +fn clone_method_rhs_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = ref_thing.clone(); +} + +fn clone_method_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing = ref_thing.clone(); +} + +fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = Clone::clone(ref_thing); +} + +fn clone_function_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing = Clone::clone(ref_thing); +} + +fn clone_function_through_trait(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = Clone::clone(ref_thing); +} + +fn clone_function_through_type(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = HasCloneFrom::clone(ref_thing); +} + +fn clone_function_fully_qualified(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = ::clone(ref_thing); +} + +fn clone_method_lhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be kept as necessary for a receiver + *(mut_thing + &mut HasCloneFrom) = ref_thing.clone(); +} + +fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be removed since they are not needed in a function argument + *mut_thing = (ref_thing + ref_thing).clone(); +} + +fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { + let mut a = HasCloneFrom; + for _ in 1..10 { + a = b.clone(); + } + a +} + +fn assign_to_late_init_mut_var(b: HasCloneFrom) { + let mut a; + a = HasCloneFrom; + a = b.clone(); +} + +fn assign_to_uninit_var(b: HasCloneFrom) { + let a; + a = b.clone(); +} + +fn assign_to_uninit_mut_var(b: HasCloneFrom) { + let mut a; + a = b.clone(); +} + +#[derive(Clone)] +pub struct HasDeriveClone; + +fn ignore_derive_clone(a: &mut HasDeriveClone, b: &HasDeriveClone) { + // Should not be linted, since the Clone impl is derived + *a = b.clone(); +} + +pub struct HasCloneImpl; + +impl Clone for HasCloneImpl { + fn clone(&self) -> Self { + Self + } +} + +fn ignore_missing_clone_from(a: &mut HasCloneImpl, b: &HasCloneImpl) { + // Should not be linted, since the Clone impl doesn't override clone_from + *a = b.clone(); +} + +struct FakeClone; + +impl FakeClone { + /// This looks just like `Clone::clone` + fn clone(&self) -> Self { + FakeClone + } +} + +fn ignore_fake_clone() { + let mut a = FakeClone; + let b = FakeClone; + // Should not be linted, since the Clone impl doesn't come from std + a = b.clone(); +} + +fn ignore_generic_clone(a: &mut T, b: &T) { + // Should not be linted, since we don't know the actual clone impl + *a = b.clone(); +} + +macro_rules! clone_inside { + ($a:expr, $b: expr) => { + $a = $b.clone(); + }; +} + +fn clone_inside_macro() { + let mut a = String::new(); + let b = String::new(); + clone_inside!(a, b); +} + +// ToOwned +fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { + *mut_string = ref_str.to_owned(); +} + +fn owned_method_val(mut mut_string: String, ref_str: &str) { + mut_string = ref_str.to_owned(); +} + +struct HasDeref { + a: String, +} + +impl Deref for HasDeref { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.a + } +} + +impl DerefMut for HasDeref { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.a + } +} + +fn owned_method_box(mut_box_string: &mut Box, ref_str: &str) { + **mut_box_string = ref_str.to_owned(); +} + +fn owned_method_deref(mut_box_string: &mut HasDeref, ref_str: &str) { + **mut_box_string = ref_str.to_owned(); +} + +fn owned_function_mut_ref(mut_thing: &mut String, ref_str: &str) { + *mut_thing = ToOwned::to_owned(ref_str); +} + +fn owned_function_val(mut mut_thing: String, ref_str: &str) { + mut_thing = ToOwned::to_owned(ref_str); +} + +struct FakeToOwned; +impl FakeToOwned { + /// This looks just like `ToOwned::to_owned` + fn to_owned(&self) -> Self { + FakeToOwned + } +} + +fn fake_to_owned() { + let mut a = FakeToOwned; + let b = FakeToOwned; + // Should not be linted, since the ToOwned impl doesn't come from std + a = b.to_owned(); +} + +fn main() {} + +/// Trait implementation to allow producing a `Thing` with a low-precedence expression. +impl Add for HasCloneFrom { + type Output = Self; + fn add(self, _: HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&Thing` with a low-precedence expression. +impl<'a> Add for &'a HasCloneFrom { + type Output = Self; + fn add(self, _: &'a HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&mut Thing` with a low-precedence expression. +impl<'a> Add for &'a mut HasCloneFrom { + type Output = Self; + fn add(self, _: &'a mut HasCloneFrom) -> Self { + self + } +} diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr new file mode 100644 index 0000000000000..b76323f360634 --- /dev/null +++ b/tests/ui/assigning_clones.stderr @@ -0,0 +1,107 @@ +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:24:5 + | +LL | *mut_thing = value_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(&value_thing)` + | + = note: `-D clippy::assigning-clones` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::assigning_clones)]` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:28:5 + | +LL | *mut_thing = ref_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:32:5 + | +LL | mut_thing = ref_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:36:5 + | +LL | *mut_thing = Clone::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:40:5 + | +LL | mut_thing = Clone::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:44:5 + | +LL | *mut_thing = Clone::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:48:5 + | +LL | *mut_thing = HasCloneFrom::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:52:5 + | +LL | *mut_thing = ::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:57:5 + | +LL | *(mut_thing + &mut HasCloneFrom) = ref_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `(mut_thing + &mut HasCloneFrom).clone_from(ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:62:5 + | +LL | *mut_thing = (ref_thing + ref_thing).clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:68:9 + | +LL | a = b.clone(); + | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:145:5 + | +LL | *mut_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:149:5 + | +LL | mut_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:170:5 + | +LL | **mut_box_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:174:5 + | +LL | **mut_box_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:178:5 + | +LL | *mut_thing = ToOwned::to_owned(ref_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:182:5 + | +LL | mut_thing = ToOwned::to_owned(ref_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` + +error: aborting due to 17 previous errors + diff --git a/tests/ui/crashes/unreachable-array-or-slice.rs b/tests/ui/crashes/unreachable-array-or-slice.rs index b56abccbd4112..e920e58d6ec6d 100644 --- a/tests/ui/crashes/unreachable-array-or-slice.rs +++ b/tests/ui/crashes/unreachable-array-or-slice.rs @@ -3,6 +3,6 @@ struct Foo(isize, isize, isize, isize); pub fn main() { let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5); match [5, 5, 5, 5] { - [..] => { } + [..] => {}, } } diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index aa7a95405e02f..6f42487bbf492 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -153,4 +153,30 @@ pub enum MissingEqNonExhaustive3 { Bar, } +mod struct_gen { + // issue 9413 + pub trait Group { + type Element: Eq + PartialEq; + } + + pub trait Suite { + type Group: Group; + } + + #[derive(PartialEq, Eq)] + //~^ ERROR: you are deriving `PartialEq` and can implement `Eq` + pub struct Foo(::Element); + + #[derive(PartialEq, Eq)] + pub struct Bar(i32, ::Element); + + // issue 9319 + #[derive(PartialEq, Eq)] + //~^ ERROR: you are deriving `PartialEq` and can implement `Eq` + pub struct Oof(T); + + #[derive(PartialEq, Eq)] + pub struct Rab(T); +} + fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs index 90ac7a7989e00..24f687c6c9db9 100644 --- a/tests/ui/derive_partial_eq_without_eq.rs +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -153,4 +153,30 @@ pub enum MissingEqNonExhaustive3 { Bar, } +mod struct_gen { + // issue 9413 + pub trait Group { + type Element: Eq + PartialEq; + } + + pub trait Suite { + type Group: Group; + } + + #[derive(PartialEq)] + //~^ ERROR: you are deriving `PartialEq` and can implement `Eq` + pub struct Foo(::Element); + + #[derive(PartialEq, Eq)] + pub struct Bar(i32, ::Element); + + // issue 9319 + #[derive(PartialEq)] + //~^ ERROR: you are deriving `PartialEq` and can implement `Eq` + pub struct Oof(T); + + #[derive(PartialEq, Eq)] + pub struct Rab(T); +} + fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.stderr b/tests/ui/derive_partial_eq_without_eq.stderr index 42b9895121ff5..3d92112dc36d4 100644 --- a/tests/ui/derive_partial_eq_without_eq.stderr +++ b/tests/ui/derive_partial_eq_without_eq.stderr @@ -67,5 +67,17 @@ error: you are deriving `PartialEq` and can implement `Eq` LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` -error: aborting due to 11 previous errors +error: you are deriving `PartialEq` and can implement `Eq` + --> tests/ui/derive_partial_eq_without_eq.rs:166:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> tests/ui/derive_partial_eq_without_eq.rs:174:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: aborting due to 13 previous errors diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 16f7bafd44b78..178d4a1aa2bbb 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -230,3 +230,8 @@ fn issue_11568() {} /// There is no try (`do()` or `do_not()`). fn parenthesized_word() {} + +/// `ABes` +/// OSes +/// UXes +fn plural_acronym_test() {} diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 4058b2bad74f7..01edb44c64c1d 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -230,3 +230,8 @@ fn issue_11568() {} /// There is no try (do() or do_not()). fn parenthesized_word() {} + +/// ABes +/// OSes +/// UXes +fn plural_acronym_test() {} diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index 7db9ff2b83b69..ac876306b39c7 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -341,5 +341,16 @@ help: try LL | /// There is no try (do() or `do_not()`). | ~~~~~~~~~~ -error: aborting due to 31 previous errors +error: item in documentation is missing backticks + --> tests/ui/doc/doc-fixable.rs:234:5 + | +LL | /// ABes + | ^^^^ + | +help: try + | +LL | /// `ABes` + | ~~~~~~ + +error: aborting due to 32 previous errors diff --git a/tests/ui/doc/issue_9473.fixed b/tests/ui/doc/issue_9473.fixed new file mode 100644 index 0000000000000..276ce7620ca76 --- /dev/null +++ b/tests/ui/doc/issue_9473.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::doc_markdown)] + +// Should not warn! +/// Blah blah blah [FooBar]<[FooBar]>. +pub struct Foo(u32); + +// Should warn. +/// Blah blah blah [FooBar]<[FooBar]>[`FooBar`]. +pub struct FooBar(u32); diff --git a/tests/ui/doc/issue_9473.rs b/tests/ui/doc/issue_9473.rs new file mode 100644 index 0000000000000..52527f7106d16 --- /dev/null +++ b/tests/ui/doc/issue_9473.rs @@ -0,0 +1,9 @@ +#![warn(clippy::doc_markdown)] + +// Should not warn! +/// Blah blah blah [FooBar]<[FooBar]>. +pub struct Foo(u32); + +// Should warn. +/// Blah blah blah [FooBar]<[FooBar]>[FooBar]. +pub struct FooBar(u32); diff --git a/tests/ui/doc/issue_9473.stderr b/tests/ui/doc/issue_9473.stderr new file mode 100644 index 0000000000000..35aa2884cc115 --- /dev/null +++ b/tests/ui/doc/issue_9473.stderr @@ -0,0 +1,15 @@ +error: item in documentation is missing backticks + --> tests/ui/doc/issue_9473.rs:8:58 + | +LL | /// Blah blah blah [FooBar]<[FooBar]>[FooBar]. + | ^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]` +help: try + | +LL | /// Blah blah blah [FooBar]<[FooBar]>[`FooBar`]. + | ~~~~~~~~ + +error: aborting due to 1 previous error + diff --git a/tests/ui/else_if_without_else.rs b/tests/ui/else_if_without_else.rs index eb5e2266540c9..e7786f7dd27da 100644 --- a/tests/ui/else_if_without_else.rs +++ b/tests/ui/else_if_without_else.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::all)] #![warn(clippy::else_if_without_else)] diff --git a/tests/ui/else_if_without_else.stderr b/tests/ui/else_if_without_else.stderr index 80355cb2dba6a..3bb840f39e78f 100644 --- a/tests/ui/else_if_without_else.stderr +++ b/tests/ui/else_if_without_else.stderr @@ -1,5 +1,5 @@ error: `if` expression with an `else if`, but without a final `else` - --> tests/ui/else_if_without_else.rs:45:12 + --> tests/ui/else_if_without_else.rs:47:12 | LL | } else if bla2() { | ____________^ @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::else_if_without_else)]` error: `if` expression with an `else if`, but without a final `else` - --> tests/ui/else_if_without_else.rs:54:12 + --> tests/ui/else_if_without_else.rs:56:12 | LL | } else if bla3() { | ____________^ diff --git a/tests/ui/empty_docs.rs b/tests/ui/empty_docs.rs index c47a603e71b43..272fab7d5ca1a 100644 --- a/tests/ui/empty_docs.rs +++ b/tests/ui/empty_docs.rs @@ -1,5 +1,7 @@ #![allow(unused)] #![warn(clippy::empty_docs)] +#![allow(clippy::mixed_attributes_style)] + mod outer { //! diff --git a/tests/ui/empty_docs.stderr b/tests/ui/empty_docs.stderr index 3f1d071fb13ce..f12aead6aa75e 100644 --- a/tests/ui/empty_docs.stderr +++ b/tests/ui/empty_docs.stderr @@ -1,5 +1,5 @@ error: empty doc comment - --> tests/ui/empty_docs.rs:4:5 + --> tests/ui/empty_docs.rs:6:5 | LL | //! | ^^^ @@ -9,7 +9,7 @@ LL | //! = help: to override `-D warnings` add `#[allow(clippy::empty_docs)]` error: empty doc comment - --> tests/ui/empty_docs.rs:12:5 + --> tests/ui/empty_docs.rs:14:5 | LL | /// | ^^^ @@ -17,7 +17,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:14:9 + --> tests/ui/empty_docs.rs:16:9 | LL | /// | ^^^ @@ -25,7 +25,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:25:5 + --> tests/ui/empty_docs.rs:27:5 | LL | #[doc = ""] | ^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | #[doc = ""] = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:28:5 + --> tests/ui/empty_docs.rs:30:5 | LL | / #[doc = ""] LL | | #[doc = ""] @@ -42,7 +42,7 @@ LL | | #[doc = ""] = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:35:5 + --> tests/ui/empty_docs.rs:37:5 | LL | /// | ^^^ @@ -50,7 +50,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:48:13 + --> tests/ui/empty_docs.rs:50:13 | LL | /*! */ | ^^^^^^ @@ -58,7 +58,7 @@ LL | /*! */ = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:56:13 + --> tests/ui/empty_docs.rs:58:13 | LL | /// | ^^^ @@ -66,7 +66,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:64:9 + --> tests/ui/empty_docs.rs:66:9 | LL | /// | ^^^ diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index 4099fe7e1393e..71ec13f461060 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -165,4 +165,15 @@ pub fn issue_10331() { } } +/// Issue 11935 +/// Do not suggest using entries if the map is used inside the `insert` expression. +pub fn issue_11935() { + let mut counts: HashMap = HashMap::new(); + if !counts.contains_key(&1) { + counts.insert(1, 1); + } else { + counts.insert(1, counts.get(&1).unwrap() + 1); + } +} + fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index 409be0aa06019..86092b7c055a7 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -169,4 +169,15 @@ pub fn issue_10331() { } } +/// Issue 11935 +/// Do not suggest using entries if the map is used inside the `insert` expression. +pub fn issue_11935() { + let mut counts: HashMap = HashMap::new(); + if !counts.contains_key(&1) { + counts.insert(1, 1); + } else { + counts.insert(1, counts.get(&1).unwrap() + 1); + } +} + fn main() {} diff --git a/tests/ui/explicit_iter_loop.fixed b/tests/ui/explicit_iter_loop.fixed index 06229a52a18c4..f38b34c5a7c47 100644 --- a/tests/ui/explicit_iter_loop.fixed +++ b/tests/ui/explicit_iter_loop.fixed @@ -6,7 +6,7 @@ clippy::deref_addrof, clippy::unnecessary_mut_passed, dead_code, - non_local_definitions, + non_local_definitions )] use core::slice; diff --git a/tests/ui/explicit_iter_loop.rs b/tests/ui/explicit_iter_loop.rs index c2bf45ab2e990..2e701ada5ac6d 100644 --- a/tests/ui/explicit_iter_loop.rs +++ b/tests/ui/explicit_iter_loop.rs @@ -6,7 +6,7 @@ clippy::deref_addrof, clippy::unnecessary_mut_passed, dead_code, - non_local_definitions, + non_local_definitions )] use core::slice; diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 2045b1eebcd7f..620cffb4f04da 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -2,6 +2,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::field_reassign_with_default)] +#![allow(clippy::assigning_clones)] #[macro_use] extern crate proc_macro_derive; diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index bc03283492085..ae909475c6f2c 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,11 +1,11 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:56:5 + --> tests/ui/field_reassign_with_default.rs:57:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:55:5 + --> tests/ui/field_reassign_with_default.rs:56:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,121 +13,121 @@ LL | let mut a: A = Default::default(); = help: to override `-D warnings` add `#[allow(clippy::field_reassign_with_default)]` error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:96:5 + --> tests/ui/field_reassign_with_default.rs:97:5 | LL | a.j = 43; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:95:5 + --> tests/ui/field_reassign_with_default.rs:96:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:101:5 + --> tests/ui/field_reassign_with_default.rs:102:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:100:5 + --> tests/ui/field_reassign_with_default.rs:101:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:107:5 + --> tests/ui/field_reassign_with_default.rs:108:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:106:5 + --> tests/ui/field_reassign_with_default.rs:107:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:117:5 + --> tests/ui/field_reassign_with_default.rs:118:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:116:5 + --> tests/ui/field_reassign_with_default.rs:117:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:121:5 + --> tests/ui/field_reassign_with_default.rs:122:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:120:5 + --> tests/ui/field_reassign_with_default.rs:121:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:143:5 + --> tests/ui/field_reassign_with_default.rs:144:5 | LL | a.i = vec![1]; | ^^^^^^^^^^^^^^ | note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:142:5 + --> tests/ui/field_reassign_with_default.rs:143:5 | LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:161:5 + --> tests/ui/field_reassign_with_default.rs:162:5 | LL | a.i = true; | ^^^^^^^^^^^ | note: consider initializing the variable with `Wrapper:: { i: true }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:160:5 + --> tests/ui/field_reassign_with_default.rs:161:5 | LL | let mut a: Wrapper = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:164:5 + --> tests/ui/field_reassign_with_default.rs:165:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `WrapperMulti:: { i: 42, ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:163:5 + --> tests/ui/field_reassign_with_default.rs:164:5 | LL | let mut a: WrapperMulti = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:235:13 + --> tests/ui/field_reassign_with_default.rs:236:13 | LL | f.name = name.len(); | ^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:234:13 + --> tests/ui/field_reassign_with_default.rs:235:13 | LL | let mut f = ImplDropAllCopy::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:251:13 + --> tests/ui/field_reassign_with_default.rs:252:13 | LL | f.name = name.len(); | ^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:250:13 + --> tests/ui/field_reassign_with_default.rs:251:13 | LL | let mut f = NoDropAllCopy::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index 660e9a21b181a..b18d8560f6f8b 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -134,7 +134,7 @@ fn main() { //~^ ERROR: this operation has no effect f(if b { 1 } else { 2 } + 3); //~^ ERROR: this operation has no effect - + const _: i32 = { 2 * 4 } + 3; //~^ ERROR: this operation has no effect const _: i32 = { 1 + 2 * 3 } + 3; diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index bbef531e9dc05..f1f01b424478c 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -134,7 +134,7 @@ fn main() { //~^ ERROR: this operation has no effect f(0 + if b { 1 } else { 2 } + 3); //~^ ERROR: this operation has no effect - + const _: i32 = { 2 * 4 } + 0 + 3; //~^ ERROR: this operation has no effect const _: i32 = 0 + { 1 + 2 * 3 } + 3; diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 2ababad7fc758..27ee2f91594bf 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![feature(inline_const)] #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 06a4defcb9314..5f62ec9b55655 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,5 +1,5 @@ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:14:20 + --> tests/ui/indexing_slicing_index.rs:16:20 | LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^ @@ -10,19 +10,19 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error[E0080]: evaluation of `main::{constant#3}` failed - --> tests/ui/indexing_slicing_index.rs:46:14 + --> tests/ui/indexing_slicing_index.rs:48:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 note: erroneous constant encountered - --> tests/ui/indexing_slicing_index.rs:46:5 + --> tests/ui/indexing_slicing_index.rs:48:5 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:27:5 + --> tests/ui/indexing_slicing_index.rs:29:5 | LL | x[index]; | ^^^^^^^^ @@ -30,7 +30,7 @@ LL | x[index]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:30:5 + --> tests/ui/indexing_slicing_index.rs:32:5 | LL | x[4]; | ^^^^ @@ -39,13 +39,13 @@ LL | x[4]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:32:5 + --> tests/ui/indexing_slicing_index.rs:34:5 | LL | x[1 << 3]; | ^^^^^^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:43:14 + --> tests/ui/indexing_slicing_index.rs:45:14 | LL | const { &ARR[idx()] }; | ^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | const { &ARR[idx()] }; = note: the suggestion might not be applicable in constant blocks error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:46:14 + --> tests/ui/indexing_slicing_index.rs:48:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ @@ -63,13 +63,13 @@ LL | const { &ARR[idx4()] }; = note: the suggestion might not be applicable in constant blocks error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:53:5 + --> tests/ui/indexing_slicing_index.rs:55:5 | LL | y[4]; | ^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:56:5 + --> tests/ui/indexing_slicing_index.rs:58:5 | LL | v[0]; | ^^^^ @@ -77,7 +77,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:58:5 + --> tests/ui/indexing_slicing_index.rs:60:5 | LL | v[10]; | ^^^^^ @@ -85,7 +85,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:60:5 + --> tests/ui/indexing_slicing_index.rs:62:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -93,13 +93,13 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:68:5 + --> tests/ui/indexing_slicing_index.rs:70:5 | LL | x[N]; | ^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:71:5 + --> tests/ui/indexing_slicing_index.rs:73:5 | LL | v[N]; | ^^^^ @@ -107,7 +107,7 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:73:5 + --> tests/ui/indexing_slicing_index.rs:75:5 | LL | v[M]; | ^^^^ @@ -115,7 +115,7 @@ LL | v[M]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:77:13 + --> tests/ui/indexing_slicing_index.rs:79:13 | LL | let _ = x[4]; | ^^^^ diff --git a/tests/ui/let_underscore_untyped.rs b/tests/ui/let_underscore_untyped.rs index bd94a3ada18b9..6275b6f970a1c 100644 --- a/tests/ui/let_underscore_untyped.rs +++ b/tests/ui/let_underscore_untyped.rs @@ -73,3 +73,5 @@ fn main() { #[allow(clippy::let_underscore_untyped)] let _ = a(); } + +async fn dont_lint_async_prototype(_: u8) {} diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 5d94660ec89ad..1fb252e3f97b6 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -1,3 +1,4 @@ +#![feature(try_blocks)] #![allow(unused_braces, unused_variables, dead_code)] #![allow( clippy::collapsible_else_if, @@ -446,3 +447,12 @@ struct U { w: T, x: T, } + +fn issue12337() { + // We want to generally silence question_mark lints within try blocks, since `?` has different + // behavior to `return`, and question_mark calls into manual_let_else logic, so make sure that + // we still emit a lint for manual_let_else + let _: Option<()> = try { + let v = if let Some(v_some) = g() { v_some } else { return }; + }; +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index b6433fc460c92..7012c6b889144 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -1,5 +1,5 @@ error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:27:5 + --> tests/ui/manual_let_else.rs:28:5 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` @@ -8,7 +8,7 @@ LL | let v = if let Some(v_some) = g() { v_some } else { return }; = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:30:5 + --> tests/ui/manual_let_else.rs:31:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -26,7 +26,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:37:5 + --> tests/ui/manual_let_else.rs:38:5 | LL | / let v = if let Some(v) = g() { LL | | @@ -47,25 +47,25 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:49:9 + --> tests/ui/manual_let_else.rs:50:9 | LL | let v = if let Some(v_some) = g() { v_some } else { continue }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:51:9 + --> tests/ui/manual_let_else.rs:52:9 | LL | let v = if let Some(v_some) = g() { v_some } else { break }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:56:5 + --> tests/ui/manual_let_else.rs:57:5 | LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:60:5 + --> tests/ui/manual_let_else.rs:61:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -83,7 +83,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:68:5 + --> tests/ui/manual_let_else.rs:69:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -101,7 +101,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:76:5 + --> tests/ui/manual_let_else.rs:77:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -121,7 +121,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:85:5 + --> tests/ui/manual_let_else.rs:86:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -141,7 +141,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:94:5 + --> tests/ui/manual_let_else.rs:95:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -168,7 +168,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:110:5 + --> tests/ui/manual_let_else.rs:111:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -190,7 +190,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:121:5 + --> tests/ui/manual_let_else.rs:122:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -217,7 +217,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:137:5 + --> tests/ui/manual_let_else.rs:138:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -239,7 +239,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:148:5 + --> tests/ui/manual_let_else.rs:149:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -257,7 +257,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:156:5 + --> tests/ui/manual_let_else.rs:157:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -278,7 +278,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:166:5 + --> tests/ui/manual_let_else.rs:167:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -299,7 +299,7 @@ LL + } }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:176:5 + --> tests/ui/manual_let_else.rs:177:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -328,7 +328,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:194:5 + --> tests/ui/manual_let_else.rs:195:5 | LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { LL | | @@ -346,7 +346,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:202:5 + --> tests/ui/manual_let_else.rs:203:5 | LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) { LL | | @@ -364,7 +364,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:212:13 + --> tests/ui/manual_let_else.rs:213:13 | LL | let $n = if let Some(v) = $e { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };` @@ -375,19 +375,19 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:221:5 + --> tests/ui/manual_let_else.rs:222:5 | LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:225:5 + --> tests/ui/manual_let_else.rs:226:5 | LL | let mut v = if let Variant::B(b) = e() { b } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:230:5 + --> tests/ui/manual_let_else.rs:231:5 | LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { LL | | @@ -405,19 +405,19 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:237:5 + --> tests/ui/manual_let_else.rs:238:5 | LL | let v = if let Variant::A(.., a) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:241:5 + --> tests/ui/manual_let_else.rs:242:5 | LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:245:5 + --> tests/ui/manual_let_else.rs:246:5 | LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -435,7 +435,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:253:5 + --> tests/ui/manual_let_else.rs:254:5 | LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -453,7 +453,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:261:5 + --> tests/ui/manual_let_else.rs:262:5 | LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::>> { LL | | @@ -471,7 +471,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:378:5 + --> tests/ui/manual_let_else.rs:379:5 | LL | / let _ = match ff { LL | | @@ -480,5 +480,11 @@ LL | | _ => macro_call!(), LL | | }; | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` -error: aborting due to 30 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:456:9 + | +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 diff --git a/tests/ui/manual_memcpy/without_loop_counters.rs b/tests/ui/manual_memcpy/without_loop_counters.rs index 8146091a2bbe4..c917fa7f2d0b1 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/tests/ui/manual_memcpy/without_loop_counters.rs @@ -1,5 +1,6 @@ -#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] -#![allow(clippy::useless_vec)] +#![warn(clippy::manual_memcpy)] +#![allow(clippy::assigning_clones, clippy::useless_vec, clippy::needless_range_loop)] + //@no-rustfix const LOOP_OFFSET: usize = 5000; @@ -158,6 +159,59 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { //~^ ERROR: it looks like you're manually copying between slices dst[i] = src[i]; } + + // Don't trigger lint for following multi-dimensional arrays + let src = [[0; 5]; 5]; + for i in 0..4 { + dst[i] = src[i + 1][i]; + } + for i in 0..5 { + dst[i] = src[i][i]; + } + for i in 0..5 { + dst[i] = src[i][3]; + } + + let src = [0; 5]; + let mut dst = [[0; 5]; 5]; + for i in 0..5 { + dst[i][i] = src[i]; + } + + let src = [[[0; 5]; 5]; 5]; + let mut dst = [0; 5]; + for i in 0..5 { + dst[i] = src[i][i][i]; + } + for i in 0..5 { + dst[i] = src[i][i][0]; + } + for i in 0..5 { + dst[i] = src[i][0][i]; + } + for i in 0..5 { + dst[i] = src[0][i][i]; + } + for i in 0..5 { + dst[i] = src[0][i][1]; + } + for i in 0..5 { + dst[i] = src[i][0][1]; + } + + // Trigger lint + let src = [[0; 5]; 5]; + let mut dst = [0; 5]; + for i in 0..5 { + //~^ ERROR: it looks like you're manually copying between slices + dst[i] = src[0][i]; + } + + let src = [[[0; 5]; 5]; 5]; + for i in 0..5 { + //~^ ERROR: it looks like you're manually copying between slices + dst[i] = src[0][1][i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy/without_loop_counters.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr index 55cca1fb584a0..803053b2db2ef 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -1,5 +1,5 @@ error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:8:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:9:5 | LL | / for i in 0..src.len() { LL | | @@ -12,7 +12,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_memcpy)]` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:15:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:16:5 | LL | / for i in 0..src.len() { LL | | @@ -21,7 +21,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:21:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:22:5 | LL | / for i in 0..src.len() { LL | | @@ -30,7 +30,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:27:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:28:5 | LL | / for i in 11..src.len() { LL | | @@ -39,7 +39,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:33:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:34:5 | LL | / for i in 0..dst.len() { LL | | @@ -48,7 +48,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:47:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:48:5 | LL | / for i in 10..256 { LL | | @@ -64,7 +64,7 @@ LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:60:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:61:5 | LL | / for i in 10..LOOP_OFFSET { LL | | @@ -73,7 +73,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:74:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:75:5 | LL | / for i in 0..src_vec.len() { LL | | @@ -82,7 +82,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:104:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:105:5 | LL | / for i in from..from + src.len() { LL | | @@ -91,7 +91,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:109:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:110:5 | LL | / for i in from..from + 3 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:115:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:116:5 | LL | / for i in 0..5 { LL | | @@ -109,7 +109,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:121:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:122:5 | LL | / for i in 0..0 { LL | | @@ -118,7 +118,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:145:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:146:5 | LL | / for i in 0..4 { LL | | @@ -127,7 +127,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..4]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:151:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:152:5 | LL | / for i in 0..5 { LL | | @@ -136,7 +136,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:157:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:158:5 | LL | / for i in 0..5 { LL | | @@ -145,7 +145,25 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:165:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:205:5 + | +LL | / for i in 0..5 { +LL | | +LL | | dst[i] = src[0][i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0]);` + +error: it looks like you're manually copying between slices + --> tests/ui/manual_memcpy/without_loop_counters.rs:211:5 + | +LL | / for i in 0..5 { +LL | | +LL | | dst[i] = src[0][1][i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0][1]);` + +error: it looks like you're manually copying between slices + --> tests/ui/manual_memcpy/without_loop_counters.rs:219:5 | LL | / for i in 0..src.len() { LL | | @@ -153,5 +171,5 @@ LL | | dst[i] = src[i].clone(); LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` -error: aborting due to 16 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index 5540029bf6b1f..e359dfbb98c80 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index cee641d9d65f9..931814f08b7c2 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index c25c804df7581..fdbbc53e4df6d 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:25:5 + --> tests/ui/manual_retain.rs:27:5 | LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` @@ -8,43 +8,43 @@ LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); = help: to override `-D warnings` add `#[allow(clippy::manual_retain)]` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:26:5 + --> tests/ui/manual_retain.rs:28:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:27:5 + --> tests/ui/manual_retain.rs:29:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:31:5 + --> tests/ui/manual_retain.rs:33:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:32:5 + --> tests/ui/manual_retain.rs:34:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:62:5 + --> tests/ui/manual_retain.rs:64:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:63:5 + --> tests/ui/manual_retain.rs:65:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:64:5 + --> tests/ui/manual_retain.rs:66:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -53,49 +53,49 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:89:5 + --> tests/ui/manual_retain.rs:91:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:90:5 + --> tests/ui/manual_retain.rs:92:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:91:5 + --> tests/ui/manual_retain.rs:93:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:95:5 + --> tests/ui/manual_retain.rs:97:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:96:5 + --> tests/ui/manual_retain.rs:98:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:126:5 + --> tests/ui/manual_retain.rs:128:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:127:5 + --> tests/ui/manual_retain.rs:129:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:128:5 + --> tests/ui/manual_retain.rs:130:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -104,133 +104,133 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:152:5 + --> tests/ui/manual_retain.rs:154:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:153:5 + --> tests/ui/manual_retain.rs:155:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:154:5 + --> tests/ui/manual_retain.rs:156:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:158:5 + --> tests/ui/manual_retain.rs:160:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:159:5 + --> tests/ui/manual_retain.rs:161:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:188:5 + --> tests/ui/manual_retain.rs:190:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:200:5 + --> tests/ui/manual_retain.rs:202:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:201:5 + --> tests/ui/manual_retain.rs:203:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:202:5 + --> tests/ui/manual_retain.rs:204:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:206:5 + --> tests/ui/manual_retain.rs:208:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:207:5 + --> tests/ui/manual_retain.rs:209:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:229:5 + --> tests/ui/manual_retain.rs:231:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:230:5 + --> tests/ui/manual_retain.rs:232:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:231:5 + --> tests/ui/manual_retain.rs:233:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:288:5 + --> tests/ui/manual_retain.rs:290:5 | LL | vec = vec.into_iter().filter(|(x, y)| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:292:5 + --> tests/ui/manual_retain.rs:294:5 | LL | tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(_, n)| *n > 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:309:5 + --> tests/ui/manual_retain.rs:311:5 | LL | vec = vec.iter().filter(|&&x| x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:310:5 + --> tests/ui/manual_retain.rs:312:5 | LL | vec = vec.iter().filter(|&&x| x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:311:5 + --> tests/ui/manual_retain.rs:313:5 | LL | vec = vec.into_iter().filter(|&x| x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:314:5 + --> tests/ui/manual_retain.rs:316:5 | LL | vec = vec.iter().filter(|&x| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:315:5 + --> tests/ui/manual_retain.rs:317:5 | LL | vec = vec.iter().filter(|&x| *x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:316:5 + --> tests/ui/manual_retain.rs:318:5 | LL | vec = vec.into_iter().filter(|x| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` diff --git a/tests/ui/many_single_char_names.rs b/tests/ui/many_single_char_names.rs index 68578340d90e1..2af45eaab8af4 100644 --- a/tests/ui/many_single_char_names.rs +++ b/tests/ui/many_single_char_names.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![allow(clippy::too_many_arguments, clippy::diverging_sub_expression)] #![warn(clippy::many_single_char_names)] diff --git a/tests/ui/many_single_char_names.stderr b/tests/ui/many_single_char_names.stderr index 131836ef7c882..3b2460b5c077e 100644 --- a/tests/ui/many_single_char_names.stderr +++ b/tests/ui/many_single_char_names.stderr @@ -1,5 +1,5 @@ error: 5 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:5:9 + --> tests/ui/many_single_char_names.rs:7:9 | LL | let a: i32; | ^ @@ -14,7 +14,7 @@ LL | let e: i32; = help: to override `-D warnings` add `#[allow(clippy::many_single_char_names)]` error: 6 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:5:9 + --> tests/ui/many_single_char_names.rs:7:9 | LL | let a: i32; | ^ @@ -28,7 +28,7 @@ LL | let f: i32; | ^ error: 5 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:5:9 + --> tests/ui/many_single_char_names.rs:7:9 | LL | let a: i32; | ^ @@ -40,13 +40,13 @@ LL | e => panic!(), | ^ error: 8 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:34:13 + --> tests/ui/many_single_char_names.rs:36:13 | LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {} | ^ ^ ^ ^ ^ ^ ^ ^ error: 8 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:38:10 + --> tests/ui/many_single_char_names.rs:40:10 | LL | let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!(); | ^ ^ ^ ^ ^ ^ ^ ^ diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index 3917bb9e03de6..2dccadd9fceec 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![feature(custom_inner_attributes)] #![clippy::msrv = "invalid.version"] //~^ ERROR: `invalid.version` is not a valid Rust version diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index cf0d08a5d21ad..b4cb1b5713f90 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -1,35 +1,35 @@ error: `invalid.version` is not a valid Rust version - --> tests/ui/min_rust_version_invalid_attr.rs:2:1 + --> tests/ui/min_rust_version_invalid_attr.rs:4:1 | LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `invalid.version` is not a valid Rust version - --> tests/ui/min_rust_version_invalid_attr.rs:7:1 + --> tests/ui/min_rust_version_invalid_attr.rs:9:1 | LL | #[clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `clippy::msrv` is defined multiple times - --> tests/ui/min_rust_version_invalid_attr.rs:14:5 + --> tests/ui/min_rust_version_invalid_attr.rs:16:5 | LL | #![clippy::msrv = "1.10.1"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first definition found here - --> tests/ui/min_rust_version_invalid_attr.rs:12:5 + --> tests/ui/min_rust_version_invalid_attr.rs:14:5 | LL | #![clippy::msrv = "1.40"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `clippy::msrv` is defined multiple times - --> tests/ui/min_rust_version_invalid_attr.rs:19:9 + --> tests/ui/min_rust_version_invalid_attr.rs:21:9 | LL | #![clippy::msrv = "1.0.0"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first definition found here - --> tests/ui/min_rust_version_invalid_attr.rs:18:9 + --> tests/ui/min_rust_version_invalid_attr.rs:20:9 | LL | #![clippy::msrv = "1"] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/mixed_attributes_style.rs b/tests/ui/mixed_attributes_style.rs new file mode 100644 index 0000000000000..ad93e3019fa65 --- /dev/null +++ b/tests/ui/mixed_attributes_style.rs @@ -0,0 +1,39 @@ +#![warn(clippy::mixed_attributes_style)] + +#[allow(unused)] //~ ERROR: item has both inner and outer attributes +fn foo1() { + #![allow(unused)] +} + +#[allow(unused)] +#[allow(unused)] +fn foo2() {} + +fn foo3() { + #![allow(unused)] + #![allow(unused)] +} + +/// linux +//~^ ERROR: item has both inner and outer attributes +fn foo4() { + //! windows +} + +/// linux +/// windows +fn foo5() {} + +fn foo6() { + //! linux + //! windows +} + +#[allow(unused)] //~ ERROR: item has both inner and outer attributes +mod bar { + #![allow(unused)] +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/mixed_attributes_style.stderr b/tests/ui/mixed_attributes_style.stderr new file mode 100644 index 0000000000000..d1d5cd3f47f31 --- /dev/null +++ b/tests/ui/mixed_attributes_style.stderr @@ -0,0 +1,30 @@ +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style.rs:3:1 + | +LL | / #[allow(unused)] +LL | | fn foo1() { +LL | | #![allow(unused)] + | |_____________________^ + | + = note: `-D clippy::mixed-attributes-style` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]` + +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style.rs:17:1 + | +LL | / /// linux +LL | | +LL | | fn foo4() { +LL | | //! windows + | |_______________^ + +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style.rs:32:1 + | +LL | / #[allow(unused)] +LL | | mod bar { +LL | | #![allow(unused)] + | |_____________________^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index 72a171119f3c5..288b003405d15 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,4 +1,6 @@ //@aux-build:proc_macros.rs +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::mut_mut)] #![allow(unused)] #![allow( diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 810c0c967437f..73f2410a252ea 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:14:11 + --> tests/ui/mut_mut.rs:16:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | fn fun(x: &mut &mut u32) -> bool { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:31:17 + --> tests/ui/mut_mut.rs:33:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:46:25 + --> tests/ui/mut_mut.rs:48:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); | ^ @@ -22,37 +22,37 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> tests/ui/mut_mut.rs:33:21 + --> tests/ui/mut_mut.rs:35:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:37:32 + --> tests/ui/mut_mut.rs:39:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:37:16 + --> tests/ui/mut_mut.rs:39:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:42:37 + --> tests/ui/mut_mut.rs:44:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:42:16 + --> tests/ui/mut_mut.rs:44:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:42:21 + --> tests/ui/mut_mut.rs:44:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ diff --git a/tests/ui/no_effect_replace.rs b/tests/ui/no_effect_replace.rs index e4fd5caae2a5d..2a940d87fb944 100644 --- a/tests/ui/no_effect_replace.rs +++ b/tests/ui/no_effect_replace.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::no_effect_replace)] fn main() { diff --git a/tests/ui/no_effect_replace.stderr b/tests/ui/no_effect_replace.stderr index ded86c5c5b8d2..ad2dcd2cc9b35 100644 --- a/tests/ui/no_effect_replace.stderr +++ b/tests/ui/no_effect_replace.stderr @@ -1,5 +1,5 @@ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:4:13 + --> tests/ui/no_effect_replace.rs:6:13 | LL | let _ = "12345".replace('1', "1"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,43 +8,43 @@ LL | let _ = "12345".replace('1', "1"); = help: to override `-D warnings` add `#[allow(clippy::no_effect_replace)]` error: replacing text with itself - --> tests/ui/no_effect_replace.rs:7:13 + --> tests/ui/no_effect_replace.rs:9:13 | LL | let _ = "12345".replace("12", "12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:9:13 + --> tests/ui/no_effect_replace.rs:11:13 | LL | let _ = String::new().replace("12", "12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:12:13 + --> tests/ui/no_effect_replace.rs:14:13 | LL | let _ = "12345".replacen('1', "1", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:14:13 + --> tests/ui/no_effect_replace.rs:16:13 | LL | let _ = "12345".replacen("12", "12", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:16:13 + --> tests/ui/no_effect_replace.rs:18:13 | LL | let _ = String::new().replacen("12", "12", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:23:13 + --> tests/ui/no_effect_replace.rs:25:13 | LL | let _ = "hello".replace(&x.f(), &x.f()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:27:13 + --> tests/ui/no_effect_replace.rs:29:13 | LL | let _ = "hello".replace(&y(), &y()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/non_canonical_clone_impl.fixed b/tests/ui/non_canonical_clone_impl.fixed index 165702b304118..7d1be412e54fd 100644 --- a/tests/ui/non_canonical_clone_impl.fixed +++ b/tests/ui/non_canonical_clone_impl.fixed @@ -1,4 +1,5 @@ #![allow(clippy::clone_on_copy, unused)] +#![allow(clippy::assigning_clones)] #![no_main] // lint diff --git a/tests/ui/non_canonical_clone_impl.rs b/tests/ui/non_canonical_clone_impl.rs index 3b07dd5ce62f2..beae05efb2fbc 100644 --- a/tests/ui/non_canonical_clone_impl.rs +++ b/tests/ui/non_canonical_clone_impl.rs @@ -1,4 +1,5 @@ #![allow(clippy::clone_on_copy, unused)] +#![allow(clippy::assigning_clones)] #![no_main] // lint diff --git a/tests/ui/non_canonical_clone_impl.stderr b/tests/ui/non_canonical_clone_impl.stderr index 8eff322fa2d74..6bfc99d988bb7 100644 --- a/tests/ui/non_canonical_clone_impl.stderr +++ b/tests/ui/non_canonical_clone_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:9:29 + --> tests/ui/non_canonical_clone_impl.rs:10:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:13:5 + --> tests/ui/non_canonical_clone_impl.rs:14:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); @@ -20,7 +20,7 @@ LL | | } | |_____^ help: remove it error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:80:29 + --> tests/ui/non_canonical_clone_impl.rs:81:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -29,7 +29,7 @@ LL | | } | |_____^ help: change this to: `{ *self }` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:84:5 + --> tests/ui/non_canonical_clone_impl.rs:85:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index 908ef6cb2a02c..38157116e9105 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -1,4 +1,5 @@ //@no-rustfix: overlapping suggestions + #![feature(lint_reasons)] #![allow( unused, @@ -173,3 +174,8 @@ fn issue_5794() { if !b == !c {} //~ ERROR: this boolean expression can be simplified if !b != !c {} //~ ERROR: this boolean expression can be simplified } + +fn issue_12371(x: usize) -> bool { + // Should not warn! + !x != 0 +} diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index 954e23452665d..b6af06d845ae3 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -1,5 +1,5 @@ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:18:13 + --> tests/ui/nonminimal_bool.rs:19:13 | LL | let _ = !true; | ^^^^^ help: try: `false` @@ -8,43 +8,43 @@ LL | let _ = !true; = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:21:13 + --> tests/ui/nonminimal_bool.rs:22:13 | LL | let _ = !false; | ^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:23:13 + --> tests/ui/nonminimal_bool.rs:24:13 | LL | let _ = !!a; | ^^^ help: try: `a` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:25:13 + --> tests/ui/nonminimal_bool.rs:26:13 | LL | let _ = false || a; | ^^^^^^^^^^ help: try: `a` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:30:13 + --> tests/ui/nonminimal_bool.rs:31:13 | LL | let _ = !(!a && b); | ^^^^^^^^^^ help: try: `a || !b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:32:13 + --> tests/ui/nonminimal_bool.rs:33:13 | LL | let _ = !(!a || b); | ^^^^^^^^^^ help: try: `a && !b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:34:13 + --> tests/ui/nonminimal_bool.rs:35:13 | LL | let _ = !a && !(b && c); | ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:43:13 + --> tests/ui/nonminimal_bool.rs:44:13 | LL | let _ = a == b && c == 5 && a == b; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = a == b && c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:45:13 + --> tests/ui/nonminimal_bool.rs:46:13 | LL | let _ = a == b || c == 5 || a == b; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ LL | let _ = a == b || c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:47:13 + --> tests/ui/nonminimal_bool.rs:48:13 | LL | let _ = a == b && c == 5 && b == a; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +83,7 @@ LL | let _ = a == b && c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:49:13 + --> tests/ui/nonminimal_bool.rs:50:13 | LL | let _ = a != b || !(a != b || c == d); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = a != b || c != d; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:51:13 + --> tests/ui/nonminimal_bool.rs:52:13 | LL | let _ = a != b && !(a != b && c == d); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -109,43 +109,43 @@ LL | let _ = a != b && c != d; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:82:8 + --> tests/ui/nonminimal_bool.rs:83:8 | LL | if matches!(true, true) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(true, true)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:162:8 + --> tests/ui/nonminimal_bool.rs:163:8 | LL | if !(12 == a) {} | ^^^^^^^^^^ help: try: `12 != a` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:163:8 + --> tests/ui/nonminimal_bool.rs:164:8 | LL | if !(a == 12) {} | ^^^^^^^^^^ help: try: `a != 12` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:164:8 + --> tests/ui/nonminimal_bool.rs:165:8 | LL | if !(12 != a) {} | ^^^^^^^^^^ help: try: `12 == a` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:165:8 + --> tests/ui/nonminimal_bool.rs:166:8 | LL | if !(a != 12) {} | ^^^^^^^^^^ help: try: `a == 12` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:169:8 + --> tests/ui/nonminimal_bool.rs:170:8 | LL | if !b == true {} | ^^^^^^^^^^ help: try: `b != true` error: this comparison might be written more concisely - --> tests/ui/nonminimal_bool.rs:169:8 + --> tests/ui/nonminimal_bool.rs:170:8 | LL | if !b == true {} | ^^^^^^^^^^ help: try simplifying it as shown: `b != true` @@ -154,61 +154,61 @@ LL | if !b == true {} = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against true are unnecessary - --> tests/ui/nonminimal_bool.rs:169:8 + --> tests/ui/nonminimal_bool.rs:170:8 | LL | if !b == true {} | ^^^^^^^^^^ help: try simplifying it as shown: `!b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:170:8 + --> tests/ui/nonminimal_bool.rs:171:8 | LL | if !b != true {} | ^^^^^^^^^^ help: try: `b == true` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:170:8 + --> tests/ui/nonminimal_bool.rs:171:8 | LL | if !b != true {} | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:171:8 + --> tests/ui/nonminimal_bool.rs:172:8 | LL | if true == !b {} | ^^^^^^^^^^ help: try: `true != b` error: this comparison might be written more concisely - --> tests/ui/nonminimal_bool.rs:171:8 + --> tests/ui/nonminimal_bool.rs:172:8 | LL | if true == !b {} | ^^^^^^^^^^ help: try simplifying it as shown: `true != b` error: equality checks against true are unnecessary - --> tests/ui/nonminimal_bool.rs:171:8 + --> tests/ui/nonminimal_bool.rs:172:8 | LL | if true == !b {} | ^^^^^^^^^^ help: try simplifying it as shown: `!b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:172:8 + --> tests/ui/nonminimal_bool.rs:173:8 | LL | if true != !b {} | ^^^^^^^^^^ help: try: `true == b` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:172:8 + --> tests/ui/nonminimal_bool.rs:173:8 | LL | if true != !b {} | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:173:8 + --> tests/ui/nonminimal_bool.rs:174:8 | LL | if !b == !c {} | ^^^^^^^^ help: try: `b == c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:174:8 + --> tests/ui/nonminimal_bool.rs:175:8 | LL | if !b != !c {} | ^^^^^^^^ help: try: `b != c` diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed index e27c0350d49e9..bd4be3e5a4408 100644 --- a/tests/ui/nonminimal_bool_methods.fixed +++ b/tests/ui/nonminimal_bool_methods.fixed @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs index 040a6e920a172..4523c7385df9a 100644 --- a/tests/ui/nonminimal_bool_methods.rs +++ b/tests/ui/nonminimal_bool_methods.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/nonminimal_bool_methods.stderr b/tests/ui/nonminimal_bool_methods.stderr index ede88ba529cdc..e32c8dacd2fa3 100644 --- a/tests/ui/nonminimal_bool_methods.stderr +++ b/tests/ui/nonminimal_bool_methods.stderr @@ -1,5 +1,5 @@ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:8:13 + --> tests/ui/nonminimal_bool_methods.rs:10:13 | LL | let _ = !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` @@ -8,73 +8,73 @@ LL | let _ = !a.is_some(); = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:10:13 + --> tests/ui/nonminimal_bool_methods.rs:12:13 | LL | let _ = !a.is_none(); | ^^^^^^^^^^^^ help: try: `a.is_some()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:12:13 + --> tests/ui/nonminimal_bool_methods.rs:14:13 | LL | let _ = !b.is_err(); | ^^^^^^^^^^^ help: try: `b.is_ok()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:14:13 + --> tests/ui/nonminimal_bool_methods.rs:16:13 | LL | let _ = !b.is_ok(); | ^^^^^^^^^^ help: try: `b.is_err()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:16:13 + --> tests/ui/nonminimal_bool_methods.rs:18:13 | LL | let _ = !(a.is_some() && !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:17:13 + --> tests/ui/nonminimal_bool_methods.rs:19:13 | LL | let _ = !(a.is_some() || !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:18:26 + --> tests/ui/nonminimal_bool_methods.rs:20:26 | LL | let _ = !(!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:19:25 + --> tests/ui/nonminimal_bool_methods.rs:21:25 | LL | let _ = (!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:20:23 + --> tests/ui/nonminimal_bool_methods.rs:22:23 | LL | let _ = !c ^ c || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:92:8 + --> tests/ui/nonminimal_bool_methods.rs:94:8 | LL | if !res.is_ok() {} | ^^^^^^^^^^^^ help: try: `res.is_err()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:93:8 + --> tests/ui/nonminimal_bool_methods.rs:95:8 | LL | if !res.is_err() {} | ^^^^^^^^^^^^^ help: try: `res.is_ok()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:96:8 + --> tests/ui/nonminimal_bool_methods.rs:98:8 | LL | if !res.is_some() {} | ^^^^^^^^^^^^^^ help: try: `res.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:97:8 + --> tests/ui/nonminimal_bool_methods.rs:99:8 | LL | if !res.is_none() {} | ^^^^^^^^^^^^^^ help: try: `res.is_some()` diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 9bbdd3aaacce5..2f6e4d7614549 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![deny(clippy::option_option)] #![allow(clippy::unnecessary_wraps)] diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 0cd048e400e40..76cb9ae944c26 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -1,77 +1,77 @@ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:4:10 + --> tests/ui/option_option.rs:6:10 | LL | const C: Option> = None; | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> tests/ui/option_option.rs:1:9 + --> tests/ui/option_option.rs:3:9 | LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:6:11 + --> tests/ui/option_option.rs:8:11 | LL | static S: Option> = None; | ^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:9:13 + --> tests/ui/option_option.rs:11:13 | LL | fn input(_: Option>) {} | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:12:16 + --> tests/ui/option_option.rs:14:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:17:27 + --> tests/ui/option_option.rs:19:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:23:30 + --> tests/ui/option_option.rs:25:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:29:8 + --> tests/ui/option_option.rs:31:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:34:23 + --> tests/ui/option_option.rs:36:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:41:22 + --> tests/ui/option_option.rs:43:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:46:11 + --> tests/ui/option_option.rs:48:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:48:17 + --> tests/ui/option_option.rs:50:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:90:14 + --> tests/ui/option_option.rs:92:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index fa15c323540ff..61d37b8ba3af8 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -1,4 +1,5 @@ //@aux-build:proc_macros.rs +//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::ptr_as_ptr)] diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index 7ab52e63da556..8f2068cd26844 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -1,4 +1,5 @@ //@aux-build:proc_macros.rs +//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::ptr_as_ptr)] diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index e162f35baf551..e6cd697c7baf9 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:18:33 + --> tests/ui/ptr_as_ptr.rs:19:33 | LL | *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::issue_11278_a::T) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `Box::into_raw(Box::new(o)).cast::>()` @@ -8,37 +8,37 @@ LL | *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::i = help: to override `-D warnings` add `#[allow(clippy::ptr_as_ptr)]` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:27:13 + --> tests/ui/ptr_as_ptr.rs:28:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:28:13 + --> tests/ui/ptr_as_ptr.rs:29:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:33:17 + --> tests/ui/ptr_as_ptr.rs:34:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:46:25 + --> tests/ui/ptr_as_ptr.rs:47:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:47:23 + --> tests/ui/ptr_as_ptr.rs:48:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:50:21 + --> tests/ui/ptr_as_ptr.rs:51:21 | LL | let _ = inline!($ptr as *const i32); | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::()` @@ -46,157 +46,157 @@ LL | let _ = inline!($ptr as *const i32); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:71:13 + --> tests/ui/ptr_as_ptr.rs:72:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:72:13 + --> tests/ui/ptr_as_ptr.rs:73:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:79:9 + --> tests/ui/ptr_as_ptr.rs:80:9 | LL | ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:83:9 + --> tests/ui/ptr_as_ptr.rs:84:9 | LL | std::ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:88:9 + --> tests/ui/ptr_as_ptr.rs:89:9 | LL | ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:92:9 + --> tests/ui/ptr_as_ptr.rs:93:9 | LL | core::ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:97:9 + --> tests/ui/ptr_as_ptr.rs:98:9 | LL | ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:101:9 + --> tests/ui/ptr_as_ptr.rs:102:9 | LL | std::ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:106:9 + --> tests/ui/ptr_as_ptr.rs:107:9 | LL | ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:110:9 + --> tests/ui/ptr_as_ptr.rs:111:9 | LL | core::ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null::()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:117:9 + --> tests/ui/ptr_as_ptr.rs:118:9 | LL | ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:121:9 + --> tests/ui/ptr_as_ptr.rs:122:9 | LL | std::ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:126:9 + --> tests/ui/ptr_as_ptr.rs:127:9 | LL | ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:130:9 + --> tests/ui/ptr_as_ptr.rs:131:9 | LL | core::ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:135:9 + --> tests/ui/ptr_as_ptr.rs:136:9 | LL | ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:139:9 + --> tests/ui/ptr_as_ptr.rs:140:9 | LL | std::ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:144:9 + --> tests/ui/ptr_as_ptr.rs:145:9 | LL | ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:148:9 + --> tests/ui/ptr_as_ptr.rs:149:9 | LL | core::ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:155:9 + --> tests/ui/ptr_as_ptr.rs:156:9 | LL | ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:159:9 + --> tests/ui/ptr_as_ptr.rs:160:9 | LL | std::ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:164:9 + --> tests/ui/ptr_as_ptr.rs:165:9 | LL | ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:168:9 + --> tests/ui/ptr_as_ptr.rs:169:9 | LL | core::ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:173:9 + --> tests/ui/ptr_as_ptr.rs:174:9 | LL | ptr::null() as _ | ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:177:9 + --> tests/ui/ptr_as_ptr.rs:178:9 | LL | std::ptr::null() as _ | ^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:182:9 + --> tests/ui/ptr_as_ptr.rs:183:9 | LL | ptr::null() as _ | ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` error: `as` casting between raw pointers without changing its mutability - --> tests/ui/ptr_as_ptr.rs:186:9 + --> tests/ui/ptr_as_ptr.rs:187:9 | LL | core::ptr::null() as _ | ^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()` diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 2ef006c141923..567472a8af22d 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -273,3 +273,13 @@ const fn issue9175(option: Option<()>) -> Option<()> { //stuff Some(()) } + +fn issue12337() -> Option { + let _: Option = try { + let Some(_) = Some(42) else { + return None; + }; + 123 + }; + Some(42) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index c170669823f3f..abf8c270de83d 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -313,3 +313,13 @@ const fn issue9175(option: Option<()>) -> Option<()> { //stuff Some(()) } + +fn issue12337() -> Option { + let _: Option = try { + let Some(_) = Some(42) else { + return None; + }; + 123 + }; + Some(42) +} diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index ce5c7f2600be7..191f7719904c1 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -111,3 +111,20 @@ fn fp_11274() { } m!(|x| println!("{x}")); } + +// Issue #12358: When a macro expands into a closure, immediately calling the expanded closure +// triggers the lint. +fn issue_12358() { + macro_rules! make_closure { + () => { + (|| || {}) + }; + (x) => { + make_closure!()() + }; + } + + // The lint would suggest to alter the line below to `make_closure!(x)`, which is semantically + // different. + make_closure!(x)(); +} diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index ac09390e6eaeb..33a3b90f9cfb2 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -111,3 +111,20 @@ fn fp_11274() { } m!(|x| println!("{x}")); } + +// Issue #12358: When a macro expands into a closure, immediately calling the expanded closure +// triggers the lint. +fn issue_12358() { + macro_rules! make_closure { + () => { + (|| || {}) + }; + (x) => { + make_closure!()() + }; + } + + // The lint would suggest to alter the line below to `make_closure!(x)`, which is semantically + // different. + make_closure!(x)(); +} diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index c578e786426f8..72fc4cb7333ec 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -20,7 +20,7 @@ struct Person { } pub struct S { - v: String, + v: usize, } fn main() { @@ -59,11 +59,22 @@ fn main() { let _ = RangeToInclusive { end }; external! { - let v = String::new(); + let v = 1; let _ = S { v: v }; } + + let v = 2; + macro_rules! internal { + ($i:ident) => { + let _ = S { v }; + let _ = S { $i: v }; + let _ = S { v: $i }; + let _ = S { $i: $i }; + }; + } + internal!(v); } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index d8c2286d5ad65..2617d7e728379 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -20,7 +20,7 @@ struct Person { } pub struct S { - v: String, + v: usize, } fn main() { @@ -59,11 +59,22 @@ fn main() { let _ = RangeToInclusive { end: end }; external! { - let v = String::new(); + let v = 1; let _ = S { v: v }; } + + let v = 2; + macro_rules! internal { + ($i:ident) => { + let _ = S { v: v }; + let _ = S { $i: v }; + let _ = S { v: $i }; + let _ = S { $i: $i }; + }; + } + internal!(v); } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 5323473620762..38c021fdba370 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -44,10 +44,21 @@ LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> tests/ui/redundant_field_names.rs:88:25 + --> tests/ui/redundant_field_names.rs:71:25 + | +LL | let _ = S { v: v }; + | ^^^^ help: replace it with: `v` +... +LL | internal!(v); + | ------------ in this macro invocation + | + = note: this error originates in the macro `internal` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: redundant field names in struct initialization + --> tests/ui/redundant_field_names.rs:99:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/renamed_builtin_attr.fixed b/tests/ui/renamed_builtin_attr.fixed index bc055215708f0..aebf8712dd923 100644 --- a/tests/ui/renamed_builtin_attr.fixed +++ b/tests/ui/renamed_builtin_attr.fixed @@ -1,2 +1,4 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #[clippy::cognitive_complexity = "1"] fn main() {} diff --git a/tests/ui/renamed_builtin_attr.rs b/tests/ui/renamed_builtin_attr.rs index fdb425363e817..6c18151195f54 100644 --- a/tests/ui/renamed_builtin_attr.rs +++ b/tests/ui/renamed_builtin_attr.rs @@ -1,2 +1,4 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #[clippy::cyclomatic_complexity = "1"] fn main() {} diff --git a/tests/ui/renamed_builtin_attr.stderr b/tests/ui/renamed_builtin_attr.stderr index f9108d169c718..fb51313dab69a 100644 --- a/tests/ui/renamed_builtin_attr.stderr +++ b/tests/ui/renamed_builtin_attr.stderr @@ -1,5 +1,5 @@ error: usage of deprecated attribute - --> tests/ui/renamed_builtin_attr.rs:1:11 + --> tests/ui/renamed_builtin_attr.rs:3:11 | LL | #[clippy::cyclomatic_complexity = "1"] | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity` diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index 0a49be2dc4f3e..6df64eb405354 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::single_match)] #![allow( unused, diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 4e35d265acbf1..4f005f4e04f5c 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::single_match)] #![allow( unused, diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 40af38879aeb2..651d0b4911d95 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:14:5 + --> tests/ui/single_match.rs:16:5 | LL | / match x { LL | | Some(y) => { @@ -19,7 +19,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:22:5 + --> tests/ui/single_match.rs:24:5 | LL | / match x { LL | | // Note the missing block braces. @@ -31,7 +31,7 @@ LL | | } | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:31:5 + --> tests/ui/single_match.rs:33:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -40,7 +40,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:60:5 + --> tests/ui/single_match.rs:62:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -49,7 +49,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:65:5 + --> tests/ui/single_match.rs:67:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -58,7 +58,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:72:5 + --> tests/ui/single_match.rs:74:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -67,7 +67,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:93:5 + --> tests/ui/single_match.rs:95:5 | LL | / match x { LL | | "test" => println!(), @@ -76,7 +76,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:106:5 + --> tests/ui/single_match.rs:108:5 | LL | / match x { LL | | Foo::A => println!(), @@ -85,7 +85,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:112:5 + --> tests/ui/single_match.rs:114:5 | LL | / match x { LL | | FOO_C => println!(), @@ -94,7 +94,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:117:5 + --> tests/ui/single_match.rs:119:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -103,7 +103,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:123:5 + --> tests/ui/single_match.rs:125:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -112,7 +112,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:140:5 + --> tests/ui/single_match.rs:142:5 | LL | / match x { LL | | Bar::A => println!(), @@ -121,7 +121,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:148:5 + --> tests/ui/single_match.rs:150:5 | LL | / match x { LL | | None => println!(), @@ -130,7 +130,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:170:5 + --> tests/ui/single_match.rs:172:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -139,7 +139,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:176:5 + --> tests/ui/single_match.rs:178:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -148,7 +148,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:182:5 + --> tests/ui/single_match.rs:184:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -157,7 +157,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:254:5 + --> tests/ui/single_match.rs:256:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -177,7 +177,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:262:5 + --> tests/ui/single_match.rs:264:5 | LL | / match bar { LL | | #[rustfmt::skip] diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index f3b1de3b44f90..2970f5485fae9 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -1,4 +1,6 @@ //@aux-build: proc_macros.rs +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] extern crate proc_macros; diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index ddee2e42ec2a5..26974b2a48ab4 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,4 +1,6 @@ //@aux-build: proc_macros.rs +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] extern crate proc_macros; diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 63c733e9a7a03..48c74c0caeaed 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:16: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:81:5 + --> tests/ui/single_match_else.rs:83: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:90:5 + --> tests/ui/single_match_else.rs:92:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -62,7 +62,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:100:5 + --> tests/ui/single_match_else.rs:102:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -82,7 +82,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:109:5 + --> tests/ui/single_match_else.rs:111:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), @@ -102,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:119:5 + --> tests/ui/single_match_else.rs:121:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -125,7 +125,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:130:5 + --> tests/ui/single_match_else.rs:132:5 | LL | / match bar { LL | | Some(v) => { @@ -149,7 +149,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:142:5 + --> tests/ui/single_match_else.rs:144:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -173,7 +173,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:154:5 + --> tests/ui/single_match_else.rs:156:5 | LL | / match bar { LL | | #[rustfmt::skip] diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index a7555704fbe01..0a734a65d2976 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -1,4 +1,5 @@ //@aux-build:proc_macro_derive.rs + #![warn(clippy::std_instead_of_core)] #![allow(unused_imports)] @@ -16,12 +17,20 @@ fn std_instead_of_core() { use ::core::hash::Hash; //~^ ERROR: used import from `std` instead of `core` // Don't lint on `env` macro - use std::env; + use core::env; // Multiple imports use core::fmt::{Debug, Result}; //~^ ERROR: used import from `std` instead of `core` + // Multiple imports multiline + #[rustfmt::skip] + use core::{ + //~^ ERROR: used import from `std` instead of `core` + fmt::Write as _, + ptr::read_unaligned, + }; + // Function calls let ptr = core::ptr::null::(); //~^ ERROR: used import from `std` instead of `core` diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index af7f3399f4923..c12c459c7eb4b 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -1,4 +1,5 @@ //@aux-build:proc_macro_derive.rs + #![warn(clippy::std_instead_of_core)] #![allow(unused_imports)] @@ -22,6 +23,14 @@ fn std_instead_of_core() { use std::fmt::{Debug, Result}; //~^ ERROR: used import from `std` instead of `core` + // Multiple imports multiline + #[rustfmt::skip] + use std::{ + //~^ ERROR: used import from `std` instead of `core` + fmt::Write as _, + ptr::read_unaligned, + }; + // Function calls let ptr = std::ptr::null::(); //~^ ERROR: used import from `std` instead of `core` diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 30dd49dc2688d..ee42b474a32e3 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:13:9 + --> tests/ui/std_instead_of_core.rs:14:9 | LL | use std::hash::Hasher; | ^^^ help: consider importing the item from `core`: `core` @@ -8,49 +8,61 @@ LL | use std::hash::Hasher; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:16:11 + --> tests/ui/std_instead_of_core.rs:17:11 | LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:22:9 + --> tests/ui/std_instead_of_core.rs:20:9 + | +LL | use std::env; + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:23:9 | LL | use std::fmt::{Debug, Result}; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:26:15 + --> tests/ui/std_instead_of_core.rs:28:9 + | +LL | use std::{ + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:35:15 | LL | let ptr = std::ptr::null::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:28:21 + --> tests/ui/std_instead_of_core.rs:37:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:32:16 + --> tests/ui/std_instead_of_core.rs:41:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:34:27 + --> tests/ui/std_instead_of_core.rs:43:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:43:9 + --> tests/ui/std_instead_of_core.rs:52:9 | LL | use std::iter::Iterator; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:50:9 + --> tests/ui/std_instead_of_core.rs:59:9 | LL | use std::vec; | ^^^ help: consider importing the item from `alloc`: `alloc` @@ -59,13 +71,13 @@ LL | use std::vec; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:52:9 + --> tests/ui/std_instead_of_core.rs:61:9 | LL | use std::vec::Vec; | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> tests/ui/std_instead_of_core.rs:58:9 + --> tests/ui/std_instead_of_core.rs:67:9 | LL | use alloc::slice::from_ref; | ^^^^^ help: consider importing the item from `core`: `core` @@ -73,5 +85,5 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 11 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/suspicious_operation_groupings.fixed b/tests/ui/suspicious_operation_groupings.fixed index 9d9732307c85e..60fde6e22cb68 100644 --- a/tests/ui/suspicious_operation_groupings.fixed +++ b/tests/ui/suspicious_operation_groupings.fixed @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::suspicious_operation_groupings)] #![allow(dead_code, unused_parens, clippy::eq_op)] diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs index 201b8e657f4b3..ce37148a853b0 100644 --- a/tests/ui/suspicious_operation_groupings.rs +++ b/tests/ui/suspicious_operation_groupings.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::suspicious_operation_groupings)] #![allow(dead_code, unused_parens, clippy::eq_op)] diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr index 9d1d8a1f34f55..7cb066d57e75f 100644 --- a/tests/ui/suspicious_operation_groupings.stderr +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -1,5 +1,5 @@ error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:15:9 + --> tests/ui/suspicious_operation_groupings.rs:17:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` @@ -8,151 +8,151 @@ LL | self.x == other.y && self.y == other.y && self.z == other.z = help: to override `-D warnings` add `#[allow(clippy::suspicious_operation_groupings)]` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:28:20 + --> tests/ui/suspicious_operation_groupings.rs:30:20 | LL | s1.a < s2.a && s1.a < s2.b | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:76:33 + --> tests/ui/suspicious_operation_groupings.rs:78:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:81:19 + --> tests/ui/suspicious_operation_groupings.rs:83:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:81:19 + --> tests/ui/suspicious_operation_groupings.rs:83:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:86:19 + --> tests/ui/suspicious_operation_groupings.rs:88:19 | LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:91:19 + --> tests/ui/suspicious_operation_groupings.rs:93:19 | LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:96:5 + --> tests/ui/suspicious_operation_groupings.rs:98:5 | LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:101:33 + --> tests/ui/suspicious_operation_groupings.rs:103:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:114:20 + --> tests/ui/suspicious_operation_groupings.rs:116:20 | LL | (s1.a * s2.a + s1.b * s1.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:119:34 + --> tests/ui/suspicious_operation_groupings.rs:121:34 | LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:124:38 + --> tests/ui/suspicious_operation_groupings.rs:126:38 | LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:129:39 + --> tests/ui/suspicious_operation_groupings.rs:131:39 | LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:134:42 + --> tests/ui/suspicious_operation_groupings.rs:136:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:134:42 + --> tests/ui/suspicious_operation_groupings.rs:136:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:139:40 + --> tests/ui/suspicious_operation_groupings.rs:141:40 | LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:144:40 + --> tests/ui/suspicious_operation_groupings.rs:146:40 | LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:149:20 + --> tests/ui/suspicious_operation_groupings.rs:151:20 | LL | (s1.a * s2.a + s2.b * s2.b) / 2 | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:154:35 + --> tests/ui/suspicious_operation_groupings.rs:156:35 | LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:159:29 + --> tests/ui/suspicious_operation_groupings.rs:161:29 | LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:164:17 + --> tests/ui/suspicious_operation_groupings.rs:166:17 | LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:173:77 + --> tests/ui/suspicious_operation_groupings.rs:175:77 | LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:187:25 + --> tests/ui/suspicious_operation_groupings.rs:189:25 | LL | s1.a <= s2.a && s1.a <= s2.b | ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:193:23 + --> tests/ui/suspicious_operation_groupings.rs:195:23 | LL | if s1.a < s2.a && s1.a < s2.b { | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:200:48 + --> tests/ui/suspicious_operation_groupings.rs:202:48 | LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) | ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c` error: this sequence of operators looks suspiciously like a bug - --> tests/ui/suspicious_operation_groupings.rs:205:27 + --> tests/ui/suspicious_operation_groupings.rs:207:27 | LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b` diff --git a/tests/ui/thread_local_initializer_can_be_made_const.fixed b/tests/ui/thread_local_initializer_can_be_made_const.fixed index bbde25b0a88b5..a6ed59d49c54f 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.fixed +++ b/tests/ui/thread_local_initializer_can_be_made_const.fixed @@ -27,4 +27,18 @@ fn main() { } //~^^^^ ERROR: initializer for `thread_local` value can be made `const` //~^^^ ERROR: initializer for `thread_local` value can be made `const` + + thread_local! { + static PEEL_ME: i32 = const { 1 }; + //~^ ERROR: initializer for `thread_local` value can be made `const` + static PEEL_ME_MANY: i32 = const { { let x = 1; x * x } }; + //~^ ERROR: initializer for `thread_local` value can be made `const` + } +} + +#[clippy::msrv = "1.58"] +fn f() { + thread_local! { + static TLS: i32 = 1; + } } diff --git a/tests/ui/thread_local_initializer_can_be_made_const.rs b/tests/ui/thread_local_initializer_can_be_made_const.rs index 3d7aacf2f094f..3f0159c58065c 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.rs +++ b/tests/ui/thread_local_initializer_can_be_made_const.rs @@ -27,4 +27,18 @@ fn main() { } //~^^^^ ERROR: initializer for `thread_local` value can be made `const` //~^^^ ERROR: initializer for `thread_local` value can be made `const` + + thread_local! { + static PEEL_ME: i32 = { 1 }; + //~^ ERROR: initializer for `thread_local` value can be made `const` + static PEEL_ME_MANY: i32 = { let x = 1; x * x }; + //~^ ERROR: initializer for `thread_local` value can be made `const` + } +} + +#[clippy::msrv = "1.58"] +fn f() { + thread_local! { + static TLS: i32 = 1; + } } diff --git a/tests/ui/thread_local_initializer_can_be_made_const.stderr b/tests/ui/thread_local_initializer_can_be_made_const.stderr index 9ee52fbbb319e..b4f8bd822b0de 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.stderr +++ b/tests/ui/thread_local_initializer_can_be_made_const.stderr @@ -25,5 +25,17 @@ error: initializer for `thread_local` value can be made `const` LL | static BUF_4_CAN_BE_MADE_CONST: RefCell = RefCell::new(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }` -error: aborting due to 4 previous errors +error: initializer for `thread_local` value can be made `const` + --> tests/ui/thread_local_initializer_can_be_made_const.rs:32:31 + | +LL | static PEEL_ME: i32 = { 1 }; + | ^^^^^ help: replace with: `const { 1 }` + +error: initializer for `thread_local` value can be made `const` + --> tests/ui/thread_local_initializer_can_be_made_const.rs:34:36 + | +LL | static PEEL_ME_MANY: i32 = { let x = 1; x * x }; + | ^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { { let x = 1; x * x } }` + +error: aborting due to 6 previous errors diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 08b8e786611da..2365695d6914b 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -82,3 +82,10 @@ fn issue_10449() { let _x: u8 = unsafe { *(f as *const u8) }; } + +// Pointers cannot be cast to integers in const contexts +const fn issue_12402

(ptr: *const P) { + unsafe { transmute::<*const i32, usize>(&42i32) }; + unsafe { transmute::(issue_12402) }; + let _ = unsafe { transmute::<_, usize>(ptr) }; +} diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index 92eb765e5f94e..cd1607b4c1999 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -82,3 +82,10 @@ fn issue_10449() { let _x: u8 = unsafe { *std::mem::transmute::(f) }; } + +// Pointers cannot be cast to integers in const contexts +const fn issue_12402

(ptr: *const P) { + unsafe { transmute::<*const i32, usize>(&42i32) }; + unsafe { transmute::(issue_12402) }; + let _ = unsafe { transmute::<_, usize>(ptr) }; +} diff --git a/tests/ui/type_complexity.rs b/tests/ui/type_complexity.rs index be28ee2da0cd4..b057dc4e89f26 100644 --- a/tests/ui/type_complexity.rs +++ b/tests/ui/type_complexity.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #![warn(clippy::all)] #![allow(unused, clippy::needless_pass_by_value, clippy::vec_box, clippy::useless_vec)] #![feature(associated_type_defaults)] diff --git a/tests/ui/type_complexity.stderr b/tests/ui/type_complexity.stderr index 9e27899e4f904..bfbab8647e85c 100644 --- a/tests/ui/type_complexity.stderr +++ b/tests/ui/type_complexity.stderr @@ -1,5 +1,5 @@ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:7:12 + --> tests/ui/type_complexity.rs:9:12 | LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,85 +8,85 @@ LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); = help: to override `-D warnings` add `#[allow(clippy::type_complexity)]` error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:10:12 + --> tests/ui/type_complexity.rs:12:12 | LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:14:8 + --> tests/ui/type_complexity.rs:16:8 | LL | f: Vec>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:18:11 + --> tests/ui/type_complexity.rs:20:11 | LL | struct Ts(Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:22:11 + --> tests/ui/type_complexity.rs:24:11 | LL | Tuple(Vec>>), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:24:17 + --> tests/ui/type_complexity.rs:26:17 | LL | Struct { f: Vec>> }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:29:14 + --> tests/ui/type_complexity.rs:31:14 | LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:31:30 + --> tests/ui/type_complexity.rs:33:30 | LL | fn impl_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:36:14 + --> tests/ui/type_complexity.rs:38:14 | LL | const A: Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:38:14 + --> tests/ui/type_complexity.rs:40:14 | LL | type B = Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:40:25 + --> tests/ui/type_complexity.rs:42:25 | LL | fn method(&self, p: Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:42:29 + --> tests/ui/type_complexity.rs:44:29 | LL | fn def_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:55:15 + --> tests/ui/type_complexity.rs:57:15 | LL | fn test1() -> Vec>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:60:14 + --> tests/ui/type_complexity.rs:62:14 | LL | fn test2(_x: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:64:13 + --> tests/ui/type_complexity.rs:66:13 | LL | let _y: Vec>> = vec![]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unknown_attribute.rs b/tests/ui/unknown_attribute.rs index 932f284d5b713..3241742b5d464 100644 --- a/tests/ui/unknown_attribute.rs +++ b/tests/ui/unknown_attribute.rs @@ -1,3 +1,5 @@ +//@compile-flags: -Zdeduplicate-diagnostics=yes + #[clippy::unknown] //~^ ERROR: usage of unknown attribute #[clippy::cognitive_complexity = "1"] diff --git a/tests/ui/unknown_attribute.stderr b/tests/ui/unknown_attribute.stderr index d1f8eeb51aeac..b306abe0a9d1f 100644 --- a/tests/ui/unknown_attribute.stderr +++ b/tests/ui/unknown_attribute.stderr @@ -1,5 +1,5 @@ error: usage of unknown attribute - --> tests/ui/unknown_attribute.rs:1:11 + --> tests/ui/unknown_attribute.rs:3:11 | LL | #[clippy::unknown] | ^^^^^^^ diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index f52d325033987..288541362cddb 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -221,4 +221,10 @@ mod fixable { fn issue_9603() { let _: f32 = -0x400 as f32; } + + // Issue #11968: The suggestion for this lint removes the parentheses and leave the code as + // `*x.pow(2)` which tries to dereference the return value rather than `x`. + fn issue_11968(x: &usize) -> usize { + { *x }.pow(2) + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index dfd8b454e6cca..eef3a42e35149 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -221,4 +221,10 @@ mod fixable { fn issue_9603() { let _: f32 = -0x400 as f32; } + + // Issue #11968: The suggestion for this lint removes the parentheses and leave the code as + // `*x.pow(2)` which tries to dereference the return value rather than `x`. + fn issue_11968(x: &usize) -> usize { + (*x as usize).pow(2) + } } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 935bb71da32bd..80fd5c13d8185 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -241,5 +241,11 @@ error: casting to the same type is unnecessary (`f32` -> `f32`) LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` -error: aborting due to 40 previous errors +error: casting to the same type is unnecessary (`usize` -> `usize`) + --> tests/ui/unnecessary_cast.rs:228:9 + | +LL | (*x as usize).pow(2) + | ^^^^^^^^^^^^^ help: try: `{ *x }` + +error: aborting due to 41 previous errors From 43f4ec3b133262e6d23a0f5401aadaf60f5bc07a Mon Sep 17 00:00:00 2001 From: Ethiraric Date: Thu, 7 Mar 2024 18:47:43 +0100 Subject: [PATCH 011/102] [`unused_enumerate_index`]: Use if-let chain --- .../src/loops/unused_enumerate_index.rs | 79 +++++++------------ 1 file changed, 28 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index dd7fae79d9ba8..e86a63a273818 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,62 +1,39 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; -use clippy_utils::{pat_is_wild, sugg}; +use clippy_utils::{match_def_path, pat_is_wild, sugg}; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { - let PatKind::Tuple([index, elem], _) = pat.kind else { - return; - }; - - let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind else { - return; - }; - - let ty = cx.typeck_results().expr_ty(arg); - - if !pat_is_wild(cx, &index.kind, body) { - return; - } - - let name = match *ty.kind() { - ty::Adt(base, _substs) => cx.tcx.def_path_str(base.did()), - _ => return, - }; - - if name != "std::iter::Enumerate" && name != "core::iter::Enumerate" { - return; +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) { + if let PatKind::Tuple([index, elem], _) = pat.kind + && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind + && let ty = cx.typeck_results().expr_ty(arg) + && pat_is_wild(cx, &index.kind, body) + && let ty::Adt(base, _) = *ty.kind() + && match_def_path(cx, base.did(), &clippy_utils::paths::CORE_ITER_ENUMERATE_STRUCT) + && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) + && match_def_path(cx, call_id, &clippy_utils::paths::CORE_ITER_ENUMERATE_METHOD) + { + span_lint_and_then( + cx, + UNUSED_ENUMERATE_INDEX, + arg.span, + "you seem to use `.enumerate()` and immediately discard the index", + |diag| { + let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter"); + multispan_sugg( + diag, + "remove the `.enumerate()` call", + vec![ + (pat.span, snippet(cx, elem.span, "..").into_owned()), + (arg.span, base_iter.to_string()), + ], + ); + }, + ); } - - let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) else { - return; - }; - - let call_name = cx.tcx.def_path_str(call_id); - - if call_name != "std::iter::Iterator::enumerate" && call_name != "core::iter::Iterator::enumerate" { - return; - } - - span_lint_and_then( - cx, - UNUSED_ENUMERATE_INDEX, - arg.span, - "you seem to use `.enumerate()` and immediately discard the index", - |diag| { - let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter"); - multispan_sugg( - diag, - "remove the `.enumerate()` call", - vec![ - (pat.span, snippet(cx, elem.span, "..").into_owned()), - (arg.span, base_iter.to_string()), - ], - ); - }, - ); } From f3879b3630f80f18fe2fbdbf048e3b693727c05b Mon Sep 17 00:00:00 2001 From: Ethiraric Date: Thu, 29 Feb 2024 14:13:54 +0100 Subject: [PATCH 012/102] [`use_self`]: Make it aware of lifetimes Have the lint trigger even if `Self` has generic lifetime parameters. ```rs impl<'a> Foo<'a> { type Item = Foo<'a>; // Can be replaced with Self fn new() -> Self { Foo { // No lifetime, but they are inferred to be that of Self // Can be replaced as well ... } } // Don't replace `Foo<'b>`, the lifetime is different! fn eq<'b>(self, other: Foo<'b>) -> bool { .. } ``` Fixes #12381 --- clippy_lints/src/use_self.rs | 53 ++++++++++++++++++--- tests/ui/use_self.fixed | 18 +++++-- tests/ui/use_self.rs | 16 +++++-- tests/ui/use_self.stderr | 92 +++++++++++++++++++----------------- 4 files changed, 122 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index a1b08d105b9d9..b28037db1121e 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -8,11 +8,12 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor}; use rustc_hir::{ - self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, + self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, + ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty as MiddleTy; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -95,10 +96,9 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args - && parameters.as_ref().map_or(true, |params| { - params.parenthesized == GenericArgsParentheses::No - && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) - }) + && parameters + .as_ref() + .map_or(true, |params| params.parenthesized == GenericArgsParentheses::No) && !item.span.from_expansion() && !is_from_proc_macro(cx, item) // expensive, should be last check @@ -226,7 +226,12 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } else { hir_ty_to_ty(cx.tcx, hir_ty) } - && same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity()) + && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() + && same_type_and_consts(ty, impl_ty) + // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that + // the lifetime parameters of `ty` are ellided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in + // which case we must still trigger the lint. + && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty)) { span_lint(cx, hir_ty.span); } @@ -318,3 +323,37 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { span_lint(cx, span); } } + +/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns +/// `false`. +/// +/// This function does not check that types `a` and `b` are the same types. +fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { + use rustc_middle::ty::{Adt, GenericArgKind}; + match (&a.kind(), &b.kind()) { + (&Adt(_, args_a), &Adt(_, args_b)) => { + args_a + .iter() + .zip(args_b.iter()) + .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { + // TODO: Handle inferred lifetimes + (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), + _ => true, + }) + }, + _ => a == b, + } +} + +/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`. +fn has_no_lifetime(ty: MiddleTy<'_>) -> bool { + use rustc_middle::ty::{Adt, GenericArgKind}; + match ty.kind() { + &Adt(_, args) => !args + .iter() + // TODO: Handle inferred lifetimes + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(..))), + _ => true, + } +} diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 787dd3ec7e680..6ea7857a238da 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -6,7 +6,8 @@ clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::needless_lifetimes )] #[macro_use] @@ -53,6 +54,7 @@ mod better { } mod lifetimes { + #[derive(Clone, Copy)] struct Foo<'a> { foo_str: &'a str, } @@ -68,11 +70,19 @@ mod lifetimes { Foo { foo_str: "foo" } } - // FIXME: the lint does not handle lifetimed struct - // `Self` should be applicable here - fn clone(&self) -> Foo<'a> { + fn clone(&self) -> Self { Foo { foo_str: self.foo_str } } + + // Cannot replace with `Self` because the lifetime is not `'a`. + fn eq<'b>(&self, other: Foo<'b>) -> bool { + let x: Foo<'_> = other; + self.foo_str == other.foo_str + } + + fn f(&self) -> Foo<'_> { + *self + } } } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 39e182faea677..338cc00e45a81 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -6,7 +6,8 @@ clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::needless_lifetimes )] #[macro_use] @@ -53,6 +54,7 @@ mod better { } mod lifetimes { + #[derive(Clone, Copy)] struct Foo<'a> { foo_str: &'a str, } @@ -68,11 +70,19 @@ mod lifetimes { Foo { foo_str: "foo" } } - // FIXME: the lint does not handle lifetimed struct - // `Self` should be applicable here fn clone(&self) -> Foo<'a> { Foo { foo_str: self.foo_str } } + + // Cannot replace with `Self` because the lifetime is not `'a`. + fn eq<'b>(&self, other: Foo<'b>) -> bool { + let x: Foo<'_> = other; + self.foo_str == other.foo_str + } + + fn f(&self) -> Foo<'_> { + *self + } } } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 8d045f05ed286..d7aa8410a47be 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> tests/ui/use_self.rs:21:21 + --> tests/ui/use_self.rs:22:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -8,250 +8,256 @@ LL | fn new() -> Foo { = help: to override `-D warnings` add `#[allow(clippy::use_self)]` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:22:13 + --> tests/ui/use_self.rs:23:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:24:22 + --> tests/ui/use_self.rs:25:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:25:13 + --> tests/ui/use_self.rs:26:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:30:25 + --> tests/ui/use_self.rs:31:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:31:13 + --> tests/ui/use_self.rs:32:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:96:24 + --> tests/ui/use_self.rs:73:28 + | +LL | fn clone(&self) -> Foo<'a> { + | ^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self.rs:106:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:96:55 + --> tests/ui/use_self.rs:106:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:111:13 + --> tests/ui/use_self.rs:121:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:146:29 + --> tests/ui/use_self.rs:156:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:147:21 + --> tests/ui/use_self.rs:157:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:158:21 + --> tests/ui/use_self.rs:168:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:159:13 + --> tests/ui/use_self.rs:169:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:176:21 + --> tests/ui/use_self.rs:186:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:177:21 + --> tests/ui/use_self.rs:187:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:178:21 + --> tests/ui/use_self.rs:188:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:220:13 + --> tests/ui/use_self.rs:230:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:221:13 + --> tests/ui/use_self.rs:231:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:223:13 + --> tests/ui/use_self.rs:233:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:242:13 + --> tests/ui/use_self.rs:252:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:256:25 + --> tests/ui/use_self.rs:266:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:257:13 + --> tests/ui/use_self.rs:267:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:261:16 + --> tests/ui/use_self.rs:271:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:261:22 + --> tests/ui/use_self.rs:271:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:284:29 + --> tests/ui/use_self.rs:294:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:285:13 + --> tests/ui/use_self.rs:295:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:457:13 + --> tests/ui/use_self.rs:467:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:494:13 + --> tests/ui/use_self.rs:504:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:531:17 + --> tests/ui/use_self.rs:541:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:532:17 + --> tests/ui/use_self.rs:542:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:538:20 + --> tests/ui/use_self.rs:548:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:562:17 + --> tests/ui/use_self.rs:572:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:563:17 + --> tests/ui/use_self.rs:573:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:564:17 + --> tests/ui/use_self.rs:574:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:570:17 + --> tests/ui/use_self.rs:580:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:571:17 + --> tests/ui/use_self.rs:581:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:572:17 + --> tests/ui/use_self.rs:582:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:588:17 + --> tests/ui/use_self.rs:598:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:593:17 + --> tests/ui/use_self.rs:603:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:600:17 + --> tests/ui/use_self.rs:610:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:605:17 + --> tests/ui/use_self.rs:615:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:644:17 + --> tests/ui/use_self.rs:654:17 | LL | E::A => {}, | ^ help: use the applicable keyword: `Self` -error: aborting due to 42 previous errors +error: aborting due to 43 previous errors From 3a6cac7c06656b410fafe73713a72a9697fcb899 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Fri, 8 Mar 2024 09:32:38 +0800 Subject: [PATCH 013/102] add `rewrite_struct` proc-macro and test case --- tests/ui/auxiliary/proc_macro_attr.rs | 28 +++++++++++++---- tests/ui/missing_doc.rs | 12 ++++++++ tests/ui/missing_doc.stderr | 43 ++++++++++++++++++--------- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index 75f7a20f961b6..7b8f45f0b120a 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -12,7 +12,7 @@ use syn::spanned::Spanned; use syn::token::Star; use syn::{ parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, - Signature, TraitItem, Type, + Signature, TraitItem, Type, ItemStruct, Visibility, }; #[proc_macro_attribute] @@ -101,9 +101,7 @@ pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream { let mut item = parse_macro_input!(item as ItemFn); let span = item.block.brace_token.span; - if item.sig.asyncness.is_some() { - item.sig.asyncness = None; - } + item.sig.asyncness = None; let crate_name = quote! { fake_crate }; let block = item.block; @@ -128,7 +126,7 @@ pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream { - let mut async_fn = syn::parse_macro_input!(input as syn::ItemFn); + let mut async_fn = parse_macro_input!(input as syn::ItemFn); for stmt in &mut async_fn.block.stmts { if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt { @@ -145,3 +143,23 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream quote!(#async_fn).into() } + +#[proc_macro_attribute] +pub fn rewrite_struct(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut item_struct = parse_macro_input!(input as syn::ItemStruct); + // remove struct attributes including doc comments. + item_struct.attrs = vec![]; + if let Visibility::Public(token) = item_struct.vis { + // set vis to `pub(crate)` to trigger `missing_docs_in_private_items` lint. + let new_vis: Visibility = syn::parse_quote_spanned!(token.span() => pub(crate)); + item_struct.vis = new_vis; + } + if let syn::Fields::Named(fields) = &mut item_struct.fields { + for field in &mut fields.named { + // remove all attributes from fields as well. + field.attrs = vec![]; + } + } + + quote!(#item_struct).into() +} diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index 9bfad3b96cffd..9c936d7fa23e1 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -1,5 +1,6 @@ //@needs-asm-support //@aux-build: proc_macros.rs +//@aux-build: proc_macro_attr.rs #![warn(clippy::missing_docs_in_private_items)] // When denying at the crate level, be sure to not get random warnings from the @@ -8,6 +9,8 @@ //! Some garbage docs for the crate here #![doc = "More garbage"] +#[macro_use] +extern crate proc_macro_attr; extern crate proc_macros; use proc_macros::with_span; @@ -112,3 +115,12 @@ with_span!(span pub enum FooPm3 { A, B(u32), C { field: u32 }}); with_span!(span pub fn foo_pm() {}); with_span!(span pub static FOO_PM: u32 = 0;); with_span!(span pub const FOO2_PM: u32 = 0;); + +// issue #12197 +// Undocumented field originated inside of spanned proc-macro attribute +/// Some dox for struct. +#[rewrite_struct] +pub struct Test { + /// Dox + a: u8, +} diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index 7e66e2097e90f..09c296e01fbcc 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -1,5 +1,5 @@ error: missing documentation for a type alias - --> tests/ui/missing_doc.rs:16:1 + --> tests/ui/missing_doc.rs:19:1 | LL | type Typedef = String; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -8,19 +8,19 @@ LL | type Typedef = String; = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` error: missing documentation for a module - --> tests/ui/missing_doc.rs:19:1 + --> tests/ui/missing_doc.rs:22:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> tests/ui/missing_doc.rs:25:1 + --> tests/ui/missing_doc.rs:28:1 | LL | fn foo3() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> tests/ui/missing_doc.rs:39:1 + --> tests/ui/missing_doc.rs:42:1 | LL | / enum Baz { LL | | BazA { a: isize, b: isize }, @@ -29,43 +29,43 @@ LL | | } | |_^ error: missing documentation for a variant - --> tests/ui/missing_doc.rs:40:5 + --> tests/ui/missing_doc.rs:43:5 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> tests/ui/missing_doc.rs:40:12 + --> tests/ui/missing_doc.rs:43:12 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a struct field - --> tests/ui/missing_doc.rs:40:22 + --> tests/ui/missing_doc.rs:43:22 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a variant - --> tests/ui/missing_doc.rs:41:5 + --> tests/ui/missing_doc.rs:44:5 | LL | BarB, | ^^^^ error: missing documentation for a constant - --> tests/ui/missing_doc.rs:65:1 + --> tests/ui/missing_doc.rs:68:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> tests/ui/missing_doc.rs:74:1 + --> tests/ui/missing_doc.rs:77:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> tests/ui/missing_doc.rs:83:1 + --> tests/ui/missing_doc.rs:86:1 | LL | / mod internal_impl { LL | | /// dox @@ -77,16 +77,31 @@ LL | | } | |_^ error: missing documentation for a function - --> tests/ui/missing_doc.rs:88:5 + --> tests/ui/missing_doc.rs:91:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> tests/ui/missing_doc.rs:94:9 + --> tests/ui/missing_doc.rs:97:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: missing documentation for a struct + --> tests/ui/missing_doc.rs:123:1 + | +LL | / pub struct Test { +LL | | /// Dox +LL | | a: u8, +LL | | } + | |_^ + +error: missing documentation for a struct field + --> tests/ui/missing_doc.rs:125:5 + | +LL | a: u8, + | ^^^^^ + +error: aborting due to 15 previous errors From adc91e4913875e430d946852e28f8a18e40afdfb Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Fri, 8 Mar 2024 16:32:47 +0800 Subject: [PATCH 014/102] support manually search for docs in case attr was removed by proc macros --- clippy_lints/src/missing_doc.rs | 64 ++++++++++++++++++++++++--- tests/ui/auxiliary/proc_macro_attr.rs | 4 +- tests/ui/missing_doc.stderr | 17 +------ 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index bf4af7946f470..6878fb3349d4a 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -8,6 +8,7 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -32,6 +33,13 @@ declare_clippy_lint! { "detects missing documentation for private members" } +macro_rules! note_prev_span_then_ret { + ($prev_span:expr, $span:expr) => {{ + $prev_span = Some($span); + return; + }}; +} + pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. @@ -39,6 +47,8 @@ pub struct MissingDoc { /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. doc_hidden_stack: Vec, + /// Used to keep tracking of the previous item, field or variants etc, to get the search span. + prev_span: Option, } impl Default for MissingDoc { @@ -54,6 +64,7 @@ impl MissingDoc { Self { crate_items_only, doc_hidden_stack: vec![false], + prev_span: None, } } @@ -108,7 +119,8 @@ impl MissingDoc { let has_doc = attrs .iter() - .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); + .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())) + || matches!(self.search_span(sp), Some(span) if span_to_snippet_contains_docs(cx, span)); if !has_doc { span_lint( @@ -119,6 +131,32 @@ impl MissingDoc { ); } } + + /// Return a span to search for doc comments manually. + /// + /// # Example + /// ```ignore + /// fn foo() { ... } + /// ^^^^^^^^^^^^^^^^ prev_span + /// ↑ + /// | search_span | + /// ↓ + /// fn bar() { ... } + /// ^^^^^^^^^^^^^^^^ cur_span + /// ``` + fn search_span(&self, cur_span: Span) -> Option { + let prev_span = self.prev_span?; + let start_pos = if prev_span.contains(cur_span) { + // In case when the prev_span is an entire struct, or enum, + // and the current span is a field, or variant, we need to search from + // the starting pos of the previous span. + prev_span.lo() + } else { + prev_span.hi() + }; + let search_span = cur_span.with_lo(start_pos).with_hi(cur_span.lo()); + Some(search_span) + } } impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); @@ -138,6 +176,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { self.check_missing_docs_attrs(cx, CRATE_DEF_ID, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate"); } + fn check_crate_post(&mut self, _: &LateContext<'tcx>) { + self.prev_span = None; + } + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { match it.kind { hir::ItemKind::Fn(..) => { @@ -145,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if it.ident.name == sym::main { let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; if at_root { - return; + note_prev_span_then_ret!(self.prev_span, it.span); } } }, @@ -164,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::Impl { .. } - | hir::ItemKind::Use(..) => return, + | hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span), }; let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); @@ -173,6 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, it) { self.check_missing_docs_attrs(cx, it.owner_id.def_id, attrs, it.span, article, desc); } + self.prev_span = Some(it.span); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { @@ -182,16 +225,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, trait_item) { self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, attrs, trait_item.span, article, desc); } + self.prev_span = Some(trait_item.span); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { if cx.tcx.impl_trait_ref(cid).is_some() { - return; + note_prev_span_then_ret!(self.prev_span, impl_item.span); } } else { - return; + note_prev_span_then_ret!(self.prev_span, impl_item.span); } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); @@ -199,6 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, impl_item) { self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, attrs, impl_item.span, article, desc); } + self.prev_span = Some(impl_item.span); } fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { @@ -208,6 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); } } + self.prev_span = Some(sf.span); } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { @@ -215,5 +261,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, v) { self.check_missing_docs_attrs(cx, v.def_id, attrs, v.span, "a", "variant"); } + self.prev_span = Some(v.span); } } + +fn span_to_snippet_contains_docs(cx: &LateContext<'_>, search_span: Span) -> bool { + let Some(snippet) = snippet_opt(cx, search_span) else { + return false; + }; + snippet.lines().rev().any(|line| line.trim().starts_with("///")) +} diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index 7b8f45f0b120a..b550d13b0b9ae 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -11,8 +11,8 @@ use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::token::Star; use syn::{ - parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, - Signature, TraitItem, Type, ItemStruct, Visibility, + parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent, + PatType, Signature, TraitItem, Type, Visibility, }; #[proc_macro_attribute] diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index 09c296e01fbcc..ef0f96a5b7130 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -88,20 +88,5 @@ error: missing documentation for a function LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a struct - --> tests/ui/missing_doc.rs:123:1 - | -LL | / pub struct Test { -LL | | /// Dox -LL | | a: u8, -LL | | } - | |_^ - -error: missing documentation for a struct field - --> tests/ui/missing_doc.rs:125:5 - | -LL | a: u8, - | ^^^^^ - -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors From 158b70a1eda04c9e8ffd9a3eea99958ccea380df Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 26 Feb 2024 21:25:27 -0500 Subject: [PATCH 015/102] Distinguish between library and lang UB in assert_unsafe_precondition --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e369cb9d0a40e..dadb0d662ce8f 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -174,7 +174,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _) + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); From 2dd085fb001cb061ee3b5ccd847135dd89c56bc4 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 17 Jan 2024 14:50:49 +0100 Subject: [PATCH 016/102] Allow lint where we don't care --- tests/ui/manual_range_patterns.fixed | 1 + tests/ui/manual_range_patterns.rs | 1 + tests/ui/manual_range_patterns.stderr | 38 +++++++++++++-------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed index b348d7071f6e9..e9f6fbcc3fc8c 100644 --- a/tests/ui/manual_range_patterns.fixed +++ b/tests/ui/manual_range_patterns.fixed @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(non_contiguous_range_endpoints)] #![warn(clippy::manual_range_patterns)] #![feature(exclusive_range_pattern)] diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs index a0750f54b73f1..d525aaa24ad1b 100644 --- a/tests/ui/manual_range_patterns.rs +++ b/tests/ui/manual_range_patterns.rs @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(non_contiguous_range_endpoints)] #![warn(clippy::manual_range_patterns)] #![feature(exclusive_range_pattern)] diff --git a/tests/ui/manual_range_patterns.stderr b/tests/ui/manual_range_patterns.stderr index 7c19fdd475f19..af9256aeea390 100644 --- a/tests/ui/manual_range_patterns.stderr +++ b/tests/ui/manual_range_patterns.stderr @@ -1,5 +1,5 @@ error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:8:25 + --> tests/ui/manual_range_patterns.rs:9:25 | LL | let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` @@ -8,109 +8,109 @@ LL | let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10); = help: to override `-D warnings` add `#[allow(clippy::manual_range_patterns)]` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:9:25 + --> tests/ui/manual_range_patterns.rs:10:25 | LL | let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:16:25 + --> tests/ui/manual_range_patterns.rs:17:25 | LL | let _ = matches!(f, 1 | (2..=4)); | ^^^^^^^^^^^ help: try: `1..=4` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:17:25 + --> tests/ui/manual_range_patterns.rs:18:25 | LL | let _ = matches!(f, 1 | (2..4)); | ^^^^^^^^^^ help: try: `1..4` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:18:25 + --> tests/ui/manual_range_patterns.rs:19:25 | LL | let _ = matches!(f, (1..=10) | (2..=13) | (14..=48324728) | 48324729); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=48324729` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:19:25 + --> tests/ui/manual_range_patterns.rs:20:25 | LL | let _ = matches!(f, 0 | (1..=10) | 48324730 | (2..=13) | (14..=48324728) | 48324729); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=48324730` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:20:25 + --> tests/ui/manual_range_patterns.rs:21:25 | LL | let _ = matches!(f, 0..=1 | 0..=2 | 0..=3); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=3` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:23:9 + --> tests/ui/manual_range_patterns.rs:24:9 | LL | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 => true, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:26:25 + --> tests/ui/manual_range_patterns.rs:27:25 | LL | let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1 | 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-5..=3` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:28:25 + --> tests/ui/manual_range_patterns.rs:29:25 | LL | let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1_000_001..=1_000_001` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:31:17 + --> tests/ui/manual_range_patterns.rs:32:17 | LL | matches!(f, 0x00 | 0x01 | 0x02 | 0x03); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x03` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:32:17 + --> tests/ui/manual_range_patterns.rs:33:17 | LL | matches!(f, 0x00..=0x05 | 0x06 | 0x07); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x07` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:33:17 + --> tests/ui/manual_range_patterns.rs:34:17 | LL | matches!(f, -0x09 | -0x08 | -0x07..=0x00); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-0x09..=0x00` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:35:17 + --> tests/ui/manual_range_patterns.rs:36:17 | LL | matches!(f, 0..5 | 5); | ^^^^^^^^ help: try: `0..=5` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:36:17 + --> tests/ui/manual_range_patterns.rs:37:17 | LL | matches!(f, 0 | 1..5); | ^^^^^^^^ help: try: `0..5` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:38:17 + --> tests/ui/manual_range_patterns.rs:39:17 | LL | matches!(f, 0..=5 | 6..10); | ^^^^^^^^^^^^^ help: try: `0..10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:39:17 + --> tests/ui/manual_range_patterns.rs:40:17 | LL | matches!(f, 0..5 | 5..=10); | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:40:17 + --> tests/ui/manual_range_patterns.rs:41:17 | LL | matches!(f, 5..=10 | 0..5); | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:44:26 + --> tests/ui/manual_range_patterns.rs:45:26 | LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` From 4e95b4a0261d7b853c7180d95104070cfe0f08be Mon Sep 17 00:00:00 2001 From: kcz Date: Fri, 8 Mar 2024 21:50:45 -0500 Subject: [PATCH 017/102] [`mut_mut`]: Fix duplicate diags --- clippy_lints/src/mut_mut.rs | 62 ++++++++++++++++--------------------- tests/ui/mut_mut.rs | 1 - tests/ui/mut_mut.stderr | 18 +++++------ 3 files changed, 36 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 72a2cca1e4024..beb6f170e4c27 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -35,9 +35,34 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) { - use rustc_hir::intravisit::Visitor; + if in_external_macro(cx.sess(), ty.span) { + return; + } - MutVisitor { cx }.visit_ty(ty); + if let hir::TyKind::Ref( + _, + hir::MutTy { + ty: pty, + mutbl: hir::Mutability::Mut, + }, + ) = ty.kind + { + if let hir::TyKind::Ref( + _, + hir::MutTy { + mutbl: hir::Mutability::Mut, + .. + }, + ) = pty.kind + { + span_lint( + cx, + MUT_MUT, + ty.span, + "generally you want to avoid `&mut &mut _` if possible", + ); + } + } } } @@ -80,37 +105,4 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { } } } - - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { - if in_external_macro(self.cx.sess(), ty.span) { - return; - } - - if let hir::TyKind::Ref( - _, - hir::MutTy { - ty: pty, - mutbl: hir::Mutability::Mut, - }, - ) = ty.kind - { - if let hir::TyKind::Ref( - _, - hir::MutTy { - mutbl: hir::Mutability::Mut, - .. - }, - ) = pty.kind - { - span_lint( - self.cx, - MUT_MUT, - ty.span, - "generally you want to avoid `&mut &mut _` if possible", - ); - } - } - - intravisit::walk_ty(self, ty); - } } diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index 288b003405d15..b454da3d20933 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::mut_mut)] #![allow(unused)] diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 73f2410a252ea..42853fdc008c1 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:16:11 + --> tests/ui/mut_mut.rs:15:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | fn fun(x: &mut &mut u32) -> bool { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:33:17 + --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:25 + --> tests/ui/mut_mut.rs:47:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); | ^ @@ -22,37 +22,37 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> tests/ui/mut_mut.rs:35:21 + --> tests/ui/mut_mut.rs:34:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:39:32 + --> tests/ui/mut_mut.rs:38:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:39:16 + --> tests/ui/mut_mut.rs:38:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:44:37 + --> tests/ui/mut_mut.rs:43:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:44:16 + --> tests/ui/mut_mut.rs:43:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:44:21 + --> tests/ui/mut_mut.rs:43:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ From 7473f0522cd59259c1f2d3d25ff2d3351700fe22 Mon Sep 17 00:00:00 2001 From: kcz Date: Fri, 8 Mar 2024 22:54:06 -0500 Subject: [PATCH 018/102] [`no_effect_replace`]: Fix duplicate diagnostics --- clippy_lints/src/methods/no_effect_replace.rs | 1 + tests/ui/no_effect_replace.rs | 2 -- tests/ui/no_effect_replace.stderr | 16 ++++++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index 81df32bdee2bc..a301a5f7d65be 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -25,6 +25,7 @@ pub(super) fn check<'tcx>( && param1 == param2.as_str() { span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + return; } if SpanlessEq::new(cx).eq_expr(arg1, arg2) { diff --git a/tests/ui/no_effect_replace.rs b/tests/ui/no_effect_replace.rs index 2a940d87fb944..e4fd5caae2a5d 100644 --- a/tests/ui/no_effect_replace.rs +++ b/tests/ui/no_effect_replace.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::no_effect_replace)] fn main() { diff --git a/tests/ui/no_effect_replace.stderr b/tests/ui/no_effect_replace.stderr index ad2dcd2cc9b35..ded86c5c5b8d2 100644 --- a/tests/ui/no_effect_replace.stderr +++ b/tests/ui/no_effect_replace.stderr @@ -1,5 +1,5 @@ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:6:13 + --> tests/ui/no_effect_replace.rs:4:13 | LL | let _ = "12345".replace('1', "1"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,43 +8,43 @@ LL | let _ = "12345".replace('1', "1"); = help: to override `-D warnings` add `#[allow(clippy::no_effect_replace)]` error: replacing text with itself - --> tests/ui/no_effect_replace.rs:9:13 + --> tests/ui/no_effect_replace.rs:7:13 | LL | let _ = "12345".replace("12", "12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:11:13 + --> tests/ui/no_effect_replace.rs:9:13 | LL | let _ = String::new().replace("12", "12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:14:13 + --> tests/ui/no_effect_replace.rs:12:13 | LL | let _ = "12345".replacen('1', "1", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:16:13 + --> tests/ui/no_effect_replace.rs:14:13 | LL | let _ = "12345".replacen("12", "12", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:18:13 + --> tests/ui/no_effect_replace.rs:16:13 | LL | let _ = String::new().replacen("12", "12", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:25:13 + --> tests/ui/no_effect_replace.rs:23:13 | LL | let _ = "hello".replace(&x.f(), &x.f()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:29:13 + --> tests/ui/no_effect_replace.rs:27:13 | LL | let _ = "hello".replace(&y(), &y()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8d78cd17e15eb22174f62d7f2267f35948bccc62 Mon Sep 17 00:00:00 2001 From: "Christopher B. Speir" Date: Fri, 8 Mar 2024 19:45:40 -0600 Subject: [PATCH 019/102] Fix duplicate lint emission from [`else_if_without_else`] --- clippy_lints/src/else_if_without_else.rs | 26 +++++---- tests/ui/else_if_without_else.rs | 68 ++++++++++++++++++++++-- tests/ui/else_if_without_else.stderr | 30 +++++++++-- 3 files changed, 104 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 47780cab9ede8..a6ca7fe9e0bb3 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -49,24 +49,22 @@ declare_clippy_lint! { declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); impl EarlyLintPass for ElseIfWithoutElse { - fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { if in_external_macro(cx.sess(), item.span) { return; } - while let ExprKind::If(_, _, Some(ref els)) = item.kind { - if let ExprKind::If(_, _, None) = els.kind { - span_lint_and_help( - cx, - ELSE_IF_WITHOUT_ELSE, - els.span, - "`if` expression with an `else if`, but without a final `else`", - None, - "add an `else` block here", - ); - } - - item = els; + if let ExprKind::If(_, _, Some(ref els)) = item.kind + && let ExprKind::If(_, _, None) = els.kind + { + span_lint_and_help( + cx, + ELSE_IF_WITHOUT_ELSE, + els.span, + "`if` expression with an `else if`, but without a final `else`", + None, + "add an `else` block here", + ); } } } diff --git a/tests/ui/else_if_without_else.rs b/tests/ui/else_if_without_else.rs index e7786f7dd27da..b04c22fa2aed6 100644 --- a/tests/ui/else_if_without_else.rs +++ b/tests/ui/else_if_without_else.rs @@ -1,7 +1,5 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - -#![warn(clippy::all)] #![warn(clippy::else_if_without_else)] +#![allow(clippy::collapsible_else_if)] fn bla1() -> bool { unimplemented!() @@ -12,6 +10,12 @@ fn bla2() -> bool { fn bla3() -> bool { unimplemented!() } +fn bla4() -> bool { + unimplemented!() +} +fn bla5() -> bool { + unimplemented!() +} fn main() { if bla1() { @@ -57,4 +61,62 @@ fn main() { //~^ ERROR: `if` expression with an `else if`, but without a final `else` println!("else if 2"); } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + println!("else if 4"); + } else { + println!("else"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + //~^ ERROR: `if` expression with an `else if`, but without a final `else` + println!("else if 4"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else { + if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + println!("else if 4"); + } else { + println!("else"); + } + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else { + if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + //~^ ERROR: `if` expression with an `else if`, but without a final `else` + println!("else if 4"); + } + } } diff --git a/tests/ui/else_if_without_else.stderr b/tests/ui/else_if_without_else.stderr index 3bb840f39e78f..bc7174852293a 100644 --- a/tests/ui/else_if_without_else.stderr +++ b/tests/ui/else_if_without_else.stderr @@ -1,5 +1,5 @@ error: `if` expression with an `else if`, but without a final `else` - --> tests/ui/else_if_without_else.rs:47:12 + --> tests/ui/else_if_without_else.rs:51:12 | LL | } else if bla2() { | ____________^ @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::else_if_without_else)]` error: `if` expression with an `else if`, but without a final `else` - --> tests/ui/else_if_without_else.rs:56:12 + --> tests/ui/else_if_without_else.rs:60:12 | LL | } else if bla3() { | ____________^ @@ -24,5 +24,29 @@ LL | | } | = help: add an `else` block here -error: aborting due to 2 previous errors +error: `if` expression with an `else if`, but without a final `else` + --> tests/ui/else_if_without_else.rs:87:12 + | +LL | } else if bla5() { + | ____________^ +LL | | +LL | | println!("else if 4"); +LL | | } + | |_____^ + | + = help: add an `else` block here + +error: `if` expression with an `else if`, but without a final `else` + --> tests/ui/else_if_without_else.rs:117:16 + | +LL | } else if bla5() { + | ________________^ +LL | | +LL | | println!("else if 4"); +LL | | } + | |_________^ + | + = help: add an `else` block here + +error: aborting due to 4 previous errors From f34804d807a8ca576c3a9096064a8b95ff7d0c2e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 28 Feb 2024 17:25:17 +0100 Subject: [PATCH 020/102] Add new `duplicated_attributes` lint --- CHANGELOG.md | 1 + .../src/attrs/duplicated_attributes.rs | 64 +++++++++++++++++++ clippy_lints/src/attrs/mod.rs | 35 +++++++++- clippy_lints/src/declared_lints.rs | 1 + 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/attrs/duplicated_attributes.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e783b593f90e..846b6b6b701e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5157,6 +5157,7 @@ Released 2018-09-13 [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument +[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs new file mode 100644 index 0000000000000..3c5ac597fd5d4 --- /dev/null +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -0,0 +1,64 @@ +use super::DUPLICATED_ATTRIBUTES; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::{Attribute, MetaItem}; +use rustc_data_structures::fx::FxHashMap; +use rustc_lint::EarlyContext; +use rustc_span::{sym, Span}; +use std::collections::hash_map::Entry; + +fn emit_if_duplicated( + cx: &EarlyContext<'_>, + attr: &MetaItem, + attr_paths: &mut FxHashMap, + complete_path: String, +) { + match attr_paths.entry(complete_path) { + Entry::Vacant(v) => { + v.insert(attr.span); + }, + Entry::Occupied(o) => { + span_lint_and_then(cx, DUPLICATED_ATTRIBUTES, attr.span, "duplicated attribute", |diag| { + diag.span_note(*o.get(), "first defined here"); + diag.span_help(attr.span, "remove this attribute"); + }); + }, + } +} + +fn check_duplicated_attr( + cx: &EarlyContext<'_>, + attr: &MetaItem, + attr_paths: &mut FxHashMap, + parent: &mut Vec, +) { + let Some(ident) = attr.ident() else { return }; + let name = ident.name; + if name == sym::doc || name == sym::cfg_attr { + // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg + // conditions are the same. + return; + } + if let Some(value) = attr.value_str() { + emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}={value}", parent.join(":"))); + } else if let Some(sub_attrs) = attr.meta_item_list() { + parent.push(name.as_str().to_string()); + for sub_attr in sub_attrs { + if let Some(meta) = sub_attr.meta_item() { + check_duplicated_attr(cx, meta, attr_paths, parent); + } + } + parent.pop(); + } else { + emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.join(":"))); + } +} + +pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { + let mut attr_paths = FxHashMap::default(); + + for attr in attrs { + if let Some(meta) = attr.meta() { + check_duplicated_attr(cx, &meta, &mut attr_paths, &mut Vec::new()); + } + } +} diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index c4c65d3248a7d..675c428948f68 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -4,6 +4,7 @@ mod allow_attributes_without_reason; mod blanket_clippy_restriction_lints; mod deprecated_cfg_attr; mod deprecated_semver; +mod duplicated_attributes; mod empty_line_after; mod inline_always; mod maybe_misused_cfg; @@ -16,7 +17,7 @@ mod useless_attribute; mod utils; use clippy_config::msrvs::Msrv; -use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; +use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, impl_lint_pass}; @@ -489,6 +490,32 @@ declare_clippy_lint! { "item has both inner and outer attributes" } +declare_clippy_lint! { + /// ### What it does + /// Checks for attributes that appear two or more times. + /// + /// ### Why is this bad? + /// Repeating an attribute on the same item (or globally on the same crate) + /// is unnecessary and doesn't have an effect. + /// + /// ### Example + /// ```no_run + /// #[allow(dead_code)] + /// #[allow(dead_code)] + /// fn foo() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[allow(dead_code)] + /// fn foo() {} + /// ``` + #[clippy::version = "1.78.0"] + pub DUPLICATED_ATTRIBUTES, + suspicious, + "duplicated attribute" +} + declare_lint_pass!(Attributes => [ ALLOW_ATTRIBUTES_WITHOUT_REASON, INLINE_ALWAYS, @@ -568,12 +595,18 @@ impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, MIXED_ATTRIBUTES_STYLE, + DUPLICATED_ATTRIBUTES, ]); impl EarlyLintPass for EarlyAttributes { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + duplicated_attributes::check(cx, &krate.attrs); + } + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { empty_line_after::check(cx, item); mixed_attributes_style::check(cx, item); + duplicated_attributes::check(cx, &item.attrs); } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7a0d57c7859ee..62da9ffc33040 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -54,6 +54,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::attrs::DEPRECATED_CFG_ATTR_INFO, crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, + crate::attrs::DUPLICATED_ATTRIBUTES_INFO, crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, From 749e225cd64b26197b62a179a11e0040dcbef8ad Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 28 Feb 2024 17:25:28 +0100 Subject: [PATCH 021/102] Add ui test for new `duplicated_attributes` lint --- tests/ui/duplicated_attributes.rs | 17 ++++ tests/ui/duplicated_attributes.stderr | 123 ++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 tests/ui/duplicated_attributes.rs create mode 100644 tests/ui/duplicated_attributes.stderr diff --git a/tests/ui/duplicated_attributes.rs b/tests/ui/duplicated_attributes.rs new file mode 100644 index 0000000000000..0f036c684c1ca --- /dev/null +++ b/tests/ui/duplicated_attributes.rs @@ -0,0 +1,17 @@ +#![warn(clippy::duplicated_attributes)] +#![cfg(any(unix, windows))] +#![allow(dead_code)] +#![allow(dead_code)] //~ ERROR: duplicated attribute +#![cfg(any(unix, windows))] +//~^ ERROR: duplicated attribute +//~| ERROR: duplicated attribute + +#[cfg(any(unix, windows, target_os = "linux"))] +#[allow(dead_code)] +#[allow(dead_code)] //~ ERROR: duplicated attribute +#[cfg(any(unix, windows, target_os = "linux"))] +//~^ ERROR: duplicated attribute +//~| ERROR: duplicated attribute +fn foo() {} + +fn main() {} diff --git a/tests/ui/duplicated_attributes.stderr b/tests/ui/duplicated_attributes.stderr new file mode 100644 index 0000000000000..1c6578dbb43a7 --- /dev/null +++ b/tests/ui/duplicated_attributes.stderr @@ -0,0 +1,123 @@ +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:4:10 + | +LL | #![allow(dead_code)] + | ^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:3:10 + | +LL | #![allow(dead_code)] + | ^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:4:10 + | +LL | #![allow(dead_code)] + | ^^^^^^^^^ + = note: `-D clippy::duplicated-attributes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:5:12 + | +LL | #![cfg(any(unix, windows))] + | ^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:2:12 + | +LL | #![cfg(any(unix, windows))] + | ^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:5:12 + | +LL | #![cfg(any(unix, windows))] + | ^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:5:18 + | +LL | #![cfg(any(unix, windows))] + | ^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:2:18 + | +LL | #![cfg(any(unix, windows))] + | ^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:5:18 + | +LL | #![cfg(any(unix, windows))] + | ^^^^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:11:9 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:10:9 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:11:9 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:12:11 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:9:11 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:12:11 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:12:17 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:9:17 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:12:17 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:12:26 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^^^^^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:9:26 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^^^^^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:12:26 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + From d57d001543474a4efd37384ed873dd528f14d383 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 28 Feb 2024 17:37:57 +0100 Subject: [PATCH 022/102] Update `cargo dev update_lints` command to fix new warning emitted by `duplicated_attributes` --- clippy_dev/src/update_lints.rs | 2 + tests/ui/rename.fixed | 1 + tests/ui/rename.rs | 1 + tests/ui/rename.stderr | 116 ++++++++++++++++----------------- 4 files changed, 62 insertions(+), 58 deletions(-) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 2222abff7adfb..76ae26dddf4de 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -689,6 +689,8 @@ fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { let mut seen_lints = HashSet::new(); let mut res: String = GENERATED_FILE_COMMENT.into(); + + res.push_str("#![allow(clippy::duplicated_attributes)]\n"); for lint in lints { if seen_lints.insert(&lint.new_name) { writeln!(res, "#![allow({})]", lint.new_name).unwrap(); diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index f4ff0f0b88bdc..24d0f79754289 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -2,6 +2,7 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. +#![allow(clippy::duplicated_attributes)] #![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 0df1098f5fb6f..be8da2fa1a38e 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -2,6 +2,7 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. +#![allow(clippy::duplicated_attributes)] #![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index e6659b109e520..777ac20153da3 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:55:9 + --> tests/ui/rename.rs:56:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,343 +8,343 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:56:9 + --> tests/ui/rename.rs:57:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:57:9 + --> tests/ui/rename.rs:58:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:58:9 + --> tests/ui/rename.rs:59:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:59:9 + --> tests/ui/rename.rs:60:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:60:9 + --> tests/ui/rename.rs:61:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:61:9 + --> tests/ui/rename.rs:62:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:62:9 + --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:63:9 + --> tests/ui/rename.rs:64:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:64:9 + --> tests/ui/rename.rs:65:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:66:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | 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` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107: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:107:9 + --> tests/ui/rename.rs:108: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 `temporary_cstring_as_ptr` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110: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:110:9 + --> tests/ui/rename.rs:111: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:111:9 + --> tests/ui/rename.rs:112: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:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` From ae52a9d428209894a67488d67458ce7e0031b2df Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 28 Feb 2024 17:38:24 +0100 Subject: [PATCH 023/102] Update ui tests --- tests/ui/allow_attributes_without_reason.rs | 2 +- tests/ui/allow_attributes_without_reason.stderr | 4 ++-- tests/ui/empty_line_after_doc_comments.rs | 2 +- tests/ui/empty_line_after_outer_attribute.rs | 2 +- tests/ui/mixed_attributes_style.rs | 1 + tests/ui/mixed_attributes_style.stderr | 6 +++--- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/ui/allow_attributes_without_reason.rs b/tests/ui/allow_attributes_without_reason.rs index 663c2eb2c3794..523148d658698 100644 --- a/tests/ui/allow_attributes_without_reason.rs +++ b/tests/ui/allow_attributes_without_reason.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(lint_reasons)] #![deny(clippy::allow_attributes_without_reason)] -#![allow(unfulfilled_lint_expectations)] +#![allow(unfulfilled_lint_expectations, clippy::duplicated_attributes)] extern crate proc_macros; use proc_macros::{external, with_span}; diff --git a/tests/ui/allow_attributes_without_reason.stderr b/tests/ui/allow_attributes_without_reason.stderr index 3c81233bf7777..770a771ec3d15 100644 --- a/tests/ui/allow_attributes_without_reason.stderr +++ b/tests/ui/allow_attributes_without_reason.stderr @@ -1,8 +1,8 @@ error: `allow` attribute without specifying a reason --> tests/ui/allow_attributes_without_reason.rs:4:1 | -LL | #![allow(unfulfilled_lint_expectations)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![allow(unfulfilled_lint_expectations, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try adding a reason at the end with `, reason = ".."` note: the lint level is defined here diff --git a/tests/ui/empty_line_after_doc_comments.rs b/tests/ui/empty_line_after_doc_comments.rs index e843770f57854..dd78491749c77 100644 --- a/tests/ui/empty_line_after_doc_comments.rs +++ b/tests/ui/empty_line_after_doc_comments.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::empty_line_after_doc_comments)] -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index 269e66ea0a816..f147cf2cd5d1e 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::empty_line_after_outer_attr)] -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/tests/ui/mixed_attributes_style.rs b/tests/ui/mixed_attributes_style.rs index ad93e3019fa65..4f89aa8a5e552 100644 --- a/tests/ui/mixed_attributes_style.rs +++ b/tests/ui/mixed_attributes_style.rs @@ -1,4 +1,5 @@ #![warn(clippy::mixed_attributes_style)] +#![allow(clippy::duplicated_attributes)] #[allow(unused)] //~ ERROR: item has both inner and outer attributes fn foo1() { diff --git a/tests/ui/mixed_attributes_style.stderr b/tests/ui/mixed_attributes_style.stderr index d1d5cd3f47f31..ed798073cb7c7 100644 --- a/tests/ui/mixed_attributes_style.stderr +++ b/tests/ui/mixed_attributes_style.stderr @@ -1,5 +1,5 @@ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:3:1 + --> tests/ui/mixed_attributes_style.rs:4:1 | LL | / #[allow(unused)] LL | | fn foo1() { @@ -10,7 +10,7 @@ LL | | #![allow(unused)] = help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]` error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:17:1 + --> tests/ui/mixed_attributes_style.rs:18:1 | LL | / /// linux LL | | @@ -19,7 +19,7 @@ LL | | //! windows | |_______________^ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:32:1 + --> tests/ui/mixed_attributes_style.rs:33:1 | LL | / #[allow(unused)] LL | | mod bar { From fa4e3aac198d14dbc5c66877af1127501fd87616 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 9 Mar 2024 00:50:46 +0100 Subject: [PATCH 024/102] add documentation to the `span_lint_hir` functions --- clippy.toml | 4 ++-- clippy_utils/src/diagnostics.rs | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/clippy.toml b/clippy.toml index 8c405ac6a4e82..3a68fa1670df6 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,7 +1,7 @@ avoid-breaking-exported-api = false -# use the various `span_lint_*` methods instead, which also add a link to the docs +# use the various `clippy_utils::diagnostics::span_lint_*` functions instead, which also add a link to the docs disallowed-methods = [ "rustc_lint::context::LintContext::span_lint", - "rustc_middle::ty::context::TyCtxt::node_span_lint" + "rustc_middle::ty::context::TyCtxt::node_span_lint", ] diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 6ed46e5dde041..303b23ba3a0fc 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -152,6 +152,25 @@ where }); } +/// Like [`span_lint`], but allows providing the `HirId` of the node that caused us to emit this +/// lint. +/// +/// The `HirId` is used for checking lint level attributes. +/// +/// For example: +/// ```ignore +/// fn f() { /* '1 */ +/// +/// #[allow(clippy::some_lint)] +/// let _x = /* '2 */; +/// } +/// ``` +/// If `some_lint` does its analysis in `LintPass::check_fn` (at `'1`) and emits a lint at `'2` +/// using [`span_lint`], then allowing the lint at `'2` as attempted in the snippet will not work! +/// Even though that is where the warning points at, which would be confusing to users. +/// +/// Instead, use this function and also pass the `HirId` of the node at `'2`, which will let the +/// compiler check lint level attributes at `'2` as one would expect, and the `#[allow]` will work. pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { #[expect(clippy::disallowed_methods)] cx.tcx.node_span_lint(lint, hir_id, sp, msg.to_string(), |diag| { @@ -159,6 +178,25 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s }); } +/// Like [`span_lint_and_then`], but allows providing the `HirId` of the node that caused us to emit +/// this lint. +/// +/// The `HirId` is used for checking lint level attributes. +/// +/// For example: +/// ```ignore +/// fn f() { /* '1 */ +/// +/// #[allow(clippy::some_lint)] +/// let _x = /* '2 */; +/// } +/// ``` +/// If `some_lint` does its analysis in `LintPass::check_fn` (at `'1`) and emits a lint at `'2` +/// using [`span_lint_and_then`], then allowing the lint at `'2` as attempted in the snippet will +/// not work! Even though that is where the warning points at, which would be confusing to users. +/// +/// Instead, use this function and also pass the `HirId` of the node at `'2`, which will let the +/// compiler check lint level attributes at `'2` as one would expect, and the `#[allow]` will work. pub fn span_lint_hir_and_then( cx: &LateContext<'_>, lint: &'static Lint, From ced8bc5b8f105745d199da526ab262d17995d503 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:09:17 +0100 Subject: [PATCH 025/102] use `span_lint_hir` instead of `span_lint` in more lints --- clippy_lints/src/let_if_seq.rs | 5 +- clippy_lints/src/mut_mut.rs | 8 ++-- clippy_lints/src/read_zero_byte_vec.rs | 56 ++++++++++++++-------- clippy_lints/src/redundant_closure_call.rs | 5 +- clippy_lints/src/returns.rs | 31 ++++++++---- clippy_lints/src/unused_io_amount.rs | 16 +++---- clippy_lints/src/unused_peekable.rs | 10 ++-- tests/ui/let_if_seq.rs | 11 +++++ tests/ui/let_if_seq.stderr | 8 ++-- tests/ui/mut_mut.rs | 5 ++ tests/ui/needless_return.fixed | 7 +++ tests/ui/needless_return.rs | 7 +++ tests/ui/read_zero_byte_vec.rs | 6 +++ tests/ui/unused_io_amount.rs | 5 ++ tests/ui/unused_peekable.rs | 6 +++ 15 files changed, 134 insertions(+), 52 deletions(-) diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 270162ae7717f..a2f1676887407 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.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::path_to_local_id; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; @@ -122,9 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { value=snippet(cx, value.span, ""), default=snippet(cx, default.span, ""), ); - span_lint_and_then( + span_lint_hir_and_then( cx, USELESS_LET_IF_SEQ, + local.hir_id, span, "`if _ { .. } else { .. }` is an expression", |diag| { diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index beb6f170e4c27..bc7b2c6b7c1b8 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_hir}; use clippy_utils::higher; use rustc_hir as hir; use rustc_hir::intravisit; @@ -87,17 +87,19 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { intravisit::walk_expr(self, body); } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { - span_lint( + span_lint_hir( self.cx, MUT_MUT, + expr.hir_id, 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.param_env) { - span_lint( + 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/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 650324d4249eb..302c9f286525c 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::get_enclosing_block; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; @@ -77,37 +77,53 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { if let Some(expr) = visitor.read_zero_expr { let applicability = Applicability::MaybeIncorrect; match vec_init_kind { - VecInitKind::WithConstCapacity(len) => { - span_lint_and_sugg( + VecInitKind::WithConstCapacity(len) => span_lint_hir_and_then( + cx, + READ_ZERO_BYTE_VEC, + expr.hir_id, + expr.span, + "reading zero byte data to `Vec`", + |diag| { + diag.span_suggestion( + expr.span, + "try", + format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")), + applicability, + ); + }, + ), + VecInitKind::WithExprCapacity(hir_id) => { + let e = cx.tcx.hir().expect_expr(hir_id); + span_lint_hir_and_then( cx, READ_ZERO_BYTE_VEC, + expr.hir_id, expr.span, "reading zero byte data to `Vec`", - "try", - format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")), - applicability, + |diag| { + diag.span_suggestion( + expr.span, + "try", + format!( + "{}.resize({}, 0); {}", + ident.as_str(), + snippet(cx, e.span, ".."), + snippet(cx, expr.span, "..") + ), + applicability, + ); + }, ); }, - VecInitKind::WithExprCapacity(hir_id) => { - let e = cx.tcx.hir().expect_expr(hir_id); - span_lint_and_sugg( + _ => { + span_lint_hir( cx, READ_ZERO_BYTE_VEC, + expr.hir_id, expr.span, "reading zero byte data to `Vec`", - "try", - format!( - "{}.resize({}, 0); {}", - ident.as_str(), - snippet(cx, e.span, ".."), - snippet(cx, expr.span, "..") - ), - applicability, ); }, - _ => { - span_lint(cx, READ_ZERO_BYTE_VEC, expr.span, "reading zero byte data to `Vec`"); - }, } } } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index f61527cc0a9fe..d53b3e1183659 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -1,5 +1,5 @@ use crate::rustc_lint::LintContext; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir}; use clippy_utils::get_parent_expr; use clippy_utils::sugg::Sugg; use hir::Param; @@ -273,9 +273,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { && ident == path.segments[0].ident && count_closure_usage(cx, block, path) == 1 { - span_lint( + span_lint_hir( cx, REDUNDANT_CLOSURE_CALL, + second.hir_id, second.span, "closure called just once immediately after it was declared", ); diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2af466d3f51e3..0b72c8a071924 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; @@ -380,7 +380,7 @@ fn check_final_expr<'tcx>( return; } - emit_return_lint(cx, ret_span, semi_spans, &replacement); + emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -415,18 +415,31 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool { contains_if(expr, false) } -fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec, replacement: &RetReplacement<'_>) { +fn emit_return_lint( + cx: &LateContext<'_>, + ret_span: Span, + semi_spans: Vec, + replacement: &RetReplacement<'_>, + at: HirId, +) { if ret_span.from_expansion() { return; } - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - let suggestions = std::iter::once((ret_span, replacement.to_string())) - .chain(semi_spans.into_iter().map(|span| (span, String::new()))) - .collect(); + span_lint_hir_and_then( + cx, + NEEDLESS_RETURN, + at, + ret_span, + "unneeded `return` statement", + |diag| { + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); - diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); - }); + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); + }, + ); } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 6b3ea7700b735..4e508161e020f 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks}; -use hir::{ExprKind, PatKind}; +use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -135,22 +135,22 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { && is_ok_wild_or_dotdot_pattern(cx, pat) && let Some(op) = should_lint(cx, init) => { - emit_lint(cx, cond.span, op, &[pat.span]); + emit_lint(cx, cond.span, cond.hir_id, op, &[pat.span]); }, // we will capture only the case where the match is Ok( ) or Err( ) // prefer to match the minimum possible, and expand later if needed // to avoid false positives on something as used as this hir::ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { if non_consuming_ok_arm(cx, arm1) && non_consuming_err_arm(cx, arm2) { - emit_lint(cx, expr.span, op, &[arm1.pat.span]); + emit_lint(cx, expr.span, expr.hir_id, op, &[arm1.pat.span]); } if non_consuming_ok_arm(cx, arm2) && non_consuming_err_arm(cx, arm1) { - emit_lint(cx, expr.span, op, &[arm2.pat.span]); + emit_lint(cx, expr.span, expr.hir_id, op, &[arm2.pat.span]); } }, hir::ExprKind::Match(_, _, hir::MatchSource::Normal) => {}, _ if let Some(op) = should_lint(cx, expr) => { - emit_lint(cx, expr.span, op, &[]); + emit_lint(cx, expr.span, expr.hir_id, op, &[]); }, _ => {}, }; @@ -279,7 +279,7 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { } } -fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) { +fn emit_lint(cx: &LateContext<'_>, span: Span, at: HirId, op: IoOp, wild_cards: &[Span]) { let (msg, help) = match op { IoOp::AsyncRead(false) => ( "read amount is not handled", @@ -301,7 +301,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) { IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None), }; - span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| { + span_lint_hir_and_then(cx, UNUSED_IO_AMOUNT, at, span, msg, |diag| { if let Some(help_str) = help { diag.help(help_str); } diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index ba72b3450b933..9fc2476d3cc99 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; use rustc_ast::Mutability; @@ -79,13 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { } if !vis.found_peek_call { - span_lint_and_help( + span_lint_hir_and_then( cx, UNUSED_PEEKABLE, + local.hir_id, ident.span, "`peek` never called on `Peekable` iterator", - None, - "consider removing the call to `peekable`", + |diag| { + diag.help("consider removing the call to `peekable`"); + }, ); } } diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 9869d945299e0..a29d35880b8df 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -57,6 +57,17 @@ fn early_return() -> u8 { foo } +fn allow_works() -> i32 { + #[allow(clippy::useless_let_if_seq)] + let x; + if true { + x = 1; + } else { + x = 2; + } + x +} + fn main() { early_return(); issue975(); diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index 87ad20dc27ee3..41930108fb1ab 100644 --- a/tests/ui/let_if_seq.stderr +++ b/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:66:5 + --> tests/ui/let_if_seq.rs:77:5 | LL | / let mut foo = 0; LL | | @@ -14,7 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::useless_let_if_seq)]` error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:73:5 + --> tests/ui/let_if_seq.rs:84:5 | LL | / let mut bar = 0; LL | | @@ -28,7 +28,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:83:5 + --> tests/ui/let_if_seq.rs:94:5 | LL | / let quz; LL | | @@ -40,7 +40,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:113:5 + --> tests/ui/let_if_seq.rs:124:5 | LL | / let mut baz = 0; LL | | diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index b454da3d20933..4c45bc9802654 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -81,3 +81,8 @@ mod issue9035 { fn bar(_: &mut impl Display) {} } + +fn allow_works() { + #[allow(clippy::mut_mut)] + let _ = &mut &mut 1; +} diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index f9eb39d49382c..2575f2449e181 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -316,4 +316,11 @@ fn test_match_as_stmt() { }; } +fn allow_works() -> i32 { + #[allow(clippy::needless_return, clippy::match_single_binding)] + match () { + () => return 42, + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 4dd2e22ea9fe6..04f21834d8853 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -326,4 +326,11 @@ fn test_match_as_stmt() { }; } +fn allow_works() -> i32 { + #[allow(clippy::needless_return, clippy::match_single_binding)] + match () { + () => return 42, + } +} + fn main() {} diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index fd5a88a37a66c..68acf4334699b 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -112,4 +112,10 @@ async fn test_tokio(r: &mut R) { //~^ ERROR: reading zero byte data to `Vec` } +fn allow_works(mut f: F) { + let mut data = Vec::with_capacity(100); + #[allow(clippy::read_zero_byte_vec)] + f.read(&mut data).unwrap(); +} + fn main() {} diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 7e5a10c911bf1..f5b200d5ffed9 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -271,5 +271,10 @@ pub fn wildcards(rdr: &mut dyn std::io::Read) { } } } +fn allow_works(mut f: F) { + let mut data = Vec::with_capacity(100); + #[allow(clippy::unused_io_amount)] + f.read(&mut data).unwrap(); +} fn main() {} diff --git a/tests/ui/unused_peekable.rs b/tests/ui/unused_peekable.rs index 131b51e01b6f0..5865bba43508b 100644 --- a/tests/ui/unused_peekable.rs +++ b/tests/ui/unused_peekable.rs @@ -174,3 +174,9 @@ fn valid() { let mut peekable = std::iter::empty::().peekable(); takes_dyn(&mut peekable); } + +fn allow_works() { + #[allow(clippy::unused_peekable)] + let iter = [1, 2, 3].iter().peekable(); + iter; +} From b44ab661568d3fc6070fe9c6e3ff87dc7884ceb2 Mon Sep 17 00:00:00 2001 From: MarcusGrass Date: Sat, 9 Mar 2024 18:37:18 +0100 Subject: [PATCH 026/102] Fix #12438 false positive regression --- clippy_lints/src/std_instead_of_core.rs | 2 ++ tests/ui/std_instead_of_core.fixed | 2 +- tests/ui/std_instead_of_core.stderr | 8 +------- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index c0e4f3c368a8d..cf839941123d1 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -109,6 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { + self.prev_span = first_segment.ident.span; return; }, }, @@ -116,6 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { (ALLOC_INSTEAD_OF_CORE, "alloc", "core") } else { + self.prev_span = first_segment.ident.span; return; } }, diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 0a734a65d2976..ec4ae2ea13c53 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -17,7 +17,7 @@ fn std_instead_of_core() { use ::core::hash::Hash; //~^ ERROR: used import from `std` instead of `core` // Don't lint on `env` macro - use core::env; + use std::env; // Multiple imports use core::fmt::{Debug, Result}; diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index ee42b474a32e3..8f920511cc5d0 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -13,12 +13,6 @@ error: used import from `std` instead of `core` LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` -error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:20:9 - | -LL | use std::env; - | ^^^ help: consider importing the item from `core`: `core` - error: used import from `std` instead of `core` --> tests/ui/std_instead_of_core.rs:23:9 | @@ -85,5 +79,5 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors From eb5ce85932e4b9fb7f7a1765f3760c3363a282d9 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 9 Mar 2024 19:40:39 +0100 Subject: [PATCH 027/102] mention `span_lint_hir` in `span_lint` and add a reason to disallowed_methods --- clippy.toml | 13 ++-- clippy_utils/src/diagnostics.rs | 81 +++++++++++++++++---- tests/ui-internal/disallow_span_lint.stderr | 3 + 3 files changed, 77 insertions(+), 20 deletions(-) diff --git a/clippy.toml b/clippy.toml index 3a68fa1670df6..62ed55beb1f34 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,7 +1,10 @@ avoid-breaking-exported-api = false -# use the various `clippy_utils::diagnostics::span_lint_*` functions instead, which also add a link to the docs -disallowed-methods = [ - "rustc_lint::context::LintContext::span_lint", - "rustc_middle::ty::context::TyCtxt::node_span_lint", -] +[[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" + + +[[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" diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 303b23ba3a0fc..df55149599309 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -36,6 +36,15 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { /// Usually it's nicer to provide more context for lint messages. /// Be sure the output is understandable when you use this method. /// +/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this +/// will be considered. +/// This can be confusing if the given span is at a different place, because users won't know where +/// `#[allow]` or `#[expect]` attributes need to be placed. +/// +/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint +/// for a particular expression within that block. +/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. +/// /// # Example /// /// ```ignore @@ -61,6 +70,15 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( /// /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// +/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this +/// will be considered. +/// This can be confusing if the given span is at a different place, because users won't know where +/// `#[allow]` or `#[expect]` attributes need to be placed. +/// +/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint +/// for a particular expression within that block. +/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. +/// /// # Example /// /// ```text @@ -139,6 +166,15 @@ pub fn span_lint_and_note( /// /// If you need to customize your lint output a lot, use this function. /// If you change the signature, remember to update the internal lint `CollapsibleCalls` +/// +/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this +/// will be considered. +/// This can be confusing if the given span is at a different place, because users won't know where +/// `#[allow]` or `#[expect]` attributes need to be placed. +/// +/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint +/// for a particular expression within that block. +/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F) where C: LintContext, @@ -155,22 +191,25 @@ where /// Like [`span_lint`], but allows providing the `HirId` of the node that caused us to emit this /// lint. /// -/// The `HirId` is used for checking lint level attributes. +/// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined +/// via the `#[expect]` attribute. /// /// For example: /// ```ignore -/// fn f() { /* '1 */ +/// fn f() { /* */ /// /// #[allow(clippy::some_lint)] -/// let _x = /* '2 */; +/// let _x = /* */; /// } /// ``` -/// If `some_lint` does its analysis in `LintPass::check_fn` (at `'1`) and emits a lint at `'2` -/// using [`span_lint`], then allowing the lint at `'2` as attempted in the snippet will not work! +/// If `some_lint` does its analysis in `LintPass::check_fn` (at ``) and emits a lint at +/// `` using [`span_lint`], then allowing the lint at `` as attempted in the snippet +/// will not work! /// Even though that is where the warning points at, which would be confusing to users. /// -/// Instead, use this function and also pass the `HirId` of the node at `'2`, which will let the -/// compiler check lint level attributes at `'2` as one would expect, and the `#[allow]` will work. +/// Instead, use this function and also pass the `HirId` of ``, which will let +/// the compiler check lint level attributes at the place of the expression and +/// the `#[allow]` will work. pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { #[expect(clippy::disallowed_methods)] cx.tcx.node_span_lint(lint, hir_id, sp, msg.to_string(), |diag| { @@ -181,22 +220,25 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s /// Like [`span_lint_and_then`], but allows providing the `HirId` of the node that caused us to emit /// this lint. /// -/// The `HirId` is used for checking lint level attributes. +/// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined +/// via the `#[expect]` attribute. /// /// For example: /// ```ignore -/// fn f() { /* '1 */ +/// fn f() { /* */ /// /// #[allow(clippy::some_lint)] -/// let _x = /* '2 */; +/// let _x = /* */; /// } /// ``` -/// If `some_lint` does its analysis in `LintPass::check_fn` (at `'1`) and emits a lint at `'2` -/// using [`span_lint_and_then`], then allowing the lint at `'2` as attempted in the snippet will -/// not work! Even though that is where the warning points at, which would be confusing to users. +/// If `some_lint` does its analysis in `LintPass::check_fn` (at ``) and emits a lint at +/// `` using [`span_lint`], then allowing the lint at `` as attempted in the snippet +/// will not work! +/// Even though that is where the warning points at, which would be confusing to users. /// -/// Instead, use this function and also pass the `HirId` of the node at `'2`, which will let the -/// compiler check lint level attributes at `'2` as one would expect, and the `#[allow]` will work. +/// Instead, use this function and also pass the `HirId` of ``, which will let +/// the compiler check lint level attributes at the place of the expression and +/// the `#[allow]` will work. pub fn span_lint_hir_and_then( cx: &LateContext<'_>, lint: &'static Lint, @@ -220,6 +262,15 @@ pub fn span_lint_hir_and_then( /// /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// +/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this +/// will be considered. +/// This can be confusing if the given span is at a different place, because users won't know where +/// `#[allow]` or `#[expect]` attributes need to be placed. +/// +/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint +/// for a particular expression within that block. +/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. +/// /// # Example /// /// ```text diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index ae5d6843406a7..cfc590bed3697 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -4,6 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` LL | cx.span_lint(lint, span, msg, |_| {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml) = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` @@ -12,6 +13,8 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_ | LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml) error: aborting due to 2 previous errors From 0e59259add77fbe673eb14566a3bceac67735853 Mon Sep 17 00:00:00 2001 From: Jacherr Date: Sun, 10 Dec 2023 22:46:25 +0000 Subject: [PATCH 028/102] add new lint `zero_repeat_side_effects` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/zero_repeat_side_effects.rs | 154 +++++++++++++++++++ tests/ui/zero_repeat_side_effects.fixed | 60 ++++++++ tests/ui/zero_repeat_side_effects.rs | 60 ++++++++ tests/ui/zero_repeat_side_effects.stderr | 77 ++++++++++ 7 files changed, 355 insertions(+) create mode 100644 clippy_lints/src/zero_repeat_side_effects.rs create mode 100644 tests/ui/zero_repeat_side_effects.fixed create mode 100644 tests/ui/zero_repeat_side_effects.rs create mode 100644 tests/ui/zero_repeat_side_effects.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e783b593f90e..2e0e14ca06edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5814,6 +5814,7 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr +[`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7a0d57c7859ee..5e22ba22e2739 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -751,5 +751,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::write::WRITE_LITERAL_INFO, crate::write::WRITE_WITH_NEWLINE_INFO, crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, + crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, ]; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b930175c4d89a..adafc92d8a355 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -373,6 +373,7 @@ mod visibility; mod wildcard_imports; mod write; mod zero_div_zero; +mod zero_repeat_side_effects; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` @@ -1120,6 +1121,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(|_| Box::new(assigning_clones::AssigningClones)); + store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs new file mode 100644 index 0000000000000..b3a80ab93a718 --- /dev/null +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -0,0 +1,154 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::VecArgs; +use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, ConstKind, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for array or vec initializations which call a function or method, + /// but which have a repeat count of zero. + /// + /// ### Why is this bad? + /// Such an initialization, despite having a repeat length of 0, will still call the inner function. + /// This may not be obvious and as such there may be unintended side effects in code. + /// + /// ### Example + /// ```no_run + /// fn side_effect(): i32 { + /// println!("side effect"); + /// 10 + /// } + /// let a = [side_effect(); 0]; + /// ``` + /// Use instead: + /// ```no_run + /// fn side_effect(): i32 { + /// println!("side effect"); + /// 10 + /// } + /// side_effect(); + /// let a: [i32; 0] = []; + /// ``` + #[clippy::version = "1.75.0"] + pub ZERO_REPEAT_SIDE_EFFECTS, + suspicious, + "usage of zero-sized initializations of arrays or vecs causing side effects" +} + +declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]); + +impl LateLintPass<'_> for ZeroRepeatSideEffects { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) { + if let Some(args) = VecArgs::hir(cx, expr) + && let VecArgs::Repeat(inner_expr, len) = args + && let ExprKind::Lit(l) = len.kind + && let LitKind::Int(i, _) = l.node + && i.0 == 0 + { + inner_check(cx, expr, inner_expr, true); + } else if let ExprKind::Repeat(inner_expr, _) = expr.kind + && let ty::Array(_, cst) = cx.typeck_results().expr_ty(expr).kind() + && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() + && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) + && element_count == 0 + { + inner_check(cx, expr, inner_expr, false); + } + } +} + +fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { + // check if expr is a call or has a call inside it + if for_each_expr(inner_expr, |x| { + if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { + std::ops::ControlFlow::Break(()) + } else { + std::ops::ControlFlow::Continue(()) + } + }) + .is_some() + { + let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); + let return_type = cx.typeck_results().expr_ty(expr); + + if let Node::Local(l) = parent_hir_node { + array_span_lint( + cx, + l.span, + inner_expr.span, + l.pat.span, + Some(return_type), + is_vec, + false, + ); + } else if let Node::Expr(x) = parent_hir_node + && let ExprKind::Assign(l, _, _) = x.kind + { + array_span_lint(cx, x.span, inner_expr.span, l.span, Some(return_type), is_vec, true); + } else { + span_lint_and_sugg( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + expr.span.source_callsite(), + "function or method calls as the initial value in zero-sized array initializers may cause side effects", + "consider using", + format!( + "{{ {}; {}[] as {return_type} }}", + snippet(cx, inner_expr.span.source_callsite(), ".."), + if is_vec { "vec!" } else { "" }, + ), + Applicability::Unspecified, + ); + } + } +} + +fn array_span_lint( + cx: &LateContext<'_>, + expr_span: Span, + func_call_span: Span, + variable_name_span: Span, + expr_ty: Option>, + is_vec: bool, + is_assign: bool, +) { + let has_ty = expr_ty.is_some(); + + span_lint_and_sugg( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + expr_span.source_callsite(), + "function or method calls as the initial value in zero-sized array initializers may cause side effects", + "consider using", + format!( + "{}; {}{}{} = {}[]{}{}", + snippet(cx, func_call_span.source_callsite(), ".."), + if has_ty && !is_assign { "let " } else { "" }, + snippet(cx, variable_name_span.source_callsite(), ".."), + if let Some(ty) = expr_ty + && !is_assign + { + format!(": {ty}") + } else { + String::new() + }, + if is_vec { "vec!" } else { "" }, + if let Some(ty) = expr_ty + && is_assign + { + format!(" as {ty}") + } else { + String::new() + }, + if is_assign { "" } else { ";" } + ), + Applicability::Unspecified, + ); +} diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed new file mode 100644 index 0000000000000..6f13252192640 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -0,0 +1,60 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::useless_vec)] +#![allow(clippy::needless_late_init)] + +fn f() -> i32 { + println!("side effect"); + 10 +} + +fn main() { + const N: usize = 0; + const M: usize = 1; + + // should trigger + + // on arrays + f(); let a: [i32; 0] = []; + f(); let a: [i32; 0] = []; + let mut b; + f(); b = [] as [i32; 0]; + f(); b = [] as [i32; 0]; + + // on vecs + // vecs dont support infering value of consts + f(); let c: std::vec::Vec = vec![]; + let d; + f(); d = vec![] as std::vec::Vec; + + // for macros + println!("side effect"); let e: [(); 0] = []; + + // for nested calls + { f() }; let g: [i32; 0] = []; + + // as function param + drop({ f(); vec![] as std::vec::Vec }); + + // when singled out/not part of assignment/local + { f(); vec![] as std::vec::Vec }; + { f(); [] as [i32; 0] }; + { f(); [] as [i32; 0] }; + + // should not trigger + + // on arrays with > 0 repeat + let a = [f(); 1]; + let a = [f(); M]; + let mut b; + b = [f(); 1]; + b = [f(); M]; + + // on vecs with > 0 repeat + let c = vec![f(); 1]; + let d; + d = vec![f(); 1]; + + // as function param + drop(vec![f(); 1]); +} diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs new file mode 100644 index 0000000000000..9d9c367375a71 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects.rs @@ -0,0 +1,60 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::useless_vec)] +#![allow(clippy::needless_late_init)] + +fn f() -> i32 { + println!("side effect"); + 10 +} + +fn main() { + const N: usize = 0; + const M: usize = 1; + + // should trigger + + // on arrays + let a = [f(); 0]; + let a = [f(); N]; + let mut b; + b = [f(); 0]; + b = [f(); N]; + + // on vecs + // vecs dont support infering value of consts + let c = vec![f(); 0]; + let d; + d = vec![f(); 0]; + + // for macros + let e = [println!("side effect"); 0]; + + // for nested calls + let g = [{ f() }; 0]; + + // as function param + drop(vec![f(); 0]); + + // when singled out/not part of assignment/local + vec![f(); 0]; + [f(); 0]; + [f(); N]; + + // should not trigger + + // on arrays with > 0 repeat + let a = [f(); 1]; + let a = [f(); M]; + let mut b; + b = [f(); 1]; + b = [f(); M]; + + // on vecs with > 0 repeat + let c = vec![f(); 1]; + let d; + d = vec![f(); 1]; + + // as function param + drop(vec![f(); 1]); +} diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr new file mode 100644 index 0000000000000..afdc60542534d --- /dev/null +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -0,0 +1,77 @@ +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:18:5 + | +LL | let a = [f(); 0]; + | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` + | + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:19:5 + | +LL | let a = [f(); N]; + | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:21:5 + | +LL | b = [f(); 0]; + | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:22:5 + | +LL | b = [f(); N]; + | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:26:5 + | +LL | let c = vec![f(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f(); let c: std::vec::Vec = vec![];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:28:5 + | +LL | d = vec![f(); 0]; + | ^^^^^^^^^^^^^^^^ help: consider using: `f(); d = vec![] as std::vec::Vec` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:31:5 + | +LL | let e = [println!("side effect"); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `println!("side effect"); let e: [(); 0] = [];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:34:5 + | +LL | let g = [{ f() }; 0]; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `{ f() }; let g: [i32; 0] = [];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:37:10 + | +LL | drop(vec![f(); 0]); + | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:40:5 + | +LL | vec![f(); 0]; + | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:41:5 + | +LL | [f(); 0]; + | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:42:5 + | +LL | [f(); N]; + | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + +error: aborting due to 12 previous errors + From 0c82fd01c7968e09c233abbf9b7728ccea230f49 Mon Sep 17 00:00:00 2001 From: Jacherr Date: Sat, 9 Mar 2024 19:04:35 +0000 Subject: [PATCH 029/102] fix example code --- clippy_lints/src/zero_repeat_side_effects.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index b3a80ab93a718..852d04cd21b2f 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// fn side_effect(): i32 { + /// fn side_effect() -> i32 { /// println!("side effect"); /// 10 /// } @@ -29,7 +29,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```no_run - /// fn side_effect(): i32 { + /// fn side_effect() -> i32 { /// println!("side effect"); /// 10 /// } From 5b1f95cbbbb0db8a0a6b046432851c8870e320d0 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 9 Mar 2024 23:20:02 +0100 Subject: [PATCH 030/102] apply review suggestions --- clippy_utils/src/diagnostics.rs | 107 ++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 39 deletions(-) diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index df55149599309..0352696f93eca 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -36,14 +36,19 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { /// Usually it's nicer to provide more context for lint messages. /// Be sure the output is understandable when you use this method. /// -/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this -/// will be considered. -/// This can be confusing if the given span is at a different place, because users won't know where -/// `#[allow]` or `#[expect]` attributes need to be placed. +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint` function, the node that was passed into the `LintPass::check_*` function is +/// used. /// -/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint -/// for a particular expression within that block. -/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir`] instead. +/// 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 +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir`] instead. /// /// # Example /// @@ -70,14 +75,19 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( /// /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// -/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this -/// will be considered. -/// This can be confusing if the given span is at a different place, because users won't know where -/// `#[allow]` or `#[expect]` attributes need to be placed. +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint_and_note` function, the node that was passed into the `LintPass::check_*` +/// function is used. /// -/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint -/// for a particular expression within that block. -/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead. +/// 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 +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// /// # Example /// @@ -167,14 +182,19 @@ pub fn span_lint_and_note( /// If you need to customize your lint output a lot, use this function. /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// -/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this -/// will be considered. -/// This can be confusing if the given span is at a different place, because users won't know where -/// `#[allow]` or `#[expect]` attributes need to be placed. +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint_and_then` function, the node that was passed into the `LintPass::check_*` +/// function is used. +/// +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead. +/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node +/// highlighted in the displayed warning. /// -/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint -/// for a particular expression within that block. -/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. +/// If you're unsure which function you should use, you can test if the `#[allow]` 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: &str, f: F) where C: LintContext, @@ -188,8 +208,10 @@ where }); } -/// Like [`span_lint`], but allows providing the `HirId` of the node that caused us to emit this -/// lint. +/// Like [`span_lint`], but emits the lint at the node identified by the given `HirId`. +/// +/// This is in contrast to [`span_lint`], which always emits the lint at the node that was last +/// passed to the `LintPass::check_*` function. /// /// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined /// via the `#[expect]` attribute. @@ -217,8 +239,10 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s }); } -/// Like [`span_lint_and_then`], but allows providing the `HirId` of the node that caused us to emit -/// this lint. +/// Like [`span_lint_and_then`], but emits the lint at the node identified by the given `HirId`. +/// +/// This is in contrast to [`span_lint_and_then`], which always emits the lint at the node that was +/// last passed to the `LintPass::check_*` function. /// /// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined /// via the `#[expect]` attribute. @@ -262,14 +286,19 @@ pub fn span_lint_hir_and_then( /// /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// -/// NOTE: only lint-level attributes at the `LintPass::check_*` node from which you are calling this -/// will be considered. -/// This can be confusing if the given span is at a different place, because users won't know where -/// `#[allow]` or `#[expect]` attributes need to be placed. +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint_and_sugg` function, the node that was passed into the `LintPass::check_*` +/// function is used. +/// +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead. +/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node +/// highlighted in the displayed warning. /// -/// This can happen if, for example, you are in `LintPass::check_block` and you are emitting a lint -/// for a particular expression within that block. -/// In those cases, consider using [`span_lint_hir`], and pass the `HirId` of that expression. +/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// /// # Example /// From a92037fce82ad15e47384f3d9ca19f21602656c5 Mon Sep 17 00:00:00 2001 From: kcz Date: Sat, 9 Mar 2024 18:08:40 -0500 Subject: [PATCH 031/102] [`option_option`]: Fix duplicate diagnostics --- clippy_lints/src/types/mod.rs | 6 +++++- tests/ui/option_option.rs | 2 -- tests/ui/option_option.stderr | 26 +++++++++++++------------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 7882bfdd09fad..280b2a6d73523 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -15,7 +15,7 @@ use rustc_hir::{ Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, TraitItemKind, TyKind, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::Span; @@ -392,6 +392,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &hir::FieldDef<'tcx>) { + if rustc_middle::lint::in_external_macro(cx.sess(), field.span) { + return; + } + let is_exported = cx.effective_visibilities.is_exported(field.def_id); self.check_ty( diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 2f6e4d7614549..9bbdd3aaacce5 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![deny(clippy::option_option)] #![allow(clippy::unnecessary_wraps)] diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 76cb9ae944c26..0cd048e400e40 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -1,77 +1,77 @@ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:6:10 + --> tests/ui/option_option.rs:4:10 | LL | const C: Option> = None; | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> tests/ui/option_option.rs:3:9 + --> tests/ui/option_option.rs:1:9 | LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:8:11 + --> tests/ui/option_option.rs:6:11 | LL | static S: Option> = None; | ^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:11:13 + --> tests/ui/option_option.rs:9:13 | LL | fn input(_: Option>) {} | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:14:16 + --> tests/ui/option_option.rs:12:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:19:27 + --> tests/ui/option_option.rs:17:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:25:30 + --> tests/ui/option_option.rs:23:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:31:8 + --> tests/ui/option_option.rs:29:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:36:23 + --> tests/ui/option_option.rs:34:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:43:22 + --> tests/ui/option_option.rs:41:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:48:11 + --> tests/ui/option_option.rs:46:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:50:17 + --> tests/ui/option_option.rs:48:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:92:14 + --> tests/ui/option_option.rs:90:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 1abf4418f80e21bd9cbc5bbdc6fe76df1eb5d494 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 8 Mar 2024 22:04:10 +0100 Subject: [PATCH 032/102] Add new `match_option_and_default` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/match_option_and_default.rs | 183 +++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 clippy_lints/src/match_option_and_default.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e0e14ca06edf..2a9688b4b51e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5390,6 +5390,7 @@ Released 2018-09-13 [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items +[`match_option_and_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_option_and_default [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats [`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5e22ba22e2739..b9b03862b7b9d 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -312,6 +312,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_strip::MANUAL_STRIP_INFO, crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, + crate::match_option_and_default::MATCH_OPTION_AND_DEFAULT_INFO, crate::match_result_ok::MATCH_RESULT_OK_INFO, crate::matches::COLLAPSIBLE_MATCH_INFO, crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index adafc92d8a355..589deb0e232a9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -212,6 +212,7 @@ mod manual_slice_size_calculation; mod manual_string_new; mod manual_strip; mod map_unit_fn; +mod match_option_and_default; mod match_result_ok; mod matches; mod mem_replace; @@ -1122,6 +1123,7 @@ 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(|_| Box::new(assigning_clones::AssigningClones)); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); + store.register_late_pass(|_| Box::new(match_option_and_default::MatchOptionAndDefault)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/match_option_and_default.rs b/clippy_lints/src/match_option_and_default.rs new file mode 100644 index 0000000000000..c39f6e93ebd89 --- /dev/null +++ b/clippy_lints/src/match_option_and_default.rs @@ -0,0 +1,183 @@ +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_default_equivalent; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; + +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.78.0"] + pub MATCH_OPTION_AND_DEFAULT, + suspicious, + "check if a `match` or `if let` can be simplified with `unwrap_or_default`" +} + +declare_lint_pass!(MatchOptionAndDefault => [MATCH_OPTION_AND_DEFAULT]); + +fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { + if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = 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) + { + let mut bindings = Vec::new(); + pat.each_binding(|_, id, _, _| bindings.push(id)); + if let &[id] = bindings.as_slice() { + Some(id) + } else { + None + } + } else { + None + } +} + +fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let PatKind::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::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 + } +} + +fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let ExprKind::Match(match_expr, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) = expr.kind else { + return false; + }; + // We don't want conditions on the arms to simplify things. + if arm1.guard.is_none() + && arm2.guard.is_none() + // We check that the returned type implements the `Default` trait. + && let match_ty = cx.typeck_results().expr_ty(expr) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, match_ty, default_trait_id, &[]) + // We now get the bodies for both the `Some` and `None` arms. + && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) + // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. + && let ExprKind::Path(QPath::Resolved(_, path)) = body_some.peel_blocks().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 = body_none.peel_blocks() + && let ExprKind::Call(_, &[]) = body_none.kind + && is_default_equivalent(cx, body_none) + && let Some(match_expr_snippet) = snippet_opt(cx, match_expr.span) + { + span_lint_and_sugg( + cx, + MATCH_OPTION_AND_DEFAULT, + expr.span, + "match can be simplified with `.unwrap_or_default()`", + "replace it with", + format!("{match_expr_snippet}.unwrap_or_default()"), + Applicability::MachineApplicable, + ); + } + true +} + +fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::If(cond, if_block, Some(else_expr)) = expr.kind + && let ExprKind::Let(let_) = cond.kind + && let ExprKind::Block(_, _) = else_expr.kind + // We check that the returned type implements the `Default` trait. + && let match_ty = cx.typeck_results().expr_ty(expr) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, match_ty, default_trait_id, &[]) + && let Some(binding_id) = get_some(cx, let_.pat) + // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. + && let ExprKind::Path(QPath::Resolved(_, path)) = if_block.peel_blocks().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_else = else_expr.peel_blocks() + && let ExprKind::Call(_, &[]) = body_else.kind + && is_default_equivalent(cx, body_else) + && let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span) + { + span_lint_and_sugg( + cx, + MATCH_OPTION_AND_DEFAULT, + expr.span, + "if let can be simplified with `.unwrap_or_default()`", + "replace it with", + format!("{if_let_expr_snippet}.unwrap_or_default()"), + Applicability::MachineApplicable, + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for MatchOptionAndDefault { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if expr.span.from_expansion() { + return; + } + if !handle_match(cx, expr) { + handle_if_let(cx, expr); + } + } +} From fadb2540732b509d8cd2a146dd14d68998b79c87 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 8 Mar 2024 22:04:31 +0100 Subject: [PATCH 033/102] Add new ui test for `match_option_and_default` --- tests/ui/match_option_and_default.fixed | 19 ++++++++ tests/ui/match_option_and_default.rs | 40 +++++++++++++++++ tests/ui/match_option_and_default.stderr | 56 ++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 tests/ui/match_option_and_default.fixed create mode 100644 tests/ui/match_option_and_default.rs create mode 100644 tests/ui/match_option_and_default.stderr diff --git a/tests/ui/match_option_and_default.fixed b/tests/ui/match_option_and_default.fixed new file mode 100644 index 0000000000000..0a90ef636e8eb --- /dev/null +++ b/tests/ui/match_option_and_default.fixed @@ -0,0 +1,19 @@ +#![warn(clippy::match_option_and_default)] +#![allow(clippy::unnecessary_literal_unwrap)] + +fn main() { + let x: Option> = None; + x.unwrap_or_default(); + + let x: Option> = None; + x.unwrap_or_default(); + + let x: Option = None; + x.unwrap_or_default(); + + let x: Option> = None; + x.unwrap_or_default(); + + let x: Option> = None; + x.unwrap_or_default(); +} diff --git a/tests/ui/match_option_and_default.rs b/tests/ui/match_option_and_default.rs new file mode 100644 index 0000000000000..8e69ef47431b4 --- /dev/null +++ b/tests/ui/match_option_and_default.rs @@ -0,0 +1,40 @@ +#![warn(clippy::match_option_and_default)] +#![allow(clippy::unnecessary_literal_unwrap)] + +fn main() { + let x: Option> = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Some(v) => v, + None => Vec::default(), + }; + + let x: Option> = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Some(v) => v, + _ => Vec::default(), + }; + + let x: Option = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Some(v) => v, + None => String::new(), + }; + + let x: Option> = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + None => Vec::default(), + Some(v) => v, + }; + + let x: Option> = None; + if let Some(v) = x { + //~^ ERROR: if let can be simplified with `.unwrap_or_default()` + v + } else { + Vec::default() + }; +} diff --git a/tests/ui/match_option_and_default.stderr b/tests/ui/match_option_and_default.stderr new file mode 100644 index 0000000000000..86d7294f95a8c --- /dev/null +++ b/tests/ui/match_option_and_default.stderr @@ -0,0 +1,56 @@ +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/match_option_and_default.rs:6:5 + | +LL | / match x { +LL | | +LL | | Some(v) => v, +LL | | None => Vec::default(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + | + = note: `-D clippy::match-option-and-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::match_option_and_default)]` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/match_option_and_default.rs:13:5 + | +LL | / match x { +LL | | +LL | | Some(v) => v, +LL | | _ => Vec::default(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/match_option_and_default.rs:20:5 + | +LL | / match x { +LL | | +LL | | Some(v) => v, +LL | | None => String::new(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/match_option_and_default.rs:27:5 + | +LL | / match x { +LL | | +LL | | None => Vec::default(), +LL | | Some(v) => v, +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/match_option_and_default.rs:34:5 + | +LL | / if let Some(v) = x { +LL | | +LL | | v +LL | | } else { +LL | | Vec::default() +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: aborting due to 5 previous errors + From b0f358fd3ce5897c43c706c7e87491a4ff081943 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 8 Mar 2024 22:04:17 +0100 Subject: [PATCH 034/102] Update ui test --- tests/ui/manual_let_else.rs | 3 +- tests/ui/manual_let_else.stderr | 62 +++++++++++++++--------------- tests/ui/manual_unwrap_or.fixed | 7 +++- tests/ui/manual_unwrap_or.rs | 7 +++- tests/ui/manual_unwrap_or.stderr | 28 +++++++------- tests/ui/match_result_ok.fixed | 2 +- tests/ui/match_result_ok.rs | 2 +- tests/ui/option_if_let_else.fixed | 3 +- tests/ui/option_if_let_else.rs | 3 +- tests/ui/option_if_let_else.stderr | 50 ++++++++++++------------ tests/ui/option_option.rs | 2 +- tests/ui/single_match.fixed | 3 +- tests/ui/single_match.rs | 3 +- tests/ui/single_match.stderr | 36 ++++++++--------- 14 files changed, 113 insertions(+), 98 deletions(-) diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 1fb252e3f97b6..2b36c3f3c2fb6 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -8,7 +8,8 @@ clippy::never_loop, clippy::needless_if, clippy::diverging_sub_expression, - clippy::single_match + clippy::single_match, + clippy::manual_unwrap_or_default )] #![warn(clippy::manual_let_else)] //@no-rustfix diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 7012c6b889144..55a410982adfd 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -1,5 +1,5 @@ error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:28:5 + --> tests/ui/manual_let_else.rs:29:5 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` @@ -8,7 +8,7 @@ LL | let v = if let Some(v_some) = g() { v_some } else { return }; = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:31:5 + --> tests/ui/manual_let_else.rs:32:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -26,7 +26,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:38:5 + --> tests/ui/manual_let_else.rs:39:5 | LL | / let v = if let Some(v) = g() { LL | | @@ -47,25 +47,25 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:50:9 + --> tests/ui/manual_let_else.rs:51:9 | LL | let v = if let Some(v_some) = g() { v_some } else { continue }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:52:9 + --> tests/ui/manual_let_else.rs:53:9 | LL | let v = if let Some(v_some) = g() { v_some } else { break }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:57:5 + --> tests/ui/manual_let_else.rs:58:5 | LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:61:5 + --> tests/ui/manual_let_else.rs:62:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -83,7 +83,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:69:5 + --> tests/ui/manual_let_else.rs:70:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -101,7 +101,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:77:5 + --> tests/ui/manual_let_else.rs:78:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -121,7 +121,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:86:5 + --> tests/ui/manual_let_else.rs:87:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -141,7 +141,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:95:5 + --> tests/ui/manual_let_else.rs:96:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -168,7 +168,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:111:5 + --> tests/ui/manual_let_else.rs:112:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -190,7 +190,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:122:5 + --> tests/ui/manual_let_else.rs:123:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -217,7 +217,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:138:5 + --> tests/ui/manual_let_else.rs:139:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -239,7 +239,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:149:5 + --> tests/ui/manual_let_else.rs:150:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -257,7 +257,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:157:5 + --> tests/ui/manual_let_else.rs:158:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -278,7 +278,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:167:5 + --> tests/ui/manual_let_else.rs:168:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -299,7 +299,7 @@ LL + } }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:177:5 + --> tests/ui/manual_let_else.rs:178:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -328,7 +328,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:195:5 + --> tests/ui/manual_let_else.rs:196:5 | LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { LL | | @@ -346,7 +346,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:203:5 + --> tests/ui/manual_let_else.rs:204:5 | LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) { LL | | @@ -364,7 +364,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:213:13 + --> tests/ui/manual_let_else.rs:214:13 | LL | let $n = if let Some(v) = $e { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };` @@ -375,19 +375,19 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:222:5 + --> tests/ui/manual_let_else.rs:223:5 | LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:226:5 + --> tests/ui/manual_let_else.rs:227:5 | LL | let mut v = if let Variant::B(b) = e() { b } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:231:5 + --> tests/ui/manual_let_else.rs:232:5 | LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { LL | | @@ -405,19 +405,19 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:238:5 + --> tests/ui/manual_let_else.rs:239:5 | LL | let v = if let Variant::A(.., a) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:242:5 + --> tests/ui/manual_let_else.rs:243:5 | LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:246:5 + --> tests/ui/manual_let_else.rs:247:5 | LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -435,7 +435,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:254:5 + --> tests/ui/manual_let_else.rs:255:5 | LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -453,7 +453,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:262:5 + --> tests/ui/manual_let_else.rs:263:5 | LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::>> { LL | | @@ -471,7 +471,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:379:5 + --> tests/ui/manual_let_else.rs:380:5 | LL | / let _ = match ff { LL | | @@ -481,7 +481,7 @@ LL | | }; | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:456:9 + --> tests/ui/manual_let_else.rs:457:9 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 737d4c90dca45..dffd44b6a7c1b 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,5 +1,10 @@ #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] +#![allow( + unused_variables, + clippy::unnecessary_wraps, + clippy::unnecessary_literal_unwrap, + clippy::manual_unwrap_or_default +)] fn option_unwrap_or() { // int case diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index f59fb87529f81..67427132c1a85 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,5 +1,10 @@ #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] +#![allow( + unused_variables, + clippy::unnecessary_wraps, + clippy::unnecessary_literal_unwrap, + clippy::manual_unwrap_or_default +)] fn option_unwrap_or() { // int case diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 511b79881ac3f..33a099680ce0e 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -1,5 +1,5 @@ error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:6:5 + --> tests/ui/manual_unwrap_or.rs:11:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -11,7 +11,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or)]` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:12:5 + --> tests/ui/manual_unwrap_or.rs:17:5 | LL | / match Some(1) { LL | | None => 42, @@ -20,7 +20,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:18:5 + --> tests/ui/manual_unwrap_or.rs:23:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -29,7 +29,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:25:5 + --> tests/ui/manual_unwrap_or.rs:30:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -50,7 +50,7 @@ LL ~ }); | error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:35:5 + --> tests/ui/manual_unwrap_or.rs:40:5 | LL | / match Some("Bob") { LL | | Some(i) => i, @@ -59,7 +59,7 @@ LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:85:5 + --> tests/ui/manual_unwrap_or.rs:90:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -68,7 +68,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:92:5 + --> tests/ui/manual_unwrap_or.rs:97:5 | LL | / match a { LL | | Ok(i) => i, @@ -77,7 +77,7 @@ LL | | }; | |_____^ help: replace with: `a.unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:98:5 + --> tests/ui/manual_unwrap_or.rs:103:5 | LL | / match Ok(1) as Result { LL | | Ok(i) => i, @@ -86,7 +86,7 @@ LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:111:5 + --> tests/ui/manual_unwrap_or.rs:116:5 | LL | / match s.method() { LL | | Some(i) => i, @@ -95,7 +95,7 @@ LL | | }; | |_____^ help: replace with: `s.method().unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:117:5 + --> tests/ui/manual_unwrap_or.rs:122:5 | LL | / match Ok::(1) { LL | | Err(_) => 42, @@ -104,7 +104,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:123:5 + --> tests/ui/manual_unwrap_or.rs:128:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -113,7 +113,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:130:5 + --> tests/ui/manual_unwrap_or.rs:135:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -134,7 +134,7 @@ LL ~ }); | error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:140:5 + --> tests/ui/manual_unwrap_or.rs:145:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -143,7 +143,7 @@ LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:200:17 + --> tests/ui/manual_unwrap_or.rs:205:17 | LL | let _ = match some_macro!() { | _________________^ diff --git a/tests/ui/match_result_ok.fixed b/tests/ui/match_result_ok.fixed index 8d7cddc0ad749..76b26f5e4386d 100644 --- a/tests/ui/match_result_ok.fixed +++ b/tests/ui/match_result_ok.fixed @@ -1,6 +1,6 @@ #![warn(clippy::match_result_ok)] #![allow(dead_code)] -#![allow(clippy::boxed_local, clippy::uninlined_format_args)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args, clippy::manual_unwrap_or_default)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.rs b/tests/ui/match_result_ok.rs index 9a18b813aca30..d6f2475ba79c8 100644 --- a/tests/ui/match_result_ok.rs +++ b/tests/ui/match_result_ok.rs @@ -1,6 +1,6 @@ #![warn(clippy::match_result_ok)] #![allow(dead_code)] -#![allow(clippy::boxed_local, clippy::uninlined_format_args)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args, clippy::manual_unwrap_or_default)] // Checking `if` cases diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index d443334bb0592..eeab801b7da81 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -3,7 +3,8 @@ clippy::ref_option_ref, clippy::equatable_if_let, clippy::let_unit_value, - clippy::redundant_locals + clippy::redundant_locals, + clippy::manual_unwrap_or_default )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 317c35bf8427b..3e5b96d7c3162 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -3,7 +3,8 @@ clippy::ref_option_ref, clippy::equatable_if_let, clippy::let_unit_value, - clippy::redundant_locals + clippy::redundant_locals, + clippy::manual_unwrap_or_default )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index a794dca762f17..f5359a0c34f99 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:10:5 + --> tests/ui/option_if_let_else.rs:11:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -12,19 +12,19 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::option_if_let_else)]` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:28:13 + --> tests/ui/option_if_let_else.rs:29:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:29:13 + --> tests/ui/option_if_let_else.rs:30:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:30:13 + --> tests/ui/option_if_let_else.rs:31:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -44,13 +44,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:36:13 + --> tests/ui/option_if_let_else.rs:37:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:37:13 + --> tests/ui/option_if_let_else.rs:38:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -70,7 +70,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:43:13 + --> tests/ui/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -90,7 +90,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:52:5 + --> tests/ui/option_if_let_else.rs:53:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -109,7 +109,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:65:13 + --> tests/ui/option_if_let_else.rs:66:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(side_effect, |x| x)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:74:13 + --> tests/ui/option_if_let_else.rs:75:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -144,7 +144,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:107:13 + --> tests/ui/option_if_let_else.rs:108:13 | LL | / if let Some(idx) = s.find('.') { LL | | vec![s[..idx].to_string(), s[idx..].to_string()] @@ -154,7 +154,7 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:118:5 + --> tests/ui/option_if_let_else.rs:119:5 | LL | / if let Ok(binding) = variable { LL | | println!("Ok {binding}"); @@ -177,13 +177,13 @@ LL + }) | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:142:13 + --> tests/ui/option_if_let_else.rs:143:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:152:13 + --> tests/ui/option_if_let_else.rs:153:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -205,13 +205,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:180:13 + --> tests/ui/option_if_let_else.rs:181:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:184:13 + --> tests/ui/option_if_let_else.rs:185:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -231,7 +231,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:223:13 + --> tests/ui/option_if_let_else.rs:224:13 | LL | let _ = match s { | _____________^ @@ -241,7 +241,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:227:13 + --> tests/ui/option_if_let_else.rs:228:13 | LL | let _ = match Some(10) { | _____________^ @@ -251,7 +251,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:233:13 + --> tests/ui/option_if_let_else.rs:234:13 | LL | let _ = match res { | _____________^ @@ -261,7 +261,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:237:13 + --> tests/ui/option_if_let_else.rs:238:13 | LL | let _ = match res { | _____________^ @@ -271,13 +271,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:241:13 + --> tests/ui/option_if_let_else.rs:242:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:258:17 + --> tests/ui/option_if_let_else.rs:259:17 | LL | let _ = match initial { | _________________^ @@ -287,7 +287,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:265:17 + --> tests/ui/option_if_let_else.rs:266:17 | LL | let _ = match initial { | _________________^ @@ -297,7 +297,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:288:24 + --> tests/ui/option_if_let_else.rs:289:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -308,7 +308,7 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:294:19 + --> tests/ui/option_if_let_else.rs:295:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 2f6e4d7614549..7dda139c88ec5 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,7 +1,7 @@ //@compile-flags: -Zdeduplicate-diagnostics=yes #![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::match_option_and_default)] const C: Option> = None; //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index 6df64eb405354..a7cb43e144652 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -6,7 +6,8 @@ clippy::uninlined_format_args, clippy::needless_if, clippy::redundant_guards, - clippy::redundant_pattern_matching + clippy::redundant_pattern_matching, + clippy::manual_unwrap_or_default )] fn dummy() {} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 4f005f4e04f5c..09db497dcd8bd 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -6,7 +6,8 @@ clippy::uninlined_format_args, clippy::needless_if, clippy::redundant_guards, - clippy::redundant_pattern_matching + clippy::redundant_pattern_matching, + clippy::manual_unwrap_or_default )] fn dummy() {} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 651d0b4911d95..f7abf996be743 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:16:5 + --> tests/ui/single_match.rs:17:5 | LL | / match x { LL | | Some(y) => { @@ -19,7 +19,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:24:5 + --> tests/ui/single_match.rs:25:5 | LL | / match x { LL | | // Note the missing block braces. @@ -31,7 +31,7 @@ LL | | } | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` 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:34:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -40,7 +40,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:62:5 + --> tests/ui/single_match.rs:63:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -49,7 +49,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:67:5 + --> tests/ui/single_match.rs:68:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -58,7 +58,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:74:5 + --> tests/ui/single_match.rs:75:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -67,7 +67,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:95:5 + --> tests/ui/single_match.rs:96:5 | LL | / match x { LL | | "test" => println!(), @@ -76,7 +76,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:108:5 + --> tests/ui/single_match.rs:109:5 | LL | / match x { LL | | Foo::A => println!(), @@ -85,7 +85,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:114:5 + --> tests/ui/single_match.rs:115:5 | LL | / match x { LL | | FOO_C => println!(), @@ -94,7 +94,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:119:5 + --> tests/ui/single_match.rs:120:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -103,7 +103,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:125:5 + --> tests/ui/single_match.rs:126:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -112,7 +112,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:142:5 + --> tests/ui/single_match.rs:143:5 | LL | / match x { LL | | Bar::A => println!(), @@ -121,7 +121,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:150:5 + --> tests/ui/single_match.rs:151:5 | LL | / match x { LL | | None => println!(), @@ -130,7 +130,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:172:5 + --> tests/ui/single_match.rs:173:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -139,7 +139,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:178:5 + --> tests/ui/single_match.rs:179:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -148,7 +148,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:184:5 + --> tests/ui/single_match.rs:185:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -157,7 +157,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:256:5 + --> tests/ui/single_match.rs:257:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -177,7 +177,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:264:5 + --> tests/ui/single_match.rs:265:5 | LL | / match bar { LL | | #[rustfmt::skip] From 98ac5f1e8cf9aaf2690665bb87f4af4135084e65 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 10 Mar 2024 01:12:15 +0100 Subject: [PATCH 035/102] Rename into `manual_unwrap_or_default` --- CHANGELOG.md | 2 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ..._and_default.rs => manual_unwrap_or_default.rs} | 12 +++++------- ...efault.fixed => manual_unwrap_or_default.fixed} | 2 +- ..._and_default.rs => manual_unwrap_or_default.rs} | 2 +- ...ault.stderr => manual_unwrap_or_default.stderr} | 14 +++++++------- tests/ui/option_option.rs | 2 +- 8 files changed, 19 insertions(+), 21 deletions(-) rename clippy_lints/src/{match_option_and_default.rs => manual_unwrap_or_default.rs} (95%) rename tests/ui/{match_option_and_default.fixed => manual_unwrap_or_default.fixed} (90%) rename tests/ui/{match_option_and_default.rs => manual_unwrap_or_default.rs} (95%) rename tests/ui/{match_option_and_default.stderr => manual_unwrap_or_default.stderr} (76%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a9688b4b51e7..3bb3ef15d5b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5377,6 +5377,7 @@ Released 2018-09-13 [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`manual_try_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or +[`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default [`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone @@ -5390,7 +5391,6 @@ Released 2018-09-13 [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items -[`match_option_and_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_option_and_default [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats [`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b9b03862b7b9d..0ccfd34494ea2 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -310,9 +310,9 @@ pub(crate) 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_option_and_default::MATCH_OPTION_AND_DEFAULT_INFO, crate::match_result_ok::MATCH_RESULT_OK_INFO, crate::matches::COLLAPSIBLE_MATCH_INFO, crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 589deb0e232a9..70292d3440edc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -211,8 +211,8 @@ mod manual_retain; mod manual_slice_size_calculation; mod manual_string_new; mod manual_strip; +mod manual_unwrap_or_default; mod map_unit_fn; -mod match_option_and_default; mod match_result_ok; mod matches; mod mem_replace; @@ -1123,7 +1123,7 @@ 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(|_| Box::new(assigning_clones::AssigningClones)); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); - store.register_late_pass(|_| Box::new(match_option_and_default::MatchOptionAndDefault)); + store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/match_option_and_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs similarity index 95% rename from clippy_lints/src/match_option_and_default.rs rename to clippy_lints/src/manual_unwrap_or_default.rs index c39f6e93ebd89..ddaf97c463ae7 100644 --- a/clippy_lints/src/match_option_and_default.rs +++ b/clippy_lints/src/manual_unwrap_or_default.rs @@ -42,12 +42,12 @@ declare_clippy_lint! { /// let y: Vec = x.unwrap_or_default(); /// ``` #[clippy::version = "1.78.0"] - pub MATCH_OPTION_AND_DEFAULT, + pub MANUAL_UNWRAP_OR_DEFAULT, suspicious, "check if a `match` or `if let` can be simplified with `unwrap_or_default`" } -declare_lint_pass!(MatchOptionAndDefault => [MATCH_OPTION_AND_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 @@ -123,13 +123,12 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { && local_id == binding_id // We now check the `None` arm is calling a method equivalent to `Default::default`. && let body_none = body_none.peel_blocks() - && let ExprKind::Call(_, &[]) = body_none.kind && is_default_equivalent(cx, body_none) && let Some(match_expr_snippet) = snippet_opt(cx, match_expr.span) { span_lint_and_sugg( cx, - MATCH_OPTION_AND_DEFAULT, + MANUAL_UNWRAP_OR_DEFAULT, expr.span, "match can be simplified with `.unwrap_or_default()`", "replace it with", @@ -155,13 +154,12 @@ fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { && local_id == binding_id // We now check the `None` arm is calling a method equivalent to `Default::default`. && let body_else = else_expr.peel_blocks() - && let ExprKind::Call(_, &[]) = body_else.kind && is_default_equivalent(cx, body_else) && let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span) { span_lint_and_sugg( cx, - MATCH_OPTION_AND_DEFAULT, + MANUAL_UNWRAP_OR_DEFAULT, expr.span, "if let can be simplified with `.unwrap_or_default()`", "replace it with", @@ -171,7 +169,7 @@ fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { } } -impl<'tcx> LateLintPass<'tcx> for MatchOptionAndDefault { +impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if expr.span.from_expansion() { return; diff --git a/tests/ui/match_option_and_default.fixed b/tests/ui/manual_unwrap_or_default.fixed similarity index 90% rename from tests/ui/match_option_and_default.fixed rename to tests/ui/manual_unwrap_or_default.fixed index 0a90ef636e8eb..c8456805ee6ed 100644 --- a/tests/ui/match_option_and_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::match_option_and_default)] +#![warn(clippy::manual_unwrap_or_default)] #![allow(clippy::unnecessary_literal_unwrap)] fn main() { diff --git a/tests/ui/match_option_and_default.rs b/tests/ui/manual_unwrap_or_default.rs similarity index 95% rename from tests/ui/match_option_and_default.rs rename to tests/ui/manual_unwrap_or_default.rs index 8e69ef47431b4..820717be53a88 100644 --- a/tests/ui/match_option_and_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -1,4 +1,4 @@ -#![warn(clippy::match_option_and_default)] +#![warn(clippy::manual_unwrap_or_default)] #![allow(clippy::unnecessary_literal_unwrap)] fn main() { diff --git a/tests/ui/match_option_and_default.stderr b/tests/ui/manual_unwrap_or_default.stderr similarity index 76% rename from tests/ui/match_option_and_default.stderr rename to tests/ui/manual_unwrap_or_default.stderr index 86d7294f95a8c..f4eb658358842 100644 --- a/tests/ui/match_option_and_default.stderr +++ b/tests/ui/manual_unwrap_or_default.stderr @@ -1,5 +1,5 @@ error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/match_option_and_default.rs:6:5 + --> tests/ui/manual_unwrap_or_default.rs:6:5 | LL | / match x { LL | | @@ -8,11 +8,11 @@ LL | | None => Vec::default(), LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` | - = note: `-D clippy::match-option-and-default` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::match_option_and_default)]` + = note: `-D clippy::manual-unwrap-or-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or_default)]` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/match_option_and_default.rs:13:5 + --> tests/ui/manual_unwrap_or_default.rs:13:5 | LL | / match x { LL | | @@ -22,7 +22,7 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/match_option_and_default.rs:20:5 + --> tests/ui/manual_unwrap_or_default.rs:20:5 | LL | / match x { LL | | @@ -32,7 +32,7 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/match_option_and_default.rs:27:5 + --> tests/ui/manual_unwrap_or_default.rs:27:5 | LL | / match x { LL | | @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: if let can be simplified with `.unwrap_or_default()` - --> tests/ui/match_option_and_default.rs:34:5 + --> tests/ui/manual_unwrap_or_default.rs:34:5 | LL | / if let Some(v) = x { LL | | diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 7dda139c88ec5..8eb75428ca576 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,7 +1,7 @@ //@compile-flags: -Zdeduplicate-diagnostics=yes #![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wraps, clippy::match_option_and_default)] +#![allow(clippy::unnecessary_wraps, clippy::manual_unwrap_or_default)] const C: Option> = None; //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if From ed6e6291dce9b4c078d59ac8fabc6e532be8517f Mon Sep 17 00:00:00 2001 From: "Christopher B. Speir" Date: Sat, 9 Mar 2024 18:56:01 -0600 Subject: [PATCH 036/102] Fix duplicate lint emission from [manual_retain] --- clippy_lints/src/manual_retain.rs | 11 ++--- tests/ui/manual_retain.fixed | 2 - tests/ui/manual_retain.rs | 2 - tests/ui/manual_retain.stderr | 76 +++++++++++++++---------------- 4 files changed, 43 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 6f15fca089eba..3ddb06a1e08a1 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; +use clippy_utils::{match_def_path, paths, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -69,17 +69,16 @@ impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]); impl<'tcx> LateLintPass<'tcx> for ManualRetain { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let Some(parent_expr) = get_parent_expr(cx, expr) - && let Assign(left_expr, collect_expr, _) = &parent_expr.kind + if let Assign(left_expr, collect_expr, _) = &expr.kind && let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind && seg.args.is_none() && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id) { - check_into_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv); - check_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv); - check_to_owned(cx, left_expr, target_expr, parent_expr.span, &self.msrv); + check_into_iter(cx, left_expr, target_expr, expr.span, &self.msrv); + check_iter(cx, left_expr, target_expr, expr.span, &self.msrv); + check_to_owned(cx, left_expr, target_expr, expr.span, &self.msrv); } } diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index e359dfbb98c80..5540029bf6b1f 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 931814f08b7c2..cee641d9d65f9 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index fdbbc53e4df6d..c25c804df7581 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:27:5 + --> tests/ui/manual_retain.rs:25:5 | LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` @@ -8,43 +8,43 @@ LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); = help: to override `-D warnings` add `#[allow(clippy::manual_retain)]` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:28:5 + --> tests/ui/manual_retain.rs:26:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:29:5 + --> tests/ui/manual_retain.rs:27:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:33:5 + --> tests/ui/manual_retain.rs:31:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:34:5 + --> tests/ui/manual_retain.rs:32:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:64:5 + --> tests/ui/manual_retain.rs:62:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:65:5 + --> tests/ui/manual_retain.rs:63:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:66:5 + --> tests/ui/manual_retain.rs:64:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -53,49 +53,49 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:91:5 + --> tests/ui/manual_retain.rs:89:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:92:5 + --> tests/ui/manual_retain.rs:90:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:93:5 + --> tests/ui/manual_retain.rs:91:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:97:5 + --> tests/ui/manual_retain.rs:95:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:98:5 + --> tests/ui/manual_retain.rs:96:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:128:5 + --> tests/ui/manual_retain.rs:126:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:129:5 + --> tests/ui/manual_retain.rs:127:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:130:5 + --> tests/ui/manual_retain.rs:128:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -104,133 +104,133 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:154:5 + --> tests/ui/manual_retain.rs:152:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:155:5 + --> tests/ui/manual_retain.rs:153:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:156:5 + --> tests/ui/manual_retain.rs:154:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:160:5 + --> tests/ui/manual_retain.rs:158:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:161:5 + --> tests/ui/manual_retain.rs:159:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:190:5 + --> tests/ui/manual_retain.rs:188:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:202:5 + --> tests/ui/manual_retain.rs:200:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:203:5 + --> tests/ui/manual_retain.rs:201:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:204:5 + --> tests/ui/manual_retain.rs:202:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:208:5 + --> tests/ui/manual_retain.rs:206:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:209:5 + --> tests/ui/manual_retain.rs:207:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:231:5 + --> tests/ui/manual_retain.rs:229:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:232:5 + --> tests/ui/manual_retain.rs:230:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:233:5 + --> tests/ui/manual_retain.rs:231:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:290:5 + --> tests/ui/manual_retain.rs:288:5 | LL | vec = vec.into_iter().filter(|(x, y)| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:294:5 + --> tests/ui/manual_retain.rs:292:5 | LL | tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(_, n)| *n > 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:311:5 + --> tests/ui/manual_retain.rs:309:5 | LL | vec = vec.iter().filter(|&&x| x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:312:5 + --> tests/ui/manual_retain.rs:310:5 | LL | vec = vec.iter().filter(|&&x| x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:313:5 + --> tests/ui/manual_retain.rs:311:5 | LL | vec = vec.into_iter().filter(|&x| x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:316:5 + --> tests/ui/manual_retain.rs:314:5 | LL | vec = vec.iter().filter(|&x| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:317:5 + --> tests/ui/manual_retain.rs:315:5 | LL | vec = vec.iter().filter(|&x| *x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:318:5 + --> tests/ui/manual_retain.rs:316:5 | LL | vec = vec.into_iter().filter(|x| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` From 6c863bc64c469faf1c437433bf98d343acfb35ff Mon Sep 17 00:00:00 2001 From: r0cky Date: Thu, 29 Feb 2024 20:53:08 +0800 Subject: [PATCH 037/102] Remove unused structs in clippy --- clippy_lints/src/loops/utils.rs | 60 ++------------------------------- clippy_lints/src/macro_use.rs | 6 ---- 2 files changed, 2 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index e685274adb8da..8bca33754e818 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -2,8 +2,8 @@ use clippy_utils::ty::{has_iter_method, implements_trait}; use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; +use rustc_hir::intravisit::{walk_expr, walk_local, Visitor}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -253,62 +253,6 @@ fn is_conditional(expr: &Expr<'_>) -> bool { matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..)) } -#[derive(PartialEq, Eq)] -pub(super) enum Nesting { - Unknown, // no nesting detected yet - RuledOut, // the iterator is initialized or assigned within scope - LookFurther, // no nesting detected, no further walk required -} - -use self::Nesting::{LookFurther, RuledOut, Unknown}; - -pub(super) struct LoopNestVisitor { - pub(super) hir_id: HirId, - pub(super) iterator: HirId, - pub(super) nesting: Nesting, -} - -impl<'tcx> Visitor<'tcx> for LoopNestVisitor { - fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { - if stmt.hir_id == self.hir_id { - self.nesting = LookFurther; - } else if self.nesting == Unknown { - walk_stmt(self, stmt); - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.nesting != Unknown { - return; - } - if expr.hir_id == self.hir_id { - self.nesting = LookFurther; - return; - } - match expr.kind { - ExprKind::Assign(path, _, _) | ExprKind::AssignOp(_, path, _) => { - if path_to_local_id(path, self.iterator) { - self.nesting = RuledOut; - } - }, - _ => walk_expr(self, expr), - } - } - - fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { - if self.nesting != Unknown { - return; - } - if let PatKind::Binding(_, id, ..) = pat.kind { - if id == self.iterator { - self.nesting = RuledOut; - return; - } - } - walk_pat(self, pat); - } -} - /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the /// actual `Iterator` that the loop uses. pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 8d3e7520a5496..067384b09015a 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -30,12 +30,6 @@ declare_clippy_lint! { "#[macro_use] is no longer needed" } -#[derive(Clone, Debug, PartialEq, Eq)] -struct PathAndSpan { - path: String, - span: Span, -} - /// `MacroRefData` includes the name of the macro. #[derive(Debug, Clone)] pub struct MacroRefData { From 8e55bbf11570470ce52b2296bdf058ba11d4bd75 Mon Sep 17 00:00:00 2001 From: WeiTheShinobi Date: Sun, 10 Mar 2024 01:19:43 +0800 Subject: [PATCH 038/102] [`single_match`]: Fix duplicate diagnostics --- clippy_lints/src/matches/single_match.rs | 31 +++++--------------- tests/ui/single_match.fixed | 2 -- tests/ui/single_match.rs | 2 -- tests/ui/single_match.stderr | 36 ++++++++++++------------ tests/ui/single_match_else.fixed | 1 - tests/ui/single_match_else.rs | 1 - tests/ui/single_match_else.stderr | 18 ++++++------ 7 files changed, 34 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 86c414dafccab..a0db8e2db1f8c 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -55,23 +55,15 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: }; let ty = cx.typeck_results().expr_ty(ex); - if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) { - check_single_pattern(cx, ex, arms, expr, els); - check_opt_like(cx, ex, arms, expr, ty, els); + if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) && + (check_single_pattern(arms) || check_opt_like(cx, arms, ty)) { + report_single_pattern(cx, ex, arms, expr, els); } } } -fn check_single_pattern( - cx: &LateContext<'_>, - ex: &Expr<'_>, - arms: &[Arm<'_>], - expr: &Expr<'_>, - els: Option<&Expr<'_>>, -) { - if is_wild(arms[1].pat) { - report_single_pattern(cx, ex, arms, expr, els); - } +fn check_single_pattern(arms: &[Arm<'_>]) -> bool { + is_wild(arms[1].pat) } fn report_single_pattern( @@ -140,19 +132,10 @@ fn report_single_pattern( span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); } -fn check_opt_like<'a>( - cx: &LateContext<'a>, - ex: &Expr<'_>, - arms: &[Arm<'_>], - expr: &Expr<'_>, - ty: Ty<'a>, - els: Option<&Expr<'_>>, -) { +fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool { // We don't want to lint if the second arm contains an enum which could // have more variants in the future. - if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) { - report_single_pattern(cx, ex, arms, expr, els); - } + form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) } /// Returns `true` if all of the types in the pattern are enums which we know diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index a7cb43e144652..acd70416d8bf0 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::single_match)] #![allow( unused, diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 09db497dcd8bd..bde7819981089 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::single_match)] #![allow( unused, diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index f7abf996be743..a249c120ee4a4 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:17:5 + --> tests/ui/single_match.rs:15:5 | LL | / match x { LL | | Some(y) => { @@ -19,7 +19,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:25:5 + --> tests/ui/single_match.rs:23:5 | LL | / match x { LL | | // Note the missing block braces. @@ -31,7 +31,7 @@ LL | | } | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:34:5 + --> tests/ui/single_match.rs:32:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -40,7 +40,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:61:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -49,7 +49,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:68:5 + --> tests/ui/single_match.rs:66:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -58,7 +58,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:75:5 + --> tests/ui/single_match.rs:73:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -67,7 +67,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:96:5 + --> tests/ui/single_match.rs:94:5 | LL | / match x { LL | | "test" => println!(), @@ -76,7 +76,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:109:5 + --> tests/ui/single_match.rs:107:5 | LL | / match x { LL | | Foo::A => println!(), @@ -85,7 +85,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:115:5 + --> tests/ui/single_match.rs:113:5 | LL | / match x { LL | | FOO_C => println!(), @@ -94,7 +94,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:120:5 + --> tests/ui/single_match.rs:118:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -103,7 +103,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:126:5 + --> tests/ui/single_match.rs:124:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -112,7 +112,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:143:5 + --> tests/ui/single_match.rs:141:5 | LL | / match x { LL | | Bar::A => println!(), @@ -121,7 +121,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:151:5 + --> tests/ui/single_match.rs:149:5 | LL | / match x { LL | | None => println!(), @@ -130,7 +130,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:173:5 + --> tests/ui/single_match.rs:171:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -139,7 +139,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:179:5 + --> tests/ui/single_match.rs:177:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -148,7 +148,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:185:5 + --> tests/ui/single_match.rs:183:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -157,7 +157,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:257:5 + --> tests/ui/single_match.rs:255:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -177,7 +177,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:265:5 + --> tests/ui/single_match.rs:263:5 | LL | / match bar { LL | | #[rustfmt::skip] diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index 2970f5485fae9..e840adf0fa34b 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -1,5 +1,4 @@ //@aux-build: proc_macros.rs -//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 26974b2a48ab4..430c4da20f12a 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,5 +1,4 @@ //@aux-build: proc_macros.rs -//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 48c74c0caeaed..f8f88379d6d12 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:18:13 + --> tests/ui/single_match_else.rs:17: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:82: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:92:5 + --> tests/ui/single_match_else.rs:91:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -62,7 +62,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:102:5 + --> tests/ui/single_match_else.rs:101:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -82,7 +82,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:111:5 + --> tests/ui/single_match_else.rs:110:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), @@ -102,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:121:5 + --> tests/ui/single_match_else.rs:120:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -125,7 +125,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:132:5 + --> tests/ui/single_match_else.rs:131:5 | LL | / match bar { LL | | Some(v) => { @@ -149,7 +149,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:144:5 + --> tests/ui/single_match_else.rs:143:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -173,7 +173,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:156:5 + --> tests/ui/single_match_else.rs:155:5 | LL | / match bar { LL | | #[rustfmt::skip] From 9c51fd99aaac804da599067d1b62057c8c10ca63 Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Sun, 10 Mar 2024 12:51:33 -0700 Subject: [PATCH 039/102] refactor: readability improvement for `seek` lint --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 830dc28a4b801..71dc45a477c82 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3183,8 +3183,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// - /// Checks an argument of `seek` method of `Seek` trait - /// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead. + /// Checks if the `seek` method of the `Seek` trait is called with `SeekFrom::Current(0)`, + /// and if it is, suggests using `stream_position` instead. /// /// ### Why is this bad? /// From d66a0ec714023101bc1e8ef63231979df20e2318 Mon Sep 17 00:00:00 2001 From: "Christopher B. Speir" Date: Mon, 11 Mar 2024 13:25:44 -0500 Subject: [PATCH 040/102] Fix typo in section '6.10. Macro Expansions' of the Clippy Book The struct returned by the `Span::ctxt` method was listed as `SpanContext`. The correct struct is currently named `SyntaxContext`. --- book/src/development/macro_expansions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/development/macro_expansions.md b/book/src/development/macro_expansions.md index aecca9ef72e98..125b6c4bc5be1 100644 --- a/book/src/development/macro_expansions.md +++ b/book/src/development/macro_expansions.md @@ -52,7 +52,7 @@ if expr.span.from_expansion() { ### `Span.ctxt` method -The `span`'s context, given by the method [`ctxt`] and returning [SpanContext], +The `span`'s context, given by the method [`ctxt`] and returning [SyntaxContext], represents if the span is from a macro expansion and, if it is, which macro call expanded this span. @@ -155,4 +155,4 @@ if in_external_macro(cx.sess(), foo_span) { [`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_middle/lint/fn.in_external_macro.html [Span]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html -[SpanContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html +[SyntaxContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html From 100ab4993e33d9d228303ce86abbfbed9abd88f6 Mon Sep 17 00:00:00 2001 From: Jacherr Date: Sun, 10 Mar 2024 00:00:35 +0000 Subject: [PATCH 041/102] new restriction lint: `division_remainder_used` --- CHANGELOG.md | 2 + clippy_lints/src/declared_lints.rs | 1 + .../src/integer_division_remainder_used.rs | 50 ++++++++++++++++ clippy_lints/src/lib.rs | 2 + tests/ui/integer_division_remainder_used.rs | 41 +++++++++++++ .../ui/integer_division_remainder_used.stderr | 59 +++++++++++++++++++ 6 files changed, 155 insertions(+) create mode 100644 clippy_lints/src/integer_division_remainder_used.rs create mode 100644 tests/ui/integer_division_remainder_used.rs create mode 100644 tests/ui/integer_division_remainder_used.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index c4651cee057f0..23e8d82e556d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5144,6 +5144,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 +[`division_remainder_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#division_remainder_used [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons @@ -5281,6 +5282,7 @@ Released 2018-09-13 [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division +[`integer_division_remainder_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division_remainder_used [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5d1eadfc7a4da..c8e148598a272 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -236,6 +236,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, + crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO, crate::item_name_repetitions::MODULE_INCEPTION_INFO, diff --git a/clippy_lints/src/integer_division_remainder_used.rs b/clippy_lints/src/integer_division_remainder_used.rs new file mode 100644 index 0000000000000..36dc45ca788da --- /dev/null +++ b/clippy_lints/src/integer_division_remainder_used.rs @@ -0,0 +1,50 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::BinOpKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of division (/) and remainder (%) operations + /// when performed on any integer types using the default Div and Rem trait implementations. + /// + /// ### Why is this bad? + /// In cryptographic contexts, division can result in timing sidechannel vulnerabilities, + /// and needs to be replaced with constant-time code instead (e.g. Barrett reduction). + /// + /// ### Example + /// ```no_run + /// let my_div = 10 / 2; + /// ``` + /// Use instead: + /// ```no_run + /// let my_div = 10 >> 1; + /// ``` + #[clippy::version = "1.78.0"] + pub INTEGER_DIVISION_REMAINDER_USED, + restriction, + "use of disallowed default division and remainder operations" +} + +declare_lint_pass!(IntegerDivisionRemainderUsed => [INTEGER_DIVISION_REMAINDER_USED]); + +impl LateLintPass<'_> for IntegerDivisionRemainderUsed { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Binary(op, lhs, rhs) = &expr.kind + && let BinOpKind::Div | BinOpKind::Rem = op.node + && let lhs_ty = cx.typeck_results().expr_ty(lhs) + && let rhs_ty = cx.typeck_results().expr_ty(rhs) + && let ty::Int(_) | ty::Uint(_) = lhs_ty.peel_refs().kind() + && let ty::Int(_) | ty::Uint(_) = rhs_ty.peel_refs().kind() + { + span_lint( + cx, + INTEGER_DIVISION_REMAINDER_USED, + expr.span.source_callsite(), + &format!("use of {} has been disallowed in this context", op.node.as_str()), + ); + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 70292d3440edc..9b22d7565e3c7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -172,6 +172,7 @@ mod init_numbered_fields; mod inline_fn_without_body; mod instant_subtraction; mod int_plus_one; +mod integer_division_remainder_used; mod invalid_upcast_comparisons; mod item_name_repetitions; mod items_after_statements; @@ -1124,6 +1125,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(assigning_clones::AssigningClones)); 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)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/integer_division_remainder_used.rs b/tests/ui/integer_division_remainder_used.rs new file mode 100644 index 0000000000000..5d1b02095d181 --- /dev/null +++ b/tests/ui/integer_division_remainder_used.rs @@ -0,0 +1,41 @@ +#![warn(clippy::integer_division_remainder_used)] +#![allow(unused_variables)] +#![allow(clippy::op_ref)] + +struct CustomOps(pub i32); +impl std::ops::Div for CustomOps { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } +} +impl std::ops::Rem for CustomOps { + type Output = Self; + + fn rem(self, rhs: Self) -> Self::Output { + Self(self.0 % rhs.0) + } +} + +fn main() { + // should trigger + let a = 10; + let b = 5; + let c = a / b; + let d = a % b; + let e = &a / b; + let f = a % &b; + let g = &a / &b; + let h = &10 % b; + let i = a / &4; + + // should not trigger on custom Div and Rem + let w = CustomOps(3); + let x = CustomOps(4); + let y = w / x; + + let w = CustomOps(3); + let x = CustomOps(4); + let z = w % x; +} diff --git a/tests/ui/integer_division_remainder_used.stderr b/tests/ui/integer_division_remainder_used.stderr new file mode 100644 index 0000000000000..8adfda28893da --- /dev/null +++ b/tests/ui/integer_division_remainder_used.stderr @@ -0,0 +1,59 @@ +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:10:14 + | +LL | Self(self.0 / rhs.0) + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::integer-division-remainder-used` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::integer_division_remainder_used)]` + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:17:14 + | +LL | Self(self.0 % rhs.0) + | ^^^^^^^^^^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:25:13 + | +LL | let c = a / b; + | ^^^^^ + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:26:13 + | +LL | let d = a % b; + | ^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:27:13 + | +LL | let e = &a / b; + | ^^^^^^ + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:28:13 + | +LL | let f = a % &b; + | ^^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:29:13 + | +LL | let g = &a / &b; + | ^^^^^^^ + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:30:13 + | +LL | let h = &10 % b; + | ^^^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:31:13 + | +LL | let i = a / &4; + | ^^^^^^ + +error: aborting due to 9 previous errors + From edcf10e2f7243d4173d2a2c635ef923ba813a10f Mon Sep 17 00:00:00 2001 From: Jacherr Date: Mon, 11 Mar 2024 22:55:31 +0000 Subject: [PATCH 042/102] update_lints --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e8d82e556d2..733858d46732e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5144,7 +5144,6 @@ 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 -[`division_remainder_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#division_remainder_used [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons From 10677d69015738e7c4434105c3077ea009d10980 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Tue, 12 Mar 2024 09:19:35 +0800 Subject: [PATCH 043/102] add `with_empty_docs` attr macro & test cases for issue #12377 --- tests/ui/auxiliary/proc_macro_attr.rs | 13 +++++++++ tests/ui/empty_docs.rs | 17 ++++++++++++ tests/ui/empty_docs.stderr | 38 ++++++++++++++++++++------- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index b550d13b0b9ae..a6f3b164c9ba1 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -163,3 +163,16 @@ pub fn rewrite_struct(_args: TokenStream, input: TokenStream) -> TokenStream { quote!(#item_struct).into() } + +#[proc_macro_attribute] +pub fn with_empty_docs(_attr: TokenStream, input: TokenStream) -> TokenStream { + let item = parse_macro_input!(input as syn::Item); + let attrs: Vec = vec![]; + let doc_comment = ""; + quote! { + #(#attrs)* + #[doc = #doc_comment] + #item + } + .into() +} diff --git a/tests/ui/empty_docs.rs b/tests/ui/empty_docs.rs index 272fab7d5ca1a..00e64eebc5fba 100644 --- a/tests/ui/empty_docs.rs +++ b/tests/ui/empty_docs.rs @@ -1,6 +1,9 @@ +//@aux-build:proc_macro_attr.rs + #![allow(unused)] #![warn(clippy::empty_docs)] #![allow(clippy::mixed_attributes_style)] +#![feature(extern_types)] mod outer { //! @@ -67,3 +70,17 @@ mod outer { y: i32, } } + +mod issue_12377 { + use proc_macro_attr::with_empty_docs; + + #[with_empty_docs] + extern "C" { + type Test; + } + + #[with_empty_docs] + struct Foo { + a: u8, + } +} diff --git a/tests/ui/empty_docs.stderr b/tests/ui/empty_docs.stderr index f12aead6aa75e..c6618ca32329c 100644 --- a/tests/ui/empty_docs.stderr +++ b/tests/ui/empty_docs.stderr @@ -1,5 +1,5 @@ error: empty doc comment - --> tests/ui/empty_docs.rs:6:5 + --> tests/ui/empty_docs.rs:9:5 | LL | //! | ^^^ @@ -9,7 +9,7 @@ LL | //! = help: to override `-D warnings` add `#[allow(clippy::empty_docs)]` error: empty doc comment - --> tests/ui/empty_docs.rs:14:5 + --> tests/ui/empty_docs.rs:17:5 | LL | /// | ^^^ @@ -17,7 +17,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:16:9 + --> tests/ui/empty_docs.rs:19:9 | LL | /// | ^^^ @@ -25,7 +25,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:27:5 + --> tests/ui/empty_docs.rs:30:5 | LL | #[doc = ""] | ^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | #[doc = ""] = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:30:5 + --> tests/ui/empty_docs.rs:33:5 | LL | / #[doc = ""] LL | | #[doc = ""] @@ -42,7 +42,7 @@ LL | | #[doc = ""] = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:37:5 + --> tests/ui/empty_docs.rs:40:5 | LL | /// | ^^^ @@ -50,7 +50,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:50:13 + --> tests/ui/empty_docs.rs:53:13 | LL | /*! */ | ^^^^^^ @@ -58,7 +58,7 @@ LL | /*! */ = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:58:13 + --> tests/ui/empty_docs.rs:61:13 | LL | /// | ^^^ @@ -66,12 +66,30 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:66:9 + --> tests/ui/empty_docs.rs:69:9 | LL | /// | ^^^ | = help: consider removing or filling it -error: aborting due to 9 previous errors +error: empty doc comment + --> tests/ui/empty_docs.rs:77:5 + | +LL | #[with_empty_docs] + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider removing or filling it + = note: this error originates in the attribute macro `with_empty_docs` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: empty doc comment + --> tests/ui/empty_docs.rs:82:5 + | +LL | #[with_empty_docs] + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider removing or filling it + = note: this error originates in the attribute macro `with_empty_docs` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 11 previous errors From 3cd6fd15a5b9f654b08d22949a21273d44ef64de Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Tue, 12 Mar 2024 10:40:40 +0800 Subject: [PATCH 044/102] fix [`empty_docs`] trigger in proc-macro --- clippy_lints/src/doc/mod.rs | 13 +++++++++++-- tests/ui/empty_docs.stderr | 20 +------------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 003d26b7b89d9..b4087c4328212 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -538,7 +538,16 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ suspicious_doc_comments::check(cx, attrs); - let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); + let (fragments, _) = attrs_to_doc_fragments( + attrs.iter().filter_map(|attr| { + if in_external_macro(cx.sess(), attr.span) { + None + } else { + Some((attr, None)) + } + }), + true, + ); let mut doc = fragments.iter().fold(String::new(), |mut acc, fragment| { add_doc_fragment(&mut acc, fragment); acc diff --git a/tests/ui/empty_docs.stderr b/tests/ui/empty_docs.stderr index c6618ca32329c..28ebea22c5db8 100644 --- a/tests/ui/empty_docs.stderr +++ b/tests/ui/empty_docs.stderr @@ -73,23 +73,5 @@ LL | /// | = help: consider removing or filling it -error: empty doc comment - --> tests/ui/empty_docs.rs:77:5 - | -LL | #[with_empty_docs] - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider removing or filling it - = note: this error originates in the attribute macro `with_empty_docs` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: empty doc comment - --> tests/ui/empty_docs.rs:82:5 - | -LL | #[with_empty_docs] - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider removing or filling it - = note: this error originates in the attribute macro `with_empty_docs` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors From b205192f65816071a0a316dd64e3204636d53347 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 23 Feb 2024 23:12:20 +0000 Subject: [PATCH 045/102] Change `DefKind::Static` to a struct variant --- clippy_lints/src/loops/needless_range_loop.rs | 2 +- clippy_lints/src/loops/while_immutable_condition.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 2 +- clippy_lints/src/multiple_unsafe_ops_per_block.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 08b8a9e2ff072..47dc3807e624f 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -273,7 +273,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } return false; // no need to walk further *on the variable* }, - Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { + Res::Def(DefKind::Static{..} | DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 9fd9b7a163121..3511d24e8134e 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -101,7 +101,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { Res::Local(hir_id) => { self.ids.insert(hir_id); }, - Res::Def(DefKind::Static(_), def_id) => { + Res::Def(DefKind::Static{..}, def_id) => { let mutable = self.cx.tcx.is_mutable_static(def_id); self.def_ids.insert(def_id, mutable); }, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index f0fc925799a35..e2c2997594ad9 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -91,7 +91,7 @@ pub(super) fn check<'tcx>( }, hir::ExprKind::Path(ref p) => matches!( cx.qpath_res(p, arg.hir_id), - hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static(_), _) + hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static{..}, _) ), _ => false, } diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 049f44f3246f4..d8caa632b9389 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -109,7 +109,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static(Mutability::Mut), _), + res: Res::Def(DefKind::Static{mt:Mutability::Mut}, _), .. }, )) => { @@ -149,7 +149,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static(Mutability::Mut), _), + res: Res::Def(DefKind::Static{mt:Mutability::Mut}, _), .. } )) From 013bf92dcc3a602bd9b82675fe86bf396e115224 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 23 Feb 2024 23:29:09 +0000 Subject: [PATCH 046/102] Add `nested` bool to `DefKind::Static`. Will be used in the next commit --- clippy_lints/src/multiple_unsafe_ops_per_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index d8caa632b9389..4155e608026e3 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -109,7 +109,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static{mt:Mutability::Mut}, _), + res: Res::Def(DefKind::Static{mt:Mutability::Mut, ..}, _), .. }, )) => { @@ -149,7 +149,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static{mt:Mutability::Mut}, _), + res: Res::Def(DefKind::Static{mt:Mutability::Mut, ..}, _), .. } )) From 97f2ade8e647e2842695c0dda30d9c72fb7f7c07 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 17:33:57 +0000 Subject: [PATCH 047/102] s/mt/mutability/ --- clippy_lints/src/multiple_unsafe_ops_per_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 4155e608026e3..70fd07cd93ce8 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -109,7 +109,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static{mt:Mutability::Mut, ..}, _), + res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _), .. }, )) => { @@ -149,7 +149,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static{mt:Mutability::Mut, ..}, _), + res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _), .. } )) From f472b5071058360c99ee18ee79b2142fe8f215cc Mon Sep 17 00:00:00 2001 From: pavedroad Date: Tue, 12 Mar 2024 14:38:57 +0800 Subject: [PATCH 048/102] chore: fix some typos Signed-off-by: pavedroad --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 71dc45a477c82..693eda6577805 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2849,7 +2849,7 @@ declare_clippy_lint! { /// the file is created from scratch, or ensure `truncate` is /// called so that the truncation behaviour is explicit. `truncate(true)` /// will ensure the file is entirely overwritten with new data, whereas - /// `truncate(false)` will explicitely keep the default behavior. + /// `truncate(false)` will explicitly keep the default behavior. /// /// ### Example /// ```rust,no_run diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 5410e8ac11781..436f0cb79fb26 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -213,7 +213,7 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { // Allow skipping imports containing user configured segments, // i.e. "...::utils::...::*" if user put `allowed-wildcard-imports = ["utils"]` in `Clippy.toml` fn is_allowed_via_config(segments: &[PathSegment<'_>], allowed_segments: &FxHashSet) -> bool { - // segment matching need to be exact instead of using 'contains', in case user unintentionaly put + // segment matching need to be exact instead of using 'contains', in case user unintentionally put // a single character in the config thus skipping most of the warnings. segments.iter().any(|seg| allowed_segments.contains(seg.ident.as_str())) } From 27c49e1756361d2fb33ae1307c615748be6b45f1 Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Tue, 12 Mar 2024 16:22:26 +0900 Subject: [PATCH 049/102] filetime::FileTime::now() is new in 0.2.9 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 64571d4cc80d5..502708e6d4bdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" # This is used by the `collect-metadata` alias. -filetime = "0.2" +filetime = "0.2.9" itertools = "0.12" # UI test dependencies From 244d7da8178f3278945dfbdf8b85734be3c995f5 Mon Sep 17 00:00:00 2001 From: Catherine Flores Date: Wed, 2 Aug 2023 14:36:25 -0500 Subject: [PATCH 050/102] [`cast_lossless`]: Suggest type alias instead of the aliased type --- clippy_lints/src/casts/cast_lossless.rs | 34 +++++++++++++---- clippy_lints/src/casts/mod.rs | 2 +- tests/ui/cast_lossless_bool.fixed | 4 ++ tests/ui/cast_lossless_bool.rs | 4 ++ tests/ui/cast_lossless_bool.stderr | 36 ++++++++++-------- tests/ui/cast_lossless_float.fixed | 5 +++ tests/ui/cast_lossless_float.rs | 5 +++ tests/ui/cast_lossless_float.stderr | 36 ++++++++++++------ tests/ui/cast_lossless_integer.fixed | 4 ++ tests/ui/cast_lossless_integer.rs | 4 ++ tests/ui/cast_lossless_integer.stderr | 50 ++++++++++++++----------- 11 files changed, 126 insertions(+), 58 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index fe2455f4b2395..fe0dd7f2eae6d 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,10 +1,10 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_constant; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; @@ -16,6 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, + cast_to_hir: &rustc_hir::Ty<'_>, msrv: &Msrv, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { @@ -24,11 +25,11 @@ pub(super) fn check( // The suggestion is to use a function call, so if the original expression // has parens on the outside, they are no longer needed. - let mut applicability = Applicability::MachineApplicable; + let mut app = Applicability::MachineApplicable; let opt = snippet_opt(cx, cast_op.span.source_callsite()); let sugg = opt.as_ref().map_or_else( || { - applicability = Applicability::HasPlaceholders; + app = Applicability::HasPlaceholders; ".." }, |snip| { @@ -40,10 +41,27 @@ pub(super) fn check( }, ); + // Display the type alias instead of the aliased type. Fixes #11285 + // + // FIXME: Once `lazy_type_alias` is stabilized(?) we should use `rustc_middle` types instead, + // this will allow us to display the right type with `cast_from` as well. + let cast_to_fmt = if let TyKind::Path(QPath::Resolved(None, path)) = cast_to_hir.kind + // It's a bit annoying but the turbofish is optional for types. A type in an `as` cast + // shouldn't have these if they're primitives, which are the only things we deal with. + // + // This could be removed for performance if this check is determined to have a pretty major + // effect. + && path.segments.iter().all(|segment| segment.args.is_none()) + { + snippet_with_applicability(cx, cast_to_hir.span, "..", &mut app) + } else { + cast_to.to_string().into() + }; + let message = if cast_from.is_bool() { - format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`") + format!("casting `{cast_from}` to `{cast_to_fmt}` is more cleanly stated with `{cast_to_fmt}::from(_)`") } else { - format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type") + format!("casting `{cast_from}` to `{cast_to_fmt}` may become silently lossy if you later change the type") }; span_lint_and_sugg( @@ -52,8 +70,8 @@ pub(super) fn check( expr.span, &message, "try", - format!("{cast_to}::from({sugg})"), - applicability, + format!("{cast_to_fmt}::from({sugg})"), + app, ); } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 14f2f4a7f59da..063aab282384b 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -791,7 +791,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index a4ce1c6f928e7..51a38a60cf6b2 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,6 +1,8 @@ #![allow(dead_code)] #![warn(clippy::cast_lossless)] +type U8 = u8; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = u8::from(true); @@ -19,6 +21,8 @@ fn main() { // Test with an expression wrapped in parens let _ = u16::from(true | false); + + let _ = U8::from(true); } // The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index e5b1c30c10350..cb307bd68e43e 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![warn(clippy::cast_lossless)] +type U8 = u8; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = true as u8; @@ -19,6 +21,8 @@ fn main() { // Test with an expression wrapped in parens let _ = (true | false) as u16; + + let _ = true as U8; } // The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 792b30b7a38b3..b47b35461f686 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> tests/ui/cast_lossless_bool.rs:6:13 + --> tests/ui/cast_lossless_bool.rs:8:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -8,82 +8,88 @@ LL | let _ = true as u8; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> tests/ui/cast_lossless_bool.rs:7:13 + --> tests/ui/cast_lossless_bool.rs:9:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> tests/ui/cast_lossless_bool.rs:8:13 + --> tests/ui/cast_lossless_bool.rs:10:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> tests/ui/cast_lossless_bool.rs:9:13 + --> tests/ui/cast_lossless_bool.rs:11:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> tests/ui/cast_lossless_bool.rs:10:13 + --> tests/ui/cast_lossless_bool.rs:12:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> tests/ui/cast_lossless_bool.rs:11:13 + --> tests/ui/cast_lossless_bool.rs:13:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> tests/ui/cast_lossless_bool.rs:13:13 + --> tests/ui/cast_lossless_bool.rs:15:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> tests/ui/cast_lossless_bool.rs:14:13 + --> tests/ui/cast_lossless_bool.rs:16:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> tests/ui/cast_lossless_bool.rs:15:13 + --> tests/ui/cast_lossless_bool.rs:17:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> tests/ui/cast_lossless_bool.rs:16:13 + --> tests/ui/cast_lossless_bool.rs:18:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> tests/ui/cast_lossless_bool.rs:17:13 + --> tests/ui/cast_lossless_bool.rs:19:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> tests/ui/cast_lossless_bool.rs:18:13 + --> tests/ui/cast_lossless_bool.rs:20:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> tests/ui/cast_lossless_bool.rs:21:13 + --> tests/ui/cast_lossless_bool.rs:23:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` +error: casting `bool` to `U8` is more cleanly stated with `U8::from(_)` + --> tests/ui/cast_lossless_bool.rs:25:13 + | +LL | let _ = true as U8; + | ^^^^^^^^^^ help: try: `U8::from(true)` + error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> tests/ui/cast_lossless_bool.rs:49:13 + --> tests/ui/cast_lossless_bool.rs:53:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/cast_lossless_float.fixed b/tests/ui/cast_lossless_float.fixed index f4f2e4773a533..96a67b1945ca6 100644 --- a/tests/ui/cast_lossless_float.fixed +++ b/tests/ui/cast_lossless_float.fixed @@ -1,11 +1,16 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type F32 = f32; +type F64 = f64; + fn main() { // Test clippy::cast_lossless with casts to floating-point types let x0 = 1i8; let _ = f32::from(x0); let _ = f64::from(x0); + let _ = F32::from(x0); + let _ = F64::from(x0); let x1 = 1u8; let _ = f32::from(x1); let _ = f64::from(x1); diff --git a/tests/ui/cast_lossless_float.rs b/tests/ui/cast_lossless_float.rs index fdd88ed36fc63..d37b2c1d920ea 100644 --- a/tests/ui/cast_lossless_float.rs +++ b/tests/ui/cast_lossless_float.rs @@ -1,11 +1,16 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type F32 = f32; +type F64 = f64; + fn main() { // Test clippy::cast_lossless with casts to floating-point types let x0 = 1i8; let _ = x0 as f32; let _ = x0 as f64; + let _ = x0 as F32; + let _ = x0 as F64; let x1 = 1u8; let _ = x1 as f32; let _ = x1 as f64; diff --git a/tests/ui/cast_lossless_float.stderr b/tests/ui/cast_lossless_float.stderr index e70f81eb91fc7..ad7de760adfb8 100644 --- a/tests/ui/cast_lossless_float.stderr +++ b/tests/ui/cast_lossless_float.stderr @@ -1,5 +1,5 @@ error: casting `i8` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:7:13 + --> tests/ui/cast_lossless_float.rs:10:13 | LL | let _ = x0 as f32; | ^^^^^^^^^ help: try: `f32::from(x0)` @@ -8,64 +8,76 @@ LL | let _ = x0 as f32; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `i8` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:8:13 + --> tests/ui/cast_lossless_float.rs:11:13 | LL | let _ = x0 as f64; | ^^^^^^^^^ help: try: `f64::from(x0)` +error: casting `i8` to `F32` may become silently lossy if you later change the type + --> tests/ui/cast_lossless_float.rs:12:13 + | +LL | let _ = x0 as F32; + | ^^^^^^^^^ help: try: `F32::from(x0)` + +error: casting `i8` to `F64` may become silently lossy if you later change the type + --> tests/ui/cast_lossless_float.rs:13:13 + | +LL | let _ = x0 as F64; + | ^^^^^^^^^ help: try: `F64::from(x0)` + error: casting `u8` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:10:13 + --> tests/ui/cast_lossless_float.rs:15:13 | LL | let _ = x1 as f32; | ^^^^^^^^^ help: try: `f32::from(x1)` error: casting `u8` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:11:13 + --> tests/ui/cast_lossless_float.rs:16:13 | LL | let _ = x1 as f64; | ^^^^^^^^^ help: try: `f64::from(x1)` error: casting `i16` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:13:13 + --> tests/ui/cast_lossless_float.rs:18:13 | LL | let _ = x2 as f32; | ^^^^^^^^^ help: try: `f32::from(x2)` error: casting `i16` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:14:13 + --> tests/ui/cast_lossless_float.rs:19:13 | LL | let _ = x2 as f64; | ^^^^^^^^^ help: try: `f64::from(x2)` error: casting `u16` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:16:13 + --> tests/ui/cast_lossless_float.rs:21:13 | LL | let _ = x3 as f32; | ^^^^^^^^^ help: try: `f32::from(x3)` error: casting `u16` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:17:13 + --> tests/ui/cast_lossless_float.rs:22:13 | LL | let _ = x3 as f64; | ^^^^^^^^^ help: try: `f64::from(x3)` error: casting `i32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:19:13 + --> tests/ui/cast_lossless_float.rs:24:13 | LL | let _ = x4 as f64; | ^^^^^^^^^ help: try: `f64::from(x4)` error: casting `u32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:21:13 + --> tests/ui/cast_lossless_float.rs:26:13 | LL | let _ = x5 as f64; | ^^^^^^^^^ help: try: `f64::from(x5)` error: casting `f32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:24:13 + --> tests/ui/cast_lossless_float.rs:29:13 | LL | let _ = 1.0f32 as f64; | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` -error: aborting due to 11 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 5e7e545e764a5..1b826e0c60073 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -1,6 +1,8 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type I64 = i64; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = i16::from(1i8); @@ -24,6 +26,8 @@ fn main() { // Test with an expression wrapped in parens let _ = u16::from(1u8 + 1u8); + + let _ = I64::from(1i8); } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 0d69ddbd586a5..f63bbf8577d79 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -1,6 +1,8 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type I64 = i64; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = 1i8 as i16; @@ -24,6 +26,8 @@ fn main() { // Test with an expression wrapped in parens let _ = (1u8 + 1u8) as u16; + + let _ = 1i8 as I64; } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_integer.stderr b/tests/ui/cast_lossless_integer.stderr index 43d4ce3ce9161..0c4b5aee62de3 100644 --- a/tests/ui/cast_lossless_integer.stderr +++ b/tests/ui/cast_lossless_integer.stderr @@ -1,5 +1,5 @@ error: casting `i8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:6:13 + --> tests/ui/cast_lossless_integer.rs:8:13 | LL | let _ = 1i8 as i16; | ^^^^^^^^^^ help: try: `i16::from(1i8)` @@ -8,124 +8,130 @@ LL | let _ = 1i8 as i16; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:7:13 + --> tests/ui/cast_lossless_integer.rs:9:13 | LL | let _ = 1i8 as i32; | ^^^^^^^^^^ help: try: `i32::from(1i8)` error: casting `i8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:8:13 + --> tests/ui/cast_lossless_integer.rs:10:13 | LL | let _ = 1i8 as i64; | ^^^^^^^^^^ help: try: `i64::from(1i8)` error: casting `u8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:9:13 + --> tests/ui/cast_lossless_integer.rs:11:13 | LL | let _ = 1u8 as i16; | ^^^^^^^^^^ help: try: `i16::from(1u8)` error: casting `u8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:10:13 + --> tests/ui/cast_lossless_integer.rs:12:13 | LL | let _ = 1u8 as i32; | ^^^^^^^^^^ help: try: `i32::from(1u8)` error: casting `u8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:11:13 + --> tests/ui/cast_lossless_integer.rs:13:13 | LL | let _ = 1u8 as i64; | ^^^^^^^^^^ help: try: `i64::from(1u8)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:12:13 + --> tests/ui/cast_lossless_integer.rs:14:13 | LL | let _ = 1u8 as u16; | ^^^^^^^^^^ help: try: `u16::from(1u8)` error: casting `u8` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:13:13 + --> tests/ui/cast_lossless_integer.rs:15:13 | LL | let _ = 1u8 as u32; | ^^^^^^^^^^ help: try: `u32::from(1u8)` error: casting `u8` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:14:13 + --> tests/ui/cast_lossless_integer.rs:16:13 | LL | let _ = 1u8 as u64; | ^^^^^^^^^^ help: try: `u64::from(1u8)` error: casting `i16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:15:13 + --> tests/ui/cast_lossless_integer.rs:17:13 | LL | let _ = 1i16 as i32; | ^^^^^^^^^^^ help: try: `i32::from(1i16)` error: casting `i16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:16:13 + --> tests/ui/cast_lossless_integer.rs:18:13 | LL | let _ = 1i16 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1i16)` error: casting `u16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:17:13 + --> tests/ui/cast_lossless_integer.rs:19:13 | LL | let _ = 1u16 as i32; | ^^^^^^^^^^^ help: try: `i32::from(1u16)` error: casting `u16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:18:13 + --> tests/ui/cast_lossless_integer.rs:20:13 | LL | let _ = 1u16 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1u16)` error: casting `u16` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:19:13 + --> tests/ui/cast_lossless_integer.rs:21:13 | LL | let _ = 1u16 as u32; | ^^^^^^^^^^^ help: try: `u32::from(1u16)` error: casting `u16` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:20:13 + --> tests/ui/cast_lossless_integer.rs:22:13 | LL | let _ = 1u16 as u64; | ^^^^^^^^^^^ help: try: `u64::from(1u16)` error: casting `i32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:21:13 + --> tests/ui/cast_lossless_integer.rs:23:13 | LL | let _ = 1i32 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1i32)` error: casting `u32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:22:13 + --> tests/ui/cast_lossless_integer.rs:24:13 | LL | let _ = 1u32 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1u32)` error: casting `u32` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:23:13 + --> tests/ui/cast_lossless_integer.rs:25:13 | LL | let _ = 1u32 as u64; | ^^^^^^^^^^^ help: try: `u64::from(1u32)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:26:13 + --> tests/ui/cast_lossless_integer.rs:28:13 | LL | let _ = (1u8 + 1u8) as u16; | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` +error: casting `i8` to `I64` may become silently lossy if you later change the type + --> tests/ui/cast_lossless_integer.rs:30:13 + | +LL | let _ = 1i8 as I64; + | ^^^^^^^^^^ help: try: `I64::from(1i8)` + error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:60:13 + --> tests/ui/cast_lossless_integer.rs:64:13 | LL | let _ = sign_cast!(x, u8, i8) as i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:61:13 + --> tests/ui/cast_lossless_integer.rs:65:13 | LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors From 65defdb47482e26037ff9e5ca311522c7823372f Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 10 Mar 2024 19:40:31 +0000 Subject: [PATCH 051/102] [`unconditional_recursion`]: catch `From` -> `Into` -> `From` --- clippy_lints/src/unconditional_recursion.rs | 54 ++++++-- clippy_utils/src/lib.rs | 19 ++- tests/ui/unconditional_recursion.rs | 50 ++++++- tests/ui/unconditional_recursion.stderr | 136 ++++++++++---------- 4 files changed, 174 insertions(+), 85 deletions(-) diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index 224ec475c5107..1ca14ca04d3cf 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{expr_or_init, get_trait_def_id, path_def_id}; +use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id}; use rustc_ast::BinOpKind; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -19,11 +19,11 @@ use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; declare_clippy_lint! { /// ### What it does - /// Checks that there isn't an infinite recursion in `PartialEq` trait - /// implementation. + /// Checks that there isn't an infinite recursion in trait + /// implementations. /// /// ### Why is this bad? - /// This is a hard to find infinite recursion which will crashing any code + /// This is a hard to find infinite recursion that will crash any code. /// using it. /// /// ### Example @@ -201,7 +201,6 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca } } -#[allow(clippy::unnecessary_def_path)] fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) { let args = cx .tcx @@ -224,7 +223,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local && let Some(trait_) = impl_.of_trait && let Some(trait_def_id) = trait_.trait_def_id() // The trait is `ToString`. - && Some(trait_def_id) == get_trait_def_id(cx, &["alloc", "string", "ToString"]) + && cx.tcx.is_diagnostic_item(sym::ToString, trait_def_id) { let is_bad = match expr.kind { ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { @@ -291,7 +290,6 @@ where self.map } - #[allow(clippy::unnecessary_def_path)] fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { if self.found_default_call { return; @@ -303,7 +301,7 @@ where && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) && let Some(method_def_id) = path_def_id(self.cx, f) && let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id) - && Some(trait_def_id) == get_trait_def_id(self.cx, &["core", "default", "Default"]) + && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { self.found_default_call = true; span_error(self.cx, self.method_span, expr); @@ -312,10 +310,9 @@ where } impl UnconditionalRecursion { - #[allow(clippy::unnecessary_def_path)] fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) { if self.default_impl_for_type.is_empty() - && let Some(default_trait_id) = get_trait_def_id(cx, &["core", "default", "Default"]) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) { let impls = cx.tcx.trait_impls_of(default_trait_id); for (ty, impl_def_ids) in impls.non_blanket_impls() { @@ -394,6 +391,34 @@ impl UnconditionalRecursion { } } +fn check_from(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, expr: &Expr<'_>) { + let Some(sig) = cx + .typeck_results() + .liberated_fn_sigs() + .get(cx.tcx.local_def_id_to_hir_id(method_def_id)) + else { + return; + }; + + // Check if we are calling `Into::into` where the node args match with our `From::from` signature: + // From::from signature: fn(S1) -> S2 + // >::into(s1), node_args=[S1, S2] + // If they do match, then it must mean that it is the blanket impl, + // which calls back into our `From::from` again (`Into` is not specializable). + // rustc's unconditional_recursion already catches calling `From::from` directly + if let Some((fn_def_id, node_args)) = fn_def_id_with_node_args(cx, expr) + && let [s1, s2] = **node_args + && let (Some(s1), Some(s2)) = (s1.as_type(), s2.as_type()) + && let Some(trait_def_id) = cx.tcx.trait_of_item(fn_def_id) + && cx.tcx.is_diagnostic_item(sym::Into, trait_def_id) + && get_impl_trait_def_id(cx, method_def_id) == cx.tcx.get_diagnostic_item(sym::From) + && s1 == sig.inputs()[0] + && s2 == sig.output() + { + span_error(cx, method_span, expr); + } +} + impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion { fn check_fn( &mut self, @@ -410,10 +435,11 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion { // Doesn't have a conditional return. && !has_conditional_return(body, expr) { - if name.name == sym::eq || name.name == sym::ne { - check_partial_eq(cx, method_span, method_def_id, name, expr); - } else if name.name == sym::to_string { - check_to_string(cx, method_span, method_def_id, name, expr); + match name.name { + sym::eq | sym::ne => check_partial_eq(cx, method_span, method_def_id, name, expr), + sym::to_string => check_to_string(cx, method_span, method_def_id, name, expr), + sym::from => check_from(cx, method_span, method_def_id, expr), + _ => {}, } self.check_default_new(cx, decl, body, method_span, method_def_id); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 708037a465558..b013e4196ea1d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2249,8 +2249,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { /// Returns the `DefId` of the callee if the given expression is a function or method call. pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + fn_def_id_with_node_args(cx, expr).map(|(did, _)| did) +} + +/// Returns the `DefId` of the callee if the given expression is a function or method call, +/// as well as its node args. +pub fn fn_def_id_with_node_args<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'_>, +) -> Option<(DefId, rustc_ty::GenericArgsRef<'tcx>)> { + let typeck = cx.typeck_results(); match &expr.kind { - ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + ExprKind::MethodCall(..) => Some(( + typeck.type_dependent_def_id(expr.hir_id)?, + typeck.node_args(expr.hir_id), + )), ExprKind::Call( Expr { kind: ExprKind::Path(qpath), @@ -2262,9 +2275,9 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or // deref to fn pointers, dyn Fn, impl Fn - #8850 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = - cx.typeck_results().qpath_res(qpath, *path_hir_id) + typeck.qpath_res(qpath, *path_hir_id) { - Some(id) + Some((id, typeck.node_args(*path_hir_id))) } else { None } diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index 35275e81ded5c..70b390b00e272 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -1,7 +1,11 @@ //@no-rustfix #![warn(clippy::unconditional_recursion)] -#![allow(clippy::partialeq_ne_impl, clippy::default_constructed_unit_structs)] +#![allow( + clippy::partialeq_ne_impl, + clippy::default_constructed_unit_structs, + clippy::only_used_in_recursion +)] enum Foo { A, @@ -350,4 +354,48 @@ mod issue12154 { } } +// From::from -> Into::into -> From::from +struct BadFromTy1<'a>(&'a ()); +struct BadIntoTy1<'b>(&'b ()); +impl<'a> From> for BadIntoTy1<'static> { + fn from(f: BadFromTy1<'a>) -> Self { + f.into() + } +} + +// Using UFCS syntax +struct BadFromTy2<'a>(&'a ()); +struct BadIntoTy2<'b>(&'b ()); +impl<'a> From> for BadIntoTy2<'static> { + fn from(f: BadFromTy2<'a>) -> Self { + Into::into(f) + } +} + +// Different Into impl (>), so no infinite recursion +struct BadFromTy3; +impl From for i32 { + fn from(f: BadFromTy3) -> Self { + Into::into(1i16) + } +} + +// A conditional return that ends the recursion +struct BadFromTy4; +impl From for i32 { + fn from(f: BadFromTy4) -> Self { + if true { + return 42; + } + f.into() + } +} + +// Types differ in refs, don't lint +impl From<&BadFromTy4> for i32 { + fn from(f: &BadFromTy4) -> Self { + BadFromTy4.into() + } +} + fn main() {} diff --git a/tests/ui/unconditional_recursion.stderr b/tests/ui/unconditional_recursion.stderr index 3fd6c91000eab..03c27bd8ed8aa 100644 --- a/tests/ui/unconditional_recursion.stderr +++ b/tests/ui/unconditional_recursion.stderr @@ -1,5 +1,5 @@ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:42:5 + --> tests/ui/unconditional_recursion.rs:46:5 | LL | fn ne(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -12,7 +12,7 @@ LL | self.ne(other) = help: to override `-D warnings` add `#[allow(unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:46:5 + --> tests/ui/unconditional_recursion.rs:50:5 | LL | fn eq(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -23,7 +23,7 @@ LL | self.eq(other) = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:211:5 + --> tests/ui/unconditional_recursion.rs:215:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -34,7 +34,7 @@ LL | self.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:221:5 + --> tests/ui/unconditional_recursion.rs:225:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -45,7 +45,7 @@ LL | x.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:232:5 + --> tests/ui/unconditional_recursion.rs:236:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -56,7 +56,7 @@ LL | (self as &Self).to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:12:5 + --> tests/ui/unconditional_recursion.rs:16:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -65,7 +65,7 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:14:9 + --> tests/ui/unconditional_recursion.rs:18:9 | LL | self != other | ^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | self != other = help: to override `-D warnings` add `#[allow(clippy::unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:16:5 + --> tests/ui/unconditional_recursion.rs:20:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -82,13 +82,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:18:9 + --> tests/ui/unconditional_recursion.rs:22:9 | LL | self == other | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:28:5 + --> tests/ui/unconditional_recursion.rs:32:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | self != &Foo2::B // no error here @@ -96,13 +96,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:29:9 + --> tests/ui/unconditional_recursion.rs:33:9 | LL | self != &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:31:5 + --> tests/ui/unconditional_recursion.rs:35:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | self == &Foo2::B // no error here @@ -110,13 +110,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:32:9 + --> tests/ui/unconditional_recursion.rs:36:9 | LL | self == &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:42:5 + --> tests/ui/unconditional_recursion.rs:46:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -125,27 +125,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:44:9 + --> tests/ui/unconditional_recursion.rs:48:9 | LL | self.ne(other) | ^^^^^^^^^^^^^^ -error: parameter is only used in recursion - --> tests/ui/unconditional_recursion.rs:42:18 - | -LL | fn ne(&self, other: &Self) -> bool { - | ^^^^^ help: if this is intentional, prefix it with an underscore: `_other` - | -note: parameter used here - --> tests/ui/unconditional_recursion.rs:44:17 - | -LL | self.ne(other) - | ^^^^^ - = note: `-D clippy::only-used-in-recursion` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::only_used_in_recursion)]` - error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:46:5 + --> tests/ui/unconditional_recursion.rs:50:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -154,25 +140,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:48:9 + --> tests/ui/unconditional_recursion.rs:52:9 | LL | self.eq(other) | ^^^^^^^^^^^^^^ -error: parameter is only used in recursion - --> tests/ui/unconditional_recursion.rs:46:18 - | -LL | fn eq(&self, other: &Self) -> bool { - | ^^^^^ help: if this is intentional, prefix it with an underscore: `_other` - | -note: parameter used here - --> tests/ui/unconditional_recursion.rs:48:17 - | -LL | self.eq(other) - | ^^^^^ - error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:90:5 + --> tests/ui/unconditional_recursion.rs:94:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -181,13 +155,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:92:9 + --> tests/ui/unconditional_recursion.rs:96:9 | LL | other != self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:94:5 + --> tests/ui/unconditional_recursion.rs:98:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -196,13 +170,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:96:9 + --> tests/ui/unconditional_recursion.rs:100:9 | LL | other == self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:104:5 + --> tests/ui/unconditional_recursion.rs:108:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -211,13 +185,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:106:9 + --> tests/ui/unconditional_recursion.rs:110:9 | LL | other != other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:106:9 + --> tests/ui/unconditional_recursion.rs:110:9 | LL | other != other | ^^^^^^^^^^^^^^ @@ -225,7 +199,7 @@ LL | other != other = note: `#[deny(clippy::eq_op)]` on by default error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:108:5 + --> tests/ui/unconditional_recursion.rs:112:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -234,19 +208,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:110:9 + --> tests/ui/unconditional_recursion.rs:114:9 | LL | other == other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:110:9 + --> tests/ui/unconditional_recursion.rs:114:9 | LL | other == other | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:117:5 + --> tests/ui/unconditional_recursion.rs:121:5 | LL | / fn ne(&self, _other: &Self) -> bool { LL | | @@ -255,19 +229,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:119:9 + --> tests/ui/unconditional_recursion.rs:123:9 | LL | self != self | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:119:9 + --> tests/ui/unconditional_recursion.rs:123:9 | LL | self != self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:121:5 + --> tests/ui/unconditional_recursion.rs:125:5 | LL | / fn eq(&self, _other: &Self) -> bool { LL | | @@ -276,19 +250,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:123:9 + --> tests/ui/unconditional_recursion.rs:127:9 | LL | self == self | ^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:123:9 + --> tests/ui/unconditional_recursion.rs:127:9 | LL | self == self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:149:13 + --> tests/ui/unconditional_recursion.rs:153:13 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -300,7 +274,7 @@ LL | impl_partial_eq!(S5); | -------------------- in this macro invocation | note: recursive call site - --> tests/ui/unconditional_recursion.rs:151:17 + --> tests/ui/unconditional_recursion.rs:155:17 | LL | self == other | ^^^^^^^^^^^^^ @@ -310,7 +284,7 @@ LL | impl_partial_eq!(S5); = note: this error originates in the macro `impl_partial_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:178:5 + --> tests/ui/unconditional_recursion.rs:182:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -321,13 +295,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:182:9 + --> tests/ui/unconditional_recursion.rs:186:9 | LL | mine == theirs | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:247:5 + --> tests/ui/unconditional_recursion.rs:251:5 | LL | / fn new() -> Self { LL | | @@ -336,13 +310,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:249:9 + --> tests/ui/unconditional_recursion.rs:253:9 | LL | Self::default() | ^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:286:5 + --> tests/ui/unconditional_recursion.rs:290:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -353,10 +327,38 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:290:9 + --> tests/ui/unconditional_recursion.rs:294:9 | LL | mine.eq(theirs) | ^^^^^^^^^^^^^^^ +error: function cannot return without recursing + --> tests/ui/unconditional_recursion.rs:361:5 + | +LL | / fn from(f: BadFromTy1<'a>) -> Self { +LL | | f.into() +LL | | } + | |_____^ + | +note: recursive call site + --> tests/ui/unconditional_recursion.rs:362:9 + | +LL | f.into() + | ^^^^^^^^ + +error: function cannot return without recursing + --> tests/ui/unconditional_recursion.rs:370:5 + | +LL | / fn from(f: BadFromTy2<'a>) -> Self { +LL | | Into::into(f) +LL | | } + | |_____^ + | +note: recursive call site + --> tests/ui/unconditional_recursion.rs:371:9 + | +LL | Into::into(f) + | ^^^^^^^^^^^^^ + error: aborting due to 27 previous errors From 11c2bad059ace0c61611fdf93c2426c2dafca039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Mar 2024 17:26:07 +0100 Subject: [PATCH 052/102] CI: replace `cancel-outdated-builds` with `concurrency` groupo --- .github/workflows/clippy.yml | 10 ++++++---- .github/workflows/clippy_bors.yml | 25 +++++-------------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 603f91a910b18..8179e3e65b541 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -26,6 +26,12 @@ env: NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 +concurrency: + # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. + # If the push is not attached to a PR, we will cancel all builds on the same branch. + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + jobs: base: # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml @@ -33,10 +39,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 0bc28c1f9d996..94515987eba4a 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -12,6 +12,11 @@ env: NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 +concurrency: + # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + defaults: run: shell: bash @@ -21,10 +26,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 with: @@ -67,10 +68,6 @@ jobs: # NOTE: If you modify this job, make sure you copy the changes to clippy.yml steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 @@ -131,10 +128,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 @@ -155,10 +148,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 @@ -211,10 +200,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 From 11759d1bad5cebc1af170090bc423f2494cfbc40 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 12 Mar 2024 15:14:44 +0100 Subject: [PATCH 053/102] Don't emit `doc_markdown` lint for missing backticks if it's inside a quote --- clippy_lints/src/doc/markdown.rs | 17 +++++++++++++---- clippy_lints/src/doc/mod.rs | 7 +++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/doc/markdown.rs b/clippy_lints/src/doc/markdown.rs index d2f147565917f..1add02af31017 100644 --- a/clippy_lints/src/doc/markdown.rs +++ b/clippy_lints/src/doc/markdown.rs @@ -8,7 +8,14 @@ use url::Url; use crate::doc::DOC_MARKDOWN; -pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet, text: &str, span: Span, code_level: isize) { +pub fn check( + cx: &LateContext<'_>, + valid_idents: &FxHashSet, + text: &str, + span: Span, + code_level: isize, + blockquote_level: isize, +) { for orig_word in text.split(|c: char| c.is_whitespace() || c == '\'') { // Trim punctuation as in `some comment (see foo::bar).` // ^^ @@ -46,11 +53,11 @@ pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet, text: &str, span.parent(), ); - check_word(cx, word, span, code_level); + check_word(cx, word, span, code_level, blockquote_level); } } -fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize) { +fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, blockquote_level: isize) { /// Checks if a string is upper-camel-case, i.e., starts with an uppercase and /// contains at least two uppercase letters (`Clippy` is ok) and one lower-case /// letter (`NASA` is ok). @@ -97,7 +104,9 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize) { } // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) - if code_level > 0 || (has_underscore(word) && has_hyphen(word)) { + // + // We also assume that backticks are not necessary if inside a quote. (Issue #10262) + if code_level > 0 || blockquote_level > 0 || (has_underscore(word) && has_hyphen(word)) { return; } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 003d26b7b89d9..10fc9ef6ecf55 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -7,7 +7,7 @@ use clippy_utils::{is_entrypoint_fn, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; -use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; +use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; @@ -602,6 +602,7 @@ fn check_doc<'a, Events: Iterator, Range, Range, isize)> = Vec::new(); let mut paragraph_range = 0..0; let mut code_level = 0; + let mut blockquote_level = 0; for (event, range) in events { match event { @@ -612,6 +613,8 @@ fn check_doc<'a, Events: Iterator, Range blockquote_level += 1, + End(BlockQuote) => blockquote_level -= 1, Start(CodeBlock(ref kind)) => { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { @@ -663,7 +666,7 @@ fn check_doc<'a, Events: Iterator, Range Date: Tue, 12 Mar 2024 15:15:19 +0100 Subject: [PATCH 054/102] Add regression test for #10262 --- tests/ui/doc/issue_10262.fixed | 8 ++++++++ tests/ui/doc/issue_10262.rs | 8 ++++++++ tests/ui/doc/issue_10262.stderr | 15 +++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 tests/ui/doc/issue_10262.fixed create mode 100644 tests/ui/doc/issue_10262.rs create mode 100644 tests/ui/doc/issue_10262.stderr diff --git a/tests/ui/doc/issue_10262.fixed b/tests/ui/doc/issue_10262.fixed new file mode 100644 index 0000000000000..8ca6f3e9718ff --- /dev/null +++ b/tests/ui/doc/issue_10262.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::doc_markdown)] + +// Should only warn for the first line! +/// `AviSynth` documentation: +//~^ ERROR: item in documentation is missing backticks +/// +/// > AvisynthPluginInit3 may be called more than once with different IScriptEnvironments. +pub struct Foo; diff --git a/tests/ui/doc/issue_10262.rs b/tests/ui/doc/issue_10262.rs new file mode 100644 index 0000000000000..67183b63cdf05 --- /dev/null +++ b/tests/ui/doc/issue_10262.rs @@ -0,0 +1,8 @@ +#![warn(clippy::doc_markdown)] + +// Should only warn for the first line! +/// AviSynth documentation: +//~^ ERROR: item in documentation is missing backticks +/// +/// > AvisynthPluginInit3 may be called more than once with different IScriptEnvironments. +pub struct Foo; diff --git a/tests/ui/doc/issue_10262.stderr b/tests/ui/doc/issue_10262.stderr new file mode 100644 index 0000000000000..f43d9551e94e0 --- /dev/null +++ b/tests/ui/doc/issue_10262.stderr @@ -0,0 +1,15 @@ +error: item in documentation is missing backticks + --> tests/ui/doc/issue_10262.rs:4:5 + | +LL | /// AviSynth documentation: + | ^^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]` +help: try + | +LL | /// `AviSynth` documentation: + | ~~~~~~~~~~ + +error: aborting due to 1 previous error + From 03d7ae81535f9427501da6f7091b73307a9ea908 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 13 Mar 2024 19:31:01 +0100 Subject: [PATCH 055/102] Also handle `

` and `` HTML tags --- clippy_lints/src/doc/mod.rs | 4 ++++ tests/ui/doc/issue_10262.fixed | 4 ++++ tests/ui/doc/issue_10262.rs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 10fc9ef6ecf55..9bf5d2652b369 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -611,6 +611,10 @@ fn check_doc<'a, Events: Iterator, Range blockquote_level += 1, diff --git a/tests/ui/doc/issue_10262.fixed b/tests/ui/doc/issue_10262.fixed index 8ca6f3e9718ff..5d067736d556c 100644 --- a/tests/ui/doc/issue_10262.fixed +++ b/tests/ui/doc/issue_10262.fixed @@ -5,4 +5,8 @@ //~^ ERROR: item in documentation is missing backticks /// /// > AvisynthPluginInit3 may be called more than once with different IScriptEnvironments. +/// +///
bla AvisynthPluginInit3 bla
+/// +/// bla AvisynthPluginInit3 bla pub struct Foo; diff --git a/tests/ui/doc/issue_10262.rs b/tests/ui/doc/issue_10262.rs index 67183b63cdf05..e2cbd938d5d79 100644 --- a/tests/ui/doc/issue_10262.rs +++ b/tests/ui/doc/issue_10262.rs @@ -5,4 +5,8 @@ //~^ ERROR: item in documentation is missing backticks /// /// > AvisynthPluginInit3 may be called more than once with different IScriptEnvironments. +/// +///
bla AvisynthPluginInit3 bla
+/// +/// bla AvisynthPluginInit3 bla pub struct Foo; From 7cdeac5773ac7664731d8cb850334559b4b9dab8 Mon Sep 17 00:00:00 2001 From: Ethiraric Date: Thu, 7 Mar 2024 18:50:47 +0100 Subject: [PATCH 056/102] [`unused_enumerate_index`]: trigger on method calls The lint used to check for patterns looking like: ```rs for (_, x) in some_iter.enumerate() { // Index is ignored } ``` This commit further checks for chained method calls constructs where we can detect that the index is unused. Currently, this checks only for the following patterns: ```rs some_iter.enumerate().map_function(|(_, x)| ..) let x = some_iter.enumerate(); x.map_function(|(_, x)| ..) ``` where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or `map`. Fixes #12411. --- .../src/loops/unused_enumerate_index.rs | 2 + clippy_lints/src/methods/mod.rs | 68 +++++++----- .../src/methods/unused_enumerate_index.rs | 102 ++++++++++++++++++ clippy_utils/src/paths.rs | 2 + tests/ui/unused_enumerate_index.fixed | 34 ++++++ tests/ui/unused_enumerate_index.rs | 34 ++++++ tests/ui/unused_enumerate_index.stderr | 42 +++++++- 7 files changed, 253 insertions(+), 31 deletions(-) create mode 100644 clippy_lints/src/methods/unused_enumerate_index.rs diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index e86a63a273818..31f0f1cfeba31 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -8,6 +8,8 @@ use rustc_lint::LateContext; use rustc_middle::ty; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. +/// +/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`. pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) { if let PatKind::Tuple([index, elem], _) = pat.kind && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8a24ccea3a1eb..d6d9425027edb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -118,6 +118,7 @@ mod unnecessary_literal_unwrap; mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; +mod unused_enumerate_index; mod unwrap_expect_used; mod useless_asref; mod utils; @@ -4403,6 +4404,7 @@ impl Methods { zst_offset::check(cx, expr, recv); }, ("all", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { iter_overeager_cloned::check( cx, @@ -4421,23 +4423,26 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, - ("any", [arg]) => match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::NeedlessMove(arg), - false, - ), - Some(("chars", recv, _, _, _)) - if let ExprKind::Closure(arg) = arg.kind - && let body = cx.tcx.hir().body(arg.body) - && let [param] = body.params => - { - string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); - }, - _ => {}, + ("any", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::NeedlessMove(arg), + false, + ), + Some(("chars", recv, _, _, _)) + if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params => + { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + }, + _ => {}, + } }, ("arg", [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); @@ -4570,14 +4575,17 @@ impl Methods { } }, ("filter_map", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, ("find_map", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); }, ("flat_map", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); }, @@ -4599,17 +4607,20 @@ impl Methods { manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv); unnecessary_fold::check(cx, expr, init, acc, span); }, - ("for_each", [arg]) => match method_call(recv) { - Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::NeedlessMove(arg), - false, - ), - _ => {}, + ("for_each", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); + match method_call(recv) { + Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::NeedlessMove(arg), + false, + ), + _ => {}, + } }, ("get", [arg]) => { get_first::check(cx, expr, recv, arg); @@ -4650,6 +4661,7 @@ impl Methods { }, (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { + unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, &self.msrv); match method_call(recv) { Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => { diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs new file mode 100644 index 0000000000000..506a0f4304adc --- /dev/null +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -0,0 +1,102 @@ +use clippy_utils::diagnostics::{multispan_sugg, span_lint_hir_and_then}; +use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT}; +use clippy_utils::source::snippet; +use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild}; +use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::AdtDef; +use rustc_span::sym; + +use crate::loops::UNUSED_ENUMERATE_INDEX; + +/// Check for the `UNUSED_ENUMERATE_INDEX` lint outside of loops. +/// +/// The lint is declared in `clippy_lints/src/loops/mod.rs`. There, the following pattern is +/// checked: +/// ```ignore +/// for (_, x) in some_iter.enumerate() { +/// // Index is ignored +/// } +/// ``` +/// +/// This `check` function checks for chained method calls constructs where we can detect that the +/// index is unused. Currently, this checks only for the following patterns: +/// ```ignore +/// some_iter.enumerate().map_function(|(_, x)| ..) +/// let x = some_iter.enumerate(); +/// x.map_function(|(_, x)| ..) +/// ``` +/// where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or +/// `map`. +/// +/// # Preconditions +/// This function must be called not on the `enumerate` call expression itself, but on any of the +/// map functions listed above. It will ensure that `recv` is a `std::iter::Enumerate` instance and +/// that the method call is one of the `std::iter::Iterator` trait. +/// +/// * `call_expr`: The map function call expression +/// * `recv`: The receiver of the call +/// * `closure_arg`: The argument to the map function call containing the closure/function to apply +pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { + let recv_ty = cx.typeck_results().expr_ty(recv); + if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) + // If we call a method on a `std::iter::Enumerate` instance + && match_def_path(cx, recv_ty_defid, &CORE_ITER_ENUMERATE_STRUCT) + // If we are calling a method of the `Iterator` trait + && is_trait_method(cx, call_expr, sym::Iterator) + // And the map argument is a closure + && let ExprKind::Closure(closure) = closure_arg.kind + && let closure_body = cx.tcx.hir().body(closure.body) + // And that closure has one argument ... + && let [closure_param] = closure_body.params + // .. which is a tuple of 2 elements + && let PatKind::Tuple([index, elem], ..) = closure_param.pat.kind + // And that the first element (the index) is either `_` or unused in the body + && pat_is_wild(cx, &index.kind, closure_body) + // Try to find the initializer for `recv`. This is needed in case `recv` is a local_binding. In the + // first example below, `expr_or_init` would return `recv`. + // ``` + // iter.enumerate().map(|(_, x)| x) + // ^^^^^^^^^^^^^^^^ `recv`, a call to `std::iter::Iterator::enumerate` + // + // let binding = iter.enumerate(); + // ^^^^^^^^^^^^^^^^ `recv_init_expr` + // binding.map(|(_, x)| x) + // ^^^^^^^ `recv`, not a call to `std::iter::Iterator::enumerate` + // ``` + && let recv_init_expr = expr_or_init(cx, recv) + // Make sure the initializer is a method call. It may be that the `Enumerate` comes from something + // that we cannot control. + // This would for instance happen with: + // ``` + // external_lib::some_function_returning_enumerate().map(|(_, x)| x) + // ``` + && let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind + && let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id) + // Make sure the method call is `std::iter::Iterator::enumerate`. + && match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD) + { + // Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we + // can get from the `MethodCall`. + span_lint_hir_and_then( + cx, + UNUSED_ENUMERATE_INDEX, + recv_init_expr.hir_id, + enumerate_span, + "you seem to use `.enumerate()` and immediately discard the index", + |diag| { + multispan_sugg( + diag, + "remove the `.enumerate()` call", + vec![ + (closure_param.span, snippet(cx, elem.span, "..").into_owned()), + ( + enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()), + String::new(), + ), + ], + ); + }, + ); + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 987f28192a85d..456b8019e95cc 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -19,6 +19,8 @@ pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "B pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; +pub const CORE_ITER_ENUMERATE_METHOD: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "enumerate"]; +pub const CORE_ITER_ENUMERATE_STRUCT: [&str; 5] = ["core", "iter", "adapters", "enumerate", "Enumerate"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; diff --git a/tests/ui/unused_enumerate_index.fixed b/tests/ui/unused_enumerate_index.fixed index d079807ab5877..1224eb54ba5c3 100644 --- a/tests/ui/unused_enumerate_index.fixed +++ b/tests/ui/unused_enumerate_index.fixed @@ -3,6 +3,10 @@ use std::iter::Enumerate; +fn get_enumerate() -> Enumerate> { + vec![1].into_iter().enumerate() +} + fn main() { let v = [1, 2, 3]; for x in v.iter() { @@ -55,4 +59,34 @@ fn main() { for x in dummy { println!("{x}"); } + + let _ = vec![1, 2, 3].into_iter().map(|x| println!("{x}")); + + let p = vec![1, 2, 3].into_iter(); + p.map(|x| println!("{x}")); + + // This shouldn't trigger the lint. `get_enumerate` may come from an external library on which we + // have no control. + let p = get_enumerate(); + p.map(|(_, x)| println!("{x}")); + + // This shouldn't trigger the lint. The `enumerate` call is in a different context. + macro_rules! mac { + () => { + [1].iter().enumerate() + }; + } + _ = mac!().map(|(_, v)| v); + + macro_rules! mac2 { + () => { + [1].iter() + }; + } + _ = mac2!().map(|_v| {}); + + // This shouldn't trigger the lint because of the `allow`. + #[allow(clippy::unused_enumerate_index)] + let v = [1].iter().enumerate(); + v.map(|(_, _x)| {}); } diff --git a/tests/ui/unused_enumerate_index.rs b/tests/ui/unused_enumerate_index.rs index 2d524da763270..66f98b690cbe9 100644 --- a/tests/ui/unused_enumerate_index.rs +++ b/tests/ui/unused_enumerate_index.rs @@ -3,6 +3,10 @@ use std::iter::Enumerate; +fn get_enumerate() -> Enumerate> { + vec![1].into_iter().enumerate() +} + fn main() { let v = [1, 2, 3]; for (_, x) in v.iter().enumerate() { @@ -55,4 +59,34 @@ fn main() { for (_, x) in dummy.enumerate() { println!("{x}"); } + + let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}")); + + let p = vec![1, 2, 3].into_iter().enumerate(); + p.map(|(_, x)| println!("{x}")); + + // This shouldn't trigger the lint. `get_enumerate` may come from an external library on which we + // have no control. + let p = get_enumerate(); + p.map(|(_, x)| println!("{x}")); + + // This shouldn't trigger the lint. The `enumerate` call is in a different context. + macro_rules! mac { + () => { + [1].iter().enumerate() + }; + } + _ = mac!().map(|(_, v)| v); + + macro_rules! mac2 { + () => { + [1].iter() + }; + } + _ = mac2!().enumerate().map(|(_, _v)| {}); + + // This shouldn't trigger the lint because of the `allow`. + #[allow(clippy::unused_enumerate_index)] + let v = [1].iter().enumerate(); + v.map(|(_, _x)| {}); } diff --git a/tests/ui/unused_enumerate_index.stderr b/tests/ui/unused_enumerate_index.stderr index 7bd7d29117e24..f8babf428c025 100644 --- a/tests/ui/unused_enumerate_index.stderr +++ b/tests/ui/unused_enumerate_index.stderr @@ -1,5 +1,5 @@ error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:8:19 + --> tests/ui/unused_enumerate_index.rs:12:19 | LL | for (_, x) in v.iter().enumerate() { | ^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | for x in v.iter() { | ~ ~~~~~~~~ error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:55:19 + --> tests/ui/unused_enumerate_index.rs:59:19 | LL | for (_, x) in dummy.enumerate() { | ^^^^^^^^^^^^^^^^^ @@ -22,5 +22,41 @@ help: remove the `.enumerate()` call LL | for x in dummy { | ~ ~~~~~ -error: aborting due to 2 previous errors +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:63:39 + | +LL | let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}")); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL - let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}")); +LL + let _ = vec![1, 2, 3].into_iter().map(|x| println!("{x}")); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:65:39 + | +LL | let p = vec![1, 2, 3].into_iter().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let p = vec![1, 2, 3].into_iter(); +LL ~ p.map(|x| println!("{x}")); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:86:17 + | +LL | _ = mac2!().enumerate().map(|(_, _v)| {}); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL - _ = mac2!().enumerate().map(|(_, _v)| {}); +LL + _ = mac2!().map(|_v| {}); + | + +error: aborting due to 5 previous errors From adcbb4a9b7427bdf508ec016961e999ed1d72612 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:32:18 +0100 Subject: [PATCH 057/102] move readonly_write_lock to perf --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/await_holding_lock.rs | 1 + tests/ui/await_holding_lock.stderr | 52 +++++++++++++++--------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e0029e0146e32..08075b23ad20a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3590,7 +3590,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub READONLY_WRITE_LOCK, - nursery, + perf, "acquiring a write lock when a read lock would work" } diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs index 27b57b6481368..8e5510e6cd01c 100644 --- a/tests/ui/await_holding_lock.rs +++ b/tests/ui/await_holding_lock.rs @@ -1,4 +1,5 @@ #![warn(clippy::await_holding_lock)] +#![allow(clippy::readonly_write_lock)] // When adding or modifying a test, please do the same for parking_lot::Mutex. mod std_mutex { diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index e58436345b5c7..0af48a36acca1 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -1,12 +1,12 @@ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:9:13 + --> tests/ui/await_holding_lock.rs:10:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:11:15 + --> tests/ui/await_holding_lock.rs:12:15 | LL | baz().await | ^^^^^ @@ -14,40 +14,40 @@ LL | baz().await = help: to override `-D warnings` add `#[allow(clippy::await_holding_lock)]` error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:25:13 + --> tests/ui/await_holding_lock.rs:26:13 | LL | let guard = x.read().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:27:15 + --> tests/ui/await_holding_lock.rs:28:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:31:13 + --> tests/ui/await_holding_lock.rs:32:13 | LL | let mut guard = x.write().unwrap(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:33:15 + --> tests/ui/await_holding_lock.rs:34:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:53:13 + --> tests/ui/await_holding_lock.rs:54:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:56:28 + --> tests/ui/await_holding_lock.rs:57:28 | LL | let second = baz().await; | ^^^^^ @@ -56,79 +56,79 @@ LL | let third = baz().await; | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:67:17 + --> tests/ui/await_holding_lock.rs:68:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:69:19 + --> tests/ui/await_holding_lock.rs:70:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:80:17 + --> tests/ui/await_holding_lock.rs:81:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:82:19 + --> tests/ui/await_holding_lock.rs:83:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:93:13 + --> tests/ui/await_holding_lock.rs:94:13 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:95:15 + --> tests/ui/await_holding_lock.rs:96:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:109:13 + --> tests/ui/await_holding_lock.rs:110:13 | LL | let guard = x.read(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:111:15 + --> tests/ui/await_holding_lock.rs:112:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:115:13 + --> tests/ui/await_holding_lock.rs:116:13 | LL | let mut guard = x.write(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:117:15 + --> tests/ui/await_holding_lock.rs:118:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:137:13 + --> tests/ui/await_holding_lock.rs:138:13 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:140:28 + --> tests/ui/await_holding_lock.rs:141:28 | LL | let second = baz().await; | ^^^^^ @@ -137,40 +137,40 @@ LL | let third = baz().await; | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:151:17 + --> tests/ui/await_holding_lock.rs:152:17 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:153:19 + --> tests/ui/await_holding_lock.rs:154:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:164:17 + --> tests/ui/await_holding_lock.rs:165:17 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:166:19 + --> tests/ui/await_holding_lock.rs:167:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:185:9 + --> tests/ui/await_holding_lock.rs:186:9 | LL | let mut guard = x.lock().unwrap(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:189:11 + --> tests/ui/await_holding_lock.rs:190:11 | LL | baz().await; | ^^^^^ From 1fe884401aa12b08408b101cf931cb396d0ccf3f Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Mon, 15 Jan 2024 17:51:29 +0800 Subject: [PATCH 058/102] fix [`dbg_macro`] FN when `dbg` is inside some complex macros --- clippy_lints/src/dbg_macro.rs | 139 +++++++++++++++------------- tests/ui/dbg_macro/dbg_macro.rs | 9 ++ tests/ui/dbg_macro/dbg_macro.stderr | 24 ++++- 3 files changed, 107 insertions(+), 65 deletions(-) diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index ec66556cebffa..a517d7bd66115 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::macros::root_macro_call; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -34,82 +34,93 @@ declare_clippy_lint! { #[derive(Copy, Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, + /// Keep tracking of the previous `dbg!` macro call site in order to + /// skip any other expressions from the same expansion, including nested macro calls. + prev_dbg_call_site: Span, } impl_lint_pass!(DbgMacro => [DBG_MACRO]); impl DbgMacro { pub fn new(allow_dbg_in_tests: bool) -> Self { - DbgMacro { allow_dbg_in_tests } + DbgMacro { + allow_dbg_in_tests, + prev_dbg_call_site: Span::default(), + } } } impl LateLintPass<'_> for DbgMacro { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + let Some(macro_call) = + root_macro_call(expr.span).filter(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) + else { return; }; - if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { - // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml - if self.allow_dbg_in_tests - && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) - { - return; - } - let mut applicability = Applicability::MachineApplicable; - - let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) - }, - _ => return, - }; + // skip previous checked exprs + if self.prev_dbg_call_site.contains(macro_call.span) { + return; + } + self.prev_dbg_call_site = macro_call.span; - span_lint_and_sugg( - cx, - DBG_MACRO, - sugg_span, - "the `dbg!` macro is intended as a debugging tool", - "remove the invocation before committing it to a version control system", - suggestion, - applicability, - ); + // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml + if self.allow_dbg_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) + { + return; } + let mut applicability = Applicability::MachineApplicable; + + let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Some(Node::Stmt(_)) = cx.tcx.hir().find_parent(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. + }, + .., + Expr { + kind: ExprKind::Match(last, ..), + .. + }, + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + DBG_MACRO, + sugg_span, + "the `dbg!` macro is intended as a debugging tool", + "remove the invocation before committing it to a version control system", + suggestion, + applicability, + ); } } diff --git a/tests/ui/dbg_macro/dbg_macro.rs b/tests/ui/dbg_macro/dbg_macro.rs index 3f4770c63d014..bdb6c379f885e 100644 --- a/tests/ui/dbg_macro/dbg_macro.rs +++ b/tests/ui/dbg_macro/dbg_macro.rs @@ -107,3 +107,12 @@ mod mod1 { //~^ ERROR: the `dbg!` macro is intended as a debugging tool } } + +mod issue12131 { + fn dbg_in_print(s: &str) { + println!("dbg: {:?}", dbg!(s)); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + print!("{}", dbg!(s)); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} diff --git a/tests/ui/dbg_macro/dbg_macro.stderr b/tests/ui/dbg_macro/dbg_macro.stderr index 5ad0bbfed9422..f632497bd736c 100644 --- a/tests/ui/dbg_macro/dbg_macro.stderr +++ b/tests/ui/dbg_macro/dbg_macro.stderr @@ -211,5 +211,27 @@ help: remove the invocation before committing it to a version control system LL | 1; | ~ -error: aborting due to 19 previous errors +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:113:31 + | +LL | println!("dbg: {:?}", dbg!(s)); + | ^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | println!("dbg: {:?}", s); + | ~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:115:22 + | +LL | print!("{}", dbg!(s)); + | ^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | print!("{}", s); + | ~ + +error: aborting due to 21 previous errors From 0535f558317ca934bccbf5cda3e0f9779dbfd972 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Wed, 21 Feb 2024 02:11:19 +0800 Subject: [PATCH 059/102] lint nested `dbg!` macros, split tests --- clippy_lints/src/dbg_macro.rs | 16 +-- tests/ui-toml/dbg_macro/dbg_macro.fixed | 38 ++++++ tests/ui-toml/dbg_macro/dbg_macro.rs | 5 +- tests/ui-toml/dbg_macro/dbg_macro.stderr | 34 +----- tests/ui/dbg_macro/dbg_macro.fixed | 110 ++++++++++++++++++ tests/ui/dbg_macro/dbg_macro.rs | 10 +- tests/ui/dbg_macro/dbg_macro.stderr | 76 ++++-------- tests/ui/dbg_macro/dbg_macro_unfixable.rs | 12 ++ tests/ui/dbg_macro/dbg_macro_unfixable.stderr | 71 +++++++++++ 9 files changed, 269 insertions(+), 103 deletions(-) create mode 100644 tests/ui-toml/dbg_macro/dbg_macro.fixed create mode 100644 tests/ui/dbg_macro/dbg_macro.fixed create mode 100644 tests/ui/dbg_macro/dbg_macro_unfixable.rs create mode 100644 tests/ui/dbg_macro/dbg_macro_unfixable.stderr diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index a517d7bd66115..bd61260d94e96 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::root_macro_call; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_cfg_test, is_in_test_function}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -31,12 +32,12 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -#[derive(Copy, Clone)] +#[derive(Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, - /// Keep tracking of the previous `dbg!` macro call site in order to - /// skip any other expressions from the same expansion, including nested macro calls. - prev_dbg_call_site: Span, + /// Keep tracks the `dbg!` macro callsites that are already checked, + /// so that we can save some performance by skipping any expressions from the same expansion. + checked_dbg_call_site: FxHashSet, } impl_lint_pass!(DbgMacro => [DBG_MACRO]); @@ -45,7 +46,7 @@ impl DbgMacro { pub fn new(allow_dbg_in_tests: bool) -> Self { DbgMacro { allow_dbg_in_tests, - prev_dbg_call_site: Span::default(), + checked_dbg_call_site: FxHashSet::default(), } } } @@ -57,11 +58,10 @@ impl LateLintPass<'_> for DbgMacro { else { return; }; - // skip previous checked exprs - if self.prev_dbg_call_site.contains(macro_call.span) { + if self.checked_dbg_call_site.contains(¯o_call.span) { return; } - self.prev_dbg_call_site = macro_call.span; + self.checked_dbg_call_site.insert(macro_call.span); // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml if self.allow_dbg_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) diff --git a/tests/ui-toml/dbg_macro/dbg_macro.fixed b/tests/ui-toml/dbg_macro/dbg_macro.fixed new file mode 100644 index 0000000000000..d42b29ba21a63 --- /dev/null +++ b/tests/ui-toml/dbg_macro/dbg_macro.fixed @@ -0,0 +1,38 @@ +//@compile-flags: --test +#![warn(clippy::dbg_macro)] +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + +fn foo(n: u32) -> u32 { + if let Some(n) = n.checked_sub(4) { n } else { n } +} + +fn factorial(n: u32) -> u32 { + if n <= 1 { + 1 + } else { + n * factorial(n - 1) + } +} + +fn main() { + 42; + foo(3) + factorial(4); + (1, 2, 3, 4, 5); +} + +#[test] +pub fn issue8481() { + dbg!(1); +} + +#[cfg(test)] +fn foo2() { + dbg!(1); +} + +#[cfg(test)] +mod mod1 { + fn func() { + dbg!(1); + } +} diff --git a/tests/ui-toml/dbg_macro/dbg_macro.rs b/tests/ui-toml/dbg_macro/dbg_macro.rs index 67129e6247712..bd189b1576f96 100644 --- a/tests/ui-toml/dbg_macro/dbg_macro.rs +++ b/tests/ui-toml/dbg_macro/dbg_macro.rs @@ -1,6 +1,7 @@ //@compile-flags: --test #![warn(clippy::dbg_macro)] -//@no-rustfix +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } } @@ -15,9 +16,7 @@ fn factorial(n: u32) -> u32 { fn main() { dbg!(42); - dbg!(dbg!(dbg!(42))); foo(3) + dbg!(factorial(4)); - dbg!(1, 2, dbg!(3, 4)); dbg!(1, 2, 3, 4, 5); } diff --git a/tests/ui-toml/dbg_macro/dbg_macro.stderr b/tests/ui-toml/dbg_macro/dbg_macro.stderr index 8ffc426be2d9d..129fab5ff97a6 100644 --- a/tests/ui-toml/dbg_macro/dbg_macro.stderr +++ b/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -1,5 +1,5 @@ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:5:22 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:6:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,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-toml/dbg_macro/dbg_macro.rs:9:8 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:10:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | if n <= 1 { | ~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:10:9 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:11:9 | LL | dbg!(1) | ^^^^^^^ @@ -34,7 +34,7 @@ LL | 1 | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:12:9 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:13:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:17:5 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:18:5 | LL | dbg!(42); | ^^^^^^^^ @@ -55,17 +55,6 @@ help: remove the invocation before committing it to a version control system LL | 42; | ~~ -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:18:5 - | -LL | dbg!(dbg!(dbg!(42))); - | ^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | dbg!(dbg!(42)); - | ~~~~~~~~~~~~~~ - error: the `dbg!` macro is intended as a debugging tool --> tests/ui-toml/dbg_macro/dbg_macro.rs:19:14 | @@ -80,17 +69,6 @@ LL | foo(3) + factorial(4); error: the `dbg!` macro is intended as a debugging tool --> tests/ui-toml/dbg_macro/dbg_macro.rs:20:5 | -LL | dbg!(1, 2, dbg!(3, 4)); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | (1, 2, dbg!(3, 4)); - | ~~~~~~~~~~~~~~~~~~ - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:21:5 - | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | @@ -99,5 +77,5 @@ help: remove the invocation before committing it to a version control system LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/dbg_macro/dbg_macro.fixed b/tests/ui/dbg_macro/dbg_macro.fixed new file mode 100644 index 0000000000000..7197c3c8f477c --- /dev/null +++ b/tests/ui/dbg_macro/dbg_macro.fixed @@ -0,0 +1,110 @@ +#![warn(clippy::dbg_macro)] +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + +fn foo(n: u32) -> u32 { + if let Some(n) = n.checked_sub(4) { n } else { n } + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} +fn bar(_: ()) {} + +fn factorial(n: u32) -> u32 { + if n <= 1 { + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + 1 + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } else { + n * factorial(n - 1) + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} + +fn main() { + 42; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + foo(3) + factorial(4); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + (1, 2, 3, 4, 5); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} + +fn issue9914() { + macro_rules! foo { + ($x:expr) => { + $x; + }; + } + macro_rules! foo2 { + ($x:expr) => { + $x; + }; + } + macro_rules! expand_to_dbg { + () => { + dbg!(); + }; + } + + + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + #[allow(clippy::let_unit_value)] + let _ = (); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + bar(()); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + foo!(()); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + foo2!(foo!(())); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + expand_to_dbg!(); +} + +mod issue7274 { + trait Thing<'b> { + fn foo(&self); + } + + macro_rules! define_thing { + ($thing:ident, $body:expr) => { + impl<'a> Thing<'a> for $thing { + fn foo<'b>(&self) { + $body + } + } + }; + } + + struct MyThing; + define_thing!(MyThing, { + 2; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + }); +} + +#[test] +pub fn issue8481() { + 1; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} + +#[cfg(test)] +fn foo2() { + 1; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} + +#[cfg(test)] +mod mod1 { + fn func() { + 1; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} + +mod issue12131 { + fn dbg_in_print(s: &str) { + println!("dbg: {:?}", s); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + print!("{}", s); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} diff --git a/tests/ui/dbg_macro/dbg_macro.rs b/tests/ui/dbg_macro/dbg_macro.rs index bdb6c379f885e..0815d9f38dc8e 100644 --- a/tests/ui/dbg_macro/dbg_macro.rs +++ b/tests/ui/dbg_macro/dbg_macro.rs @@ -1,9 +1,5 @@ -//@no-rustfix - #![warn(clippy::dbg_macro)] - -#[path = "auxiliary/submodule.rs"] -mod submodule; +#![allow(clippy::unnecessary_operation, clippy::no_effect)] fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } @@ -25,12 +21,8 @@ fn factorial(n: u32) -> u32 { fn main() { dbg!(42); //~^ ERROR: the `dbg!` macro is intended as a debugging tool - dbg!(dbg!(dbg!(42))); - //~^ ERROR: the `dbg!` macro is intended as a debugging tool foo(3) + dbg!(factorial(4)); //~^ ERROR: the `dbg!` macro is intended as a debugging tool - dbg!(1, 2, dbg!(3, 4)); - //~^ ERROR: the `dbg!` macro is intended as a debugging tool dbg!(1, 2, 3, 4, 5); //~^ ERROR: the `dbg!` macro is intended as a debugging tool } diff --git a/tests/ui/dbg_macro/dbg_macro.stderr b/tests/ui/dbg_macro/dbg_macro.stderr index f632497bd736c..5ff49816d00c3 100644 --- a/tests/ui/dbg_macro/dbg_macro.stderr +++ b/tests/ui/dbg_macro/dbg_macro.stderr @@ -1,30 +1,18 @@ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5 - | -LL | dbg!(); - | ^^^^^^^ - | - = note: `-D clippy::dbg-macro` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` -help: remove the invocation before committing it to a version control system - | -LL - dbg!(); -LL + - | - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:9:22 + --> tests/ui/dbg_macro/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | + = note: `-D clippy::dbg-macro` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` help: remove the invocation before committing it to a version control system | 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:15:8 + --> tests/ui/dbg_macro/dbg_macro.rs:11:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -35,7 +23,7 @@ LL | if n <= 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:13:9 | LL | dbg!(1) | ^^^^^^^ @@ -46,7 +34,7 @@ LL | 1 | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:20:9 + --> tests/ui/dbg_macro/dbg_macro.rs:16:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +45,7 @@ LL | n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:26:5 + --> tests/ui/dbg_macro/dbg_macro.rs:22:5 | LL | dbg!(42); | ^^^^^^^^ @@ -68,18 +56,7 @@ LL | 42; | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:28:5 - | -LL | dbg!(dbg!(dbg!(42))); - | ^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | dbg!(dbg!(42)); - | ~~~~~~~~~~~~~~ - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:30:14 + --> tests/ui/dbg_macro/dbg_macro.rs:24:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -90,18 +67,7 @@ LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:32:5 - | -LL | dbg!(1, 2, dbg!(3, 4)); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | (1, 2, dbg!(3, 4)); - | ~~~~~~~~~~~~~~~~~~ - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:34:5 + --> tests/ui/dbg_macro/dbg_macro.rs:26:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -112,7 +78,7 @@ LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:55:5 + --> tests/ui/dbg_macro/dbg_macro.rs:47:5 | LL | dbg!(); | ^^^^^^^ @@ -124,7 +90,7 @@ LL + | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:58:13 + --> tests/ui/dbg_macro/dbg_macro.rs:50:13 | LL | let _ = dbg!(); | ^^^^^^ @@ -135,7 +101,7 @@ LL | let _ = (); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:60:9 + --> tests/ui/dbg_macro/dbg_macro.rs:52:9 | LL | bar(dbg!()); | ^^^^^^ @@ -146,7 +112,7 @@ LL | bar(()); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:62:10 + --> tests/ui/dbg_macro/dbg_macro.rs:54:10 | LL | foo!(dbg!()); | ^^^^^^ @@ -157,7 +123,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:56:16 | LL | foo2!(foo!(dbg!())); | ^^^^^^ @@ -168,7 +134,7 @@ LL | foo2!(foo!(())); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:86:9 + --> tests/ui/dbg_macro/dbg_macro.rs:78:9 | LL | dbg!(2); | ^^^^^^^ @@ -179,7 +145,7 @@ LL | 2; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:93:5 + --> tests/ui/dbg_macro/dbg_macro.rs:85:5 | LL | dbg!(1); | ^^^^^^^ @@ -190,7 +156,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:99:5 + --> tests/ui/dbg_macro/dbg_macro.rs:91:5 | LL | dbg!(1); | ^^^^^^^ @@ -201,7 +167,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:106:9 + --> tests/ui/dbg_macro/dbg_macro.rs:98:9 | LL | dbg!(1); | ^^^^^^^ @@ -212,7 +178,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:113:31 + --> tests/ui/dbg_macro/dbg_macro.rs:105:31 | LL | println!("dbg: {:?}", dbg!(s)); | ^^^^^^^ @@ -223,7 +189,7 @@ LL | println!("dbg: {:?}", s); | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:115:22 + --> tests/ui/dbg_macro/dbg_macro.rs:107:22 | LL | print!("{}", dbg!(s)); | ^^^^^^^ @@ -233,5 +199,5 @@ help: remove the invocation before committing it to a version control system LL | print!("{}", s); | ~ -error: aborting due to 21 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/dbg_macro/dbg_macro_unfixable.rs b/tests/ui/dbg_macro/dbg_macro_unfixable.rs new file mode 100644 index 0000000000000..0e83766ccaec7 --- /dev/null +++ b/tests/ui/dbg_macro/dbg_macro_unfixable.rs @@ -0,0 +1,12 @@ +//@no-rustfix +#![warn(clippy::dbg_macro)] + +#[path = "auxiliary/submodule.rs"] +mod submodule; + +fn main() { + dbg!(dbg!(dbg!(42))); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + dbg!(1, 2, dbg!(3, 4)); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} diff --git a/tests/ui/dbg_macro/dbg_macro_unfixable.stderr b/tests/ui/dbg_macro/dbg_macro_unfixable.stderr new file mode 100644 index 0000000000000..d21595c2fcd42 --- /dev/null +++ b/tests/ui/dbg_macro/dbg_macro_unfixable.stderr @@ -0,0 +1,71 @@ +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5 + | +LL | dbg!(); + | ^^^^^^^ + | + = note: `-D clippy::dbg-macro` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` +help: remove the invocation before committing it to a version control system + | +LL - dbg!(); +LL + + | + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:5 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(dbg!(42)); + | ~~~~~~~~~~~~~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:10 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(dbg!(42)); + | ~~~~~~~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:15 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(dbg!(42)); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:5 + | +LL | dbg!(1, 2, dbg!(3, 4)); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | (1, 2, dbg!(3, 4)); + | ~~~~~~~~~~~~~~~~~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:16 + | +LL | dbg!(1, 2, dbg!(3, 4)); + | ^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(1, 2, (3, 4)); + | ~~~~~~ + +error: aborting due to 6 previous errors + From fe4e0aca73666b1be9a7f285fb9ae6e03d58a54c Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Wed, 6 Mar 2024 23:08:59 +0800 Subject: [PATCH 060/102] checks `dbg` inside other macros as well (but no ext macro); some refractoring; --- clippy_lints/src/dbg_macro.rs | 152 +++++++++++++++------------- tests/ui/dbg_macro/dbg_macro.fixed | 3 +- tests/ui/dbg_macro/dbg_macro.rs | 1 + tests/ui/dbg_macro/dbg_macro.stderr | 40 +++++--- 4 files changed, 113 insertions(+), 83 deletions(-) diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index bd61260d94e96..e229676743195 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,13 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::root_macro_call; +use clippy_utils::macros::{macro_backtrace, MacroCall}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node}; +use rustc_hir::{Expr, ExprKind, HirId, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -35,9 +36,10 @@ declare_clippy_lint! { #[derive(Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, - /// Keep tracks the `dbg!` macro callsites that are already checked, - /// so that we can save some performance by skipping any expressions from the same expansion. + /// Tracks the `dbg!` macro callsites that are already checked. checked_dbg_call_site: FxHashSet, + /// Tracks the previous `SyntaxContext`, to avoid walking the same context chain. + prev_ctxt: SyntaxContext, } impl_lint_pass!(DbgMacro => [DBG_MACRO]); @@ -47,80 +49,90 @@ impl DbgMacro { DbgMacro { allow_dbg_in_tests, checked_dbg_call_site: FxHashSet::default(), + prev_ctxt: SyntaxContext::root(), } } } impl LateLintPass<'_> for DbgMacro { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some(macro_call) = - root_macro_call(expr.span).filter(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) - else { - return; - }; - if self.checked_dbg_call_site.contains(¯o_call.span) { - return; - } - self.checked_dbg_call_site.insert(macro_call.span); + let cur_syntax_ctxt = expr.span.ctxt(); - // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml - if self.allow_dbg_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) + if cur_syntax_ctxt != self.prev_ctxt && + let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) && + !in_external_macro(cx.sess(), macro_call.span) && + self.checked_dbg_call_site.insert(macro_call.span) && + // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml + !(self.allow_dbg_in_tests && is_in_test(cx, expr.hir_id)) { - return; - } - let mut applicability = Applicability::MachineApplicable; + let mut applicability = Applicability::MachineApplicable; + + let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. + }, + .., + Expr { + kind: ExprKind::Match(last, ..), + .. + }, + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) + }, + _ => return, + }; - let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Some(Node::Stmt(_)) = cx.tcx.hir().find_parent(expr.hir_id) - && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) - }, - _ => return, - }; + self.prev_ctxt = cur_syntax_ctxt; + + span_lint_and_sugg( + cx, + DBG_MACRO, + sugg_span, + "the `dbg!` macro is intended as a debugging tool", + "remove the invocation before committing it to a version control system", + suggestion, + applicability, + ); + } + } - span_lint_and_sugg( - cx, - DBG_MACRO, - sugg_span, - "the `dbg!` macro is intended as a debugging tool", - "remove the invocation before committing it to a version control system", - suggestion, - applicability, - ); + fn check_crate_post(&mut self, _: &LateContext<'_>) { + self.checked_dbg_call_site = FxHashSet::default(); } } + +fn is_in_test(cx: &LateContext<'_>, hir_id: HirId) -> bool { + is_in_test_function(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id) +} + +fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option { + macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) +} diff --git a/tests/ui/dbg_macro/dbg_macro.fixed b/tests/ui/dbg_macro/dbg_macro.fixed index 7197c3c8f477c..e352519142332 100644 --- a/tests/ui/dbg_macro/dbg_macro.fixed +++ b/tests/ui/dbg_macro/dbg_macro.fixed @@ -40,7 +40,8 @@ fn issue9914() { } macro_rules! expand_to_dbg { () => { - dbg!(); + + //~^ ERROR: the `dbg!` macro is intended as a debugging tool }; } diff --git a/tests/ui/dbg_macro/dbg_macro.rs b/tests/ui/dbg_macro/dbg_macro.rs index 0815d9f38dc8e..80606c2db054a 100644 --- a/tests/ui/dbg_macro/dbg_macro.rs +++ b/tests/ui/dbg_macro/dbg_macro.rs @@ -41,6 +41,7 @@ fn issue9914() { macro_rules! expand_to_dbg { () => { dbg!(); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool }; } diff --git a/tests/ui/dbg_macro/dbg_macro.stderr b/tests/ui/dbg_macro/dbg_macro.stderr index 5ff49816d00c3..86667701da0f3 100644 --- a/tests/ui/dbg_macro/dbg_macro.stderr +++ b/tests/ui/dbg_macro/dbg_macro.stderr @@ -78,7 +78,7 @@ LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:47:5 + --> tests/ui/dbg_macro/dbg_macro.rs:48:5 | LL | dbg!(); | ^^^^^^^ @@ -90,7 +90,7 @@ LL + | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:50:13 + --> tests/ui/dbg_macro/dbg_macro.rs:51:13 | LL | let _ = dbg!(); | ^^^^^^ @@ -101,7 +101,7 @@ LL | let _ = (); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:52:9 + --> tests/ui/dbg_macro/dbg_macro.rs:53:9 | LL | bar(dbg!()); | ^^^^^^ @@ -112,7 +112,7 @@ LL | bar(()); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:54:10 + --> tests/ui/dbg_macro/dbg_macro.rs:55:10 | LL | foo!(dbg!()); | ^^^^^^ @@ -123,7 +123,7 @@ LL | foo!(()); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:56:16 + --> tests/ui/dbg_macro/dbg_macro.rs:57:16 | LL | foo2!(foo!(dbg!())); | ^^^^^^ @@ -134,7 +134,23 @@ LL | foo2!(foo!(())); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:78:9 + --> tests/ui/dbg_macro/dbg_macro.rs:43:13 + | +LL | dbg!(); + | ^^^^^^^ +... +LL | expand_to_dbg!(); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `expand_to_dbg` (in Nightly builds, run with -Z macro-backtrace for more info) +help: remove the invocation before committing it to a version control system + | +LL - dbg!(); +LL + + | + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro.rs:79:9 | LL | dbg!(2); | ^^^^^^^ @@ -145,7 +161,7 @@ LL | 2; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:85:5 + --> tests/ui/dbg_macro/dbg_macro.rs:86:5 | LL | dbg!(1); | ^^^^^^^ @@ -156,7 +172,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:91:5 + --> tests/ui/dbg_macro/dbg_macro.rs:92:5 | LL | dbg!(1); | ^^^^^^^ @@ -167,7 +183,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:98:9 + --> tests/ui/dbg_macro/dbg_macro.rs:99:9 | LL | dbg!(1); | ^^^^^^^ @@ -178,7 +194,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:105:31 + --> tests/ui/dbg_macro/dbg_macro.rs:106:31 | LL | println!("dbg: {:?}", dbg!(s)); | ^^^^^^^ @@ -189,7 +205,7 @@ LL | println!("dbg: {:?}", s); | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:107:22 + --> tests/ui/dbg_macro/dbg_macro.rs:108:22 | LL | print!("{}", dbg!(s)); | ^^^^^^^ @@ -199,5 +215,5 @@ help: remove the invocation before committing it to a version control system LL | print!("{}", s); | ~ -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors From 20e4c7452135275ad69f412d59848239f8006931 Mon Sep 17 00:00:00 2001 From: maekawatoshiki Date: Tue, 13 Feb 2024 03:01:56 +0900 Subject: [PATCH 061/102] Handle false positive with map_clone --- clippy_lints/src/methods/map_clone.rs | 10 ++++++++-- tests/ui/map_clone.fixed | 16 +++++++++++++++- tests/ui/map_clone.rs | 16 +++++++++++++++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 27e17b43b01cc..0378c8171c073 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -86,8 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ } } }, - hir::ExprKind::Call(call, [_]) => { - if let hir::ExprKind::Path(qpath) = call.kind { + hir::ExprKind::Call(call, args) => { + if let hir::ExprKind::Path(qpath) = call.kind + && let [arg] = args + && ident_eq(name, arg) + { handle_path(cx, call, &qpath, e, recv); } }, @@ -119,6 +122,9 @@ fn handle_path( && let args = args.as_slice() && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) && ty.is_ref() + && 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)) { lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 395eea69294dd..e58b6b2f19ea3 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -6,7 +6,8 @@ clippy::redundant_clone, clippy::redundant_closure, clippy::useless_asref, - clippy::useless_vec + clippy::useless_vec, + clippy::empty_loop )] fn main() { @@ -117,4 +118,17 @@ fn main() { let y = x.as_ref().map(|x| String::clone(x)); let x: Result = Ok(String::new()); let y = x.as_ref().map(|x| String::clone(x)); + + // Issue #12271 + { + // Don't lint these + let x: Option<&u8> = None; + let y = x.map(|x| String::clone(loop {})); + let x: Option<&u8> = None; + let y = x.map(|x| u8::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| String::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| u8::clone(loop {})); + } } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 82a103edf5a9c..e642e4046f8b7 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -6,7 +6,8 @@ clippy::redundant_clone, clippy::redundant_closure, clippy::useless_asref, - clippy::useless_vec + clippy::useless_vec, + clippy::empty_loop )] fn main() { @@ -117,4 +118,17 @@ fn main() { let y = x.as_ref().map(|x| String::clone(x)); let x: Result = Ok(String::new()); let y = x.as_ref().map(|x| String::clone(x)); + + // Issue #12271 + { + // Don't lint these + let x: Option<&u8> = None; + let y = x.map(|x| String::clone(loop {})); + let x: Option<&u8> = None; + let y = x.map(|x| u8::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| String::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| u8::clone(loop {})); + } } From 560a5a8cd1f93c0fd258e3c984ce957c80d9de8f Mon Sep 17 00:00:00 2001 From: maekawatoshiki Date: Tue, 13 Feb 2024 11:07:06 +0900 Subject: [PATCH 062/102] Fix logic --- clippy_lints/src/methods/map_clone.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 0378c8171c073..8d2a2410ed426 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -121,10 +121,10 @@ fn handle_path( 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()) - && ty.is_ref() && 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)) + && let ty::Adt(_, args) = cx.typeck_results().expr_ty(e).kind() + && let args = args.as_slice() + && args.iter().find_map(|generic_arg| generic_arg.as_type()) == Some(*ty) { lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } From c5d3b62cfc0cc4b743e27d03b5532c9aa0321db1 Mon Sep 17 00:00:00 2001 From: maekawatoshiki Date: Thu, 14 Mar 2024 12:40:33 +0900 Subject: [PATCH 063/102] Fix conflict --- tests/ui/map_clone.stderr | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 1a26a26a4cacb..d9e025de4abe9 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,5 +1,5 @@ error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:13:22 + --> tests/ui/map_clone.rs:14:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` @@ -8,85 +8,85 @@ LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); = help: to override `-D warnings` add `#[allow(clippy::map_clone)]` error: you are using an explicit closure for cloning elements - --> tests/ui/map_clone.rs:14:26 + --> tests/ui/map_clone.rs:15:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:15:23 + --> tests/ui/map_clone.rs:16:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:17:26 + --> tests/ui/map_clone.rs:18:26 | LL | let _: Option = Some(&16).map(|b| *b); | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:18:25 + --> tests/ui/map_clone.rs:19:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` error: you are needlessly cloning iterator elements - --> tests/ui/map_clone.rs:29:29 + --> tests/ui/map_clone.rs:30:29 | LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:68:13 + --> tests/ui/map_clone.rs:69:13 | LL | let y = x.map(|x| String::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:70:13 + --> tests/ui/map_clone.rs:71:13 | LL | let y = x.map(Clone::clone); | ^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:73:13 + --> tests/ui/map_clone.rs:74:13 | LL | let y = x.map(String::clone); | ^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:79:13 + --> tests/ui/map_clone.rs:80:13 | LL | let y = x.map(|x| u32::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:82:13 + --> tests/ui/map_clone.rs:83:13 | LL | let y = x.map(|x| Clone::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:94:13 + --> tests/ui/map_clone.rs:95:13 | LL | let y = x.map(|x| String::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:97:13 + --> tests/ui/map_clone.rs:98:13 | LL | let y = x.map(|x| Clone::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:103:13 + --> tests/ui/map_clone.rs:104:13 | LL | let y = x.map(|x| u32::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:106:13 + --> tests/ui/map_clone.rs:107:13 | LL | let y = x.map(|x| Clone::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` From 5f8d8f165606775fcf81dfb27ac9de19f60a7bee Mon Sep 17 00:00:00 2001 From: maekawatoshiki Date: Thu, 14 Mar 2024 12:48:34 +0900 Subject: [PATCH 064/102] Simplify logic --- clippy_lints/src/methods/map_clone.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 8d2a2410ed426..c3c7a3a003307 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -122,9 +122,8 @@ fn handle_path( && 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::Adt(_, args) = cx.typeck_results().expr_ty(e).kind() - && let args = args.as_slice() - && args.iter().find_map(|generic_arg| generic_arg.as_type()) == Some(*ty) + && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() + && lst.iter().all(|l| l.as_type() == Some(*ty)) { lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } From da2795fe487832e27eb92f9601877a6eba80adea Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 14 Mar 2024 11:25:05 +0100 Subject: [PATCH 065/102] Rename `ast::StmtKind::Local` into `ast::StmtKind::Let` --- clippy_utils/src/ast_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 3b3939da7b6bc..3874c1169e48c 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -267,7 +267,7 @@ pub fn eq_block(l: &Block, r: &Block) -> bool { pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { use StmtKind::*; match (&l.kind, &r.kind) { - (Local(l), Local(r)) => { + (Let(l), Let(r)) => { eq_pat(&l.pat, &r.pat) && both(&l.ty, &r.ty, |l, r| eq_ty(l, r)) && eq_local_kind(&l.kind, &r.kind) From 0e2897fd4f23ce8bdc1a847c850214f5733a5645 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 14 Mar 2024 11:53:38 +0100 Subject: [PATCH 066/102] Rename `hir::StmtKind::Local` into `hir::StmtKind::Let` --- clippy_lints/src/attrs/utils.rs | 2 +- clippy_lints/src/copies.rs | 8 ++++---- clippy_lints/src/default.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/entry.rs | 2 +- clippy_lints/src/explicit_write.rs | 2 +- clippy_lints/src/let_if_seq.rs | 2 +- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/loops/manual_while_let_some.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/loops/while_let_loop.rs | 2 +- clippy_lints/src/manual_let_else.rs | 2 +- clippy_lints/src/map_unit_fn.rs | 2 +- clippy_lints/src/methods/needless_collect.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 2 +- .../src/methods/unnecessary_result_map_or_else.rs | 2 +- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/mixed_read_write_in_expression.rs | 4 ++-- clippy_lints/src/needless_late_init.rs | 2 +- clippy_lints/src/no_effect.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 2 +- clippy_lints/src/question_mark.rs | 2 +- clippy_lints/src/read_zero_byte_vec.rs | 2 +- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/returns.rs | 2 +- clippy_lints/src/significant_drop_tightening.rs | 6 +++--- clippy_lints/src/slow_vector_initialization.rs | 2 +- clippy_lints/src/swap.rs | 4 ++-- clippy_lints/src/undocumented_unsafe_blocks.rs | 4 ++-- clippy_lints/src/uninit_vec.rs | 2 +- clippy_lints/src/unused_io_amount.rs | 4 ++-- clippy_lints/src/unused_peekable.rs | 4 ++-- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/hir_utils.rs | 4 ++-- clippy_utils/src/lib.rs | 2 +- 35 files changed, 46 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/attrs/utils.rs b/clippy_lints/src/attrs/utils.rs index 9b36cc00444f3..91ae19acbf7f6 100644 --- a/clippy_lints/src/attrs/utils.rs +++ b/clippy_lints/src/attrs/utils.rs @@ -52,7 +52,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ .as_ref() .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)), |stmt| match &stmt.kind { - StmtKind::Local(_) => true, + StmtKind::Let(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), StmtKind::Item(_) => false, }, diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 247048bbc49da..acdcb54be2719 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -349,7 +349,7 @@ impl BlockEq { /// If the statement is a local, checks if the bound names match the expected list of names. fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { - if let StmtKind::Local(l) = s.kind { + if let StmtKind::Let(l) = s.kind { let mut i = 0usize; let mut res = true; l.pat.each_binding_or_first(&mut |_, _, _, name| { @@ -389,7 +389,7 @@ fn eq_stmts( eq: &mut HirEqInterExpr<'_, '_, '_>, moved_bindings: &mut Vec<(HirId, Symbol)>, ) -> bool { - (if let StmtKind::Local(l) = stmt.kind { + (if let StmtKind::Let(l) = stmt.kind { let old_count = moved_bindings.len(); l.pat.each_binding_or_first(&mut |_, id, _, name| { moved_bindings.push((id, name.name)); @@ -432,7 +432,7 @@ fn scan_block_for_eq<'tcx>( .iter() .enumerate() .find(|&(i, stmt)| { - if let StmtKind::Local(l) = stmt.kind + if let StmtKind::Let(l) = stmt.kind && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id)) { local_needs_ordered_drop = true; @@ -509,7 +509,7 @@ fn scan_block_for_eq<'tcx>( // Clear out all locals seen at the end so far. None of them can be moved. let stmts = &blocks[0].stmts; for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { - if let StmtKind::Local(l) = stmt.kind { + if let StmtKind::Let(l) = stmt.kind { l.pat.each_binding_or_first(&mut |_, id, _, _| { // FIXME(rust/#120456) - is `swap_remove` correct? eq.locals.swap_remove(&id); diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 8789efcc99444..98a6d9370c344 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // find all binding statements like `let mut _ = T::default()` where `T::default()` is the // `default` method of the `Default` trait, and store statement index in current block being // checked and the name of the bound variable - let (local, variant, binding_name, binding_type, span) = if let StmtKind::Local(local) = stmt.kind + let (local, variant, binding_name, binding_type, span) = if let StmtKind::Let(local) = stmt.kind // only take `let ...` statements && let Some(expr) = local.init && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 59d2df0295fb4..1d6c4ce72e18b 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -221,7 +221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` - StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())), + StmtKind::Let(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())), _ => self.ty_bounds.push(ExplicitTyBound(false)), } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index de6073c272360..ebda2ad83870d 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -423,7 +423,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } }, StmtKind::Expr(e) => self.visit_expr(e), - StmtKind::Local(l) => { + StmtKind::Let(l) => { self.visit_pat(l.pat); if let Some(e) = l.init { self.allow_insert_closure &= !self.in_tail_pos; diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index de048fef5f224..2e9bec6a7b083 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> { if let ExprKind::Block(block, _label @ None) = kind && let Block { - stmts: [Stmt { kind: StmtKind::Local(local), .. }], + stmts: [Stmt { kind: StmtKind::Let(local), .. }], expr: Some(expr_end_of_block), rules: BlockCheckMode::DefaultBlock, .. diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 270162ae7717f..f084d89ccc282 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { let mut it = block.stmts.iter().peekable(); while let Some(stmt) = it.next() { if let Some(expr) = it.peek() - && let hir::StmtKind::Local(local) = stmt.kind + && let hir::StmtKind::Let(local) = stmt.kind && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind && let hir::StmtKind::Expr(if_) = expr.kind && let hir::ExprKind::If( diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 18f799e875a07..a7c1d1bd6cd36 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -410,7 +410,7 @@ fn get_assignments<'a, 'tcx>( stmts .iter() .filter_map(move |stmt| match stmt.kind { - StmtKind::Local(..) | StmtKind::Item(..) => None, + StmtKind::Let(..) | StmtKind::Item(..) => None, StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) .chain(*expr) diff --git a/clippy_lints/src/loops/manual_while_let_some.rs b/clippy_lints/src/loops/manual_while_let_some.rs index ca584a454d035..b00a082bb8cf9 100644 --- a/clippy_lints/src/loops/manual_while_let_some.rs +++ b/clippy_lints/src/loops/manual_while_let_some.rs @@ -72,7 +72,7 @@ fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr } fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) { - if let StmtKind::Local(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && is_vec_pop_unwrap(cx, init, is_empty_recv) { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 65d922f03df3a..6cc79440f39a0 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -137,7 +137,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t match stmt.kind { StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)), // add the let...else expression (if present) - StmtKind::Local(local) => local.init.map(|init| (init, local.els)), + StmtKind::Let(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 735d704a43cee..93774b8976824 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -11,7 +11,7 @@ use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) { ([stmt, stmts @ ..], expr) => { - if let StmtKind::Local(&Local { + if let StmtKind::Let(&Local { init: Some(e), els: None, .. diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index fdf8fa4e2771f..03e4d668dd8fb 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -53,7 +53,7 @@ impl<'tcx> QuestionMark { return; } - if let StmtKind::Local(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && local.els.is_none() && local.ty.is_none() diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 3b82c50a84e62..c9eab7109ebcd 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -138,7 +138,7 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option< // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Local(local) => Some(local.span), + hir::StmtKind::Let(local) => Some(local.span), hir::StmtKind::Expr(e) => Some(e.span), hir::StmtKind::Semi(..) => Some(inner_stmt.span), hir::StmtKind::Item(..) => None, diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 55050ae693e76..78540353005d3 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -424,7 +424,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v> match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)), StmtKind::Item(..) => None, - StmtKind::Local(Local { init, pat, .. }) => { + StmtKind::Let(Local { init, pat, .. }) => { if let PatKind::Binding(_, hir_id, ..) = pat.kind { init.map(|init_expr| (init_expr, Some(hir_id))) } else { diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 0e7ad8fc996e5..55cd1a38ec96c 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -198,7 +198,7 @@ fn indirect_usage<'tcx>( binding: HirId, ctxt: SyntaxContext, ) -> Option> { - if let StmtKind::Local(&Local { + if let StmtKind::Let(&Local { pat: Pat { kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None), .. diff --git a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index 7b0cf48ac43be..cdfaa690d5f4b 100644 --- a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -27,7 +27,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option { for stmt in statements { - if let StmtKind::Local(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind && let hir::def::Res::Local(local_hir_id) = path.res diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b9784a58596c1..4094d7ded7d82 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if !in_external_macro(cx.tcx.sess, stmt.span) - && let StmtKind::Local(local) = stmt.kind + && let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index a1f7dc7b38c40..12c7c18afde6c 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) => { + StmtKind::Let(local) => { if let Local { init: Some(e), .. } = local { DivergenceVisitor { cx }.visit_expr(e); } @@ -291,7 +291,7 @@ fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> St StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer // expression if it has one. Otherwise, keep going. - StmtKind::Local(local) => local + StmtKind::Let(local) => local .init .as_ref() .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 3e63c0a1d36e7..4cda4b171e31b 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -86,7 +86,7 @@ fn contains_let(cond: &Expr<'_>) -> bool { } fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { - let StmtKind::Local(local) = stmt.kind else { + let StmtKind::Let(local) = stmt.kind else { return false; }; !local.pat.walk_short(|pat| { diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index cac34c8ce06e9..43810ec0ec743 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -174,7 +174,7 @@ impl NoEffect { ); return true; } - } else if let StmtKind::Local(local) = stmt.kind { + } 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 diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 60ced9c12082d..fbca4329342a9 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -82,7 +82,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Local(local) = stmt.kind { + if let StmtKind::Let(local) = stmt.kind { if in_external_macro(cx.sess(), local.pat.span) { return; } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index cf7f730140ceb..831c291ed7cc5 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -109,7 +109,7 @@ fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir } fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(Local { + if let StmtKind::Let(Local { pat, init: Some(init_expr), els: Some(els), diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 650324d4249eb..d0b37cd92e002 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { return; } - if let StmtKind::Local(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let Local { pat, init: Some(init), .. } = local diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index f61527cc0a9fe..c2673bc409fa8 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -262,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } for w in block.stmts.windows(2) { - if let hir::StmtKind::Local(local) = w[0].kind + if let hir::StmtKind::Let(local) = w[0].kind && let Option::Some(t) = local.init && let hir::ExprKind::Closure { .. } = t.kind && let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2af466d3f51e3..196975274674e 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { // we need both a let-binding stmt and an expr if let Some(retexpr) = block.expr && let Some(stmt) = block.stmts.iter().last() - && let StmtKind::Local(local) = &stmt.kind + && let StmtKind::Let(local) = &stmt.kind && local.ty.is_none() && cx.tcx.hir().attrs(local.hir_id).is_empty() && let Some(initexpr) = &local.init diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 6c99ccda7ea73..f8726aa173a9b 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -236,7 +236,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx fn manage_has_expensive_expr_after_last_attr(&mut self) { let has_expensive_stmt = match self.ap.curr_stmt.kind { hir::StmtKind::Expr(expr) if is_inexpensive_expr(expr) => false, - hir::StmtKind::Local(local) + hir::StmtKind::Let(local) if let Some(expr) = local.init && let hir::ExprKind::Path(_) = expr.kind => { @@ -290,7 +290,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o }; let mut ac = AttrChecker::new(self.cx, self.seen_types, self.type_cache); if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) { - if let hir::StmtKind::Local(local) = self.ap.curr_stmt.kind + if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) && { @@ -326,7 +326,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o return; }; match self.ap.curr_stmt.kind { - hir::StmtKind::Local(local) => { + hir::StmtKind::Let(local) => { if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind { apa.last_bind_ident = ident; } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 4837f2858a669..ff8e8fe702176 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` // or `Vec::new()` - if let StmtKind::Local(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind && let Some(init) = local.init && let Some(size_expr) = Self::as_vec_initializer(cx, init) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index daa6fe8715ca8..be590aede158d 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -148,7 +148,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { } for [s1, s2, s3] in block.stmts.array_windows::<3>() { - if let StmtKind::Local(tmp) = s1.kind + if let StmtKind::Let(tmp) = s1.kind // let t = foo(); && let Some(tmp_init) = tmp.init && let PatKind::Binding(.., ident, None) = tmp.pat.kind @@ -243,7 +243,7 @@ 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::Local(expr) = stmt.kind { + } 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)); diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 559d7ace40edc..0efa65b28e230 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { - let (hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) + let (hir::StmtKind::Let(&hir::Local { init: Some(expr), .. }) | hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr)) = stmt.kind else { @@ -358,7 +358,7 @@ fn block_parents_have_safety_comment( }, Node::Stmt(hir::Stmt { kind: - hir::StmtKind::Local(hir::Local { span, hir_id, .. }) + hir::StmtKind::Let(hir::Local { span, hir_id, .. }) | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), .. diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index fc8519d562835..9ffcfcc0f50cb 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -153,7 +153,7 @@ impl<'tcx> VecLocation<'tcx> { /// or `self` expression for `Vec::reserve()`. fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option> { match stmt.kind { - StmtKind::Local(local) => { + StmtKind::Let(local) => { if let Some(init_expr) = local.init && let PatKind::Binding(_, hir_id, _, None) = local.pat.kind && let Some(init_kind) = get_vec_init_kind(cx, init_expr) diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 6b3ea7700b735..eb64dd633f606 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -61,10 +61,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { /// we need to check them at `check_expr` or `check_block` as they are not stmts /// but we can't check them at `check_expr` because we need the broader context /// because we should do this only for the final expression of the block, and not for - /// `StmtKind::Local` which binds values => the io amount is used. + /// `StmtKind::Let` which binds values => the io amount is used. /// /// To check for unused io amount in stmts, we only consider `StmtKind::Semi`. - /// `StmtKind::Local` is not considered because it binds values => the io amount is used. + /// `StmtKind::Let` is not considered because it binds values => the io amount is used. /// `StmtKind::Expr` is not considered because requires unit type => the io amount is used. /// `StmtKind::Item` is not considered because it's not an expression. /// diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index ba72b3450b933..f1d0c22b1aec1 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { for (idx, stmt) in block.stmts.iter().enumerate() { if !stmt.span.from_expansion() - && let StmtKind::Local(local) = stmt.kind + && let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(_, binding, ident, _) = local.pat.kind && let Some(init) = local.init && !init.span.from_expansion() @@ -197,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { }, Node::Stmt(stmt) => { match stmt.kind { - StmtKind::Local(_) | StmtKind::Item(_) => self.found_peek_call = true, + StmtKind::Let(_) | StmtKind::Item(_) => self.found_peek_call = true, StmtKind::Expr(_) | StmtKind::Semi(_) => {}, } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index a0a6382046d02..187bfda129cd7 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -724,7 +724,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } match stmt.value.kind { - StmtKind::Local(local) => { + StmtKind::Let(local) => { bind!(self, local); kind!("Local({local})"); self.option(field!(local.init), "init", |init| { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f7f5f7ca35ff0..106d1d0d77f01 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -108,7 +108,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::Local(l), &StmtKind::Local(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 { @@ -1030,7 +1030,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&b.kind).hash(&mut self.s); match &b.kind { - StmtKind::Local(local) => { + StmtKind::Let(local) => { self.hash_pat(local.pat); if let Some(init) = local.init { self.hash_expr(init); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 708037a465558..dc0725730322b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2161,7 +2161,7 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { Node::Stmt(Stmt { kind: StmtKind::Expr(_) | StmtKind::Semi(_) - | StmtKind::Local(Local { + | StmtKind::Let(Local { pat: Pat { kind: PatKind::Wild, .. From f55a04928fc181d0eb38b58ca9a70843a024c2fe Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 14 Mar 2024 21:05:06 +0300 Subject: [PATCH 067/102] hir: Remove `opt_local_def_id_to_hir_id` and `opt_hir_node_by_def_id` Also replace a few `hir_node()` calls with `hir_node_by_def_id()` --- clippy_lints/src/escape.rs | 3 +-- clippy_lints/src/exit.rs | 2 +- clippy_lints/src/functions/result.rs | 2 +- clippy_lints/src/misc.rs | 3 +-- clippy_lints/src/missing_fields_in_debug.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 5 +---- clippy_lints/src/self_named_constructors.rs | 3 +-- clippy_lints/src/single_call_fn.rs | 2 +- clippy_lints/src/types/mod.rs | 6 +++--- clippy_lints/src/zero_sized_map_values.rs | 2 +- clippy_utils/src/lib.rs | 22 ++++++++------------ 11 files changed, 21 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8857cb8e3827f..ad589dad350b3 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -76,10 +76,9 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { .hir() .get_parent_item(cx.tcx.local_def_id_to_hir_id(fn_def_id)) .def_id; - let parent_node = cx.tcx.opt_hir_node_by_def_id(parent_id); let mut trait_self_ty = None; - if let Some(Node::Item(item)) = parent_node { + if let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_id) { // If the method is an impl for a trait, don't warn. if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind { return; diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 6603512c73cdf..106844dd43489 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for Exit { && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) && let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id - && let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.opt_hir_node_by_def_id(parent) + && let Node::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_node_by_def_id(parent) // If the next item up is a function we check if it is an entry point // and only then emit a linter warning && !is_entrypoint_fn(cx, parent.to_def_id()) diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 7f36f33fe708c..37fbf2c7d5960 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -92,7 +92,7 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty .expect("already checked this is adt") .did() .as_local() - && let Some(hir::Node::Item(item)) = cx.tcx.opt_hir_node_by_def_id(local_def_id) + && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_def_id) && let hir::ItemKind::Enum(ref def, _) = item.kind { let variants_size = AdtVariantInfo::new(cx, *adt, subst); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b9784a58596c1..ac9df8bfca3eb 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -225,10 +225,9 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) && let Some(local_did) = field.did.as_local() - && let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(local_did) && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() { - (hir_id, ident) + (cx.tcx.local_def_id_to_hir_id(local_did), ident) } else { return; } diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 88b331ddefdb7..3bf9f75e22619 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { && let self_ty = cx.tcx.type_of(self_path_did).skip_binder().peel_refs() && let Some(self_adt) = self_ty.ty_adt_def() && let Some(self_def_id) = self_adt.did().as_local() - && let Some(Node::Item(self_item)) = cx.tcx.opt_hir_node_by_def_id(self_def_id) + && let Node::Item(self_item) = cx.tcx.hir_node_by_def_id(self_def_id) // 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) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index a5b58f9910ad5..a450dee305005 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -112,10 +112,7 @@ fn check_closures<'tcx>( } ctx.prev_bind = None; ctx.prev_move_to_closure.clear(); - if let Some(body) = cx - .tcx - .opt_hir_node_by_def_id(closure) - .and_then(associated_body) + if let Some(body) = associated_body(cx.tcx.hir_node_by_def_id(closure)) .map(|(_, body_id)| hir.body(body_id)) { euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body); diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs index fc5a45dd56d6b..85a2b1a673525 100644 --- a/clippy_lints/src/self_named_constructors.rs +++ b/clippy_lints/src/self_named_constructors.rs @@ -72,8 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { if let Some(self_def) = self_ty.ty_adt_def() && let Some(self_local_did) = self_def.did().as_local() - && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) - && let Node::Item(x) = cx.tcx.hir_node(self_id) + && let Node::Item(x) = cx.tcx.hir_node_by_def_id(self_local_did) && let type_name = x.ident.name.as_str().to_lowercase() && (impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name) diff --git a/clippy_lints/src/single_call_fn.rs b/clippy_lints/src/single_call_fn.rs index 223cbb3fae106..2ce7e714c6424 100644 --- a/clippy_lints/src/single_call_fn.rs +++ b/clippy_lints/src/single_call_fn.rs @@ -95,7 +95,7 @@ impl SingleCallFn { /// to be considered. fn is_valid_item_kind(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { matches!( - cx.tcx.hir_node(cx.tcx.local_def_id_to_hir_id(def_id)), + cx.tcx.hir_node_by_def_id(def_id), Node::Item(_) | Node::ImplItem(_) | Node::TraitItem(_) ) } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 7882bfdd09fad..bdef82e9c5eed 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -321,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { _: Span, def_id: LocalDefId, ) { - let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx.tcx.opt_hir_node_by_def_id( + let is_in_trait_impl = if let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id( cx.tcx .hir() .get_parent_item(cx.tcx.local_def_id_to_hir_id(def_id)) @@ -366,9 +366,9 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { match item.kind { ImplItemKind::Const(ty, _) => { - let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx + let is_in_trait_impl = if let hir::Node::Item(item) = cx .tcx - .opt_hir_node_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) + .hir_node_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 81d4a26e9da43..4aaf3b0a0b674 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -74,7 +74,7 @@ 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 Some(Node::Item(item)) = cx.tcx.opt_hir_node_by_def_id(second_parent_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; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 708037a465558..1cf896d743453 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -330,8 +330,7 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) /// Checks if the `def_id` belongs to a function that is part of a trait impl. pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - if let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(def_id) - && let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id)) && let ItemKind::Impl(imp) = item.kind { imp.of_trait.is_some() @@ -574,12 +573,12 @@ fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symb let hir = tcx.hir(); let root_mod; - let item_kind = match tcx.opt_hir_node_by_def_id(local_id) { - Some(Node::Crate(r#mod)) => { + let item_kind = match tcx.hir_node_by_def_id(local_id) { + Node::Crate(r#mod) => { root_mod = ItemKind::Mod(r#mod); &root_mod }, - Some(Node::Item(item)) => &item.kind, + Node::Item(item) => &item.kind, _ => return Vec::new(), }; @@ -1254,12 +1253,10 @@ pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Gets the name of the item the expression is in, if available. 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.opt_hir_node_by_def_id(parent_id) { - Some( - Node::Item(Item { ident, .. }) - | Node::TraitItem(TraitItem { ident, .. }) - | Node::ImplItem(ImplItem { ident, .. }), - ) => Some(ident.name), + match cx.tcx.hir_node_by_def_id(parent_id) { + Node::Item(Item { ident, .. }) + | Node::TraitItem(TraitItem { ident, .. }) + | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name), _ => None, } } @@ -2667,11 +2664,10 @@ impl<'tcx> ExprUseNode<'tcx> { .and(Binder::dummy(cx.tcx.type_of(id).instantiate_identity())), )), Self::Return(id) => { - let hir_id = cx.tcx.local_def_id_to_hir_id(id.def_id); if let Node::Expr(Expr { kind: ExprKind::Closure(c), .. - }) = cx.tcx.hir_node(hir_id) + }) = cx.tcx.hir_node_by_def_id(id.def_id) { match c.fn_decl.output { FnRetTy::DefaultReturn(_) => None, From dadcd94b2af45be66281bd8400ec72485e2f84fc Mon Sep 17 00:00:00 2001 From: Ethiraric Date: Wed, 13 Mar 2024 20:28:57 +0100 Subject: [PATCH 068/102] [`unused_enumerate_index`]: Keep explicit element type Prior to this change, it might be that the lint would remove an explicit type that was necessary for the type system to keep track of types. This should be the last change that prevented this lint to be machine applicable. --- .../src/methods/unused_enumerate_index.rs | 45 ++++++++++++++++--- tests/ui/unused_enumerate_index.fixed | 16 ++++++- tests/ui/unused_enumerate_index.rs | 16 ++++++- tests/ui/unused_enumerate_index.stderr | 38 +++++++++++++++- 4 files changed, 106 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index 506a0f4304adc..e5cc898612e9a 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,11 +1,12 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then}; use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT}; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild}; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::AdtDef; -use rustc_span::sym; +use rustc_span::{sym, Span}; use crate::loops::UNUSED_ENUMERATE_INDEX; @@ -76,6 +77,18 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, // Make sure the method call is `std::iter::Iterator::enumerate`. && match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD) { + // Check if the tuple type was explicit. It may be the type system _needs_ the type of the element + // that would be explicited in the closure. + let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) { + // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`. + // Fallback to `..` if we fail getting either snippet. + Some(ty_span) => snippet_opt(cx, elem.span) + .and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}"))) + .unwrap_or_else(|| "..".to_string()), + // Otherwise, we have no explicit type. We can replace with the binding name of the element. + None => snippet(cx, elem.span, "..").into_owned(), + }; + // Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we // can get from the `MethodCall`. span_lint_hir_and_then( @@ -85,11 +98,12 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, enumerate_span, "you seem to use `.enumerate()` and immediately discard the index", |diag| { - multispan_sugg( + multispan_sugg_with_applicability( diag, "remove the `.enumerate()` call", + Applicability::MachineApplicable, vec![ - (closure_param.span, snippet(cx, elem.span, "..").into_owned()), + (closure_param.span, new_closure_param), ( enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()), String::new(), @@ -100,3 +114,22 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, ); } } + +/// Find the span of the explicit type of the element. +/// +/// # Returns +/// If the tuple argument: +/// * Has no explicit type, returns `None` +/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None` +/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for +/// the element type. +fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option { + if let [tuple_ty] = fn_decl.inputs + && let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind + && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer) + { + Some(elem_ty.span) + } else { + None + } +} diff --git a/tests/ui/unused_enumerate_index.fixed b/tests/ui/unused_enumerate_index.fixed index 1224eb54ba5c3..cffd02b0acc8c 100644 --- a/tests/ui/unused_enumerate_index.fixed +++ b/tests/ui/unused_enumerate_index.fixed @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused, clippy::map_identity)] #![warn(clippy::unused_enumerate_index)] use std::iter::Enumerate; @@ -89,4 +89,18 @@ fn main() { #[allow(clippy::unused_enumerate_index)] let v = [1].iter().enumerate(); v.map(|(_, _x)| {}); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x: i32| x).sum::(); + assert_eq!(x, 6); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x: i32| x).sum::(); + assert_eq!(x, 6); + + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x| x).sum::(); + assert_eq!(x, 6); } diff --git a/tests/ui/unused_enumerate_index.rs b/tests/ui/unused_enumerate_index.rs index 66f98b690cbe9..f2b5f8b912476 100644 --- a/tests/ui/unused_enumerate_index.rs +++ b/tests/ui/unused_enumerate_index.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused, clippy::map_identity)] #![warn(clippy::unused_enumerate_index)] use std::iter::Enumerate; @@ -89,4 +89,18 @@ fn main() { #[allow(clippy::unused_enumerate_index)] let v = [1].iter().enumerate(); v.map(|(_, _x)| {}); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x): (usize, i32)| x).sum::(); + assert_eq!(x, 6); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x): (_, i32)| x).sum::(); + assert_eq!(x, 6); + + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x)| x).sum::(); + assert_eq!(x, 6); } diff --git a/tests/ui/unused_enumerate_index.stderr b/tests/ui/unused_enumerate_index.stderr index f8babf428c025..6ec07dcbff0a3 100644 --- a/tests/ui/unused_enumerate_index.stderr +++ b/tests/ui/unused_enumerate_index.stderr @@ -58,5 +58,41 @@ LL - _ = mac2!().enumerate().map(|(_, _v)| {}); LL + _ = mac2!().map(|_v| {}); | -error: aborting due to 5 previous errors +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:94:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x: i32| x).sum::(); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:99:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x: i32| x).sum::(); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:103:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x| x).sum::(); + | + +error: aborting due to 8 previous errors From d3f8f3e9d7e59cce5875920eddfef54970e9fe51 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:57:22 +0100 Subject: [PATCH 069/102] fix span calculation for non-ascii in `needless_return` --- clippy_lints/src/returns.rs | 4 ++-- tests/ui/crashes/ice-12491.fixed | 7 +++++++ tests/ui/crashes/ice-12491.rs | 8 ++++++++ tests/ui/crashes/ice-12491.stderr | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/ui/crashes/ice-12491.fixed create mode 100644 tests/ui/crashes/ice-12491.rs create mode 100644 tests/ui/crashes/ice-12491.stderr diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 0b72c8a071924..77a954cff62bc 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -465,8 +465,8 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) // Go backwards while encountering whitespace and extend the given Span to that point. fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { - let ws = [' ', '\t', '\n']; - if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) { + let ws = [b' ', b'\t', b'\n']; + if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { let len = prev_source.len() - non_ws_pos - 1; return sp.with_lo(sp.lo() - BytePos::from_usize(len)); } diff --git a/tests/ui/crashes/ice-12491.fixed b/tests/ui/crashes/ice-12491.fixed new file mode 100644 index 0000000000000..4ea480b0663ca --- /dev/null +++ b/tests/ui/crashes/ice-12491.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::needless_return)] + +fn main() { + if (true) { + // anything一些中文 + } +} diff --git a/tests/ui/crashes/ice-12491.rs b/tests/ui/crashes/ice-12491.rs new file mode 100644 index 0000000000000..60add6afa2c4f --- /dev/null +++ b/tests/ui/crashes/ice-12491.rs @@ -0,0 +1,8 @@ +#![warn(clippy::needless_return)] + +fn main() { + if (true) { + // anything一些中文 + return; + } +} diff --git a/tests/ui/crashes/ice-12491.stderr b/tests/ui/crashes/ice-12491.stderr new file mode 100644 index 0000000000000..7cc418898e881 --- /dev/null +++ b/tests/ui/crashes/ice-12491.stderr @@ -0,0 +1,19 @@ +error: unneeded `return` statement + --> tests/ui/crashes/ice-12491.rs:5:24 + | +LL | // anything一些中文 + | ____________________________^ +LL | | return; + | |______________^ + | + = note: `-D clippy::needless-return` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_return)]` +help: remove `return` + | +LL - // anything一些中文 +LL - return; +LL + // anything一些中文 + | + +error: aborting due to 1 previous error + From 9408c5974ec27d7119330a3d7057df92b5f59edc Mon Sep 17 00:00:00 2001 From: kcz Date: Fri, 15 Mar 2024 21:25:24 -0400 Subject: [PATCH 070/102] [`option_option`]: Use span.from_expansion --- clippy_lints/src/types/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 280b2a6d73523..ae2ac4ab03e57 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -15,7 +15,7 @@ use rustc_hir::{ Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, TraitItemKind, TyKind, }; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::Span; @@ -392,7 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &hir::FieldDef<'tcx>) { - if rustc_middle::lint::in_external_macro(cx.sess(), field.span) { + if field.span.from_expansion() { return; } From 4e72ca31b5ff35bbe6ff02431bc214138417ed30 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:15:45 +0100 Subject: [PATCH 071/102] [`map_entry`]: call the visitor on the local's `else` block --- clippy_lints/src/entry.rs | 5 ++++- tests/ui/entry.fixed | 10 ++++++++++ tests/ui/entry.rs | 10 ++++++++++ tests/ui/entry.stderr | 23 ++++++++++++++++++++++- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index de6073c272360..219fe588d3ce7 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -358,7 +358,7 @@ struct InsertSearcher<'cx, 'tcx> { can_use_entry: bool, /// Whether this expression is the final expression in this code path. This may be a statement. in_tail_pos: bool, - // Is this expression a single insert. A slightly better suggestion can be made in this case. + /// Is this expression a single insert. A slightly better suggestion can be made in this case. is_single_insert: bool, /// If the visitor has seen the map being used. is_map_used: bool, @@ -431,6 +431,9 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { self.is_single_insert = false; self.visit_expr(e); } + if let Some(els) = &l.els { + self.visit_block(els); + } }, StmtKind::Item(_) => { self.allow_insert_closure &= !self.in_tail_pos; diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index 71ec13f461060..abdfae2a3e133 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -176,4 +176,14 @@ pub fn issue_11935() { } } +fn issue12489(map: &mut HashMap) -> Option<()> { + if let std::collections::hash_map::Entry::Vacant(e) = map.entry(1) { + let Some(1) = Some(2) else { + return None; + }; + e.insert(42); + } + Some(()) +} + fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index 86092b7c055a7..7774f99a2a226 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -180,4 +180,14 @@ pub fn issue_11935() { } } +fn issue12489(map: &mut HashMap) -> Option<()> { + if !map.contains_key(&1) { + let Some(1) = Some(2) else { + return None; + }; + map.insert(1, 42); + } + Some(()) +} + fn main() {} diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index ef4c36bcf546c..fb4676766066a 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -214,5 +214,26 @@ LL + v LL + }); | -error: aborting due to 10 previous errors +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry.rs:184:5 + | +LL | / if !map.contains_key(&1) { +LL | | let Some(1) = Some(2) else { +LL | | return None; +LL | | }; +LL | | map.insert(1, 42); +LL | | } + | |_____^ + | +help: try + | +LL ~ if let std::collections::hash_map::Entry::Vacant(e) = map.entry(1) { +LL + let Some(1) = Some(2) else { +LL + return None; +LL + }; +LL + e.insert(42); +LL + } + | + +error: aborting due to 11 previous errors From 003c4bc7bf9ddeeb1067d517dcdb43b044e3e275 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 14 Mar 2024 09:10:28 +0000 Subject: [PATCH 072/102] Avoid various uses of `Option` in favor of using `DUMMY_SP` in the few cases that used `None` --- clippy_lints/src/non_copy_const.rs | 10 +++++----- clippy_utils/src/consts.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index ea73d9afa2ea0..cebd23856565d 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -290,14 +290,14 @@ impl NonCopyConst { promoted: None, }; let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); - let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None); + let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, rustc_span::DUMMY_SP); self.is_value_unfrozen_raw(cx, result, ty) } fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { let args = cx.typeck_results().node_args(hir_id); - let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None); + let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), rustc_span::DUMMY_SP); self.is_value_unfrozen_raw(cx, result, ty) } @@ -305,7 +305,7 @@ impl NonCopyConst { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) { Ok(Some(instance)) => { @@ -315,8 +315,8 @@ impl NonCopyConst { }; tcx.const_eval_global_id_for_typeck(param_env, cid, span) }, - Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))), - Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))), + Ok(None) => Err(ErrorHandled::TooGeneric(span)), + Err(err) => Err(ErrorHandled::Reported(err.into(), span)), } } } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 07ed4fbbf8e9e..e75d5953faefd 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -550,7 +550,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let result = self .lcx .tcx - .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) + .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; let result = mir_to_const(self.lcx, result)?; From 8339474a7cdfda94412ca64b41b55747683f7f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 5 Mar 2024 16:24:30 +0000 Subject: [PATCH 073/102] When displaying multispans, ignore empty lines adjacent to `...` ``` error[E0308]: `match` arms have incompatible types --> tests/ui/codemap_tests/huge_multispan_highlight.rs:98:18 | 6 | let _ = match true { | ---------- `match` arms have incompatible types 7 | true => ( | _________________- 8 | | // last line shown in multispan header ... | 96 | | 97 | | ), | |_________- this is found to be of type `()` 98 | false => " | __________________^ ... | 119 | | 120 | | ", | |_________^ expected `()`, found `&str` error[E0308]: `match` arms have incompatible types --> tests/ui/codemap_tests/huge_multispan_highlight.rs:215:18 | 122 | let _ = match true { | ---------- `match` arms have incompatible types 123 | true => ( | _________________- 124 | | 125 | | 1 // last line shown in multispan header ... | 213 | | 214 | | ), | |_________- this is found to be of type `{integer}` 215 | false => " | __________________^ 216 | | 217 | | 218 | | 1 last line shown in multispan ... | 237 | | 238 | | ", | |_________^ expected integer, found `&str` ``` --- tests/ui/async_yields_async.stderr | 3 +-- tests/ui/empty_line_after_outer_attribute.stderr | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index f1fae6549de4c..991ad7ae0ae2f 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -81,8 +81,7 @@ LL | let _m = async || { | _______________________- LL | | println!("I'm bored"); LL | | // Some more stuff -LL | | -LL | | // Finally something to await +... | LL | | CustomFutureType | | ^^^^^^^^^^^^^^^^ | | | diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index 1b5b00a4a83bb..b43e6e30da220 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -22,8 +22,7 @@ error: found an empty line after an outer attribute. Perhaps you forgot to add a --> tests/ui/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] -LL | | -LL | | +... | LL | | fn with_two_newlines() { assert!(true) } | |_ From 0dc876991c07dc8346fd3009b6edf1f46bf5e24e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 Mar 2024 09:42:25 +0000 Subject: [PATCH 074/102] Deduplicate `associated_body` and `body_id` They match on almost the same patterns, which is fishy. Also turn `associated_body` into a method and do some cleanups nearby the call sites --- clippy_lints/src/needless_pass_by_ref_mut.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index a450dee305005..30d3e86dc4ed7 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -13,7 +13,6 @@ use rustc_hir::{ use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::associated_body; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; use rustc_session::impl_lint_pass; @@ -112,7 +111,10 @@ fn check_closures<'tcx>( } ctx.prev_bind = None; ctx.prev_move_to_closure.clear(); - if let Some(body) = associated_body(cx.tcx.hir_node_by_def_id(closure)) + if let Some(body) = cx + .tcx + .hir_node_by_def_id(closure) + .associated_body() .map(|(_, body_id)| hir.body(body_id)) { euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body); From 83af0e5928cc16e587eaf2753c291c2e56a95e5a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 18 Mar 2024 22:59:44 +0100 Subject: [PATCH 075/102] Update version attribute for 1.77 lints --- clippy_lints/src/methods/mod.rs | 12 ++++++------ .../thread_local_initializer_can_be_made_const.rs | 2 +- clippy_lints/src/transmute/mod.rs | 2 +- clippy_lints/src/unconditional_recursion.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c992589404f18..58ac40eb9fb53 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2863,7 +2863,7 @@ declare_clippy_lint! { /// /// OpenOptions::new().create(true).truncate(true); /// ``` - #[clippy::version = "1.75.0"] + #[clippy::version = "1.77.0"] pub SUSPICIOUS_OPEN_OPTIONS, suspicious, "suspicious combination of options for opening a file" @@ -3817,7 +3817,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = std::iter::empty::>().flatten(); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub RESULT_FILTER_MAP, complexity, "filtering `Result` for `Ok` then force-unwrapping, which can be one type-safe operation" @@ -3843,7 +3843,7 @@ declare_clippy_lint! { /// // example code which does not raise clippy warning /// vec![Some(1)].into_iter().flatten(); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub ITER_FILTER_IS_SOME, pedantic, "filtering an iterator over `Option`s for `Some` can be achieved with `flatten`" @@ -3869,7 +3869,7 @@ declare_clippy_lint! { /// // example code which does not raise clippy warning /// vec![Ok::(1)].into_iter().flatten(); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub ITER_FILTER_IS_OK, pedantic, "filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`" @@ -3896,7 +3896,7 @@ declare_clippy_lint! { /// option.is_some_and(|a| a > 10); /// result.is_ok_and(|a| a > 10); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub MANUAL_IS_VARIANT_AND, pedantic, "using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`" @@ -3926,7 +3926,7 @@ declare_clippy_lint! { /// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is /// valid. /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub STR_SPLIT_AT_NEWLINE, pedantic, "splitting a trimmed string at hard-coded newlines" diff --git a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs index 1af3733ebfa4b..f8bdb866ca391 100644 --- a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs +++ b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// static BUF: String = const { String::new() }; /// } /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST, perf, "suggest using `const` in `thread_local!` macro" diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index e47b14bf63b21..3c11b48131051 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -514,7 +514,7 @@ declare_clippy_lint! { /// ^^^^ ^^ `bool::then` only executes the closure if the condition is true! /// } /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub EAGER_TRANSMUTE, correctness, "eager evaluation of `transmute`" diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index 1ca14ca04d3cf..d638af2b78b36 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { /// Use instead: /// /// In such cases, either use `#[derive(PartialEq)]` or don't implement it. - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub UNCONDITIONAL_RECURSION, suspicious, "detect unconditional recursion in some traits implementation" From 87d45e5e2cc4e39f4bbb47632dfe64c82c3d24c5 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 18 Mar 2024 22:55:41 +0100 Subject: [PATCH 076/102] Changelog for Clippy 1.77 :school: --- CHANGELOG.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 733858d46732e..76ef84a48b817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,65 @@ document. ## Unreleased / Beta / In Rust Nightly -[a859e5cc...master](https://github.com/rust-lang/rust-clippy/compare/a859e5cc...master) +[66c29b97...master](https://github.com/rust-lang/rust-clippy/compare/66c29b97...master) + +## Rust 1.77 + +Current stable, released 2024-03-18 + +[View all 93 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-12-16T18%3A20%3A00Z..2024-01-25T18%3A15%3A56Z+base%3Amaster) + +### New Lints + +* [`suspicious_open_options`] + [#11608](https://github.com/rust-lang/rust-clippy/pull/11608) +* [`option_as_ref_cloned`] + [#12051](https://github.com/rust-lang/rust-clippy/pull/12051) +* [`thread_local_initializer_can_be_made_const`] + [#12026](https://github.com/rust-lang/rust-clippy/pull/12026) +* [`str_split_at_newline`] + [#11987](https://github.com/rust-lang/rust-clippy/pull/11987) +* [`empty_enum_variants_with_brackets`] + [#12047](https://github.com/rust-lang/rust-clippy/pull/12047) +* [`manual_is_variant_and`] + [#11865](https://github.com/rust-lang/rust-clippy/pull/11865) +* [`pub_underscore_fields`] + [#10283](https://github.com/rust-lang/rust-clippy/pull/10283) +* [`eager_transmute`] + [#11981](https://github.com/rust-lang/rust-clippy/pull/11981) +* [`iter_filter_is_some`] + [#12004](https://github.com/rust-lang/rust/pull/12004) +* [`iter_filter_is_ok`] + [#12004](https://github.com/rust-lang/rust/pull/12004) +* [`result_filter_map`] + [#11869](https://github.com/rust-lang/rust-clippy/pull/11869) +* [`unconditional_recursion`] + [#11938](https://github.com/rust-lang/rust-clippy/pull/11938) + +### Enhancements + +* [`multiple_crate_versions`]: Added the [`allowed-duplicate-crates`] configuration to allow specific crates + [#12179](https://github.com/rust-lang/rust-clippy/pull/12179) +* [`single_call_fn`]: No longer ignores `#[allow]` attributes + [#12183](https://github.com/rust-lang/rust-clippy/pull/12183) +* [`read_zero_byte_vec`]: Updated the heuristics used for linting + [#11766](https://github.com/rust-lang/rust-clippy/pull/11766) + +### ICE Fixes + +* [`unit_arg`]: No longer crashes when checking for const in nested bodies + [#11977](https://github.com/rust-lang/rust-clippy/pull/11977) +* [`indexing_slicing`]: No longer crashes when the array index exceeds `usize` + [#12266](https://github.com/rust-lang/rust-clippy/pull/12266) + +### Others + +* Warnings about invalid fields inside `clippy.toml` files now include suggestions for existing fields + [#12180](https://github.com/rust-lang/rust-clippy/pull/12180) ## Rust 1.76 -Current stable, released 2024-02-08 +Released 2024-02-08 [View all 85 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-11-02T20%3A23%3A40Z..2023-12-16T13%3A11%3A08Z+base%3Amaster) From e7c3e049f8d6fa710d3c0ece5e1c7211c3a843dd Mon Sep 17 00:00:00 2001 From: goodmost Date: Tue, 19 Mar 2024 22:09:05 +0800 Subject: [PATCH 077/102] chore: fix typo Signed-off-by: goodmost --- clippy_lints/src/functions/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 96da2ec2a1a8b..9cc51fa8cd5dd 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -250,7 +250,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// A `Result` is at least as large as the `Err`-variant. While we - /// expect that variant to be seldomly used, the compiler needs to reserve + /// expect that variant to be seldom used, the compiler needs to reserve /// and move that much memory every single time. /// Furthermore, errors are often simply passed up the call-stack, making /// use of the `?`-operator and its type-conversion mechanics. If the From 21a97f0192fe4a8d6d3ab88481e4653bc7dce84d Mon Sep 17 00:00:00 2001 From: humannum14916 Date: Tue, 19 Mar 2024 17:05:32 -0400 Subject: [PATCH 078/102] Specify an MSRV of 1.63.0 for assigning_clones --- clippy_config/src/msrvs.rs | 1 + clippy_lints/src/assigning_clones.rs | 23 +++++++++++++++++++++-- clippy_lints/src/lib.rs | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index a8a32f7ed208b..bf4da5f14fe0a 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -23,6 +23,7 @@ msrv_aliases! { 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } + 1,63,0 { ASSIGNING_CLONES } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index b1c552c7a8dd2..ea5578572ea70 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -1,3 +1,4 @@ +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::HirNode; use clippy_utils::sugg::Sugg; @@ -6,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Instance, Mutability}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; use rustc_span::symbol::sym; use rustc_span::ExpnKind; @@ -49,10 +50,26 @@ declare_clippy_lint! { perf, "assigning the result of cloning may be inefficient" } -declare_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); + +pub struct AssigningClones { + msrv: Msrv, +} + +impl AssigningClones { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) { + if !self.msrv.meets(msrvs::ASSIGNING_CLONES) { + return; + } + // Do not fire the lint in macros let expn_data = assign_expr.span().ctxt().outer_expn_data(); match expn_data.kind { @@ -72,6 +89,8 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { suggest(cx, assign_expr, lhs, &call); } } + + extract_msrv_attr!(LateContext); } // Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9b22d7565e3c7..57fac35104272 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1122,7 +1122,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); - store.register_late_pass(|_| Box::new(assigning_clones::AssigningClones)); + store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(msrv()))); 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)); From 8c866d36cc4a6cd073683b4e7fd51aa2371aca24 Mon Sep 17 00:00:00 2001 From: humannum14916 Date: Tue, 19 Mar 2024 17:07:27 -0400 Subject: [PATCH 079/102] Add test for MSRV checking for assigning_clones --- tests/ui/assigning_clones.fixed | 11 +++++++++++ tests/ui/assigning_clones.rs | 11 +++++++++++ tests/ui/assigning_clones.stderr | 20 +++++++++++++------- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed index c66e0c1f6028c..771c7f6f56a12 100644 --- a/tests/ui/assigning_clones.fixed +++ b/tests/ui/assigning_clones.fixed @@ -128,6 +128,17 @@ fn ignore_generic_clone(a: &mut T, b: &T) { *a = b.clone(); } +#[clippy::msrv = "1.62"] +fn msrv_1_62(mut a: String, b: &str) { + // Should not be linted, as clone_into wasn't stabilized until 1.63 + a = b.to_owned(); +} + +#[clippy::msrv = "1.63"] +fn msrv_1_63(mut a: String, b: &str) { + b.clone_into(&mut a); +} + macro_rules! clone_inside { ($a:expr, $b: expr) => { $a = $b.clone(); diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs index b9f994d3e034c..1320481322f63 100644 --- a/tests/ui/assigning_clones.rs +++ b/tests/ui/assigning_clones.rs @@ -128,6 +128,17 @@ fn ignore_generic_clone(a: &mut T, b: &T) { *a = b.clone(); } +#[clippy::msrv = "1.62"] +fn msrv_1_62(mut a: String, b: &str) { + // Should not be linted, as clone_into wasn't stabilized until 1.63 + a = b.to_owned(); +} + +#[clippy::msrv = "1.63"] +fn msrv_1_63(mut a: String, b: &str) { + a = b.to_owned(); +} + macro_rules! clone_inside { ($a:expr, $b: expr) => { $a = $b.clone(); diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr index b76323f360634..b8e20cee16024 100644 --- a/tests/ui/assigning_clones.stderr +++ b/tests/ui/assigning_clones.stderr @@ -68,40 +68,46 @@ LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:145:5 + --> tests/ui/assigning_clones.rs:139:5 + | +LL | a = b.to_owned(); + | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `b.clone_into(&mut a)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:156:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:149:5 + --> tests/ui/assigning_clones.rs:160:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:170:5 + --> tests/ui/assigning_clones.rs:181:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:174:5 + --> tests/ui/assigning_clones.rs:185:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:178:5 + --> tests/ui/assigning_clones.rs:189:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:182:5 + --> tests/ui/assigning_clones.rs:193:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` -error: aborting due to 17 previous errors +error: aborting due to 18 previous errors From 7c0b2dd3523e43edeb67c074e895fc20a5491375 Mon Sep 17 00:00:00 2001 From: humannum14916 Date: Tue, 19 Mar 2024 17:12:16 -0400 Subject: [PATCH 080/102] Add assigning_clones to MSRV config option docs --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index a985346b3c05f..a923489974646 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -602,6 +602,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio **Affected lints:** * [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) * [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) +* [`assigning_clones`](https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones) * [`borrow_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) * [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) * [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 673b6328b3901..3218fe7f45627 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -262,7 +262,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES. /// /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = ""] From 477108d382c325134b8578dc327804a157caa19e Mon Sep 17 00:00:00 2001 From: Jacherr Date: Fri, 15 Mar 2024 23:05:59 +0000 Subject: [PATCH 081/102] FP: `cast_lossless`: disable lint when casting to (u)128 from any (u)int type --- clippy_lints/src/casts/cast_lossless.rs | 7 ++-- tests/ui/cast_lossless_integer.fixed | 6 ++++ tests/ui/cast_lossless_integer.rs | 6 ++++ tests/ui/cast_lossless_integer.stderr | 44 ++++++++++++------------- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index fe0dd7f2eae6d..86f4332d05aad 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_middle::ty::{self, FloatTy, Ty, UintTy}; use super::{utils, CAST_LOSSLESS}; @@ -77,7 +77,10 @@ pub(super) fn check( fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). - if in_constant(cx, expr.hir_id) { + // + // If destination is u128, do not lint because source type cannot be larger + // If source is bool, still lint due to the lint message differing (refers to style) + if in_constant(cx, expr.hir_id) || (!cast_from.is_bool() && matches!(cast_to.kind(), ty::Uint(UintTy::U128))) { return false; } diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 1b826e0c60073..291556a977410 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -2,6 +2,7 @@ #![warn(clippy::cast_lossless)] type I64 = i64; +type U128 = u128; fn main() { // Test clippy::cast_lossless with casts to integer types @@ -28,6 +29,11 @@ fn main() { let _ = u16::from(1u8 + 1u8); let _ = I64::from(1i8); + + // Do not lint if destination type is u128 + // see https://github.com/rust-lang/rust-clippy/issues/12492 + let _ = 1u8 as u128; + let _ = 1u8 as U128; } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index f63bbf8577d79..a917c7a371d8a 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -2,6 +2,7 @@ #![warn(clippy::cast_lossless)] type I64 = i64; +type U128 = u128; fn main() { // Test clippy::cast_lossless with casts to integer types @@ -28,6 +29,11 @@ fn main() { let _ = (1u8 + 1u8) as u16; let _ = 1i8 as I64; + + // Do not lint if destination type is u128 + // see https://github.com/rust-lang/rust-clippy/issues/12492 + let _ = 1u8 as u128; + let _ = 1u8 as U128; } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_integer.stderr b/tests/ui/cast_lossless_integer.stderr index 0c4b5aee62de3..aaece9392856f 100644 --- a/tests/ui/cast_lossless_integer.stderr +++ b/tests/ui/cast_lossless_integer.stderr @@ -1,5 +1,5 @@ error: casting `i8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:8:13 + --> tests/ui/cast_lossless_integer.rs:9:13 | LL | let _ = 1i8 as i16; | ^^^^^^^^^^ help: try: `i16::from(1i8)` @@ -8,127 +8,127 @@ LL | let _ = 1i8 as i16; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:9:13 + --> tests/ui/cast_lossless_integer.rs:10:13 | LL | let _ = 1i8 as i32; | ^^^^^^^^^^ help: try: `i32::from(1i8)` error: casting `i8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:10:13 + --> tests/ui/cast_lossless_integer.rs:11:13 | LL | let _ = 1i8 as i64; | ^^^^^^^^^^ help: try: `i64::from(1i8)` error: casting `u8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:11:13 + --> tests/ui/cast_lossless_integer.rs:12:13 | LL | let _ = 1u8 as i16; | ^^^^^^^^^^ help: try: `i16::from(1u8)` error: casting `u8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:12:13 + --> tests/ui/cast_lossless_integer.rs:13:13 | LL | let _ = 1u8 as i32; | ^^^^^^^^^^ help: try: `i32::from(1u8)` error: casting `u8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:13:13 + --> tests/ui/cast_lossless_integer.rs:14:13 | LL | let _ = 1u8 as i64; | ^^^^^^^^^^ help: try: `i64::from(1u8)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:14:13 + --> tests/ui/cast_lossless_integer.rs:15:13 | LL | let _ = 1u8 as u16; | ^^^^^^^^^^ help: try: `u16::from(1u8)` error: casting `u8` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:15:13 + --> tests/ui/cast_lossless_integer.rs:16:13 | LL | let _ = 1u8 as u32; | ^^^^^^^^^^ help: try: `u32::from(1u8)` error: casting `u8` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:16:13 + --> tests/ui/cast_lossless_integer.rs:17:13 | LL | let _ = 1u8 as u64; | ^^^^^^^^^^ help: try: `u64::from(1u8)` error: casting `i16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:17:13 + --> tests/ui/cast_lossless_integer.rs:18:13 | LL | let _ = 1i16 as i32; | ^^^^^^^^^^^ help: try: `i32::from(1i16)` error: casting `i16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:18:13 + --> tests/ui/cast_lossless_integer.rs:19:13 | LL | let _ = 1i16 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1i16)` error: casting `u16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:19:13 + --> tests/ui/cast_lossless_integer.rs:20:13 | LL | let _ = 1u16 as i32; | ^^^^^^^^^^^ help: try: `i32::from(1u16)` error: casting `u16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:20:13 + --> tests/ui/cast_lossless_integer.rs:21:13 | LL | let _ = 1u16 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1u16)` error: casting `u16` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:21:13 + --> tests/ui/cast_lossless_integer.rs:22:13 | LL | let _ = 1u16 as u32; | ^^^^^^^^^^^ help: try: `u32::from(1u16)` error: casting `u16` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:22:13 + --> tests/ui/cast_lossless_integer.rs:23:13 | LL | let _ = 1u16 as u64; | ^^^^^^^^^^^ help: try: `u64::from(1u16)` error: casting `i32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:23:13 + --> tests/ui/cast_lossless_integer.rs:24:13 | LL | let _ = 1i32 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1i32)` error: casting `u32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:24:13 + --> tests/ui/cast_lossless_integer.rs:25:13 | LL | let _ = 1u32 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1u32)` error: casting `u32` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:25:13 + --> tests/ui/cast_lossless_integer.rs:26:13 | LL | let _ = 1u32 as u64; | ^^^^^^^^^^^ help: try: `u64::from(1u32)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:28:13 + --> tests/ui/cast_lossless_integer.rs:29:13 | LL | let _ = (1u8 + 1u8) as u16; | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` error: casting `i8` to `I64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:30:13 + --> tests/ui/cast_lossless_integer.rs:31:13 | LL | let _ = 1i8 as I64; | ^^^^^^^^^^ help: try: `I64::from(1i8)` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:64:13 + --> tests/ui/cast_lossless_integer.rs:70:13 | LL | let _ = sign_cast!(x, u8, i8) as i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:65:13 + --> tests/ui/cast_lossless_integer.rs:71:13 | LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)` From 5f7b3c5eea02b697a0a45ba969b7b42b10aed528 Mon Sep 17 00:00:00 2001 From: humannum14916 Date: Wed, 20 Mar 2024 14:04:12 -0400 Subject: [PATCH 082/102] Only enforce MSRV check on .clone_into() suggestions --- clippy_lints/src/assigning_clones.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index ea5578572ea70..88d9f762a87b3 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -66,10 +66,6 @@ impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) { - if !self.msrv.meets(msrvs::ASSIGNING_CLONES) { - return; - } - // Do not fire the lint in macros let expn_data = assign_expr.span().ctxt().outer_expn_data(); match expn_data.kind { @@ -85,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { return; }; - if is_ok_to_suggest(cx, lhs, &call) { + if is_ok_to_suggest(cx, lhs, &call, &self.msrv) { suggest(cx, assign_expr, lhs, &call); } } @@ -154,7 +150,13 @@ fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option< // Return true if we find that the called method has a custom implementation and isn't derived or // provided by default by the corresponding trait. -fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) -> bool { +fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool { + // For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63. + // If the current MSRV is below that, don't suggest the lint. + if !msrv.meets(msrvs::ASSIGNING_CLONES) && matches!(call.target, TargetTrait::ToOwned) { + return false; + } + // If the left-hand side is a local variable, it might be uninitialized at this point. // In that case we do not want to suggest the lint. if let Some(local) = path_to_local(lhs) { From db7c9feaa00dcc5adcbf6d345ddcfa54bcf3debf Mon Sep 17 00:00:00 2001 From: humannum14916 Date: Wed, 20 Mar 2024 14:07:53 -0400 Subject: [PATCH 083/102] Add UI test for new MSRV check --- tests/ui/assigning_clones.fixed | 10 ++++++---- tests/ui/assigning_clones.rs | 10 ++++++---- tests/ui/assigning_clones.stderr | 32 ++++++++++++++++++++++---------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed index 771c7f6f56a12..160f3b9466316 100644 --- a/tests/ui/assigning_clones.fixed +++ b/tests/ui/assigning_clones.fixed @@ -129,14 +129,16 @@ fn ignore_generic_clone(a: &mut T, b: &T) { } #[clippy::msrv = "1.62"] -fn msrv_1_62(mut a: String, b: &str) { +fn msrv_1_62(mut a: String, b: String, c: &str) { + a.clone_from(&b); // Should not be linted, as clone_into wasn't stabilized until 1.63 - a = b.to_owned(); + a = c.to_owned(); } #[clippy::msrv = "1.63"] -fn msrv_1_63(mut a: String, b: &str) { - b.clone_into(&mut a); +fn msrv_1_63(mut a: String, b: String, c: &str) { + a.clone_from(&b); + c.clone_into(&mut a); } macro_rules! clone_inside { diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs index 1320481322f63..14ba1d4db9a83 100644 --- a/tests/ui/assigning_clones.rs +++ b/tests/ui/assigning_clones.rs @@ -129,14 +129,16 @@ fn ignore_generic_clone(a: &mut T, b: &T) { } #[clippy::msrv = "1.62"] -fn msrv_1_62(mut a: String, b: &str) { +fn msrv_1_62(mut a: String, b: String, c: &str) { + a = b.clone(); // Should not be linted, as clone_into wasn't stabilized until 1.63 - a = b.to_owned(); + a = c.to_owned(); } #[clippy::msrv = "1.63"] -fn msrv_1_63(mut a: String, b: &str) { - a = b.to_owned(); +fn msrv_1_63(mut a: String, b: String, c: &str) { + a = b.clone(); + a = c.to_owned(); } macro_rules! clone_inside { diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr index b8e20cee16024..ba59f067431a7 100644 --- a/tests/ui/assigning_clones.stderr +++ b/tests/ui/assigning_clones.stderr @@ -67,47 +67,59 @@ error: assigning the result of `Clone::clone()` may be inefficient LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:133:5 + | +LL | a = b.clone(); + | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:140:5 + | +LL | a = b.clone(); + | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:139:5 + --> tests/ui/assigning_clones.rs:141:5 | -LL | a = b.to_owned(); - | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `b.clone_into(&mut a)` +LL | a = c.to_owned(); + | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:156:5 + --> tests/ui/assigning_clones.rs:158:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:160:5 + --> tests/ui/assigning_clones.rs:162:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:181:5 + --> tests/ui/assigning_clones.rs:183:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:185:5 + --> tests/ui/assigning_clones.rs:187:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:189:5 + --> tests/ui/assigning_clones.rs:191:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:193:5 + --> tests/ui/assigning_clones.rs:195:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` -error: aborting due to 18 previous errors +error: aborting due to 20 previous errors From 19cb05fd7807e2ea0d7b4f8613eb8b0ad646ce61 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 2 Mar 2024 01:51:58 +0000 Subject: [PATCH 084/102] std::net: adding acceptfilter feature for netbsd/freebsd. similar to linux's ext deferaccept, to filter incoming connections before accept. --- library/std/src/os/freebsd/net.rs | 26 ++++++++++++++++++++++++ library/std/src/os/netbsd/net.rs | 26 ++++++++++++++++++++++++ library/std/src/sys/pal/unix/net.rs | 31 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index 33990d54caae0..b7e0fdc0a9aab 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index 5c82f43077d4c..b9679c7b3af33 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds(&self, local_creds: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index 09750b6ffc897..1f140f7844f7c 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -453,6 +453,37 @@ impl Socket { Ok(raw as u32) } + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + if !name.to_bytes().is_empty() { + const AF_NAME_MAX: usize = 16; + let mut buf = [0; AF_NAME_MAX]; + for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { + *dst = *src as i8; + } + let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; + arg.af_name = buf; + setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + } else { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn acceptfilter(&self) -> io::Result<&CStr> { + let arg: libc::accept_filter_arg = + getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + let s: &[u8] = + unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; + let name = CStr::from_bytes_with_nul(s).unwrap(); + Ok(name) + } + #[cfg(any(target_os = "android", target_os = "linux",))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) From f6f89dc2023315b777f0ffef3b4c4573d364beb8 Mon Sep 17 00:00:00 2001 From: ultrabear Date: Thu, 21 Mar 2024 00:54:50 -0700 Subject: [PATCH 085/102] BTree(Set|Map): Guarantee that `IntoIter` will iterate in sorted by key order --- library/alloc/src/collections/btree/map.rs | 3 ++- library/alloc/src/collections/btree/set.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e99c6220e2064..66b45f866017b 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -415,7 +415,7 @@ impl<'a, K: 'a, V: 'a> Default for IterMut<'a, K, V> { } } -/// An owning iterator over the entries of a `BTreeMap`. +/// An owning iterator over the entries of a `BTreeMap`, sorted by key. /// /// This `struct` is created by the [`into_iter`] method on [`BTreeMap`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. @@ -1632,6 +1632,7 @@ impl<'a, K, V> IterMut<'a, K, V> { } } +/// Gets an owning iterator over the entries of the map, sorted by key. #[stable(feature = "rust1", since = "1.0.0")] impl IntoIterator for BTreeMap { type Item = (K, V); diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index ed91ae1a66e3d..9c0b89188d8c5 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -140,7 +140,7 @@ impl fmt::Debug for Iter<'_, T> { } } -/// An owning iterator over the items of a `BTreeSet`. +/// An owning iterator over the items of a `BTreeSet` in ascending order. /// /// This `struct` is created by the [`into_iter`] method on [`BTreeSet`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. @@ -1232,6 +1232,7 @@ impl From<[T; N]> for BTreeSet { } } +/// Gets an owning iterator over the elements of the `BTreeSet` in ascending order. #[stable(feature = "rust1", since = "1.0.0")] impl IntoIterator for BTreeSet { type Item = T; From f8a093c8ea456cb64966da2983eb988e74700fde Mon Sep 17 00:00:00 2001 From: ultrabear Date: Thu, 21 Mar 2024 02:05:11 -0700 Subject: [PATCH 086/102] document iteration ordering on into_iter method instead of IntoIterator implementation --- library/alloc/src/collections/btree/map.rs | 2 +- library/alloc/src/collections/btree/set.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 66b45f866017b..53b25a9ef25f8 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1632,12 +1632,12 @@ impl<'a, K, V> IterMut<'a, K, V> { } } -/// Gets an owning iterator over the entries of the map, sorted by key. #[stable(feature = "rust1", since = "1.0.0")] impl IntoIterator for BTreeMap { type Item = (K, V); type IntoIter = IntoIter; + /// Gets an owning iterator over the entries of the map, sorted by key. fn into_iter(self) -> IntoIter { let mut me = ManuallyDrop::new(self); if let Some(root) = me.root.take() { diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 9c0b89188d8c5..a110ee9c7f36c 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1232,13 +1232,12 @@ impl From<[T; N]> for BTreeSet { } } -/// Gets an owning iterator over the elements of the `BTreeSet` in ascending order. #[stable(feature = "rust1", since = "1.0.0")] impl IntoIterator for BTreeSet { type Item = T; type IntoIter = IntoIter; - /// Gets an iterator for moving out the `BTreeSet`'s contents. + /// Gets an iterator for moving out the `BTreeSet`'s contents in ascending order. /// /// # Examples /// From beb0c22c096c68fd853cf65bb44408cf0d5ac99a Mon Sep 17 00:00:00 2001 From: ultrabear Date: Thu, 21 Mar 2024 02:07:46 -0700 Subject: [PATCH 087/102] document into_iter in top level docs iterator ordering guarantees --- library/alloc/src/collections/btree/map.rs | 2 +- library/alloc/src/collections/btree/set.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 53b25a9ef25f8..3875f61efafdf 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -72,7 +72,7 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// `BTreeMap` that observed the logic error and not result in undefined behavior. This could /// include panics, incorrect results, aborts, memory leaks, and non-termination. /// -/// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::values`], or +/// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::into_iter`], [`BTreeMap::values`], or /// [`BTreeMap::keys`] produce their items in order by key, and take worst-case logarithmic and /// amortized constant time per item returned. /// diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index a110ee9c7f36c..7508ae468ae5b 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -27,7 +27,7 @@ use crate::alloc::{Allocator, Global}; /// `BTreeSet` that observed the logic error and not result in undefined behavior. This could /// include panics, incorrect results, aborts, memory leaks, and non-termination. /// -/// Iterators returned by [`BTreeSet::iter`] produce their items in order, and take worst-case +/// Iterators returned by [`BTreeSet::iter`] and [`BTreeSet::into_iter`] produce their items in order, and take worst-case /// logarithmic and amortized constant time per item returned. /// /// [`Cell`]: core::cell::Cell From 8ca7aac3eb51e4ca3a3ae48659f88e8f3dea380b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 21 Mar 2024 10:28:52 -0400 Subject: [PATCH 088/102] Add tests for shortcomings of associated type bounds --- .../cant-see-copy-bound-from-child-rigid-2.rs | 18 ++++++++++++++++++ ...nt-see-copy-bound-from-child-rigid-2.stderr | 13 +++++++++++++ .../cant-see-copy-bound-from-child-rigid.rs | 18 ++++++++++++++++++ ...cant-see-copy-bound-from-child-rigid.stderr | 14 ++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.rs create mode 100644 tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.stderr create mode 100644 tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs create mode 100644 tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.rs new file mode 100644 index 0000000000000..1768b006622e2 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.rs @@ -0,0 +1,18 @@ +trait Id { + type This: ?Sized; +} +impl Id for T { + type This = T; +} + +trait Trait { + type Assoc: Id; +} + +// We can't see use the `T::Assoc::This: Copy` bound to prove `T::Assoc: Copy` +fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) { + (x, x) + //~^ ERROR use of moved value +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.stderr new file mode 100644 index 0000000000000..cd0e026905c9a --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid-2.rs:14:9 + | +LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) { + | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs new file mode 100644 index 0000000000000..6b3fd7e898d18 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs @@ -0,0 +1,18 @@ +trait Id { + type This: ?Sized; +} + +trait Trait { + type Assoc: Id; +} + +// We can't see use the `T::Assoc::This: Copy` bound to prove `T::Assoc: Copy` +fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) +where + T::Assoc: Id, +{ + (x, x) + //~^ ERROR use of moved value +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr new file mode 100644 index 0000000000000..3ed73918de313 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:14:9 + | +LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) + | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait +... +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. From e50540817be34e8890e86aee07303d9be2559732 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 21 Mar 2024 16:22:09 +0100 Subject: [PATCH 089/102] Add xFrednet back to the reviewing rotation --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 1a81394af10d5..d455d967e30c0 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -19,7 +19,7 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" -users_on_vacation = ["xFrednet"] +users_on_vacation = [] [assign.owners] "/.github" = ["@flip1995"] From 796105ef63acdacd9407a7d22f34af18438801e7 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 21 Mar 2024 19:48:18 +0300 Subject: [PATCH 090/102] make failure logs less verbose Signed-off-by: onur-ozkan --- src/bootstrap/src/lib.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 64bb7bf01f796..e94a767a194a2 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1009,15 +1009,23 @@ impl Build { let result = if !output.status.success() { if print_error { println!( - "\n\ncommand did not execute successfully: {:?}\n\ - expected success, got: {}\n\n\ - stdout ----\n{}\n\ - stderr ----\n{}\n\n", - command.command, + "\n\nCommand did not execute successfully.\ + \nExpected success, got: {}", output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) ); + + if !self.is_verbose() { + println!("Add `-v` to see more details.\n"); + } + + self.verbose(|| { + println!( + "\nSTDOUT ----\n{}\n\ + STDERR ----\n{}\n", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + }); } Err(()) } else { From ae4c5c891ec55e2bf78615650daded9124552ca0 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 22 Mar 2024 00:48:36 +0900 Subject: [PATCH 091/102] Implement `FusedIterator` for `gen` block --- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_span/src/symbol.rs | 2 ++ .../src/solve/assembly/mod.rs | 9 +++++ .../src/solve/normalizes_to/mod.rs | 7 ++++ .../src/solve/trait_goals.rs | 22 +++++++++++++ .../src/traits/select/candidate_assembly.rs | 33 +++++++++++++++---- .../src/traits/select/confirmation.rs | 2 ++ .../src/traits/select/mod.rs | 14 ++++++++ library/core/src/iter/traits/marker.rs | 1 + tests/ui/coroutine/gen_block_is_fused_iter.rs | 21 ++++++++++++ 10 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 tests/ui/coroutine/gen_block_is_fused_iter.rs diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5118bf5c3b7ab..dbf86f5cf747d 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -214,6 +214,7 @@ language_item_table! { FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); + FusedIterator, sym::fused_iterator, fused_iterator_trait, Target::Trait, GenericRequirement::Exact(0); Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b911a41a112f..c28c577d78014 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -207,6 +207,7 @@ symbols! { FromResidual, FsOpenOptions, FsPermissions, + FusedIterator, Future, FutureOutput, GlobalAlloc, @@ -885,6 +886,7 @@ symbols! { fsub_algebraic, fsub_fast, fundamental, + fused_iterator, future, future_trait, gdb_script_file, diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 9f33dce2a6dfe..d92bae2528fbc 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -215,6 +215,13 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + /// A coroutine (that comes from a `gen` desugaring) is known to implement + /// `FusedIterator` + fn consider_builtin_fused_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -497,6 +504,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.iterator_trait() == Some(trait_def_id) { G::consider_builtin_iterator_candidate(self, goal) + } else if lang_items.fused_iterator_trait() == Some(trait_def_id) { + G::consider_builtin_fused_iterator_candidate(self, goal) } else if lang_items.async_iterator_trait() == Some(trait_def_id) { G::consider_builtin_async_iterator_candidate(self, goal) } else if lang_items.coroutine_trait() == Some(trait_def_id) { diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 85bb6338daff9..6668889323512 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -647,6 +647,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ) } + fn consider_builtin_fused_iterator_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`FusedIterator` does not have an associated type: {:?}", goal); + } + fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index c252ad76dfe1d..184ba31f19d20 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -456,6 +456,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + fn consider_builtin_fused_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_gen(def_id) { + return Err(NoSolution); + } + + // Gen coroutines unconditionally implement `FusedIterator` + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 49091e53be713..9fb4577fb2169 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -118,6 +118,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_future_candidates(obligation, &mut candidates); } else if lang_items.iterator_trait() == Some(def_id) { self.assemble_iterator_candidates(obligation, &mut candidates); + } else if lang_items.fused_iterator_trait() == Some(def_id) { + self.assemble_fused_iterator_candidates(obligation, &mut candidates); } else if lang_items.async_iterator_trait() == Some(def_id) { self.assemble_async_iterator_candidates(obligation, &mut candidates); } else if lang_items.async_fn_kind_helper() == Some(def_id) { @@ -302,14 +304,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>, ) { let self_ty = obligation.self_ty().skip_binder(); - if let ty::Coroutine(did, ..) = self_ty.kind() { - // gen constructs get lowered to a special kind of coroutine that - // should directly `impl Iterator`. - if self.tcx().coroutine_is_gen(*did) { - debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl Iterator`. + if let ty::Coroutine(did, ..) = self_ty.kind() + && self.tcx().coroutine_is_gen(*did) + { + debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); - candidates.vec.push(IteratorCandidate); - } + candidates.vec.push(IteratorCandidate); + } + } + + fn assemble_fused_iterator_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl FusedIterator`. + if let ty::Coroutine(did, ..) = self_ty.kind() + && self.tcx().coroutine_is_gen(*did) + { + debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",); + + candidates.vec.push(BuiltinCandidate { has_nested: false }); } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 51fc223a5d1b3..aeac2ad77d70f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -267,6 +267,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.copy_clone_conditions(obligation) } else if Some(trait_def) == lang_items.clone_trait() { self.copy_clone_conditions(obligation) + } else if Some(trait_def) == lang_items.fused_iterator_trait() { + self.fused_iterator_conditions(obligation) } else { bug!("unexpected builtin trait {:?}", trait_def) }; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 53aadfb8a44d8..adbc7d12a648d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2259,6 +2259,20 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } + fn fused_iterator_conditions( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + if let ty::Coroutine(did, ..) = *self_ty.kind() + && self.tcx().coroutine_is_gen(did) + { + BuiltinImplConditions::Where(ty::Binder::dummy(Vec::new())) + } else { + BuiltinImplConditions::None + } + } + /// For default impls, we need to break apart a type into its /// "constituent types" -- meaning, the types that it contains. /// diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index 8bdbca120d7f9..ad4d63d83b5be 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -28,6 +28,7 @@ pub unsafe trait TrustedFused {} #[rustc_unsafe_specialization_marker] // FIXME: this should be a #[marker] and have another blanket impl for T: TrustedFused // but that ICEs iter::Fuse specializations. +#[cfg_attr(not(bootstrap), lang = "fused_iterator")] pub trait FusedIterator: Iterator {} #[stable(feature = "fused", since = "1.26.0")] diff --git a/tests/ui/coroutine/gen_block_is_fused_iter.rs b/tests/ui/coroutine/gen_block_is_fused_iter.rs new file mode 100644 index 0000000000000..f3e19a7f54f03 --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_fused_iter.rs @@ -0,0 +1,21 @@ +//@ revisions: next old +//@compile-flags: --edition 2024 -Zunstable-options +//@[next] compile-flags: -Znext-solver +//@ check-pass +#![feature(gen_blocks)] + +use std::iter::FusedIterator; + +fn foo() -> impl FusedIterator { + gen { yield 42 } +} + +fn bar() -> impl FusedIterator { + gen { yield 42 } +} + +fn baz() -> impl FusedIterator + Iterator { + gen { yield 42 } +} + +fn main() {} From 238cd366712e3652f7cfb8029550c9336cb6a5e7 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 21 Mar 2024 18:22:23 +0100 Subject: [PATCH 092/102] Fix documentation typo "appects" > "affects" --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 58ac40eb9fb53..b6c474212cd4b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3826,7 +3826,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for usage of `.filter(Option::is_some)` that may be replaced with a `.flatten()` call. - /// This lint will require additional changes to the follow-up calls as it appects the type. + /// This lint will require additional changes to the follow-up calls as it affects the type. /// /// ### Why is this bad? /// This pattern is often followed by manual unwrapping of the `Option`. The simplification @@ -3852,7 +3852,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for usage of `.filter(Result::is_ok)` that may be replaced with a `.flatten()` call. - /// This lint will require additional changes to the follow-up calls as it appects the type. + /// This lint will require additional changes to the follow-up calls as it affects the type. /// /// ### Why is this bad? /// This pattern is often followed by manual unwrapping of `Result`. The simplification From daa65539ce2d1fe51bc92af4607d33c5916f70fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 21 Mar 2024 20:31:31 +0100 Subject: [PATCH 093/102] add test for #122549 Fixes #122549 --- .../ice-unexpected-inference-var-122549.rs | 29 ++++++++ ...ice-unexpected-inference-var-122549.stderr | 67 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 tests/ui/const-generics/ice-unexpected-inference-var-122549.rs create mode 100644 tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr diff --git a/tests/ui/const-generics/ice-unexpected-inference-var-122549.rs b/tests/ui/const-generics/ice-unexpected-inference-var-122549.rs new file mode 100644 index 0000000000000..126ea667290d3 --- /dev/null +++ b/tests/ui/const-generics/ice-unexpected-inference-var-122549.rs @@ -0,0 +1,29 @@ +// Regression test for https://github.com/rust-lang/rust/issues/122549 +// was fixed by https://github.com/rust-lang/rust/pull/122749 + +trait ConstChunksExactTrait { + fn const_chunks_exact(&self) -> ConstChunksExact<'a, T, { N }>; + //~^ ERROR undeclared lifetime +} + +impl ConstChunksExactTrait for [T] {} +//~^ ERROR not all trait items implemented, missing: `const_chunks_exact` +struct ConstChunksExact<'rem, T: 'a, const N: usize> {} +//~^ ERROR use of undeclared lifetime name `'a` +//~^^ ERROR lifetime parameter +//~^^^ ERROR type parameter +impl<'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { +//~^ ERROR the const parameter `N` is not constrained by the impl trait, self type, or predicates +//~^^ ERROR mismatched types + type Item = &'a [T; N]; +} + +fn main() { + let slice = &[1i32, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let mut iter = [[1, 2, 3], [4, 5, 6], [7, 8, 9]].iter(); + + for a in slice.const_chunks_exact::<3>() { + assert_eq!(a, iter.next().unwrap()); + } +} diff --git a/tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr b/tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr new file mode 100644 index 0000000000000..afad3388145c7 --- /dev/null +++ b/tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr @@ -0,0 +1,67 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ice-unexpected-inference-var-122549.rs:5:70 + | +LL | fn const_chunks_exact(&self) -> ConstChunksExact<'a, T, { N }>; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn const_chunks_exact<'a, const N: usize>(&self) -> ConstChunksExact<'a, T, { N }>; + | +++ +help: consider introducing lifetime `'a` here + | +LL | trait ConstChunksExactTrait<'a, T> { + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ice-unexpected-inference-var-122549.rs:11:34 + | +LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {} + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `'a,` + +error[E0046]: not all trait items implemented, missing: `const_chunks_exact` + --> $DIR/ice-unexpected-inference-var-122549.rs:9:1 + | +LL | fn const_chunks_exact(&self) -> ConstChunksExact<'a, T, { N }>; + | ------------------------------------------------------------------------------- `const_chunks_exact` from trait +... +LL | impl ConstChunksExactTrait for [T] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `const_chunks_exact` in implementation + +error[E0392]: lifetime parameter `'rem` is never used + --> $DIR/ice-unexpected-inference-var-122549.rs:11:25 + | +LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {} + | ^^^^ unused lifetime parameter + | + = help: consider removing `'rem`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0392]: type parameter `T` is never used + --> $DIR/ice-unexpected-inference-var-122549.rs:11:31 + | +LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {} + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/ice-unexpected-inference-var-122549.rs:15:13 + | +LL | impl<'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { + | ^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0308]: mismatched types + --> $DIR/ice-unexpected-inference-var-122549.rs:15:66 + | +LL | impl<'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { + | ^^ expected `usize`, found `()` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0046, E0207, E0261, E0308, E0392. +For more information about an error, try `rustc --explain E0046`. From 6f2c6efe019ef57585e1099e701c800417037e04 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 21 Mar 2024 19:47:46 +0000 Subject: [PATCH 094/102] Change syntax of the never type attribute thingy Previous: ```rust #![rustc_never_type_mode = "fallback_to_unit|..."] ``` New: ```rust #![rustc_never_type_options(fallback = "unit|...")] ``` This allows adding other options for other never-related experiments. --- compiler/rustc_feature/src/builtin_attrs.rs | 7 ++- compiler/rustc_hir_typeck/src/fallback.rs | 36 ++------------ compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 52 +++++++++++++++++++- compiler/rustc_span/src/symbol.rs | 8 ++- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d4d7833cb218a..22cf50fce7f49 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -597,9 +597,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( - rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|fallback_to_never|no_fallback"), ErrorFollowing, + rustc_never_type_options, + Normal, + template!(List: r#"/*opt*/ fallback = "unit|niko|never|no""#), + ErrorFollowing, EncodeCrossCrate::No, - "`rustc_never_type_fallback` is used to experiment with never type fallback and work on \ + "`rustc_never_type_options` is used to experiment with never type fallback and work on \ never type stabilization, and will never be stable" ), diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c16e941d4c5b7..140618e97cca1 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -4,12 +4,11 @@ use rustc_data_structures::{ graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; -use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -enum DivergingFallbackBehavior { +#[derive(Copy, Clone)] +pub enum DivergingFallbackBehavior { /// Always fallback to `()` (aka "always spontaneous decay") FallbackToUnit, /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken. @@ -78,9 +77,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { return false; } - let diverging_behavior = self.diverging_fallback_behavior(); - let diverging_fallback = - self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior); + let diverging_fallback = self + .calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior); // We do fallback in two passes, to try to generate // better error messages. @@ -94,32 +92,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fallback_occurred } - fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior { - let Some((mode, span)) = self - .tcx - .get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode) - .map(|attr| (attr.value_str().unwrap(), attr.span)) - else { - if self.tcx.features().never_type_fallback { - return DivergingFallbackBehavior::FallbackToNiko; - } - - return DivergingFallbackBehavior::FallbackToUnit; - }; - - match mode { - sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit, - sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko, - sym::fallback_to_never => DivergingFallbackBehavior::FallbackToNever, - sym::no_fallback => DivergingFallbackBehavior::NoFallback, - _ => { - self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, `fallback_to_never` and `no_fallback`)")); - - DivergingFallbackBehavior::FallbackToUnit - } - } - } - fn fallback_effects(&self) -> bool { let unsolved_effects = self.unsolved_effects(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 685b1af931e34..fa948451d70ae 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -5,7 +5,9 @@ mod checks; mod suggestions; use crate::coercion::DynamicCoerceMany; +use crate::fallback::DivergingFallbackBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited}; +use hir::def_id::CRATE_DEF_ID; use rustc_errors::{DiagCtxt, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -18,7 +20,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span, DUMMY_SP}; +use rustc_span::{self, sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; @@ -108,6 +110,8 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) inh: &'a Inherited<'tcx>, pub(super) fallback_has_occurred: Cell, + + pub(super) diverging_fallback_behavior: DivergingFallbackBehavior, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -116,6 +120,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { + let diverging_fallback_behavior = parse_never_type_options_attr(inh.tcx); FnCtxt { body_id, param_env, @@ -131,6 +136,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), inh, fallback_has_occurred: Cell::new(false), + diverging_fallback_behavior, } } @@ -374,3 +380,47 @@ impl<'tcx> LoweredTy<'tcx> { LoweredTy { raw, normalized } } } + +fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { + use DivergingFallbackBehavior::*; + + // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. + // Just don't write incorrect attributes <3 + + let mut fallback = None; + + let items = tcx + .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) + .map(|attr| attr.meta_item_list().unwrap()) + .unwrap_or_default(); + + for item in items { + if item.has_name(sym::fallback) && fallback.is_none() { + let mode = item.value_str().unwrap(); + match mode { + sym::unit => fallback = Some(FallbackToUnit), + sym::niko => fallback = Some(FallbackToNiko), + sym::never => fallback = Some(FallbackToNever), + sym::no => fallback = Some(NoFallback), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)")); + } + }; + continue; + } + + tcx.dcx().span_err( + item.span(), + format!( + "unknown never type option: `{}` (supported: `fallback`)", + item.name_or_empty() + ), + ); + } + + let fallback = fallback.unwrap_or_else(|| { + if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } + }); + + fallback +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b911a41a112f..77170406c0602 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -814,9 +814,7 @@ symbols! { fadd_algebraic, fadd_fast, fake_variadic, - fallback_to_never, - fallback_to_niko, - fallback_to_unit, + fallback, fdiv_algebraic, fdiv_fast, feature, @@ -1227,6 +1225,7 @@ symbols! { new_v1, new_v1_formatted, next, + niko, nll, no, no_builtins, @@ -1235,7 +1234,6 @@ symbols! { no_crate_inject, no_debug, no_default_passes, - no_fallback, no_implicit_prelude, no_inline, no_link, @@ -1553,7 +1551,7 @@ symbols! { rustc_mir, rustc_must_implement_one_of, rustc_never_returns_null_ptr, - rustc_never_type_mode, + rustc_never_type_options, rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, From 35a78459aac12b079750ebc3997136311fc7ece8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 21 Mar 2024 20:57:54 +0100 Subject: [PATCH 095/102] add test for https://github.com/rust-lang/rust/issues/122098 ICE: index out of bounds, snapshot_vec.rs Fixes #122098 --- ...-ifer-var-leaked-out-of-rollback-122098.rs | 25 +++++++ ...r-var-leaked-out-of-rollback-122098.stderr | 72 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.rs create mode 100644 tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.stderr diff --git a/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.rs b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.rs new file mode 100644 index 0000000000000..3c2aa176c0fa3 --- /dev/null +++ b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.rs @@ -0,0 +1,25 @@ +// test for #122098 ICE snapshot_vec.rs: index out of bounds: the len is 4 but the index is 4 + +trait LendingIterator { + type Item<'q>: 'a; + //~^ ERROR use of undeclared lifetime name `'a` + + fn for_each(mut self, mut f: Box) + 'static>) {} + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +struct Query<'q> {} +//~^ ERROR lifetime parameter `'q` is never used + +impl<'static> Query<'q> { +//~^ ERROR invalid lifetime parameter name: `'static` +//~^^ ERROR use of undeclared lifetime name `'q` + pub fn new() -> Self {} +} + +fn data() { + LendingIterator::for_each(Query::new(&data), Box::new); + //~^ ERROR this function takes 0 arguments but 1 argument was supplied +} + +pub fn main() {} diff --git a/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.stderr b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.stderr new file mode 100644 index 0000000000000..e2ddf474c4a97 --- /dev/null +++ b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.stderr @@ -0,0 +1,72 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:4:20 + | +LL | type Item<'q>: 'a; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | type Item<'a, 'q>: 'a; + | +++ +help: consider introducing lifetime `'a` here + | +LL | trait LendingIterator<'a> { + | ++++ + +error[E0262]: invalid lifetime parameter name: `'static` + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:14:6 + | +LL | impl<'static> Query<'q> { + | ^^^^^^^ 'static is a reserved lifetime name + +error[E0261]: use of undeclared lifetime name `'q` + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:14:21 + | +LL | impl<'static> Query<'q> { + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'q` here: `'q,` + +error[E0392]: lifetime parameter `'q` is never used + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:11:14 + | +LL | struct Query<'q> {} + | ^^ unused lifetime parameter + | + = help: consider removing `'q`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:7:17 + | +LL | fn for_each(mut self, mut f: Box) + 'static>) {} + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider further restricting `Self` + | +LL | fn for_each(mut self, mut f: Box) + 'static>) where Self: Sized {} + | +++++++++++++++++ +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn for_each(mut &self, mut f: Box) + 'static>) {} + | + + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:21:31 + | +LL | LendingIterator::for_each(Query::new(&data), Box::new); + | ^^^^^^^^^^ ----- + | | + | unexpected argument of type `&fn() {data}` + | help: remove the extra argument + | +note: associated function defined here + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:17:12 + | +LL | pub fn new() -> Self {} + | ^^^ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0061, E0261, E0262, E0277, E0392. +For more information about an error, try `rustc --explain E0061`. From c00920c5daf8c9b6422639c947b5f6ce28241201 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 21 Mar 2024 12:57:39 -0700 Subject: [PATCH 096/102] Avoid noop rewrite of issues.txt This can trigger incremental rebuilds since incr doesn't realize nothing changed. --- src/tools/tidy/src/ui_tests.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 22927cffd1b9b..fa24447f69992 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -183,9 +183,10 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { } }); - // if an excluded file is renamed, it must be removed from this list + // if there are any file names remaining, they were moved on the fs. + // our data must remain up to date, so it must be removed from issues.txt // do this automatically on bless, otherwise issue a tidy error - if bless { + if bless && !remaining_issue_names.is_empty() { let issues_txt_header = r#" /* ============================================================ From bdafec33d542262e50d4a899d33c8284a3ae8888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 21 Mar 2024 21:27:37 +0100 Subject: [PATCH 097/102] add test for #121807 Fixes #121807 --- .../ice-mutability-error-slicing-121807.rs | 27 ++++++++++ ...ice-mutability-error-slicing-121807.stderr | 53 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/ui/borrowck/ice-mutability-error-slicing-121807.rs create mode 100644 tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr diff --git a/tests/ui/borrowck/ice-mutability-error-slicing-121807.rs b/tests/ui/borrowck/ice-mutability-error-slicing-121807.rs new file mode 100644 index 0000000000000..bbdd895d763a5 --- /dev/null +++ b/tests/ui/borrowck/ice-mutability-error-slicing-121807.rs @@ -0,0 +1,27 @@ +//@ edition:2015 +// test for ICE #121807 begin <= end (12 <= 11) when slicing 'Self::Assoc<'_>' +// fixed by #122749 + +trait MemoryUnit { // ERROR: not all trait items implemented, missing: `read_word` + extern "C" fn read_word(&mut self) -> u8; + extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + //~^ WARN anonymous parameters are deprecated and will be removed in the next edition + //~^^ WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + //~^^^ ERROR associated type `Assoc` not found for `Self` +} + +struct ROM {} + +impl MemoryUnit for ROM { +//~^ ERROR not all trait items implemented, missing: `read_word` + extern "C" fn read_dword(&'s self) -> u16 { + //~^ ERROR use of undeclared lifetime name `'s` + //~^^ ERROR method `read_dword` has a `&self` declaration in the impl, but not in the trait + let a16 = self.read_word() as u16; + let b16 = self.read_word() as u16; + + (b16 << 8) | a16 + } +} + +pub fn main() {} diff --git a/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr b/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr new file mode 100644 index 0000000000000..3a6b8008fceb3 --- /dev/null +++ b/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr @@ -0,0 +1,53 @@ +error[E0261]: use of undeclared lifetime name `'s` + --> $DIR/ice-mutability-error-slicing-121807.rs:17:31 + | +LL | extern "C" fn read_dword(&'s self) -> u16 { + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'s` here + | +LL | extern "C" fn read_dword<'s>(&'s self) -> u16 { + | ++++ +help: consider introducing lifetime `'s` here + | +LL | impl<'s> MemoryUnit for ROM { + | ++++ + +warning: anonymous parameters are deprecated and will be removed in the next edition + --> $DIR/ice-mutability-error-slicing-121807.rs:7:30 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^^^^^^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: Self::Assoc<'_>` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #41686 + = note: `#[warn(anonymous_parameters)]` on by default + +error[E0220]: associated type `Assoc` not found for `Self` + --> $DIR/ice-mutability-error-slicing-121807.rs:7:36 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^ associated type `Assoc` not found + +error[E0185]: method `read_dword` has a `&self` declaration in the impl, but not in the trait + --> $DIR/ice-mutability-error-slicing-121807.rs:17:5 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ------------------------------------------------- trait method declared without `&self` +... +LL | extern "C" fn read_dword(&'s self) -> u16 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&self` used in impl + +error[E0046]: not all trait items implemented, missing: `read_word` + --> $DIR/ice-mutability-error-slicing-121807.rs:15:1 + | +LL | extern "C" fn read_word(&mut self) -> u8; + | ----------------------------------------- `read_word` from trait +... +LL | impl MemoryUnit for ROM { + | ^^^^^^^^^^^^^^^^^^^^^^^ missing `read_word` in implementation + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0046, E0185, E0220, E0261. +For more information about an error, try `rustc --explain E0046`. From 847311e36f9e194a2b06b583df61bf6770038b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 21 Mar 2024 22:02:32 +0100 Subject: [PATCH 098/102] add test for ice "cannot relate region: LUB(ReErased, ReError)" Fixes #109178 --- .../ice-cannot-relate-region-109178.rs | 14 +++++++++++++ .../ice-cannot-relate-region-109178.stderr | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/ui/inference/ice-cannot-relate-region-109178.rs create mode 100644 tests/ui/inference/ice-cannot-relate-region-109178.stderr diff --git a/tests/ui/inference/ice-cannot-relate-region-109178.rs b/tests/ui/inference/ice-cannot-relate-region-109178.rs new file mode 100644 index 0000000000000..3282f95a992ab --- /dev/null +++ b/tests/ui/inference/ice-cannot-relate-region-109178.rs @@ -0,0 +1,14 @@ +// test for ice #109178 cannot relate region: LUB(ReErased, ReError) + +#![allow(incomplete_features)] +#![crate_type = "lib"] +#![feature(adt_const_params, generic_const_exprs)] + +struct Changes +//~^ ERROR `&` without an explicit lifetime name cannot be used here +where + [(); CHANGES.len()]:, {} + +impl Changes where [(); CHANGES.len()]: {} +//~^ ERROR `&` without an explicit lifetime name cannot be used here +//~^^ ERROR `&` without an explicit lifetime name cannot be used here diff --git a/tests/ui/inference/ice-cannot-relate-region-109178.stderr b/tests/ui/inference/ice-cannot-relate-region-109178.stderr new file mode 100644 index 0000000000000..0ac924452c072 --- /dev/null +++ b/tests/ui/inference/ice-cannot-relate-region-109178.stderr @@ -0,0 +1,21 @@ +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/ice-cannot-relate-region-109178.rs:7:31 + | +LL | struct Changes + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/ice-cannot-relate-region-109178.rs:12:21 + | +LL | impl Changes where [(); CHANGES.len()]: {} + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/ice-cannot-relate-region-109178.rs:12:23 + | +LL | impl Changes where [(); CHANGES.len()]: {} + | ^ explicit lifetime name needed here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0637`. From bb8665476972bb5adb9d675efd31c37441975770 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 21 Mar 2024 22:05:51 +0100 Subject: [PATCH 099/102] Bump nightly version -> 2024-03-21 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 070b62887d5de..a63e66f3214c9 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-03-07" +channel = "nightly-2024-03-21" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From e1d15b5b877064cd703040f1a41ce2ece3d63749 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 21 Mar 2024 22:06:41 +0100 Subject: [PATCH 100/102] Bump Clippy version -> 0.1.79 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 502708e6d4bdb..2b37b54c0048b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.78" +version = "0.1.79" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 2edc5ed592cb0..8ba2ab5662569 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.78" +version = "0.1.79" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 6ae089b3e0321..1d954607eee8c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.78" +version = "0.1.79" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index bf55040ddbc11..d2bb719a517f5 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.78" +version = "0.1.79" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 296eb8dd34008..9a3a41e1d1ead 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.78" +version = "0.1.79" edition = "2021" publish = false From 8e53d5335630023d862f98e18f169a51d1c49bf1 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 21 Mar 2024 22:20:59 +0100 Subject: [PATCH 101/102] Update Cargo.lock --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b9eb88ec2b39..e97d29d57ee3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,7 +574,7 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clippy" -version = "0.1.78" +version = "0.1.79" dependencies = [ "anstream", "clippy_config", @@ -602,7 +602,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.78" +version = "0.1.79" dependencies = [ "rustc-semver", "serde", @@ -625,7 +625,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.78" +version = "0.1.79" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -650,7 +650,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.78" +version = "0.1.79" dependencies = [ "arrayvec", "clippy_config", @@ -971,7 +971,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.78" +version = "0.1.79" dependencies = [ "itertools 0.12.1", "quote", @@ -2205,7 +2205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.48.5", ] [[package]] From 93297bfb052a8dd72123547072872915f4d60675 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 21 Mar 2024 20:53:22 +0000 Subject: [PATCH 102/102] Add a never type option to make diverging blocks `()` ```rust #![allow(internal_features)] #![feature(never_type, rustc_attrs)] #![rustc_never_type_options(diverging_block_default = "unit")] fn main() { let _: u8 = { //~ error: expected `u8`, found `()` return; }; } ``` --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 27 ++++++++++++++++++- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 27 ++++++++++++++++--- compiler/rustc_span/src/symbol.rs | 1 + 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 5c56c6acd2791..19f839b6240c6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -45,6 +45,29 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; +#[derive(Clone, Copy, Default)] +pub enum DivergingBlockBehavior { + /// This is the current stable behavior: + /// + /// ```rust + /// { + /// return; + /// } // block has type = !, even though we are supposedly dropping it with `;` + /// ``` + #[default] + Never, + + /// Alternative behavior: + /// + /// ```ignore (very-unstable-new-attribute) + /// #![rustc_never_type_options(diverging_block_default = "unit")] + /// { + /// return; + /// } // block has type = (), since we are dropping `!` from `return` with `;` + /// ``` + Unit, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { // don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors @@ -1710,7 +1733,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // #41425 -- label the implicit `()` as being the // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { + if !self.diverges.get().is_always() + || matches!(self.diverging_block_behavior, DivergingBlockBehavior::Unit) + { // #50009 -- Do not point at the entire fn block span, point at the return type // span, as it is the cause of the requirement, and // `consider_hint_about_removing_semicolon` will point at the last expression diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index fa948451d70ae..1850463194dc8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -6,6 +6,7 @@ mod suggestions; use crate::coercion::DynamicCoerceMany; use crate::fallback::DivergingFallbackBehavior; +use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited}; use hir::def_id::CRATE_DEF_ID; use rustc_errors::{DiagCtxt, ErrorGuaranteed}; @@ -112,6 +113,7 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) fallback_has_occurred: Cell, pub(super) diverging_fallback_behavior: DivergingFallbackBehavior, + pub(super) diverging_block_behavior: DivergingBlockBehavior, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -120,7 +122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { - let diverging_fallback_behavior = parse_never_type_options_attr(inh.tcx); + let (diverging_fallback_behavior, diverging_block_behavior) = + parse_never_type_options_attr(inh.tcx); FnCtxt { body_id, param_env, @@ -137,6 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inh, fallback_has_occurred: Cell::new(false), diverging_fallback_behavior, + diverging_block_behavior, } } @@ -381,13 +385,16 @@ impl<'tcx> LoweredTy<'tcx> { } } -fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { +fn parse_never_type_options_attr( + tcx: TyCtxt<'_>, +) -> (DivergingFallbackBehavior, DivergingBlockBehavior) { use DivergingFallbackBehavior::*; // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. // Just don't write incorrect attributes <3 let mut fallback = None; + let mut block = None; let items = tcx .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) @@ -409,6 +416,18 @@ fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { continue; } + if item.has_name(sym::diverging_block_default) && fallback.is_none() { + let mode = item.value_str().unwrap(); + match mode { + sym::unit => block = Some(DivergingBlockBehavior::Unit), + sym::never => block = Some(DivergingBlockBehavior::Never), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)")); + } + }; + continue; + } + tcx.dcx().span_err( item.span(), format!( @@ -422,5 +441,7 @@ fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } }); - fallback + let block = block.unwrap_or_default(); + + (fallback, block) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 77170406c0602..4fa8ba2aa050e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -689,6 +689,7 @@ symbols! { dispatch_from_dyn, div, div_assign, + diverging_block_default, do_not_recommend, doc, doc_alias,